diff --git a/.github/workflows/build-mbslave-container-image.yml b/.github/workflows/build-mbslave-container-image.yml index 21347ed..b5e1f44 100644 --- a/.github/workflows/build-mbslave-container-image.yml +++ b/.github/workflows/build-mbslave-container-image.yml @@ -4,15 +4,17 @@ on: push: branches: - main + - dev tags: - v* pull_request: branches: - main + - dev env: REGISTRY: ghcr.io - IMAGE_NAME: acoustid/mbslave + IMAGE_NAME: ${{ github.repository_owner }}/mbslave jobs: @@ -23,27 +25,38 @@ jobs: packages: write steps: - - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=sha - name: Log in to the Container registry if: github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: file: Dockerfile.mbslave context: . push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ee1325..258e77a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,29 +7,54 @@ on: jobs: test: runs-on: ubuntu-latest + container: python:${{ matrix.python_version }} strategy: matrix: python_version: [ '3.8', '3.9', '3.10' ] + services: + postgresql: + image: postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: notapassword + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - - uses: actions/checkout@v2 - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python_version }} + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + apt-get update + apt-get install -y postgresql-client libpq-dev sudo - name: Install Poetry uses: snok/install-poetry@v1 + with: + version: 1.8.5 + virtualenvs-create: true + virtualenvs-in-project: true - name: Install application dependencies run: poetry install - name: Run tests - run: ./check.sh + run: | + poetry run mbslave init --create-user --create-database --empty + ./check.sh + env: + MBSLAVE_DB_HOST: postgresql + MBSLAVE_DB_PORT: 5432 + MBSLAVE_DB_DB: musicbrainz + MBSLAVE_DB_USER: musicbrainz + MBSLAVE_DB_PASSWORD: reallynotapassword + MBSLAVE_DB_ADMIN_USER: postgres + MBSLAVE_DB_ADMIN_PASSWORD: notapassword mbslave_initdb_test: runs-on: ubuntu-latest + container: python:3.10 services: postgresql: image: postgres - ports: - - 5432/tcp env: POSTGRES_USER: postgres POSTGRES_PASSWORD: notapassword @@ -40,21 +65,25 @@ jobs: --health-retries 5 steps: - name: Checkout source code - uses: actions/checkout@v3 - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' + uses: actions/checkout@v4 + - name: Install dependencies + run: | + apt-get update + apt-get install -y postgresql-client libpq-dev sudo - name: Install Poetry uses: snok/install-poetry@v1 + with: + version: 1.8.5 + virtualenvs-create: true + virtualenvs-in-project: true - name: Install application dependencies run: poetry install - name: Run `mbslave init` run: poetry run mbslave init --create-user --create-database --empty env: - MBSLAVE_DB_HOST: 127.0.0.1 - MBSLAVE_DB_PORT: ${{ job.services.postgresql.ports['5432'] }} - MBSLAVE_DB_NAME: musicbrainz + MBSLAVE_DB_HOST: postgresql + MBSLAVE_DB_PORT: 5432 + MBSLAVE_DB_DB: musicbrainz MBSLAVE_DB_USER: musicbrainz MBSLAVE_DB_PASSWORD: reallynotapassword MBSLAVE_DB_ADMIN_USER: postgres diff --git a/.gitignore b/.gitignore index bb6f2ac..7ee748c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ mbdata.egg-info/ venv/ result +/.idea/ +/.bin/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 088e3ea..c4c4939 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,12 @@ +Version 30.0.0 +============== + +- Massive optimization of initial import by temporarily disabling triggers and constraints (MBS-13492). +- Updated to MusicBrainz schema 30. +- Added progress bars (`tqdm`) during data import. +- Improved Docker environment with `docker-compose.dev.yml` and a more robust entrypoint script. +- Updated dependencies. + Version 29.1.0 ============== diff --git a/DEVELOPMENT.rst b/DEVELOPMENT.rst index 8b0da9b..69509b5 100644 --- a/DEVELOPMENT.rst +++ b/DEVELOPMENT.rst @@ -5,15 +5,50 @@ Development Guide Development Setup ================= -Clone the repository and setup virtualenv:: +Clone the repository and install the project using `poetry `__. +If you don't have poetry installed, you can install it using ``pipx install poetry`` or ``pip install poetry``. + +:: git clone https://github.com/acoustid/mbslave.git cd mbslave/ - virtualenv -p python3 venv - source venv/bin/activate - pip install poetry poetry install +Poetry will automatically create a virtual environment for you. You can then run commands +within this environment using ``poetry run mbslave `` or by activating the shell with ``poetry shell``. +For example, to see available commands: + +:: + + poetry run mbslave --help + +Development Database (PostgreSQL) +================================= + +For development and testing, you can use the provided ``Dockerfile.postgres`` to start a pre-initialized PostgreSQL database with the MusicBrainz schema. + +Using Docker Compose: + +:: + + docker compose -f docker-compose.dev.yml up -d + +This will start a PostgreSQL container on port 5432 with: +* Database: ``musicbrainz`` +* User: ``musicbrainz`` +* Password: ``musicbrainz`` + +The database will be automatically initialized with the schema when the container starts. + +Running Tests +============= + +You can run the tests using ``pytest``. Some tests require a running development database (see above). + +:: + + poetry run pytest + Updating SQL files and models ============================= @@ -37,3 +72,5 @@ Release a new version rm -rf dist/ python setup.py sdist twine upload dist/mbslave-*.tar.gz + +.. diff --git a/Dockerfile.mbslave b/Dockerfile.mbslave index 94d6cf4..90bdfb8 100644 --- a/Dockerfile.mbslave +++ b/Dockerfile.mbslave @@ -1,7 +1,7 @@ FROM python:3.10 AS build RUN python3 -m venv /opt/poetry && \ - /opt/poetry/bin/pip install poetry && \ + /opt/poetry/bin/pip install poetry==1.8.5 && \ ln -s /opt/poetry/bin/poetry /usr/local/bin/ && \ poetry --version @@ -18,5 +18,7 @@ RUN apt-get update && \ COPY --from=build /tmp/mbdata/dist/ /tmp/dist/ RUN python -m pip install "$(ls -1 /tmp/dist/*.whl)[replication,models]" && rm -rf /tmp/dist/ -ENTRYPOINT ["/usr/bin/dumb-init", "--"] -CMD ["mbslave", "sync", "-r"] +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["/usr/bin/dumb-init", "--", "docker-entrypoint.sh"] diff --git a/Dockerfile.postgres b/Dockerfile.postgres new file mode 100644 index 0000000..ba1bb7c --- /dev/null +++ b/Dockerfile.postgres @@ -0,0 +1,49 @@ +FROM postgres:16 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 \ + python3-pip \ + python3-venv \ + postgresql-server-dev-16 \ + gcc \ + python3-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN python3 -m venv /opt/mbslave-venv +ENV PATH="/opt/mbslave-venv/bin:$PATH" + +WORKDIR /usr/src/app +COPY . . + +# Install mbslave and its dependencies +RUN pip install . + +ENV POSTGRES_DB=musicbrainz +ENV POSTGRES_USER=musicbrainz +ENV POSTGRES_PASSWORD=musicbrainz + +# Create a default config file for mbslave +RUN cat < /etc/mbslave.conf +[database] +host=/var/run/postgresql +port=5432 +name=musicbrainz +user=musicbrainz +password=musicbrainz +admin_user=musicbrainz +EOF + +# Add initialization script +COPY <<-'INIT_DB' /docker-entrypoint-initdb.d/01-init-mbslave.sh +#!/bin/bash +set -e + +# mbslave init expects to be able to connect to the database +# When scripts in /docker-entrypoint-initdb.d/ are run, the server is running +# and accepting connections. +# We use PGUSER=postgres to connect as the superuser that exists during init. + +PGUSER=postgres MBSLAVE_CONFIG=/etc/mbslave.conf mbslave init --empty +INIT_DB + +RUN chmod +x /docker-entrypoint-initdb.d/01-init-mbslave.sh diff --git a/README.rst b/README.rst index 9e4dce3..a89bc2a 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,6 @@ Installation You need to have `Python 3.x `__ installed on your system. You can use `pipx `__ to install this package:: - sudo apt install python3 pipx pipx install 'mbslave' There are two ways to configure the application. @@ -174,3 +173,5 @@ data import. You can temporarily map all schemas to e.g. "musicbrainz_NEW", impo database there and then rename it:: echo 'BEGIN; ALTER SCHEMA musicbrainz RENAME TO musicbrainz_OLD; ALTER SCHEMA musicbrainz_NEW RENAME TO musicbrainz; COMMIT;' | mbslave psql -S + +.. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..3b59a7f --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,33 @@ +services: + db: + build: + context: . + dockerfile: Dockerfile.postgres + environment: + - POSTGRES_PASSWORD=musicbrainz + ports: + - "5432:5432" + volumes: + - db_data:/var/lib/postgresql/data + shm_size: 8gb + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U musicbrainz -d musicbrainz" ] + interval: 5s + timeout: 5s + retries: 5 + + mbslave: + build: + context: . + dockerfile: Dockerfile.mbslave + environment: + - KEEP_ALIVE=true + volumes: + - mbslave_config:/etc/mbslave + depends_on: + db: + condition: service_healthy + +volumes: + db_data: + mbslave_config: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..5dd958a --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +if [ "$KEEP_ALIVE" = "1" ] || [ "$KEEP_ALIVE" = "true" ] || [ $# -eq 0 ]; then + echo "KEEP_ALIVE is set or no command provided, staying up..." + # Keep the container running + tail -f /dev/null +else + exec "$@" +fi 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" diff --git a/mbslave/replication.py b/mbslave/replication.py index e33d68e..da8f1bc 100644 --- a/mbslave/replication.py +++ b/mbslave/replication.py @@ -8,7 +8,7 @@ import argparse import logging from io import BytesIO -from urllib.request import urlopen +from urllib.request import urlopen, Request from urllib.parse import urljoin from urllib.error import HTTPError import tempfile @@ -19,10 +19,22 @@ from contextlib import ExitStack import prometheus_client +from tqdm import tqdm logger = logging.getLogger(__name__) +class TqdmFileReader(object): + def __init__(self, fileobj, pbar): + self.fileobj = fileobj + self.pbar = pbar + + def read(self, size=-1): + data = self.fileobj.read(size) + self.pbar.update(len(data)) + return data + + CURRENT_SCHEMA_SEQUENCE = prometheus_client.Gauge('mbslave_current_schema_sequence', 'Current schema sequence number') CURRENT_REPLICATION_REQUENCE = prometheus_client.Gauge('mbslave_current_replication_sequence', 'Current replication sequence number') PROCESSED_PACKETS = prometheus_client.Counter('mbslave_processed_packets_total', 'Total number of processed packets') @@ -38,7 +50,7 @@ def read_env_item(obj, key, name, convert=None): value = os.environ[name] if name + '_FILE' in os.environ: value = open(os.environ[name + '_FILE']).read().strip() - if value is not None: + if value: if convert is not None: value = convert(value) setattr(obj, key, value) @@ -238,44 +250,119 @@ def check_table_exists(db, schema, table): return True +def download_url(url: str, dest_path: str) -> None: + headers = {} + file_mode = 'wb' + resume_size = 0 + if os.path.exists(dest_path): + resume_size = os.path.getsize(dest_path) + headers['Range'] = 'bytes=%d-' % resume_size + file_mode = 'ab' + + request = Request(url, headers=headers) + try: + response = urlopen(request) + except HTTPError as e: + if e.code == 416: # Range Not Satisfiable + # Check if the file is already complete + with urlopen(url) as r: + total_size = int(r.info().get('Content-Length', 0)) + if resume_size >= total_size: + logger.info("File %s already downloaded", dest_path) + return + raise + raise + + content_length = response.info().get('Content-Length') + total_size = int(content_length) if content_length else None + + if response.getcode() == 206: + if total_size is not None: + total_size += resume_size + else: + file_mode = 'wb' + resume_size = 0 + + with open(dest_path, file_mode) as f: + with tqdm(total=total_size, unit='B', unit_scale=True, desc=os.path.basename(url), initial=resume_size, leave=False) as pbar: + while True: + chunk = response.read(1024 * 1024) + if not chunk: + break + f.write(chunk) + pbar.update(len(chunk)) + + def load_tar(source: str, fileobj: BytesIO, db, config, ignored_schemas, ignored_tables): logger.info("Importing data from %s", source) tar = tarfile.open(fileobj=fileobj, mode='r|*') cursor = db.cursor() - for member in tar: - if not member.name.startswith('mbdump/'): - continue - name = member.name.split('/')[1].replace('_sanitised', '') - schema, table = parse_name(config, name) - fulltable = fqn(schema, table) - if schema in ignored_schemas: - logger.info("Ignoring %s", name) - continue - if table in ignored_tables: - logger.info("Ignoring %s", name) - continue - if not check_table_exists(db, schema, table): - logger.info("Skipping %s (table %s does not exist)", name, fulltable) - continue - cursor.execute("SELECT 1 FROM %s LIMIT 1" % fulltable) - if cursor.fetchone(): - logger.info("Skipping %s (table %s already contains data)", name, fulltable) - continue - logger.info("Loading %s to %s", name, fulltable) - cursor.copy_expert('COPY {} FROM STDIN'.format(fulltable), tar.extractfile(member)) + try: + cursor.execute("SET session_replication_role = 'replica'") + except psycopg2.Error: + db.rollback() + logger.warning("Could not set session_replication_role to 'replica', import might be slow") + + try: + for member in tar: + if not member.name.startswith('mbdump/'): + continue + name = member.name.split('/')[1].replace('_sanitised', '') + schema, table = parse_name(config, name) + fulltable = fqn(schema, table) + if schema in ignored_schemas: + logger.info("Ignoring %s", name) + continue + if table in ignored_tables: + logger.info("Ignoring %s", name) + continue + if not check_table_exists(db, schema, table): + logger.info("Skipping %s (table %s does not exist)", name, fulltable) + continue + cursor.execute("SELECT 1 FROM %s LIMIT 1" % fulltable) + if cursor.fetchone(): + logger.info("Skipping %s (table %s already contains data)", name, fulltable) + continue + try: + cursor.execute("ALTER TABLE %s DISABLE TRIGGER ALL" % fulltable) + except psycopg2.Error: + db.rollback() + logger.info("Loading %s to %s", name, fulltable) + with tqdm(total=member.size, unit='B', unit_scale=True, desc=name, leave=False) as pbar: + fileobj = tar.extractfile(member) + if fileobj is None: + continue + cursor.copy_expert('COPY {} FROM STDIN'.format(fulltable), TqdmFileReader(fileobj, pbar)) + try: + cursor.execute("ALTER TABLE %s ENABLE TRIGGER ALL" % fulltable) + except psycopg2.Error: + db.rollback() + db.commit() + finally: + try: + cursor.execute("SET session_replication_role = 'origin'") + except psycopg2.Error: + db.rollback() db.commit() def mbslave_import_main(config, args): db = connect_db(config, superuser=True, set_search_path=True) + work_dir = args.work_dir or os.getcwd() + if not os.path.exists(work_dir): + os.makedirs(work_dir) + for source in args.sources: - with ExitStack() as exit_stack: - if source.startswith('http://') or source.startswith('https://'): - fileobj = exit_stack.enter_context(urlopen(source)) - else: - fileobj = exit_stack.enter_context(open(source, 'rb')) - load_tar(source, fileobj, db, config, config.schemas.ignored_schemas, config.tables.ignored_tables) + if source.startswith('http://') or source.startswith('https://'): + filename = source.split('/')[-1] + dest_path = os.path.join(work_dir, filename) + download_url(source, dest_path) + with open(dest_path, 'rb') as fileobj: + load_tar(source, fileobj, db, config, config.schemas.ignored_schemas, config.tables.ignored_tables) + else: + with open(source, 'rb') as fileobj: + load_tar(source, fileobj, db, config, config.schemas.ignored_schemas, config.tables.ignored_tables) def mbslave_auto_import_main(config: Config, args) -> None: @@ -289,7 +376,11 @@ def mbslave_auto_import_main(config: Config, args) -> None: logger.info("Latest dump is %s", latest) - db = connect_db(config) + db = connect_db(config, superuser=True) + + work_dir = args.work_dir or os.getcwd() + if not os.path.exists(work_dir): + os.makedirs(work_dir) files = [ 'mbdump.tar.bz2', @@ -300,7 +391,9 @@ def mbslave_auto_import_main(config: Config, args) -> None: ] for file in files: url = urljoin(base_url, latest + '/' + file) - with urlopen(url) as fileobj: + dest_path = os.path.join(work_dir, file) + download_url(url, dest_path) + with open(dest_path, 'rb') as fileobj: load_tar(url, fileobj, db, config, config.schemas.ignored_schemas, config.tables.ignored_tables) @@ -573,15 +666,19 @@ def create_user(config: Config) -> None: db = connect_db(config, superuser=True, no_db=True) db.autocommit = True cursor = db.cursor() - cursor.execute(f"CREATE USER {config.database.user} PASSWORD %s", (config.database.password,)) + cursor.execute("SELECT 1 FROM pg_roles WHERE rolname=%s", (config.database.user,)) + if not cursor.fetchone(): + cursor.execute(f"CREATE USER {config.database.user} PASSWORD %s", (config.database.password,)) def create_database(config: Config) -> None: db = connect_db(config, superuser=True, no_db=True) db.autocommit = True cursor = db.cursor() - cursor.execute(f"CREATE DATABASE {config.database.name} WITH OWNER {config.database.user}") - cursor.execute(f"ALTER DATABASE {config.database.name} SET timezone TO 'UTC'") + cursor.execute("SELECT 1 FROM pg_database WHERE datname=%s", (config.database.name,)) + if not cursor.fetchone(): + cursor.execute(f"CREATE DATABASE {config.database.name} WITH OWNER {config.database.user}") + cursor.execute(f"ALTER DATABASE {config.database.name} SET timezone TO 'UTC'") def create_schemas(config: Config) -> None: @@ -622,6 +719,9 @@ def mbslave_init_main(config: Config, args: argparse.Namespace) -> None: if args.create_database: create_database(config) + if args.only_db: + return + create_schemas(config) # https://github.com/metabrainz/musicbrainz-server/blob/master/admin/InitDb.pl#L254 @@ -753,11 +853,13 @@ def main(): parser_init = subparsers.add_parser('init') parser_init.add_argument('--create-user', action='store_true') parser_init.add_argument('--create-database', action='store_true') + parser_init.add_argument('--only-db', action='store_true') parser_init.add_argument('--empty', action='store_true') - parser_init.set_defaults(func=mbslave_init_main, create_user=False, create_database=False, empty=False) + parser_init.set_defaults(func=mbslave_init_main, create_user=False, create_database=False, only_db=False, empty=False) parser_import = subparsers.add_parser('import') parser_import.add_argument('sources', metavar='FILE_OR_URL', nargs='+', help='tar.bz2 file to import') + parser_import.add_argument('--work-dir', metavar='DIR', help='directory to store downloaded files') parser_import.set_defaults(func=mbslave_import_main) parser_auto_import = subparsers.add_parser('auto-import') @@ -768,6 +870,7 @@ def main(): help='URL of the data dump mirror', default='http://ftp.musicbrainz.org/pub/musicbrainz/data/fullexport/', ) + parser_auto_import.add_argument('--work-dir', metavar='DIR', help='directory to store downloaded files') parser_auto_import.set_defaults(func=mbslave_auto_import_main) parser_sync = subparsers.add_parser('sync') 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..3a56b05 --- /dev/null +++ b/mbslave/sql/updates/20250425-mbs-13966.sql @@ -0,0 +1,69 @@ +\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'; + +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 + 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 tmp_release_first_release_date_2025_q2 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 + ); + +-- 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 new file mode 100644 index 0000000..c9d8553 --- /dev/null +++ b/mbslave/sql/updates/schema-change/30.all.sql @@ -0,0 +1,420 @@ +-- 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'; + +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 + 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 tmp_release_first_release_date_2025_q2 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 + ); + +-- 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.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; diff --git a/mbslave/tests/test_docker_db.py b/mbslave/tests/test_docker_db.py new file mode 100644 index 0000000..60ba883 --- /dev/null +++ b/mbslave/tests/test_docker_db.py @@ -0,0 +1,36 @@ +import psycopg2 +import pytest +import os + + +def test_db_connection(): + # Use the default credentials from Dockerfile.postgres + conn = psycopg2.connect( + host=os.environ.get("MBSLAVE_DB_HOST", "localhost"), + port=int(os.environ.get("MBSLAVE_DB_PORT", "5432")), + dbname=os.environ.get("MBSLAVE_DB_NAME", "musicbrainz"), + user=os.environ.get("MBSLAVE_DB_USER", "musicbrainz"), + password=os.environ.get("MBSLAVE_DB_PASSWORD", "musicbrainz") + ) + cur = conn.cursor() + cur.execute("SELECT 1") + assert cur.fetchone()[0] == 1 + cur.close() + conn.close() + + +def test_schemas_exist(): + conn = psycopg2.connect( + host=os.environ.get("MBSLAVE_DB_HOST", "localhost"), + port=int(os.environ.get("MBSLAVE_DB_PORT", "5432")), + dbname=os.environ.get("MBSLAVE_DB_NAME", "musicbrainz"), + user=os.environ.get("MBSLAVE_DB_USER", "musicbrainz"), + password=os.environ.get("MBSLAVE_DB_PASSWORD", "musicbrainz") + ) + cur = conn.cursor() + schemas = ['musicbrainz', 'statistics', 'cover_art_archive', 'event_art_archive', 'wikidocs', 'documentation', 'dbmirror2'] + for schema in schemas: + cur.execute("SELECT schema_name FROM information_schema.schemata WHERE schema_name = %s", (schema,)) + assert cur.fetchone() is not None, f"Schema {schema} does not exist" + cur.close() + conn.close() diff --git a/poetry.lock b/poetry.lock index 8e82936..3e0c211 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "attrs" @@ -6,16 +6,17 @@ version = "22.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests-no-zope = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "colorama" @@ -23,10 +24,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} [[package]] name = "exceptiongroup" @@ -34,6 +37,8 @@ version = "1.0.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.0.2-py3-none-any.whl", hash = "sha256:c22f11ec6a10d2b453871c5c5fe887436c4d1961324ce9090f2ca6ddc4180c27"}, {file = "exceptiongroup-1.0.2.tar.gz", hash = "sha256:a31cd183c3dea02e617aab5153588d5f7258a77b51f0ef41b3815ae8a0d0f695"}, @@ -48,6 +53,7 @@ version = "5.0.4" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.6.1" +groups = ["dev"] files = [ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, @@ -64,6 +70,7 @@ version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -75,6 +82,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -86,6 +94,7 @@ version = "0.991" description = "Optional static typing for Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, @@ -136,6 +145,7 @@ version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, @@ -147,6 +157,7 @@ version = "21.3" description = "Core utilities for Python packages" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, @@ -161,6 +172,7 @@ version = "1.0.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, @@ -176,6 +188,7 @@ version = "0.20.0" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, @@ -190,6 +203,7 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, @@ -197,6 +211,7 @@ files = [ {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, + {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"}, {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, @@ -208,6 +223,7 @@ version = "2.9.1" description = "Python style guide checker" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, @@ -219,6 +235,7 @@ version = "2.5.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, @@ -230,6 +247,7 @@ version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" +groups = ["dev"] files = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, @@ -244,6 +262,7 @@ version = "7.2.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, @@ -267,6 +286,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -278,6 +298,7 @@ version = "0.4.2" description = "A non-validating SQL parser." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, @@ -289,17 +310,42 @@ version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "types-psycopg2" version = "2.9.21.2" description = "Typing stubs for psycopg2" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-psycopg2-2.9.21.2.tar.gz", hash = "sha256:bff045579642ce00b4a3c8f2e401b7f96dfaa34939f10be64b0dd3b53feca57d"}, {file = "types_psycopg2-2.9.21.2-py3-none-any.whl", hash = "sha256:084558d6bc4b2cfa249b06be0fdd9a14a69d307bae5bb5809a2f14cfbaa7a23f"}, @@ -311,6 +357,7 @@ version = "1.16.21.4" description = "Typing stubs for six" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-six-1.16.21.4.tar.gz", hash = "sha256:daaf1b506137d37257fad7a747857c57d5331611919d0d94b8195a06bdbc02af"}, {file = "types_six-1.16.21.4-py3-none-any.whl", hash = "sha256:3849354bd07b9274436aaa7d5d834594d8f0aa74581f88c632188c58f2abed23"}, @@ -322,12 +369,13 @@ version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.8" -content-hash = "b267dba7d1a750a410c81c56bd929555430d9cc1cea51684271719fba3f2cf0e" +content-hash = "8fff0a612a1396668807525189e7cb23957ad2cf63f2be27f161730bafbf1efc" diff --git a/pyproject.toml b/pyproject.toml index 2a49c69..55994a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,9 @@ python = "^3.8" psycopg2 = "^2.9.2" six = "^1.16.0" prometheus-client = "^0.20.0" +tqdm = "^4.64.0" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] flake8 = "^5.0.0" psycopg2 = "^2.9.2" sqlparse = "^0.4.2"