You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
9.9 KiB
219 lines
9.9 KiB
-- Identidades artísticas, créditos de interpretación y representaciones de
|
|
-- audio. Todo es aditivo: las columnas audio_id/muestra_id y el rol interprete
|
|
-- anterior se mantienen hasta que las lecturas nuevas hayan demostrado paridad.
|
|
|
|
CREATE SEQUENCE "cancion_codigo_seq";--> statement-breakpoint
|
|
ALTER TABLE "cancion" ADD COLUMN "codigo" text;--> statement-breakpoint
|
|
|
|
WITH numeradas AS (
|
|
SELECT id, row_number() OVER (ORDER BY creado_en, id) AS numero
|
|
FROM cancion
|
|
)
|
|
UPDATE cancion c
|
|
SET codigo = 'CAN-' || lpad(n.numero::text, 6, '0')
|
|
FROM numeradas n
|
|
WHERE n.id = c.id;--> statement-breakpoint
|
|
|
|
SELECT setval(
|
|
'cancion_codigo_seq',
|
|
COALESCE((SELECT max(substring(codigo FROM 5)::bigint) FROM cancion), 1),
|
|
EXISTS (SELECT 1 FROM cancion)
|
|
);--> statement-breakpoint
|
|
|
|
ALTER TABLE "cancion" ALTER COLUMN "codigo" SET DEFAULT
|
|
('CAN-' || lpad(nextval('cancion_codigo_seq')::text, 6, '0'));--> statement-breakpoint
|
|
ALTER TABLE "cancion" ALTER COLUMN "codigo" SET NOT NULL;--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "cancion_codigo_unico" ON "cancion" ("codigo");--> statement-breakpoint
|
|
|
|
ALTER TABLE "cancion" ADD COLUMN "version_codigo" text;--> statement-breakpoint
|
|
UPDATE cancion v
|
|
SET version_codigo = CASE
|
|
WHEN v.slug LIKE p.slug || '-%' THEN substring(v.slug FROM char_length(p.slug) + 2)
|
|
ELSE v.slug
|
|
END
|
|
FROM cancion p
|
|
WHERE v.version_de_id = p.id;--> statement-breakpoint
|
|
|
|
ALTER TABLE "cancion" ADD CONSTRAINT "cancion_version_codigo_presente"
|
|
CHECK (
|
|
("version_de_id" IS NULL AND "version_codigo" IS NULL)
|
|
OR ("version_de_id" IS NOT NULL AND nullif(btrim("version_codigo"), '') IS NOT NULL)
|
|
);--> statement-breakpoint
|
|
ALTER TABLE "cancion" ADD CONSTRAINT "cancion_version_no_se_vende"
|
|
CHECK ("version_de_id" IS NULL OR NOT "ala_venta");--> statement-breakpoint
|
|
ALTER TABLE "cancion" ADD CONSTRAINT "cancion_estado_conocido"
|
|
CHECK ("estado" IN ('borrador', 'publicado', 'retirado'));--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "cancion_version_codigo_unico"
|
|
ON "cancion" ("version_de_id", "version_codigo")
|
|
WHERE "version_de_id" IS NOT NULL;--> statement-breakpoint
|
|
|
|
-- La vista se creó con SELECT *, pero PostgreSQL fija sus columnas en ese
|
|
-- instante. Se recrea para que incluya codigo y los campos añadidos después.
|
|
DROP VIEW "cancion_principal";--> statement-breakpoint
|
|
CREATE VIEW "cancion_principal" AS
|
|
SELECT * FROM "cancion" WHERE "version_de_id" IS NULL;--> statement-breakpoint
|
|
|
|
CREATE SEQUENCE "artista_codigo_seq";--> statement-breakpoint
|
|
CREATE TABLE "artista" (
|
|
"id" text PRIMARY KEY NOT NULL,
|
|
"codigo" text DEFAULT ('ART-' || lpad(nextval('artista_codigo_seq')::text, 6, '0')) NOT NULL,
|
|
"slug" text NOT NULL,
|
|
"tipo" text DEFAULT 'persona' NOT NULL,
|
|
"nombre" text NOT NULL,
|
|
"descripcion_corta" text DEFAULT '' NOT NULL,
|
|
"biografia" text DEFAULT '' NOT NULL,
|
|
"imagen_id" text,
|
|
"pais_codigo" char(2),
|
|
"lugar_origen" text,
|
|
"inicio" integer,
|
|
"fin" integer,
|
|
"sitio_web" text,
|
|
"estado" text DEFAULT 'borrador' NOT NULL,
|
|
"origen" text DEFAULT 'manual' NOT NULL,
|
|
"modelo_origen" text,
|
|
"importado_en" timestamp with time zone,
|
|
"revisado_por" text,
|
|
"revisado_en" timestamp with time zone,
|
|
"creado_en" timestamp with time zone DEFAULT now() NOT NULL,
|
|
"actualizado_en" timestamp with time zone,
|
|
CONSTRAINT "artista_tipo_conocido" CHECK ("tipo" IN ('persona', 'grupo', 'proyecto')),
|
|
CONSTRAINT "artista_estado_conocido" CHECK ("estado" IN ('borrador', 'publicado', 'retirado')),
|
|
CONSTRAINT "artista_origen_conocido" CHECK ("origen" IN ('manual', 'importado')),
|
|
CONSTRAINT "artista_periodo_valido" CHECK ("fin" IS NULL OR "inicio" IS NULL OR "fin" >= "inicio"),
|
|
CONSTRAINT "artista_imagen_fk" FOREIGN KEY ("imagen_id") REFERENCES "medio"("id") ON DELETE set null,
|
|
CONSTRAINT "artista_pais_fk" FOREIGN KEY ("pais_codigo") REFERENCES "pais"("codigo")
|
|
);--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "artista_codigo_unico" ON "artista" ("codigo");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "artista_slug_unico" ON "artista" ("slug");--> statement-breakpoint
|
|
CREATE INDEX "artista_nombre" ON "artista" ("nombre");--> statement-breakpoint
|
|
CREATE INDEX "artista_estado" ON "artista" ("estado");--> statement-breakpoint
|
|
|
|
CREATE TABLE "artista_persona" (
|
|
"artista_id" text NOT NULL,
|
|
"persona_id" text NOT NULL,
|
|
"relacion" text NOT NULL,
|
|
"desde" integer,
|
|
"hasta" integer,
|
|
"orden" integer DEFAULT 0 NOT NULL,
|
|
CONSTRAINT "artista_persona_pk" PRIMARY KEY ("artista_id", "persona_id", "relacion"),
|
|
CONSTRAINT "artista_persona_relacion_conocida" CHECK ("relacion" IN ('identidad', 'miembro', 'fundador')),
|
|
CONSTRAINT "artista_persona_periodo_valido" CHECK ("hasta" IS NULL OR "desde" IS NULL OR "hasta" >= "desde"),
|
|
CONSTRAINT "artista_persona_artista_fk" FOREIGN KEY ("artista_id") REFERENCES "artista"("id") ON DELETE cascade,
|
|
CONSTRAINT "artista_persona_persona_fk" FOREIGN KEY ("persona_id") REFERENCES "persona"("id") ON DELETE cascade
|
|
);--> statement-breakpoint
|
|
CREATE INDEX "artista_persona_persona" ON "artista_persona" ("persona_id");--> statement-breakpoint
|
|
|
|
CREATE TABLE "artista_identificador" (
|
|
"artista_id" text NOT NULL,
|
|
"sistema" text NOT NULL,
|
|
"valor" text NOT NULL,
|
|
CONSTRAINT "artista_identificador_pk" PRIMARY KEY ("artista_id", "sistema"),
|
|
CONSTRAINT "artista_identificador_artista_fk" FOREIGN KEY ("artista_id") REFERENCES "artista"("id") ON DELETE cascade
|
|
);--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "artista_identificador_externo_unico"
|
|
ON "artista_identificador" ("sistema", "valor");--> statement-breakpoint
|
|
|
|
CREATE TABLE "cancion_interprete" (
|
|
"cancion_id" text NOT NULL,
|
|
"artista_id" text NOT NULL,
|
|
"rol" text DEFAULT 'principal' NOT NULL,
|
|
"instrumento" text,
|
|
"acreditado_como" text,
|
|
"orden" integer DEFAULT 0 NOT NULL,
|
|
CONSTRAINT "cancion_interprete_pk" PRIMARY KEY ("cancion_id", "artista_id", "rol"),
|
|
CONSTRAINT "cancion_interprete_rol_conocido" CHECK ("rol" IN ('principal', 'invitado', 'coros', 'instrumentista')),
|
|
CONSTRAINT "cancion_interprete_cancion_fk" FOREIGN KEY ("cancion_id") REFERENCES "cancion"("id") ON DELETE cascade,
|
|
CONSTRAINT "cancion_interprete_artista_fk" FOREIGN KEY ("artista_id") REFERENCES "artista"("id")
|
|
);--> statement-breakpoint
|
|
CREATE INDEX "cancion_interprete_artista" ON "cancion_interprete" ("artista_id");--> statement-breakpoint
|
|
|
|
CREATE TABLE "cancion_audio" (
|
|
"cancion_id" text NOT NULL,
|
|
"medio_id" text NOT NULL,
|
|
"uso" text NOT NULL,
|
|
"formato" text NOT NULL,
|
|
"bitrate_kbps" integer,
|
|
"frecuencia_hz" integer,
|
|
"bits_muestra" integer,
|
|
"canales" integer,
|
|
"orden" integer DEFAULT 0 NOT NULL,
|
|
CONSTRAINT "cancion_audio_pk" PRIMARY KEY ("cancion_id", "uso", "formato"),
|
|
CONSTRAINT "cancion_audio_uso_conocido" CHECK ("uso" IN ('stream', 'descarga', 'master')),
|
|
CONSTRAINT "cancion_audio_bitrate_valido" CHECK ("bitrate_kbps" IS NULL OR "bitrate_kbps" > 0),
|
|
CONSTRAINT "cancion_audio_frecuencia_valida" CHECK ("frecuencia_hz" IS NULL OR "frecuencia_hz" > 0),
|
|
CONSTRAINT "cancion_audio_bits_validos" CHECK ("bits_muestra" IS NULL OR "bits_muestra" > 0),
|
|
CONSTRAINT "cancion_audio_canales_validos" CHECK ("canales" IS NULL OR "canales" > 0),
|
|
CONSTRAINT "cancion_audio_cancion_fk" FOREIGN KEY ("cancion_id") REFERENCES "cancion"("id") ON DELETE cascade,
|
|
CONSTRAINT "cancion_audio_medio_fk" FOREIGN KEY ("medio_id") REFERENCES "medio"("id") ON DELETE restrict
|
|
);--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "cancion_audio_medio_unico" ON "cancion_audio" ("medio_id");--> statement-breakpoint
|
|
CREATE INDEX "cancion_audio_cancion" ON "cancion_audio" ("cancion_id");--> statement-breakpoint
|
|
|
|
-- Conserva los masters actuales en el nuevo modelo. Las muestras recortadas no
|
|
-- se migran como stream: el nuevo stream tiene que contener la canción completa.
|
|
INSERT INTO cancion_audio (cancion_id, medio_id, uso, formato)
|
|
SELECT c.id, c.audio_id, 'master',
|
|
CASE
|
|
WHEN m.mime IN ('audio/mpeg', 'audio/mp3') THEN 'mp3'
|
|
WHEN m.mime IN ('audio/wav', 'audio/x-wav') THEN 'wav'
|
|
WHEN m.mime = 'audio/flac' THEN 'flac'
|
|
WHEN m.mime IN ('audio/aac', 'audio/mp4') THEN 'aac'
|
|
ELSE 'audio'
|
|
END
|
|
FROM cancion c
|
|
INNER JOIN medio m ON m.id = c.audio_id
|
|
WHERE c.audio_id IS NOT NULL
|
|
ON CONFLICT DO NOTHING;--> statement-breakpoint
|
|
|
|
-- Migra cada nombre acreditado como intérprete a una identidad artística. Los
|
|
-- dos nombres colectivos conocidos se clasifican explícitamente; las personas
|
|
-- quedan vinculadas a su fila legal/biográfica actual.
|
|
INSERT INTO artista (
|
|
id, slug, tipo, nombre, biografia, imagen_id, pais_codigo, estado, origen,
|
|
modelo_origen, importado_en, revisado_por, revisado_en
|
|
)
|
|
SELECT gen_random_uuid()::text, p.slug,
|
|
CASE
|
|
WHEN p.nombre = 'Los del Puerto' THEN 'grupo'
|
|
WHEN p.nombre = 'Senza Paura' THEN 'proyecto'
|
|
ELSE 'persona'
|
|
END,
|
|
p.nombre, COALESCE(p.semblanza, ''), p.retrato_id, p.pais_codigo, p.estado,
|
|
p.origen, p.modelo_origen, p.importado_en, p.revisado_por, p.revisado_en
|
|
FROM persona p
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM cancion_persona cp
|
|
WHERE cp.persona_id = p.id AND cp.rol = 'interprete'
|
|
);--> statement-breakpoint
|
|
|
|
INSERT INTO artista_persona (artista_id, persona_id, relacion, orden)
|
|
SELECT a.id, p.id, 'identidad', 0
|
|
FROM artista a
|
|
INNER JOIN persona p ON p.slug = a.slug
|
|
WHERE a.tipo = 'persona';--> statement-breakpoint
|
|
|
|
INSERT INTO cancion_interprete (
|
|
cancion_id, artista_id, rol, acreditado_como, orden
|
|
)
|
|
SELECT cp.cancion_id, a.id, 'principal', p.nombre, cp.orden
|
|
FROM cancion_persona cp
|
|
INNER JOIN persona p ON p.id = cp.persona_id
|
|
INNER JOIN artista a ON a.slug = p.slug
|
|
WHERE cp.rol = 'interprete'
|
|
ON CONFLICT DO NOTHING;
|
|
--> statement-breakpoint
|
|
|
|
-- El legado solo acreditaba la principal. Una versión empieza heredando una
|
|
-- copia de esos créditos, que después puede editarse de forma independiente.
|
|
INSERT INTO cancion_interprete (
|
|
cancion_id, artista_id, rol, instrumento, acreditado_como, orden
|
|
)
|
|
SELECT v.id, ci.artista_id, ci.rol, ci.instrumento, ci.acreditado_como, ci.orden
|
|
FROM cancion v
|
|
INNER JOIN cancion_interprete ci ON ci.cancion_id = v.version_de_id
|
|
WHERE v.version_de_id IS NOT NULL
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM cancion_interprete propia WHERE propia.cancion_id = v.id
|
|
)
|
|
ON CONFLICT DO NOTHING;
|