Changed the core schema to use UUIDs instead of SERIALs for all record IDs.

This commit is contained in:
Jonathan Bernard 2016-03-30 01:08:27 -05:00
parent 8ea88337b0
commit 9c007b3de5
7 changed files with 101 additions and 63 deletions

View File

@ -133,11 +133,14 @@ public class MediaLibrary {
public def getByIdOrName(Class modelClass, String input) { public def getByIdOrName(Class modelClass, String input) {
def match def match
if (safeToInteger(input)) {
match = dbapi.getById(modelClass, safeToInteger(input)) if (safeToUUID(input)) {
match = dbapi.getById(modelClass, safeToUUID(input))
if (match) match = [match] } if (match) match = [match] }
else { else {
match = dbapi.getByName(modelClass, input) match = dbapi.getByIdLike(modelClass, input)
if (!match) match = dbapi.getByName(modelClass, input)
if (!match) match = dbapi.getLike(modelClass, ["name"], [input]) } if (!match) match = dbapi.getLike(modelClass, ["name"], [input]) }
return match } return match }
@ -252,6 +255,11 @@ public class MediaLibrary {
if (b != parentPath.length) return "" if (b != parentPath.length) return ""
return (['.'] + childPath[b..<childPath.length]).join('/') } return (['.'] + childPath[b..<childPath.length]).join('/') }
public static UUID safeToUUID(def val) {
if (val == null) return null
try { return UUID.fromString(val as String) }
catch (IllegalArgumentException iae) { return null } }
public static Integer safeToInteger(def val) { public static Integer safeToInteger(def val) {
if (val == null) return null if (val == null) return null
try { return val.trim() as Integer } try { return val.trim() as Integer }

View File

@ -46,11 +46,32 @@ public class DbApi {
logger.debug('Selecting models.\n\tSQL: {}', query) logger.debug('Selecting models.\n\tSQL: {}', query)
return sql.rows(query).collect { recordToModel(modelClass, it) } } return sql.rows(query).collect { recordToModel(modelClass, it) } }
public def getById(Class modelClass, int id) { public List<UUID> getAllIds(Class modelClass) {
String query = 'SELECT id FROM ' +
pluralize(nameFromModel(modelClass.simpleName))
logger.debug('Selecting model ids.\n\tSQL: {}', query)
return sql.rows(query).collect { it.id } }
public Model getById(Class<? extends Model> modelClass, UUID id) {
def model = modelClass.newInstance() def model = modelClass.newInstance()
model.id = id model.id = id
return refresh(model) } return refresh(model) }
public List<Model> getByIdLike(Class<? extends Model> modelClass,
String partialId) {
String likeVal = partialId + '%'
String query = new StringBuilder()
.append('SELECT * FROM ')
.append(pluralize(nameFromModel(modelClass.simpleName)))
.append(' WHERE id LIKE ?')
.toString()
logger.debug('Selectin models like id.\n\tSQL: {}\n\tPARAMS: {}',
query, likeVal)
return sql.rows(query, likeVal)
.collect { recordToModel(modelClass, it) } }
public def refresh(def model) { public def refresh(def model) {
def query = new StringBuilder() def query = new StringBuilder()
.append('SELECT * FROM ') .append('SELECT * FROM ')
@ -110,7 +131,7 @@ public class DbApi {
case Tag: return getTagsWhere(criteria) } } case Tag: return getTagsWhere(criteria) } }
public def save(def model) { public def save(def model) {
if (model.id > 0) return update(model) if (model.id) return update(model)
else return create(model) } else return create(model) }
public def update(def model) { public def update(def model) {
@ -142,9 +163,8 @@ public class DbApi {
def columns = [] def columns = []
def params = [] def params = []
getInstanceFields(model.class) if (!model.id) model.id = UUID.randomUUID()
.findAll { it.name != 'id' } getInstanceFields(model.class).each { field ->
.each { field ->
//if (field.class.getAnnotation(Entity)) // check to see if we //if (field.class.getAnnotation(Entity)) // check to see if we
// have nested models // have nested models
columns << '"' + nameFromModel(field.name) + '"' columns << '"' + nameFromModel(field.name) + '"'
@ -160,7 +180,7 @@ public class DbApi {
.append(')').toString() .append(')').toString()
logger.debug('Creating model.\n\tSQL: {}\n\tPARAMS: {}', query, params) logger.debug('Creating model.\n\tSQL: {}\n\tPARAMS: {}', query, params)
model.id = sql.executeInsert(query, params)[0][0] sql.executeInsert(query, params)
return model } return model }
public def delete(def model) { public def delete(def model) {
@ -176,7 +196,7 @@ public class DbApi {
return sql.updateCount } return sql.updateCount }
public def associate(Class modelClass1, Class modelClass2 , public def associate(Class modelClass1, Class modelClass2 ,
Integer firstId, Integer secondId) { UUID firstId, UUID secondId) {
String linkTable = pluralize(nameFromModel(modelClass1.simpleName)) + String linkTable = pluralize(nameFromModel(modelClass1.simpleName)) +
'_' + pluralize(nameFromModel(modelClass2.simpleName)) '_' + pluralize(nameFromModel(modelClass2.simpleName))
String col1 = nameFromModel(modelClass1.simpleName) + '_id' String col1 = nameFromModel(modelClass1.simpleName) + '_id'
@ -203,7 +223,7 @@ public class DbApi {
/// ### Album-specific methods /// ### Album-specific methods
// ======================================================================= // =======================================================================
public Album getAlbumById(int id) { return getById(Album, id) } public Album getAlbumById(UUID id) { return getById(Album, id) }
public List<Album> getAlbums() { return getAll(Album) } public List<Album> getAlbums() { return getAll(Album) }
@ -268,7 +288,7 @@ public class DbApi {
/// ### Artist-specific methods /// ### Artist-specific methods
public List<Artist> getArtists() { return getAll(Artist) } public List<Artist> getArtists() { return getAll(Artist) }
public Artist getArtistById(int id) { return getById(Artist, id) } public Artist getArtistById(UUID id) { return getById(Artist, id) }
public Artist getArtistByName(String name) { public Artist getArtistByName(String name) {
def artists = getByName(Artist, name) def artists = getByName(Artist, name)
@ -314,7 +334,7 @@ public class DbApi {
public List<Artist> getArtistsLikeName(String name) { public List<Artist> getArtistsLikeName(String name) {
return getLike(Artist, ['name'], [name]) } return getLike(Artist, ['name'], [name]) }
public def addAlbumArtist(int albumId, int artistId) { public def addAlbumArtist(UUID albumId, UUID artistId) {
return associate(Artist, Album, artistId, albumId) } return associate(Artist, Album, artistId, albumId) }
public void removeEmptyArtists() { public void removeEmptyArtists() {
@ -330,7 +350,7 @@ public class DbApi {
sql.execute(query) } sql.execute(query) }
/// ### Bookmark-specific methods /// ### Bookmark-specific methods
public Bookmark getBookmarkById(int id) { return getById(Bookmark, id) } public Bookmark getBookmarkById(UUID id) { return getById(Bookmark, id) }
public List<Bookmark> getBookmarkByName(String name) { public List<Bookmark> getBookmarkByName(String name) {
return getByName(Bookmark, name) } return getByName(Bookmark, name) }
@ -394,10 +414,10 @@ public class DbApi {
.collect { recordToModel(Bookmark, it) } } .collect { recordToModel(Bookmark, it) } }
/// ### Image-specific methods /// ### Image-specific methods
public Image getImageById(int id) { return getById(Image, id) } public Image getImageById(UUID id) { return getById(Image, id) }
/// ### MediaFile-specific methods /// ### MediaFile-specific methods
public MediaFile getMediaFileById(int id) { return getById(MediaFile, id) } public MediaFile getMediaFileById(UUID id) { return getById(MediaFile, id) }
public List<MediaFile> getMediaFileByName(String name) { public List<MediaFile> getMediaFileByName(String name) {
return getByName(MediaFile, name) } return getByName(MediaFile, name) }
@ -460,21 +480,25 @@ public class DbApi {
sqlParams << params.name } sqlParams << params.name }
if (params.discNumber) { if (params.discNumber) {
query.append('mf.disc_number = ?') whereClauses << 'mf.disc_number = ?'
sqlParams << params.discNumber } sqlParams << params.discNumber }
if (params.trackNumber) { if (params.trackNumber) {
query.append('mf.track_number = ?') whereClauses << 'mf.track_number = ?'
sqlParams << params.trackNumber } sqlParams << params.trackNumber }
if (params.filePath) { if (params.filePath) {
query.append('mf.file_path = ?') whereClauses << 'mf.file_path = ?'
sqlParams << params.filePath } sqlParams << params.filePath }
if (params.fileHash) { if (params.fileHash) {
query.append('mf.file_hash = ?') whereClauses << 'mf.file_hash = ?'
sqlParams << params.fileHash } sqlParams << params.fileHash }
if (params.containsKey('presentLocally')) {
whereClauses << 'mf.present_locally = ?'
sqlParams << params.presentLocally }
if (params.tags) { if (params.tags) {
params.tags.eachWithIndex { tag, idx -> params.tags.eachWithIndex { tag, idx ->
String L = "mft${idx}"; String L = "mft${idx}";
@ -518,13 +542,13 @@ public class DbApi {
logger.debug('Finding untagged media files.\n\tSQL: {}', query) logger.debug('Finding untagged media files.\n\tSQL: {}', query)
return sql.rows(query).collect { recordToModel(MediaFile, it) } } return sql.rows(query).collect { recordToModel(MediaFile, it) } }
public def associateMediaFileWithAlbum(int mediaFileId, int albumId) { public def associateMediaFileWithAlbum(UUID mediaFileId, UUID albumId) {
return associate(Album, MediaFile, albumId, mediaFileId) } return associate(Album, MediaFile, albumId, mediaFileId) }
public def associateMediaFileWithArtist(int mediaFileId, int artistId) { public def associateMediaFileWithArtist(UUID mediaFileId, UUID artistId) {
return associate(Album, MediaFile, artistId, mediaFileId) } return associate(Album, MediaFile, artistId, mediaFileId) }
public def incrementPlayCount(int mediaFileId) { public def incrementPlayCount(UUID mediaFileId) {
def query = 'UPDATE media_files SET play_count = play_count + 1 WHERE ID = ?' def query = 'UPDATE media_files SET play_count = play_count + 1 WHERE ID = ?'
def params = [mediaFileId] def params = [mediaFileId]
@ -541,7 +565,7 @@ public class DbApi {
/// ### Playlist-specific methods /// ### Playlist-specific methods
public List<Playlist> getPlaylists() { return getAll(Playlist) } public List<Playlist> getPlaylists() { return getAll(Playlist) }
public Playlist getPlaylistById(int id) { return getById(Playlist, id) } public Playlist getPlaylistById(UUID id) { return getById(Playlist, id) }
public List<Playlist> getPlaylistByName(String name) { public List<Playlist> getPlaylistByName(String name) {
return getByName(Playlist, name) } return getByName(Playlist, name) }
@ -613,7 +637,7 @@ public class DbApi {
return sql.rows(query, sqlParams) return sql.rows(query, sqlParams)
.collect { recordToModel(Playlist, it) } } .collect { recordToModel(Playlist, it) } }
public int getNextPlaylistPosition(int playlistId) { public int getNextPlaylistPosition(UUID playlistId) {
String query = """\ String query = """\
SELECT COALESCE(MAX(position), 0) + 1 SELECT COALESCE(MAX(position), 0) + 1
FROM playlists_media_files FROM playlists_media_files
@ -624,7 +648,7 @@ public class DbApi {
return sql.firstRow(query, playlistId)[0] } return sql.firstRow(query, playlistId)[0] }
private int incrementPlaylistPositions(int playlistId, int startPosition, private int incrementPlaylistPositions(UUID playlistId, int startPosition,
int incrementAmount) { int incrementAmount) {
String query = """\ String query = """\
UPDATE playlists_media_files UPDATE playlists_media_files
@ -636,8 +660,8 @@ public class DbApi {
query, params) query, params)
return sql.executeUpdate(query, params) } return sql.executeUpdate(query, params) }
public Playlist addToPlaylist(int playlistId, public Playlist addToPlaylist(UUID playlistId,
List<Integer> mediaFileIds, int startPosition = -1) { List<UUID> mediaFileIds, int startPosition = -1) {
String query String query
def params def params
@ -665,7 +689,7 @@ public class DbApi {
p.lastUsed = new Timestamp(new Date().time) p.lastUsed = new Timestamp(new Date().time)
return update(p) } } return update(p) } }
public Playlist addToPlaylist(int playlistId, int mediaFileId, public Playlist addToPlaylist(UUID playlistId, UUID mediaFileId,
int position = -1) { int position = -1) {
String query String query
@ -690,7 +714,7 @@ public class DbApi {
p.lastUsed = new Timestamp(new Date().time) p.lastUsed = new Timestamp(new Date().time)
return update(p) } } return update(p) } }
public Playlist removeFromPlaylist(int playlistId, int mediaFileId) { public Playlist removeFromPlaylist(UUID playlistId, UUID mediaFileId) {
String getPositionQuery = """\ String getPositionQuery = """\
SELECT position FROM playlists_media_files SELECT position FROM playlists_media_files
WHERE playlist_id = ? AND media_file_id = ?""" WHERE playlist_id = ? AND media_file_id = ?"""
@ -735,7 +759,7 @@ public class DbApi {
return getById(Playlist, playlistId) } } return getById(Playlist, playlistId) } }
public Playlist removeAllFromPlaylist(int playlistId) { public Playlist removeAllFromPlaylist(UUID playlistId) {
return withTransaction { return withTransaction {
def p = getById(Playlist, playlistId) def p = getById(Playlist, playlistId)
if (!p) return null if (!p) return null
@ -762,7 +786,7 @@ public class DbApi {
sql.execute(query) } sql.execute(query) }
/// ### Tag-specific methods /// ### Tag-specific methods
public Tag getTagById(int id) { return getById(Tag, id) } public Tag getTagById(UUID id) { return getById(Tag, id) }
public Tag getTagByName(String name) { return getByName(Tag, name)[0] } public Tag getTagByName(String name) { return getByName(Tag, name)[0] }
public List<Tag> getTags() { return getAll(Tag, 'name ASC') } public List<Tag> getTags() { return getAll(Tag, 'name ASC') }
@ -807,7 +831,7 @@ public class DbApi {
query, sqlParams) query, sqlParams)
return sql.rows(query, sqlParams).collect { recordToModel(Tag, it) } } return sql.rows(query, sqlParams).collect { recordToModel(Tag, it) } }
public def tagMediaFiles(List<Integer> mediaFileIds, List<String> tagNames) { public def tagMediaFiles(List<UUID> mediaFileIds, List<String> tagNames) {
String insertQuery = 'INSERT INTO media_files_tags VALUES (?, ?)' String insertQuery = 'INSERT INTO media_files_tags VALUES (?, ?)'
String checkQuery = 'SELECT * FROM media_files_tags WHERE media_file_id = ? AND tag_id = ?' String checkQuery = 'SELECT * FROM media_files_tags WHERE media_file_id = ? AND tag_id = ?'
def params def params
@ -828,8 +852,7 @@ public class DbApi {
params) params)
sql.executeInsert(insertQuery, params) } } } } } sql.executeInsert(insertQuery, params) } } } } }
public def untagMediaFiles(List<Integer> mediaFileIds, public def untagMediaFiles(List<UUID> mediaFileIds, List<String> tagNames) {
List<String> tagNames) {
withTransaction { withTransaction {
List<Tag> tags = tagNames.collect(this.&getTagByName).findAll() List<Tag> tags = tagNames.collect(this.&getTagByName).findAll()

View File

@ -2,11 +2,12 @@ package com.jdbernard.wdiwtlt.db.models;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Date; import java.util.Date;
import java.util.UUID;
public class Bookmark extends Model { public class Bookmark extends Model {
public String name; public String name;
public int playlistId; public UUID playlistId;
public int mediaFileId; public UUID mediaFileId;
public int playIndex; public int playIndex;
public int playTimeMs = 0; public int playTimeMs = 0;
public boolean userCreated; public boolean userCreated;

View File

@ -16,6 +16,7 @@ public class MediaFile extends Model {
public String metaInfoSource = TAG_INFO; public String metaInfoSource = TAG_INFO;
public Timestamp dateAdded = new Timestamp(new Date().getTime()); public Timestamp dateAdded = new Timestamp(new Date().getTime());
public Timestamp lastPlayed; public Timestamp lastPlayed;
public boolean presentLocally = true;
public String comment; public String comment;
public String toString() { public String toString() {

View File

@ -1,16 +1,19 @@
package com.jdbernard.wdiwtlt.db.models; package com.jdbernard.wdiwtlt.db.models;
import java.util.UUID;
import javax.persistence.Entity; import javax.persistence.Entity;
@Entity @Entity
public class Model implements Comparable<Model> { public class Model implements Comparable<Model> {
public Integer id; public UUID id;
public boolean equals(Object thatObj) { public boolean equals(Object thatObj) {
if (thatObj == null) return false; if (thatObj == null) return false;
if (this.getClass() != thatObj.getClass()) return false; if (this.getClass() != thatObj.getClass()) return false;
Model that = (Model) thatObj; Model that = (Model) thatObj;
if (this.id == null || that.id == null) return false;
return this.id.equals(that.id); } return this.id.equals(that.id); }
public int compareTo(Model that) { public int compareTo(Model that) {

View File

@ -2,12 +2,13 @@ package com.jdbernard.wdiwtlt.db.models;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Date; import java.util.Date;
import java.util.UUID;
public class Playlist extends Model { public class Playlist extends Model {
public boolean userCreated = false; public boolean userCreated = false;
public String name; public String name;
public int mediaFileCount = 0; public int mediaFileCount = 0;
public Integer copiedFromId = null; public UUID copiedFromId = null;
public Timestamp createdAt = new Timestamp(new Date().getTime()); public Timestamp createdAt = new Timestamp(new Date().getTime());
public Timestamp lastUsed = new Timestamp(new Date().getTime()); public Timestamp lastUsed = new Timestamp(new Date().getTime());

View File

@ -1,12 +1,12 @@
CREATE TABLE artists ( CREATE TABLE artists (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL name VARCHAR UNIQUE NOT NULL
); );
CREATE INDEX artists_name_idx ON artists(name); CREATE INDEX artists_name_idx ON artists(name);
CREATE TABLE albums ( CREATE TABLE albums (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR NOT NULL, name VARCHAR NOT NULL,
year INTEGER, year INTEGER,
track_total INTEGER track_total INTEGER
@ -15,7 +15,7 @@ CREATE TABLE albums (
CREATE INDEX albums_name_idx ON albums(name); CREATE INDEX albums_name_idx ON albums(name);
CREATE TABLE media_files ( CREATE TABLE media_files (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR NOT NULL, name VARCHAR NOT NULL,
disc_number VARCHAR NOT NULL DEFAULT '1', disc_number VARCHAR NOT NULL DEFAULT '1',
track_number INTEGER, track_number INTEGER,
@ -25,53 +25,54 @@ CREATE TABLE media_files (
meta_info_source VARCHAR NOT NULL, -- 'tag' or 'filesystem' meta_info_source VARCHAR NOT NULL, -- 'tag' or 'filesystem'
date_added TIMESTAMP NOT NULL DEFAULT NOW(), date_added TIMESTAMP NOT NULL DEFAULT NOW(),
last_played TIMESTAMP, last_played TIMESTAMP,
present_locally BOOLEAN NOT NULL DEFAULT TRUE,
comment VARCHAR DEFAULT '' comment VARCHAR DEFAULT ''
); );
CREATE INDEX media_files_name_idx ON media_files(name); CREATE INDEX media_files_name_idx ON media_files(name);
CREATE TABLE artists_media_files ( CREATE TABLE artists_media_files (
artist_id INTEGER NOT NULL REFERENCES artists(id) ON DELETE CASCADE, artist_id UUID NOT NULL REFERENCES artists(id) ON DELETE CASCADE ON UPDATE CASCADE,
media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (artist_id, media_file_id) PRIMARY KEY (artist_id, media_file_id)
); );
CREATE TABLE albums_media_files ( CREATE TABLE albums_media_files (
album_id INTEGER NOT NULL REFERENCES albums(id) ON DELETE CASCADE, album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE ON UPDATE CASCADE,
media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (album_id, media_file_id) PRIMARY KEY (album_id, media_file_id)
); );
CREATE TABLE tags ( CREATE TABLE tags (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL, name VARCHAR UNIQUE NOT NULL,
description VARCHAR NOT NULL DEFAULT '' description VARCHAR NOT NULL DEFAULT ''
); );
CREATE TABLE playlists ( CREATE TABLE playlists (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
user_created BOOLEAN NOT NULL DEFAULT FALSE, user_created BOOLEAN NOT NULL DEFAULT FALSE,
name VARCHAR NOT NULL, name VARCHAR NOT NULL,
media_file_count INTEGER NOT NULL DEFAULT 0, media_file_count INTEGER NOT NULL DEFAULT 0,
copied_from_id INTEGER DEFAULT NULL, copied_from_id UUID DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(), created_at TIMESTAMP NOT NULL DEFAULT NOW(),
last_used TIMESTAMP NOT NULL DEFAULT NOW() last_used TIMESTAMP NOT NULL DEFAULT NOW()
); );
CREATE TABLE playlists_media_files ( CREATE TABLE playlists_media_files (
playlist_id INTEGER NOT NULL REFERENCES playlists(id) ON DELETE CASCADE, playlist_id UUID NOT NULL REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE,
media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE,
position INTEGER NOT NULL DEFAULT 0, position INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (playlist_id, media_file_id, position), PRIMARY KEY (playlist_id, media_file_id, position),
UNIQUE (playlist_id, position) UNIQUE (playlist_id, position)
); );
CREATE TABLE bookmarks ( CREATE TABLE bookmarks (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR, name VARCHAR,
user_created BOOLEAN NOT NULL DEFAULT FALSE, user_created BOOLEAN NOT NULL DEFAULT FALSE,
playlist_id INTEGER NOT NULL REFERENCES playlists(id) ON DELETE CASCADE, playlist_id UUID NOT NULL REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE,
media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE,
play_index INTEGER NOT NULL, play_index INTEGER NOT NULL,
play_time_ms INTEGER NOT NULL DEFAULT 0, play_time_ms INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(), created_at TIMESTAMP NOT NULL DEFAULT NOW(),
@ -82,30 +83,30 @@ CREATE INDEX bookmarks_playlist_id_idx ON bookmarks (playlist_id);
CREATE INDEX bookmarks_media_file_id_idx ON bookmarks (media_file_id); CREATE INDEX bookmarks_media_file_id_idx ON bookmarks (media_file_id);
CREATE TABLE media_files_tags ( CREATE TABLE media_files_tags (
media_file_id INTEGER REFERENCES media_files(id) ON DELETE CASCADE, media_file_id UUID REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE,
tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE, tag_id UUID REFERENCES tags(id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (media_file_id, tag_id) PRIMARY KEY (media_file_id, tag_id)
); );
CREATE TABLE images ( CREATE TABLE images (
id SERIAL PRIMARY KEY, id UUID PRIMARY KEY,
url VARCHAR url VARCHAR
); );
CREATE TABLE artists_images ( CREATE TABLE artists_images (
artist_id INTEGER REFERENCES artists (id) ON DELETE CASCADE, artist_id UUID REFERENCES artists (id) ON DELETE CASCADE ON UPDATE CASCADE,
image_id INTEGER REFERENCES images (id) ON DELETE CASCADE, image_id UUID REFERENCES images (id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (artist_id, image_id) PRIMARY KEY (artist_id, image_id)
); );
CREATE TABLE albums_images ( CREATE TABLE albums_images (
album_id INTEGER REFERENCES albums (id) ON DELETE CASCADE, album_id UUID REFERENCES albums (id) ON DELETE CASCADE ON UPDATE CASCADE,
image_id INTEGER REFERENCES images (id) ON DELETE CASCADE, image_id UUID REFERENCES images (id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (album_id, image_id) PRIMARY KEY (album_id, image_id)
); );
CREATE TABLE artists_albums ( CREATE TABLE artists_albums (
artist_id INTEGER NOT NULL REFERENCES artists (id) ON DELETE CASCADE, artist_id UUID NOT NULL REFERENCES artists (id) ON DELETE CASCADE ON UPDATE CASCADE,
album_id INTEGER NOT NULL REFERENCES albums (id) ON DELETE CASCADE, album_id UUID NOT NULL REFERENCES albums (id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (artist_id, album_id) PRIMARY KEY (artist_id, album_id)
); );