Browse files

Merge branch 'mbs-2547'

* mbs-2547:
  Add cron tasks to run RemoveEmpty
  Remove now EmptyLabels.pm as this is now part of RemoveEmpty
  Regenerate SQL scripts
  Fix empty_* tests check for last_updated
  Add empty_release_groups() clean up to remove unused release groups
  Fix failing tests now that the remove_unused_link triggers are deferred
  Factor all clean up scripts into a common 'RemoveEmpty' script.
  Add a script to remove empty labels
  Make all the remove_unused_link() triggers deferred
  • Loading branch information...
2 parents 0f1241f + c8337de commit a79a0254e559f22009d0698b5657737db70f8e86 Oliver Charles committed Jun 14, 2012
View
160 admin/cleanup/EmptyArtists.pl
@@ -1,160 +0,0 @@
-#!/usr/bin/env perl
-
-use warnings;
-# vi: set ts=4 sw=4 :
-#____________________________________________________________________________
-#
-# MusicBrainz -- The community music metadata project.
-#
-# Copyright (C) 1998 Robert Kaye
-# Copyright (C) 2001 Luke Harless
-# Copyright (C) 2010 MetaBrainz Foundation
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-# $Id$
-#____________________________________________________________________________
-
-use 5.008;
-use strict;
-
-use FindBin;
-use lib "$FindBin::Bin/../../lib";
-
-use Getopt::Long;
-
-use DBDefs;
-use MusicBrainz::Server::Context;
-use MusicBrainz::Server::Constants qw ( $EDIT_ARTIST_DELETE $BOT_FLAG $AUTO_EDITOR_FLAG );
-use Sql;
-use ModDefs;
-
-my $use_auto_mod = 1;
-my $moderator = &ModDefs::MODBOT_MODERATOR;
-my $remove = 1;
-my $verbose;
-my $summary = 1;
-
-my $c = MusicBrainz::Server::Context->create_script_context();
-my $sql = Sql->new($c->conn);
-my $sqlWrite = Sql->new($c->conn);
-
-GetOptions(
- "automod!" => \$use_auto_mod,
- "moderator=s" => sub {
- my $user = $_[1];
- my $editor = $c->model('Editor')->get_by_name ($user);
- $editor or die "No such moderator '$user'";
- $moderator = $editor->id;
- },
- "remove!" => \$remove,
- "verbose!" => \$verbose,
- "summary!" => \$summary,
- "help|h|?" => sub { usage(); exit },
-) or exit 2;
-
-usage(), exit 2 if @ARGV;
-
-sub usage
-{
- print <<EOF;
-Usage: EmptyArtists.pl [OPTIONS]
-
-Allowed options are:
- --[no]automod [don't] automod the inserted moderations
- (default is to automod)
- --moderator=NAME insert the moderations as moderator NAME
- (default is the 'ModBot')
- --[no]remove [don't] remove unused artists
- (default is --remove)
- --[no]verbose [don't] show information about each artist
- --[no]summary [don't] show summary information at the end
- (default is --summary)
- -h, --help show this help (also "-?")
-
-EOF
-}
-
-$verbose = ($remove ? 0 : 1)
- unless defined $verbose;
-
-print(STDERR "Running with --noremove --noverbose --nosummary is pointless\n"), exit 1
- unless $remove or $verbose or $summary;
-
-print localtime() . " : Finding unused artists (using artist credit/AR/edit criteria)\n";
-
-# XXX FIXME Join into 1 query
-$sql->select(
- 'SELECT artist.id, name.name, sort_name.name
- FROM empty_artists() artist
- JOIN artist_name name ON name.id = artist.name
- JOIN artist_name sort_name ON sort_name.id = artist.sort_name
- WHERE NOT EXISTS (
- SELECT TRUE FROM edit_artist
- WHERE edit_artist.artist = artist.id
- AND edit_artist.status = 1
- )',
-);
-
-my $count = 0;
-my $removed = 0;
-my $privs = $BOT_FLAG;
-$privs |= $AUTO_EDITOR_FLAG if $use_auto_mod;
-
-while (my ($id, $name, $sortname) = $sql->next_row)
-{
- next if $id == &ModDefs::VARTIST_ID;
- next if $id == &ModDefs::DARTIST_ID;
-
- ++$count;
-
- if (not $remove)
- {
- printf "%s : Need to remove %6d %-30.30s (%s)\n",
- scalar localtime, $id, $name, $sortname if $verbose;
- next;
- }
-
- my $artist = $c->model('Artist')->get_by_id ($id);
- my $edit = $c->model('Edit')->create(
- edit_type => $EDIT_ARTIST_DELETE,
- to_delete => $artist,
- editor_id => $moderator,
- privileges => $privs
- );
-
-
- printf "%s : Inserted mod %6d for %6d %-30.30s (%s)\n",
- scalar localtime, $edit->id,
- $id, $name, $sortname if $verbose;
-
- ++$removed;
- 1;
-}
-
-if ($summary)
-{
- printf "%s : Found %d unused artist%s.\n",
- scalar localtime,
- $count, ($count==1 ? "" : "s");
- printf "%s : Successfully removed %d artist%s\n",
- scalar localtime,
- $removed, ($removed==1 ? "" : "s")
- if $remove;
-}
-
-print localtime() . " : EmptyArtists.pl finished\n";
-
-# eof EmptyArtists.pl
View
149 admin/cleanup/EmptyWorks.pl
@@ -1,149 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use FindBin;
-use lib "$FindBin::Bin/../../lib";
-
-use Getopt::Long;
-
-use DBDefs;
-use MusicBrainz::Server::Context;
-use MusicBrainz::Server::Constants qw ( $EDITOR_MODBOT $EDIT_WORK_DELETE $BOT_FLAG $AUTO_EDITOR_FLAG );
-
-my $use_auto_mod = 1;
-my $moderator = $EDITOR_MODBOT;
-my $remove = 1;
-my $verbose;
-my $summary = 1;
-
-my $c = MusicBrainz::Server::Context->create_script_context();
-
-GetOptions(
- "automod!" => \$use_auto_mod,
- "moderator=s" => sub {
- my $user = $_[1];
- my $editor = $c->model('Editor')->get_by_name ($user);
- $editor or die "No such moderator '$user'";
- $moderator = $editor->id;
- },
- "remove!" => \$remove,
- "verbose!" => \$verbose,
- "summary!" => \$summary,
- "help|h|?" => sub { usage(); exit },
-) or exit 2;
-
-usage(), exit 2 if @ARGV;
-
-sub usage
-{
- print <<EOF;
-Usage: EmptyWorks.pl [OPTIONS]
-
-Allowed options are:
- --[no]automod [don't] automod the inserted moderations
- (default is to automod)
- --moderator=NAME insert the moderations as moderator NAME
- (default is the 'ModBot')
- --[no]remove [don't] remove unused works
- (default is --remove)
- --[no]verbose [don't] show information about each work
- --[no]summary [don't] show summary information at the end
- (default is --summary)
- -h, --help show this help (also "-?")
-
-EOF
-}
-
-$verbose = ($remove ? 0 : 1)
- unless defined $verbose;
-
-print(STDERR "Running with --noremove --noverbose --nosummary is pointless\n"), exit 1
- unless $remove or $verbose or $summary;
-
-print localtime() . " : Finding unused works (using AR criteria)\n";
-
-my $count = 0;
-my $removed = 0;
-my $privs = $BOT_FLAG;
-$privs |= $AUTO_EDITOR_FLAG if $use_auto_mod;
-
-# XXX FIXME Join into 1 query
-my @works = values %{
- $c->model('Work')->get_by_ids(@{
- $c->sql->select_single_column_array(
- 'SELECT work.id
- FROM (SELECT unnest(?::INTEGER[])) work(id)
- WHERE NOT EXISTS (
- SELECT TRUE FROM edit_work
- JOIN edit ON edit.id = edit_work.edit
- WHERE edit_work.work = work.id
- AND edit.status = 1
- )',
- $c->sql->select_single_column_array(
- "SELECT work.id
- FROM work
- WHERE (last_updated < NOW() - '1 day'::INTERVAL
- OR last_updated IS NULL)
- AND work.edits_pending = 0
- AND work.id NOT IN (
- SELECT entity1 FROM l_artist_work
- UNION ALL
- SELECT entity1 FROM l_label_work
- UNION ALL
- SELECT entity1 FROM l_recording_work
- UNION ALL
- SELECT entity1 FROM l_release_work
- UNION ALL
- SELECT entity1 FROM l_release_group_work
- UNION ALL
- SELECT entity1 FROM l_url_work
- UNION ALL
- SELECT entity0 FROM l_work_work
- UNION ALL
- SELECT entity1 FROM l_work_work
- )")
- )
- })
-};
-
-for my $work (@works) {
- ++$count;
-
- if (not $remove)
- {
- printf "%s : Need to remove %6d %-30.30s\n",
- scalar localtime, $work->id, $work->name if $verbose;
- next;
- }
-
- my $edit = $c->model('Edit')->create(
- edit_type => $EDIT_WORK_DELETE,
- to_delete => $work,
- editor_id => $moderator,
- privileges => $privs
- );
-
- printf "%s : Inserted mod %6d for %6d %-30.30s\n",
- scalar localtime, $edit->id,
- $work->id, $work->name if $verbose;
-
- ++$removed;
- 1;
-}
-
-if ($summary)
-{
- printf "%s : Found %d unused work%s.\n",
- scalar localtime,
- $count, ($count==1 ? "" : "s");
- printf "%s : Successfully removed %d work%s\n",
- scalar localtime,
- $removed, ($removed==1 ? "" : "s")
- if $remove;
-}
-
-print localtime() . " : EmptyWorks.pl finished\n";
-
-# eof EmptyArtists.pl
View
11 admin/cleanup/RemoveEmpty
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../../lib";
+
+use DBDefs;
+
+use MooseX::Runnable::Run;
+run_application 'MusicBrainz::Script::RemoveEmpty', @ARGV;
View
11 admin/cron/daily.sh
@@ -28,12 +28,17 @@ echo `date`" : Collecting statistics"
DATETIME=`date +'%Y%m%d-%H%M%S'`
-# Identify and remove unused artists
echo `date`" : Removing unused artists"
-carton exec -- ./admin/cleanup/EmptyArtists.pl --remove --summary --noverbose
+OUTPUT=`./admin/cleanup/RemoveEmpty artist` || echo "$OUTPUT"
+
+echo `date`" : Removing unused labels"
+OUTPUT=`./admin/cleanup/RemoveEmpty label` || echo "$OUTPUT"
+
+echo `date`" : Removing unused release groups"
+OUTPUT=`./admin/cleanup/RemoveEmpty release_group` || echo "$OUTPUT"
echo `date`" : Removing unused works"
-carton exec -- ./admin/cleanup/EmptyWorks.pl --remove --summary --noverbose
+OUTPUT=`./admin/cleanup/RemoveEmpty work` || echo "$OUTPUT"
# Dump all the data
# Only do this on the nominated days (0=Sun 6=Sat)
View
215 admin/sql/CreateFunctions.sql
@@ -488,8 +488,8 @@ END;
$$ LANGUAGE 'plpgsql';
-------------------------------------------------------------------
--- Find artists that are empty, and have been updated within the
--- last $interval
+-- Find artists that are empty, and have not been updated within the
+-- last 1 days
-------------------------------------------------------------------
CREATE OR REPLACE FUNCTION empty_artists() RETURNS SETOF artist AS
@@ -502,6 +502,12 @@ BEGIN
WHERE edits_pending = 0
AND (last_updated < NOW() - '1 day'::INTERVAL OR
last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_artist
+ WHERE edit_artist.artist = artist.id
+ AND edit_artist.status = 1
+ LIMIT 1
+ )
LOOP
CONTINUE WHEN
(
@@ -549,6 +555,211 @@ END
$BODY$
LANGUAGE 'plpgsql' ;
+-------------------------------------------------------------------
+-- Find labels that are empty, and have not been updated within the
+-- last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_labels() RETURNS SETOF label AS
+$BODY$
+DECLARE
+ label_row label%rowtype;
+BEGIN
+ FOR label_row IN
+ SELECT * FROM label
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_label
+ WHERE edit_label.label = label.id
+ AND edit_label.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM release_label
+ WHERE label = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_label
+ WHERE entity1 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_label
+ WHERE entity0 = label_row.id OR entity1 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_recording
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release_group
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_url
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_work
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ );
+ RETURN NEXT label_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+-------------------------------------------------------------------
+-- Find release groups that are empty, and have not been updated
+-- within the last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_release_groups() RETURNS SETOF release_group AS
+$BODY$
+DECLARE
+ rg_row release_group%rowtype;
+BEGIN
+ FOR rg_row IN
+ SELECT * FROM release_group
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_release_group
+ JOIN edit ON edit_release_group.edit = edit.id
+ WHERE edit_release_group.release_group = release_group.id
+ AND edit.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM release
+ WHERE release_group = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_recording_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_release_group
+ WHERE entity0 = rg_row.id OR entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_url
+ WHERE entity0 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_work
+ WHERE entity0 = rg_row.id
+ LIMIT 1
+ );
+ RETURN NEXT rg_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+
+-------------------------------------------------------------------
+-- Find works that are empty, and have not been updated within the
+-- last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_works() RETURNS SETOF work AS
+$BODY$
+DECLARE
+ work_row work%rowtype;
+BEGIN
+ FOR work_row IN
+ SELECT * FROM work
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_work
+ JOIN edit ON edit.id = edit_work.edit
+ WHERE edit_work.work = work.id
+ AND edit.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM l_artist_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_recording_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_url_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_work_work
+ WHERE entity0 = work_row.id OR entity1 = work_row.id
+ LIMIT 1
+ );
+ RETURN NEXT work_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
CREATE OR REPLACE FUNCTION deny_special_purpose_artist_deletion() RETURNS trigger AS $$
BEGIN
IF OLD.id IN (1, 2) THEN
View
142 admin/sql/CreateTriggers.sql
@@ -257,91 +257,119 @@ CREATE TRIGGER a_ins_edit_artist BEFORE INSERT ON edit_artist
CREATE TRIGGER a_ins_edit_artist BEFORE INSERT ON edit_label
FOR EACH ROW EXECUTE PROCEDURE b_ins_edit_materialize_status();
-
--------------------------------------------------------------------------------
-CREATE TRIGGER a_upd_l_artist_artist AFTER UPDATE ON l_artist_artist FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_artist AFTER DELETE ON l_artist_artist FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_artist DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_label AFTER UPDATE ON l_artist_label FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_label AFTER DELETE ON l_artist_label FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_label DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_recording AFTER UPDATE ON l_artist_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_recording AFTER DELETE ON l_artist_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_release AFTER UPDATE ON l_artist_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_release AFTER DELETE ON l_artist_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_release_group AFTER UPDATE ON l_artist_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_release_group AFTER DELETE ON l_artist_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_url AFTER UPDATE ON l_artist_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_url AFTER DELETE ON l_artist_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_artist_work AFTER UPDATE ON l_artist_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_artist_work AFTER DELETE ON l_artist_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_label AFTER UPDATE ON l_label_label FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_label AFTER DELETE ON l_label_label FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_label DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_recording AFTER UPDATE ON l_label_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_recording AFTER DELETE ON l_label_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_release AFTER UPDATE ON l_label_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_release AFTER DELETE ON l_label_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_release_group AFTER UPDATE ON l_label_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_release_group AFTER DELETE ON l_label_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_url AFTER UPDATE ON l_label_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_url AFTER DELETE ON l_label_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_label_work AFTER UPDATE ON l_label_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_label_work AFTER DELETE ON l_label_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_recording_recording AFTER UPDATE ON l_recording_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_recording_recording AFTER DELETE ON l_recording_recording FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_recording_release AFTER UPDATE ON l_recording_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_recording_release AFTER DELETE ON l_recording_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_recording_release_group AFTER UPDATE ON l_recording_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_recording_release_group AFTER DELETE ON l_recording_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_recording_url AFTER UPDATE ON l_recording_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_recording_url AFTER DELETE ON l_recording_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_recording_work AFTER UPDATE ON l_recording_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_recording_work AFTER DELETE ON l_recording_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_release AFTER UPDATE ON l_release_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_release AFTER DELETE ON l_release_release FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_release_group AFTER UPDATE ON l_release_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_release_group AFTER DELETE ON l_release_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_url AFTER UPDATE ON l_release_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_url AFTER DELETE ON l_release_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_work AFTER UPDATE ON l_release_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_work AFTER DELETE ON l_release_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_group_release_group AFTER UPDATE ON l_release_group_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_group_release_group AFTER DELETE ON l_release_group_release_group FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_group_url AFTER UPDATE ON l_release_group_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_group_url AFTER DELETE ON l_release_group_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_release_group_work AFTER UPDATE ON l_release_group_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_release_group_work AFTER DELETE ON l_release_group_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_url_url AFTER UPDATE ON l_url_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_url_url AFTER DELETE ON l_url_url FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_url_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_url_work AFTER UPDATE ON l_url_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_url_work AFTER DELETE ON l_url_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_url_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_upd_l_work_work AFTER UPDATE ON l_work_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
-CREATE TRIGGER a_del_l_work_work AFTER DELETE ON l_work_work FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_work_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+--------------------------------------------------------------------------------
CREATE CONSTRAINT TRIGGER url_gc_a_upd_l_artist_url
AFTER UPDATE ON l_artist_url DEFERRABLE INITIALLY DEFERRED
View
3 admin/sql/DropFunctions.sql
@@ -30,6 +30,9 @@ DROP FUNCTION delete_unused_url(ids INTEGER[]);
DROP FUNCTION deny_special_purpose_artist_deletion();
DROP FUNCTION deny_special_purpose_label_deletion();
DROP FUNCTION empty_artists();
+DROP FUNCTION empty_labels();
+DROP FUNCTION empty_release_groups();
+DROP FUNCTION empty_works();
DROP FUNCTION end_date_implies_ended();
DROP FUNCTION from_hex(t text);
DROP FUNCTION generate_uuid_v3(namespace varchar, name varchar);
View
84 admin/sql/DropTriggers.sql
@@ -86,62 +86,34 @@ DROP TRIGGER b_upd_work_tag ON work_tag;
DROP TRIGGER a_upd_edit ON edit;
DROP TRIGGER a_ins_edit_artist ON edit_artist;
DROP TRIGGER a_ins_edit_artist ON edit_label;
-DROP TRIGGER a_upd_l_artist_artist ON l_artist_artist;
-DROP TRIGGER a_del_l_artist_artist ON l_artist_artist;
-DROP TRIGGER a_upd_l_artist_label ON l_artist_label;
-DROP TRIGGER a_del_l_artist_label ON l_artist_label;
-DROP TRIGGER a_upd_l_artist_recording ON l_artist_recording;
-DROP TRIGGER a_del_l_artist_recording ON l_artist_recording;
-DROP TRIGGER a_upd_l_artist_release ON l_artist_release;
-DROP TRIGGER a_del_l_artist_release ON l_artist_release;
-DROP TRIGGER a_upd_l_artist_release_group ON l_artist_release_group;
-DROP TRIGGER a_del_l_artist_release_group ON l_artist_release_group;
-DROP TRIGGER a_upd_l_artist_url ON l_artist_url;
-DROP TRIGGER a_del_l_artist_url ON l_artist_url;
-DROP TRIGGER a_upd_l_artist_work ON l_artist_work;
-DROP TRIGGER a_del_l_artist_work ON l_artist_work;
-DROP TRIGGER a_upd_l_label_label ON l_label_label;
-DROP TRIGGER a_del_l_label_label ON l_label_label;
-DROP TRIGGER a_upd_l_label_recording ON l_label_recording;
-DROP TRIGGER a_del_l_label_recording ON l_label_recording;
-DROP TRIGGER a_upd_l_label_release ON l_label_release;
-DROP TRIGGER a_del_l_label_release ON l_label_release;
-DROP TRIGGER a_upd_l_label_release_group ON l_label_release_group;
-DROP TRIGGER a_del_l_label_release_group ON l_label_release_group;
-DROP TRIGGER a_upd_l_label_url ON l_label_url;
-DROP TRIGGER a_del_l_label_url ON l_label_url;
-DROP TRIGGER a_upd_l_label_work ON l_label_work;
-DROP TRIGGER a_del_l_label_work ON l_label_work;
-DROP TRIGGER a_upd_l_recording_recording ON l_recording_recording;
-DROP TRIGGER a_del_l_recording_recording ON l_recording_recording;
-DROP TRIGGER a_upd_l_recording_release ON l_recording_release;
-DROP TRIGGER a_del_l_recording_release ON l_recording_release;
-DROP TRIGGER a_upd_l_recording_release_group ON l_recording_release_group;
-DROP TRIGGER a_del_l_recording_release_group ON l_recording_release_group;
-DROP TRIGGER a_upd_l_recording_url ON l_recording_url;
-DROP TRIGGER a_del_l_recording_url ON l_recording_url;
-DROP TRIGGER a_upd_l_recording_work ON l_recording_work;
-DROP TRIGGER a_del_l_recording_work ON l_recording_work;
-DROP TRIGGER a_upd_l_release_release ON l_release_release;
-DROP TRIGGER a_del_l_release_release ON l_release_release;
-DROP TRIGGER a_upd_l_release_release_group ON l_release_release_group;
-DROP TRIGGER a_del_l_release_release_group ON l_release_release_group;
-DROP TRIGGER a_upd_l_release_url ON l_release_url;
-DROP TRIGGER a_del_l_release_url ON l_release_url;
-DROP TRIGGER a_upd_l_release_work ON l_release_work;
-DROP TRIGGER a_del_l_release_work ON l_release_work;
-DROP TRIGGER a_upd_l_release_group_release_group ON l_release_group_release_group;
-DROP TRIGGER a_del_l_release_group_release_group ON l_release_group_release_group;
-DROP TRIGGER a_upd_l_release_group_url ON l_release_group_url;
-DROP TRIGGER a_del_l_release_group_url ON l_release_group_url;
-DROP TRIGGER a_upd_l_release_group_work ON l_release_group_work;
-DROP TRIGGER a_del_l_release_group_work ON l_release_group_work;
-DROP TRIGGER a_upd_l_url_url ON l_url_url;
-DROP TRIGGER a_del_l_url_url ON l_url_url;
-DROP TRIGGER a_upd_l_url_work ON l_url_work;
-DROP TRIGGER a_del_l_url_work ON l_url_work;
-DROP TRIGGER a_upd_l_work_work ON l_work_work;
-DROP TRIGGER a_del_l_work_work ON l_work_work;
+DROP TRIGGER remove_unused_links ON l_artist_artist;
+DROP TRIGGER remove_unused_links ON l_artist_label;
+DROP TRIGGER remove_unused_links ON l_artist_recording;
+DROP TRIGGER remove_unused_links ON l_artist_release;
+DROP TRIGGER remove_unused_links ON l_artist_release_group;
+DROP TRIGGER remove_unused_links ON l_artist_url;
+DROP TRIGGER remove_unused_links ON l_artist_work;
+DROP TRIGGER remove_unused_links ON l_label_label;
+DROP TRIGGER remove_unused_links ON l_label_recording;
+DROP TRIGGER remove_unused_links ON l_label_release;
+DROP TRIGGER remove_unused_links ON l_label_release_group;
+DROP TRIGGER remove_unused_links ON l_label_url;
+DROP TRIGGER remove_unused_links ON l_label_work;
+DROP TRIGGER remove_unused_links ON l_recording_recording;
+DROP TRIGGER remove_unused_links ON l_recording_release;
+DROP TRIGGER remove_unused_links ON l_recording_release_group;
+DROP TRIGGER remove_unused_links ON l_recording_url;
+DROP TRIGGER remove_unused_links ON l_recording_work;
+DROP TRIGGER remove_unused_links ON l_release_release;
+DROP TRIGGER remove_unused_links ON l_release_release_group;
+DROP TRIGGER remove_unused_links ON l_release_url;
+DROP TRIGGER remove_unused_links ON l_release_work;
+DROP TRIGGER remove_unused_links ON l_release_group_release_group;
+DROP TRIGGER remove_unused_links ON l_release_group_url;
+DROP TRIGGER remove_unused_links ON l_release_group_work;
+DROP TRIGGER remove_unused_links ON l_url_url;
+DROP TRIGGER remove_unused_links ON l_url_work;
+DROP TRIGGER remove_unused_links ON l_work_work;
DROP TRIGGER url_gc_a_upd_l_artist_url ON l_artist_url;
DROP TRIGGER url_gc_a_del_l_artist_url ON l_artist_url;
DROP TRIGGER url_gc_a_upd_l_label_url ON l_label_url;
View
174 admin/sql/updates/20120613-deferred-link-triggers.sql
@@ -0,0 +1,174 @@
+BEGIN;
+
+DROP TRIGGER a_upd_l_artist_artist ON l_artist_artist;
+DROP TRIGGER a_del_l_artist_artist ON l_artist_artist;
+DROP TRIGGER a_upd_l_artist_label ON l_artist_label;
+DROP TRIGGER a_del_l_artist_label ON l_artist_label;
+DROP TRIGGER a_upd_l_artist_recording ON l_artist_recording;
+DROP TRIGGER a_del_l_artist_recording ON l_artist_recording;
+DROP TRIGGER a_upd_l_artist_release ON l_artist_release;
+DROP TRIGGER a_del_l_artist_release ON l_artist_release;
+DROP TRIGGER a_upd_l_artist_release_group ON l_artist_release_group;
+DROP TRIGGER a_del_l_artist_release_group ON l_artist_release_group;
+DROP TRIGGER a_upd_l_artist_url ON l_artist_url;
+DROP TRIGGER a_del_l_artist_url ON l_artist_url;
+DROP TRIGGER a_upd_l_artist_work ON l_artist_work;
+DROP TRIGGER a_del_l_artist_work ON l_artist_work;
+DROP TRIGGER a_upd_l_label_label ON l_label_label;
+DROP TRIGGER a_del_l_label_label ON l_label_label;
+DROP TRIGGER a_upd_l_label_recording ON l_label_recording;
+DROP TRIGGER a_del_l_label_recording ON l_label_recording;
+DROP TRIGGER a_upd_l_label_release ON l_label_release;
+DROP TRIGGER a_del_l_label_release ON l_label_release;
+DROP TRIGGER a_upd_l_label_release_group ON l_label_release_group;
+DROP TRIGGER a_del_l_label_release_group ON l_label_release_group;
+DROP TRIGGER a_upd_l_label_url ON l_label_url;
+DROP TRIGGER a_del_l_label_url ON l_label_url;
+DROP TRIGGER a_upd_l_label_work ON l_label_work;
+DROP TRIGGER a_del_l_label_work ON l_label_work;
+DROP TRIGGER a_upd_l_recording_recording ON l_recording_recording;
+DROP TRIGGER a_del_l_recording_recording ON l_recording_recording;
+DROP TRIGGER a_upd_l_recording_release ON l_recording_release;
+DROP TRIGGER a_del_l_recording_release ON l_recording_release;
+DROP TRIGGER a_upd_l_recording_release_group ON l_recording_release_group;
+DROP TRIGGER a_del_l_recording_release_group ON l_recording_release_group;
+DROP TRIGGER a_upd_l_recording_url ON l_recording_url;
+DROP TRIGGER a_del_l_recording_url ON l_recording_url;
+DROP TRIGGER a_upd_l_recording_work ON l_recording_work;
+DROP TRIGGER a_del_l_recording_work ON l_recording_work;
+DROP TRIGGER a_upd_l_release_release ON l_release_release;
+DROP TRIGGER a_del_l_release_release ON l_release_release;
+DROP TRIGGER a_upd_l_release_release_group ON l_release_release_group;
+DROP TRIGGER a_del_l_release_release_group ON l_release_release_group;
+DROP TRIGGER a_upd_l_release_url ON l_release_url;
+DROP TRIGGER a_del_l_release_url ON l_release_url;
+DROP TRIGGER a_upd_l_release_work ON l_release_work;
+DROP TRIGGER a_del_l_release_work ON l_release_work;
+DROP TRIGGER a_upd_l_release_group_release_group ON l_release_group_release_group;
+DROP TRIGGER a_del_l_release_group_release_group ON l_release_group_release_group;
+DROP TRIGGER a_upd_l_release_group_url ON l_release_group_url;
+DROP TRIGGER a_del_l_release_group_url ON l_release_group_url;
+DROP TRIGGER a_upd_l_release_group_work ON l_release_group_work;
+DROP TRIGGER a_del_l_release_group_work ON l_release_group_work;
+DROP TRIGGER a_upd_l_url_url ON l_url_url;
+DROP TRIGGER a_del_l_url_url ON l_url_url;
+DROP TRIGGER a_upd_l_url_work ON l_url_work;
+DROP TRIGGER a_del_l_url_work ON l_url_work;
+DROP TRIGGER a_upd_l_work_work ON l_work_work;
+DROP TRIGGER a_del_l_work_work ON l_work_work;
+
+--------------------------------------------------------------------------------
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_artist DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_label DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_artist_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_label DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_label_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_recording DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_recording_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_release DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_release_group DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_release_group_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_url_url DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_url_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+
+CREATE CONSTRAINT TRIGGER remove_unused_links
+ AFTER DELETE OR UPDATE ON l_work_work DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE remove_unused_links();
+--------------------------------------------------------------------------------
+
+ROLLBACK;
View
275 admin/sql/updates/20120613-remove-empty.sql
@@ -0,0 +1,275 @@
+BEGIN;
+
+-------------------------------------------------------------------
+-- Find artists that are empty, and have not been updated within the
+-- last 1 days
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_artists() RETURNS SETOF artist AS
+$BODY$
+DECLARE
+ artist_row artist%rowtype;
+BEGIN
+ FOR artist_row IN
+ SELECT * FROM artist
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_artist
+ WHERE edit_artist.artist = artist.id
+ AND edit_artist.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM artist_credit_name
+ WHERE artist = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_recording
+ WHERE entity0 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_work
+ WHERE entity0 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_url
+ WHERE entity0 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_artist
+ WHERE entity0 = artist_row.id OR entity1 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_label
+ WHERE entity0 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_release
+ WHERE entity0 = artist_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_release_group WHERE entity0 = artist_row.id
+ LIMIT 1
+ );
+ RETURN NEXT artist_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+-------------------------------------------------------------------
+-- Find labels that are empty, and have not been updated within the
+-- last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_labels() RETURNS SETOF label AS
+$BODY$
+DECLARE
+ label_row label%rowtype;
+BEGIN
+ FOR label_row IN
+ SELECT * FROM label
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_label
+ WHERE edit_label.label = label.id
+ AND edit_label.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM release_label
+ WHERE label = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_label
+ WHERE entity1 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_label
+ WHERE entity0 = label_row.id OR entity1 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_recording
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release_group
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_url
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_work
+ WHERE entity0 = label_row.id
+ LIMIT 1
+ );
+ RETURN NEXT label_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+-------------------------------------------------------------------
+-- Find works that are empty, and have not been updated within the
+-- last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_works() RETURNS SETOF work AS
+$BODY$
+DECLARE
+ work_row work%rowtype;
+BEGIN
+ FOR work_row IN
+ SELECT * FROM work
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_work
+ JOIN edit ON edit.id = edit_work.edit
+ WHERE edit_work.work = work.id
+ AND edit.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM l_artist_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_recording_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_url_work
+ WHERE entity1 = work_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_work_work
+ WHERE entity0 = work_row.id OR entity1 = work_row.id
+ LIMIT 1
+ );
+ RETURN NEXT work_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+-------------------------------------------------------------------
+-- Find release groups that are empty, and have not been updated
+-- within the last 1 day
+-------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION empty_release_groups() RETURNS SETOF release_group AS
+$BODY$
+DECLARE
+ rg_row release_group%rowtype;
+BEGIN
+ FOR rg_row IN
+ SELECT * FROM release_group
+ WHERE edits_pending = 0
+ AND (last_updated < NOW() - '1 day'::INTERVAL OR
+ last_updated IS NULL)
+ AND NOT EXISTS (
+ SELECT TRUE FROM edit_release_group
+ JOIN edit ON edit_release_group.edit = edit.id
+ WHERE edit_release_group.release_group = release_group.id
+ AND edit.status = 1
+ LIMIT 1
+ )
+ LOOP
+ CONTINUE WHEN
+ (
+ SELECT TRUE FROM release
+ WHERE release_group = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_artist_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_label_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_recording_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_release_group
+ WHERE entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_release_group
+ WHERE entity0 = rg_row.id OR entity1 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_url
+ WHERE entity0 = rg_row.id
+ LIMIT 1
+ ) OR
+ (
+ SELECT TRUE FROM l_release_group_work
+ WHERE entity0 = rg_row.id
+ LIMIT 1
+ );
+ RETURN NEXT rg_row;
+ END LOOP;
+END
+$BODY$
+LANGUAGE 'plpgsql' ;
+
+COMMIT;
View
105 lib/MusicBrainz/Script/RemoveEmpty.pm
@@ -0,0 +1,105 @@
+package MusicBrainz::Script::RemoveEmpty;
+use Moose;
+
+use DBDefs;
+use List::AllUtils qw( any );
+use MusicBrainz::Server::Context;
+use MusicBrainz::Server::Constants
+ qw( $EDITOR_MODBOT $VARTIST_ID $DARTIST_ID $DLABEL_ID $EDIT_ARTIST_DELETE
+ $EDIT_LABEL_DELETE $BOT_FLAG $AUTO_EDITOR_FLAG $EDIT_WORK_DELETE $EDIT_RELEASEGROUP_DELETE );
+use MusicBrainz::Server::Log qw( log_debug log_warning log_notice );
+use MusicBrainz::Server::Data::Utils qw( type_to_model );
+
+with 'MooseX::Runnable';
+with 'MooseX::Getopt';
+with 'MusicBrainz::Script::Role::Context';
+
+my %entity_query_map = (
+ artist => 'SELECT id FROM empty_artists()',
+ label => 'SELECT id FROM empty_labels()',
+ release_group => 'SELECT id FROM empty_release_groups()',
+ work => 'SELECT id FROM empty_works()',
+);
+
+my %skip_ids = (
+ artist => [ $VARTIST_ID, $DARTIST_ID ],
+ label => [ $DLABEL_ID ],
+ release_group => [],
+ work => []
+);
+
+my %edit_class = (
+ artist => $EDIT_ARTIST_DELETE,
+ label => $EDIT_LABEL_DELETE,
+ release_group => $EDIT_RELEASEGROUP_DELETE,
+ work => $EDIT_WORK_DELETE,
+);
+
+has dry_run => (
+ isa => 'Bool',
+ is => 'ro',
+ default => 0,
+ traits => [ 'Getopt' ],
+ cmd_flag => 'dry-run'
+);
+
+has summary => (
+ isa => 'Bool',
+ is => 'ro',
+ default => 1,
+);
+
+has verbose => (
+ isa => 'Bool',
+ is => 'ro',
+ default => 0,
+);
+
+sub run {
+ my ($self, $entity) = @_;
+ my $query = $entity_query_map{$entity} or $self->usage, exit 1;
+
+ print localtime() . " : Finding unused entities of type '$entity'\n";
+
+ my ($count, $removed) = (0, 0);
+ my @entities = values %{
+ $self->c->model(type_to_model($entity))->get_by_ids(
+ @{ $self->c->sql->select_single_column_array($query) }
+ )
+ };
+
+ for my $e (@entities) {
+ next if any { $e->id == $_ } @{ $skip_ids{$entity} // [] };
+ ++$count;
+
+ if ($self->dry_run) {
+ printf "%s : Need to remove $entity gid=%s name=%s\n",
+ scalar localtime, $e->id, $e->name
+ if $self->verbose;
+
+ }
+ else {
+ Sql::run_in_transaction(sub {
+ my $edit = $self->c->model('Edit')->create(
+ edit_type => $edit_class{$entity},
+ to_delete => $e,
+ editor_id => $EDITOR_MODBOT,
+ privileges => $BOT_FLAG | $AUTO_EDITOR_FLAG
+ );
+ ++$removed
+ }, $self->c->sql);
+ }
+ }
+
+ if ($self->summary) {
+ printf "%s : Found %d unused $entity%s.\n",
+ scalar localtime,
+ $count, ($count==1 ? "" : "s");
+ printf "%s : Successfully removed %d $entity%s\n",
+ scalar localtime,
+ $removed, ($removed==1 ? "" : "s")
+ if !$self->dry_run;
+ }
+}
+
+1;
View
2 t/lib/t/MusicBrainz/Server/Edit/Relationship/Edit.pm
@@ -61,6 +61,8 @@ is($rel->link->end_date->year, 1995, "... end year 1995");
is($rel->entity0_id, 1, '... entity 0 is artist 1');
is($rel->entity1_id, 3, '... entity 1 is artist 3');
+$c->sql->do('SET CONSTRAINTS ALL IMMEDIATE');
+$c->sql->do('SET CONSTRAINTS ALL DEFERRED');
$c->sql->do('TRUNCATE artist CASCADE');
$c->sql->do('TRUNCATE link_type CASCADE');
$c->model('Edit')->load_all($edit);
View
171 t/pgtap/empty_artists.sql
@@ -0,0 +1,171 @@
+SET search_path = 'musicbrainz', 'public';
+
+BEGIN;
+SELECT no_plan();
+
+--------------------------------------------------------------------------------
+-- Test setup. See below for tests.
+INSERT INTO editor (id, name, password) VALUES (1, 'editor', 'pass');
+INSERT INTO edit (id, type, status, data, open_time, close_time, expire_time, editor)
+ VALUES (1, 1, 1, '', now(), now(), now(), 1);
+
+INSERT INTO link_type (id, gid, entity_type0, entity_type1, name, link_phrase,
+ reverse_link_phrase, short_link_phrase)
+ VALUES (1, '0059c07e-e9f5-4680-b48b-b40c6f82dd72', 'artist', 'label', '', '', '', '');
+INSERT INTO link (id, link_type) VALUES (1, 1);
+
+INSERT INTO artist_name (id, name) VALUES (1, 'Test');
+INSERT INTO label_name (id, name) VALUES (1, 'Test');
+INSERT INTO release_name (id, name) VALUES (1, 'Test');
+INSERT INTO track_name (id, name) VALUES (1, 'Test');
+INSERT INTO work_name (id, name) VALUES (1, 'Test');
+
+INSERT INTO artist (id, gid, name, sort_name, last_updated, edits_pending)
+ VALUES (1, '159cb1fa-dbe9-4777-abf6-7ecb3ce84f91', 1, 1, now(), 0),
+ (2, 'fbbf7950-eebe-49e5-86d6-058ecc2bf4ac', 1, 1, now(), 10);
+INSERT INTO artist_credit (id, name, artist_count) VALUES (1, 1, 1);
+
+INSERT INTO label (id, gid, name, sort_name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO recording (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release_group (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release (id, gid, name, artist_credit, release_group)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1, 1);
+INSERT INTO url (id, gid, url)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 'http://google.com/');
+INSERT INTO work (id, gid, name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1);
+
+-- Disable triggers so we can actually update the last_updated field
+ALTER TABLE artist DISABLE TRIGGER USER;
+
+--------------------------------------------------------------------------------
+-- Newly created artists are not in empty_artists()
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+
+--------------------------------------------------------------------------------
+-- Artists > 1 day old are eligible for empty_artists().
+UPDATE artist SET last_updated = '1970-01-01' WHERE id = 1;
+
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()',
+ ARRAY[ 1 ]
+);
+
+--------------------------------------------------------------------------------
+-- Artists with edits pending are not eligible for empty_artists()
+UPDATE artist SET edits_pending = edits_pending + 1 WHERE id = 1;
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+UPDATE artist SET edits_pending = 0;
+
+--------------------------------------------------------------------------------
+-- l_artist_artist entries exclude artists from empty_artists()
+INSERT INTO l_artist_artist (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_artist;
+
+--------------------------------------------------------------------------------
+-- l_artist_label entries exclude artists from empty_artists()
+INSERT INTO l_artist_label (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_label;
+
+--------------------------------------------------------------------------------
+-- l_artist_recording entries exclude artists from empty_artists()
+INSERT INTO l_artist_recording (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_recording;
+
+--------------------------------------------------------------------------------
+-- l_artist_recording entries exclude artists from empty_artists()
+INSERT INTO l_artist_release (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_release;
+
+--------------------------------------------------------------------------------
+-- l_artist_release_group entries exclude artists from empty_artists()
+INSERT INTO l_artist_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_release_group;
+
+--------------------------------------------------------------------------------
+-- l_artist_url entries exclude artists from empty_artists()
+INSERT INTO l_artist_url (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_url;
+
+--------------------------------------------------------------------------------
+-- l_artist_work entries exclude artists from empty_artists()
+INSERT INTO l_artist_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_artist_work;
+
+--------------------------------------------------------------------------------
+-- A artist with recordings is excluded from empty_artists()
+INSERT INTO artist_credit (id, artist_count, name) VALUES (2, 1, 1);
+INSERT INTO artist_credit_name (artist_credit, artist, name, join_phrase, position)
+ VALUES (2, 1, 1, NULL, 1);
+
+UPDATE recording SET artist_credit = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM recording;
+
+-- A artist with releases is excluded from empty_artists()
+INSERT INTO artist_credit (id, artist_count, name) VALUES (2, 1, 1);
+INSERT INTO artist_credit_name (artist_credit, artist, name, join_phrase, position)
+ VALUES (2, 1, 1, NULL, 1);
+
+UPDATE release SET artist_credit = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM release;
+
+-- A artist with release groups is excluded from empty_artists()
+INSERT INTO artist_credit (id, artist_count, name) VALUES (2, 1, 1);
+INSERT INTO artist_credit_name (artist_credit, artist, name, join_phrase, position)
+ VALUES (2, 1, 1, NULL, 1);
+
+UPDATE release_group SET artist_credit = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM release_group;
+
+--------------------------------------------------------------------------------
+-- A artist with open edits linked to it is excluded from empty_artists()
+INSERT INTO edit_artist (edit, artist) VALUES (1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+
+-- But edits that aren't open don't block empty_artists()
+UPDATE edit SET status = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{1}'::INT[]
+);
+
+SELECT finish();
+ROLLBACK;
View
146 t/pgtap/empty_labels.sql
@@ -0,0 +1,146 @@
+SET search_path = 'musicbrainz', 'public';
+
+BEGIN;
+SELECT no_plan();
+
+--------------------------------------------------------------------------------
+-- Test setup. See below for tests.
+INSERT INTO editor (id, name, password) VALUES (1, 'editor', 'pass');
+INSERT INTO edit (id, type, status, data, open_time, close_time, expire_time, editor)
+ VALUES (1, 1, 1, '', now(), now(), now(), 1);
+
+INSERT INTO link_type (id, gid, entity_type0, entity_type1, name, link_phrase,
+ reverse_link_phrase, short_link_phrase)
+ VALUES (1, '0059c07e-e9f5-4680-b48b-b40c6f82dd72', 'artist', 'label', '', '', '', '');
+INSERT INTO link (id, link_type) VALUES (1, 1);
+
+INSERT INTO artist_name (id, name) VALUES (1, 'Test');
+INSERT INTO release_name (id, name) VALUES (1, 'Test');
+INSERT INTO track_name (id, name) VALUES (1, 'Test');
+INSERT INTO work_name (id, name) VALUES (1, 'Test');
+
+INSERT INTO artist (id, gid, name, sort_name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO artist_credit (id, name, artist_count) VALUES (1, 1, 1);
+
+INSERT INTO recording (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release_group (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release (id, gid, name, artist_credit, release_group)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1, 1);
+INSERT INTO url (id, gid, url)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 'http://google.com/');
+INSERT INTO work (id, gid, name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1);
+
+INSERT INTO label_name (id, name) VALUES (1, 'Test Label');
+INSERT INTO label (id, gid, name, sort_name, last_updated, edits_pending)
+ VALUES (1, '159cb1fa-dbe9-4777-abf6-7ecb3ce84f91', 1, 1, now(), 0),
+ (2, 'fbbf7950-eebe-49e5-86d6-058ecc2bf4ac', 1, 1, now(), 10);
+
+-- Disable triggers so we can actually update the last_updated field
+ALTER TABLE label DISABLE TRIGGER USER;
+
+--------------------------------------------------------------------------------
+-- Newly created labels are not in empty_labels()
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+
+--------------------------------------------------------------------------------
+-- Labels > 1 day old are eligible for empty_labels().
+UPDATE label SET last_updated = '1970-01-01' WHERE id = 1;
+
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()',
+ ARRAY[ 1 ]
+);
+
+--------------------------------------------------------------------------------
+-- Labels with edits pending are not eligible for empty_labels()
+UPDATE label SET edits_pending = edits_pending + 1 WHERE id = 1;
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+UPDATE label SET edits_pending = 0;
+
+--------------------------------------------------------------------------------
+-- l_artist_label entries exclude labels from empty_labels()
+INSERT INTO l_artist_label (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_artist_label;
+
+--------------------------------------------------------------------------------
+-- l_label_label entries exclude labels from empty_labels()
+INSERT INTO l_label_label (id, entity0, entity1, link) VALUES (1, 1, 2, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_label;
+
+--------------------------------------------------------------------------------
+-- l_label_recording entries exclude labels from empty_labels()
+INSERT INTO l_label_recording (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_recording;
+
+--------------------------------------------------------------------------------
+-- l_label_recording entries exclude labels from empty_labels()
+INSERT INTO l_label_release (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_release;
+
+--------------------------------------------------------------------------------
+-- l_label_release_group entries exclude labels from empty_labels()
+INSERT INTO l_label_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_release_group;
+
+--------------------------------------------------------------------------------
+-- l_label_url entries exclude labels from empty_labels()
+INSERT INTO l_label_url (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_url;
+
+--------------------------------------------------------------------------------
+-- l_label_work entries exclude labels from empty_labels()
+INSERT INTO l_label_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM l_label_work;
+
+--------------------------------------------------------------------------------
+-- A label with releases is excluded from empty_labels()
+INSERT INTO release_label (id, release, label) VALUES (1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM release_label;
+
+--------------------------------------------------------------------------------
+-- A label with open edits linked to it is excluded from empty_labels()
+INSERT INTO edit_label (edit, label) VALUES (1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+
+-- But edits that aren't open don't block empty_labels()
+UPDATE edit SET status = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{1}'::INT[]
+);
+
+SELECT finish();
+ROLLBACK;
View
144 t/pgtap/empty_release_groups.sql
@@ -0,0 +1,144 @@
+SET search_path = 'musicbrainz', 'public';
+
+BEGIN;
+SELECT no_plan();
+
+--------------------------------------------------------------------------------
+-- Test setup. See below for tests.
+INSERT INTO editor (id, name, password) VALUES (1, 'editor', 'pass');
+INSERT INTO edit (id, type, status, data, open_time, close_time, expire_time, editor)
+ VALUES (1, 1, 1, '', now(), now(), now(), 1);
+
+INSERT INTO link_type (id, gid, entity_type0, entity_type1, name, link_phrase,
+ reverse_link_phrase, short_link_phrase)
+ VALUES (1, '0059c07e-e9f5-4680-b48b-b40c6f82dd72', 'artist', 'label', '', '', '', '');
+INSERT INTO link (id, link_type) VALUES (1, 1);
+
+INSERT INTO artist_name (id, name) VALUES (1, 'Test');
+INSERT INTO label_name (id, name) VALUES (1, 'Test');
+INSERT INTO release_name (id, name) VALUES (1, 'Test');
+INSERT INTO track_name (id, name) VALUES (1, 'Test');
+INSERT INTO work_name (id, name) VALUES (1, 'Test');
+
+INSERT INTO artist (id, gid, name, sort_name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO artist_credit (id, name, artist_count) VALUES (1, 1, 1);
+INSERT INTO label (id, gid, name, sort_name)
+ VALUES (1, '159cb1fa-dbe9-4777-abf6-7ecb3ce84f91', 1, 1);
+INSERT INTO recording (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release_group (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1),
+ (2, '9c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release (id, gid, name, artist_credit, release_group)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1, 2);
+INSERT INTO url (id, gid, url)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 'http://google.com/');
+INSERT INTO work (id, gid, name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1);
+
+-- Disable triggers so we can actually update the last_updated field
+ALTER TABLE release_group DISABLE TRIGGER USER;
+
+--------------------------------------------------------------------------------
+-- Newly created release groups are not in empty_release_groups()
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+
+--------------------------------------------------------------------------------
+-- Release groups > 1 day old are eligible for empty_release_groups().
+UPDATE release_group SET last_updated = '1970-01-01' WHERE id = 1;
+
+SELECT set_eq(
+ 'SELECT id FROM release_group WHERE edits_pending = 0 AND last_updated < now() - ''1 day''::interval',
+ ARRAY[ 1 ]
+);
+
+--------------------------------------------------------------------------------
+-- Release groups with edits pending are not eligible for empty_release_groups()
+UPDATE release_group SET edits_pending = edits_pending + 1 WHERE id = 1;
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+UPDATE release_group SET edits_pending = 0;
+
+--------------------------------------------------------------------------------
+-- l_artist_release_group entries exclude release_groups from empty_release_groups()
+INSERT INTO l_artist_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_artist_release_group;
+
+--------------------------------------------------------------------------------
+-- l_label_release_group entries exclude release_groups from empty_release_groups()
+INSERT INTO l_label_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_label_release_group;
+
+--------------------------------------------------------------------------------
+-- l_recording_release_group entries exclude release_groups from empty_release_groups()
+INSERT INTO l_recording_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_recording_release_group;
+
+--------------------------------------------------------------------------------
+-- l_release_release_group entries exclude release_groups from empty_release_groups()
+INSERT INTO l_release_release_group (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_release_release_group;
+
+--------------------------------------------------------------------------------
+-- l_release_group_release_group entries exclude release_groups from empty_release_groups()
+INSERT INTO l_release_group_release_group (id, entity0, entity1, link) VALUES (1, 1, 2, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_release_group_release_group;
+
+--------------------------------------------------------------------------------
+-- l_release_group_url entries exclude release_groups from empty_release_groups()
+INSERT INTO l_release_group_url (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_release_group_url;
+
+--------------------------------------------------------------------------------
+-- l_release_group_work entries exclude release_groups from empty_release_groups()
+INSERT INTO l_release_group_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+DELETE FROM l_release_group_work;
+
+--------------------------------------------------------------------------------
+-- A release_group with releases is excluded from empty_release_groups()
+UPDATE release SET release_group = 1;
+SELECT set_eq(
+ 'SELECT id FROM empty_labels()', '{}'::INT[]
+);
+DELETE FROM release;
+
+--------------------------------------------------------------------------------
+-- A release_group with open edits linked to it is excluded from empty_release_groups()
+INSERT INTO edit_release_group (edit, release_group) VALUES (1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{}'::INT[]
+);
+
+-- But edits that aren't open don't block empty_release_groups()
+UPDATE edit SET status = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_release_groups()', '{1}'::INT[]
+);
+
+SELECT finish();
+ROLLBACK;
View
136 t/pgtap/empty_works.sql
@@ -0,0 +1,136 @@
+SET search_path = 'musicbrainz', 'public';
+
+BEGIN;
+SELECT no_plan();
+
+--------------------------------------------------------------------------------
+-- Test setup. See below for tests.
+INSERT INTO editor (id, name, password) VALUES (1, 'editor', 'pass');
+INSERT INTO edit (id, type, status, data, open_time, close_time, expire_time, editor)
+ VALUES (1, 1, 1, '', now(), now(), now(), 1);
+
+INSERT INTO link_type (id, gid, entity_type0, entity_type1, name, link_phrase,
+ reverse_link_phrase, short_link_phrase)
+ VALUES (1, '0059c07e-e9f5-4680-b48b-b40c6f82dd72', 'artist', 'label', '', '', '', '');
+INSERT INTO link (id, link_type) VALUES (1, 1);
+
+INSERT INTO artist_name (id, name) VALUES (1, 'Test');
+INSERT INTO label_name (id, name) VALUES (1, 'Test');
+INSERT INTO release_name (id, name) VALUES (1, 'Test');
+INSERT INTO track_name (id, name) VALUES (1, 'Test');
+INSERT INTO work_name (id, name) VALUES (1, 'Test');
+
+INSERT INTO artist (id, gid, name, sort_name, last_updated, edits_pending)
+ VALUES (1, '159cb1fa-dbe9-4777-abf6-7ecb3ce84f91', 1, 1, now(), 0);
+INSERT INTO artist_credit (id, name, artist_count) VALUES (1, 1, 1);
+INSERT INTO label (id, gid, name, sort_name, last_updated, edits_pending)
+ VALUES (1, '159cb1fa-dbe9-4777-abf6-7ecb3ce84f91', 1, 1, now(), 0);
+INSERT INTO recording (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release_group (id, gid, name, artist_credit)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1);
+INSERT INTO release (id, gid, name, artist_credit, release_group)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1, 1, 1);
+INSERT INTO url (id, gid, url)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 'http://google.com/');
+INSERT INTO work (id, gid, name)
+ VALUES (1, '0c172831-ff88-4eff-9c58-47fa1408b6b2', 1),
+ (2, '9c172831-ff88-4eff-9c58-47fa1408b6b2', 1);
+
+-- Disable triggers so we can actually update the last_updated field
+ALTER TABLE work DISABLE TRIGGER USER;
+
+--------------------------------------------------------------------------------
+-- Newly created works are not in empty_works()
+SELECT set_eq(
+ 'SELECT id FROM empty_works()', '{}'::INT[]
+);
+
+--------------------------------------------------------------------------------
+-- Works > 1 day old are eligible for empty_works().
+UPDATE work SET last_updated = '1970-01-01' WHERE id = 1;
+
+SELECT set_eq(
+ 'SELECT id FROM empty_works()',
+ ARRAY[ 1 ]
+);
+
+--------------------------------------------------------------------------------
+-- Works with edits pending are not eligible for empty_works()
+UPDATE work SET edits_pending = edits_pending + 1 WHERE id = 1;
+SELECT set_eq(
+ 'SELECT id FROM empty_works()', '{}'::INT[]
+);
+UPDATE work SET edits_pending = 0;
+
+--------------------------------------------------------------------------------
+-- l_artist_work entries exclude works from empty_works()
+INSERT INTO l_artist_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_works()', '{}'::INT[]
+);
+DELETE FROM l_artist_work;
+
+--------------------------------------------------------------------------------
+-- l_label_work entries exclude artists from empty_artists()
+INSERT INTO l_label_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_label_work;
+
+--------------------------------------------------------------------------------
+-- l_recording_work entries exclude artists from empty_artists()
+INSERT INTO l_recording_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_recording_work;
+
+--------------------------------------------------------------------------------
+-- l_artist_recording entries exclude artists from empty_artists()
+INSERT INTO l_release_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_release_work;
+
+--------------------------------------------------------------------------------
+-- l_artist_release_group entries exclude artists from empty_artists()
+INSERT INTO l_release_group_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_release_group_work;
+
+--------------------------------------------------------------------------------
+-- l_artist_url entries exclude artists from empty_artists()
+INSERT INTO l_url_work (id, entity0, entity1, link) VALUES (1, 1, 1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_url_work;
+
+--------------------------------------------------------------------------------
+-- l_artist_work entries exclude artists from empty_artists()
+INSERT INTO l_work_work (id, entity0, entity1, link) VALUES (1, 1, 2, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_artists()', '{}'::INT[]
+);
+DELETE FROM l_work_work;
+
+--------------------------------------------------------------------------------
+-- A work with open edits linked to it is excluded from empty_works()
+INSERT INTO edit_work (edit, work) VALUES (1, 1);
+SELECT set_eq(
+ 'SELECT id FROM empty_works()', '{}'::INT[]
+);
+
+-- But edits that aren't open don't block empty_works()
+UPDATE edit SET status = 2;
+SELECT set_eq(
+ 'SELECT id FROM empty_works()', '{1}'::INT[]
+);
+
+SELECT finish();
+ROLLBACK;

0 comments on commit a79a025

Please sign in to comment.