1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-22 08:56:17 +00:00

Use hex-encoded string for id instead of bytes on DBMSs that don't provide native UUID support

Closes #241
This commit is contained in:
Alban Féron 2023-01-11 23:12:13 +01:00
parent ba1fbf4b73
commit 4bbd7e94b0
No known key found for this signature in database
GPG Key ID: 8CE0313646D16165
4 changed files with 152 additions and 27 deletions

View File

@ -15,7 +15,6 @@ from datetime import datetime
from hashlib import sha1 from hashlib import sha1
from peewee import ( from peewee import (
AutoField, AutoField,
BinaryUUIDField,
BlobField, BlobField,
BooleanField, BooleanField,
CharField, CharField,
@ -24,6 +23,7 @@ from peewee import (
ForeignKeyField, ForeignKeyField,
IntegerField, IntegerField,
TextField, TextField,
UUIDField,
) )
from peewee import CompositeKey, DatabaseProxy, Model, MySQLDatabase from peewee import CompositeKey, DatabaseProxy, Model, MySQLDatabase
from peewee import fn from peewee import fn
@ -31,7 +31,7 @@ from playhouse.db_url import parseresult_to_dict, schemes
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import UUID, uuid4 from uuid import UUID, uuid4
SCHEMA_VERSION = "20200607" SCHEMA_VERSION = "20230111"
def now(): def now():
@ -45,7 +45,7 @@ def random():
def PrimaryKeyField(**kwargs): def PrimaryKeyField(**kwargs):
return BinaryUUIDField(primary_key=True, default=uuid4, **kwargs) return UUIDField(primary_key=True, default=uuid4, **kwargs)
db = DatabaseProxy() db = DatabaseProxy()

View File

@ -0,0 +1,75 @@
# This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API.
#
# Copyright (C) 2023 Alban 'spl0k' Féron
#
# Distributed under terms of the GNU AGPLv3 license.
# Converts ids from binary data to hex-encoded strings
try:
import MySQLdb as provider
except ImportError:
import pymysql as provider
from uuid import UUID
from warnings import filterwarnings
def process_table(connection, table, fields, nullable_fields=()):
to_update = {field: set() for field in fields + nullable_fields}
c = connection.cursor()
c.execute("SELECT {1} FROM {0}".format(table, ",".join(fields + nullable_fields)))
for row in c:
for field, value in zip(fields + nullable_fields, row):
if value is None or not isinstance(value, bytes):
continue
to_update[field].add(value)
for field in fields:
sql = "ALTER TABLE {} MODIFY {} BINARY(32) NOT NULL".format(table, field)
c.execute(sql)
for field in nullable_fields:
sql = "ALTER TABLE {} MODIFY {} BINARY(32)".format(table, field)
c.execute(sql)
for field, values in to_update.items():
if not values:
continue
sql = "UPDATE {0} SET {1}=%s WHERE {1}=%s".format(table, field)
c.executemany(
sql, map(lambda v: (UUID(bytes=v).hex, v + (b"\x00" * 16)), values)
)
for field in fields:
sql = "ALTER TABLE {} MODIFY {} CHAR(32) NOT NULL".format(table, field)
c.execute(sql)
for field in nullable_fields:
sql = "ALTER TABLE {} MODIFY {} CHAR(32)".format(table, field)
c.execute(sql)
connection.commit()
def apply(args):
filterwarnings("ignore", category=provider.Warning)
conn = provider.connect(**args)
conn.cursor().execute("SET FOREIGN_KEY_CHECKS = 0")
process_table(conn, "artist", ("id",))
process_table(conn, "album", ("id", "artist_id"))
process_table(conn, "track", ("id", "album_id", "artist_id"))
process_table(conn, "user", ("id",), ("last_play_id",))
process_table(conn, "client_prefs", ("user_id",))
process_table(conn, "starred_folder", ("user_id",))
process_table(conn, "starred_artist", ("user_id", "starred_id"))
process_table(conn, "starred_album", ("user_id", "starred_id"))
process_table(conn, "starred_track", ("user_id", "starred_id"))
process_table(conn, "rating_folder", ("user_id",))
process_table(conn, "rating_track", ("user_id", "rated_id"))
process_table(conn, "chat_message", ("id", "user_id"))
process_table(conn, "playlist", ("id", "user_id"))
process_table(conn, "radio_station", ("id",))
conn.cursor().execute("SET FOREIGN_KEY_CHECKS = 1")
conn.close()

View File

@ -0,0 +1,50 @@
# This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API.
#
# Copyright (C) 2023 Alban 'spl0k' Féron
#
# Distributed under terms of the GNU AGPLv3 license.
# Converts ids from binary data to hex-encoded strings
import sqlite3
from uuid import UUID
def process_table(connection, table, fields):
to_update = {field: set() for field in fields}
c = connection.cursor()
for row in c.execute("SELECT {1} FROM {0}".format(table, ",".join(fields))):
for field, value in zip(fields, row):
if value is None or not isinstance(value, bytes):
continue
to_update[field].add(value)
for field, values in to_update.items():
sql = "UPDATE {0} SET {1}=? WHERE {1}=?".format(table, field)
c.executemany(sql, map(lambda v: (UUID(bytes=v).hex, v), values))
connection.commit()
def apply(args):
file = args.pop("database")
with sqlite3.connect(file, **args) as conn:
conn.cursor().execute("PRAGMA foreign_keys = OFF")
process_table(conn, "artist", ("id",))
process_table(conn, "album", ("id", "artist_id"))
process_table(conn, "track", ("id", "album_id", "artist_id"))
process_table(conn, "user", ("id", "last_play_id"))
process_table(conn, "client_prefs", ("user_id",))
process_table(conn, "starred_folder", ("user_id",))
process_table(conn, "starred_artist", ("user_id", "starred_id"))
process_table(conn, "starred_album", ("user_id", "starred_id"))
process_table(conn, "starred_track", ("user_id", "starred_id"))
process_table(conn, "rating_folder", ("user_id",))
process_table(conn, "rating_track", ("user_id", "rated_id"))
process_table(conn, "chat_message", ("id", "user_id"))
process_table(conn, "playlist", ("id", "user_id"))
process_table(conn, "radio_station", ("id",))

View File

@ -12,19 +12,19 @@ CREATE TABLE IF NOT EXISTS folder (
CREATE INDEX index_folder_parent_id_fk ON folder(parent_id); CREATE INDEX index_folder_parent_id_fk ON folder(parent_id);
CREATE TABLE IF NOT EXISTS artist ( CREATE TABLE IF NOT EXISTS artist (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
name VARCHAR(256) NOT NULL name VARCHAR(256) NOT NULL
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS album ( CREATE TABLE IF NOT EXISTS album (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
name VARCHAR(256) NOT NULL, name VARCHAR(256) NOT NULL,
artist_id BINARY(16) NOT NULL REFERENCES artist(id) artist_id CHAR(32) NOT NULL REFERENCES artist(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX index_album_artist_id_fk ON album(artist_id); CREATE INDEX index_album_artist_id_fk ON album(artist_id);
CREATE TABLE IF NOT EXISTS track ( CREATE TABLE IF NOT EXISTS track (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
disc INTEGER NOT NULL, disc INTEGER NOT NULL,
number INTEGER NOT NULL, number INTEGER NOT NULL,
title VARCHAR(256) NOT NULL, title VARCHAR(256) NOT NULL,
@ -32,8 +32,8 @@ CREATE TABLE IF NOT EXISTS track (
genre VARCHAR(256), genre VARCHAR(256),
duration INTEGER NOT NULL, duration INTEGER NOT NULL,
has_art BOOLEAN NOT NULL DEFAULT false, has_art BOOLEAN NOT NULL DEFAULT false,
album_id BINARY(16) NOT NULL REFERENCES album(id), album_id CHAR(32) NOT NULL REFERENCES album(id),
artist_id BINARY(16) NOT NULL REFERENCES artist(id), artist_id CHAR(32) NOT NULL REFERENCES artist(id),
bitrate INTEGER NOT NULL, bitrate INTEGER NOT NULL,
path VARCHAR(4096) NOT NULL, path VARCHAR(4096) NOT NULL,
path_hash BINARY(20) UNIQUE NOT NULL, path_hash BINARY(20) UNIQUE NOT NULL,
@ -50,7 +50,7 @@ CREATE INDEX index_track_folder_id_fk ON track(folder_id);
CREATE INDEX index_track_root_folder_id_fk ON track(root_folder_id); CREATE INDEX index_track_root_folder_id_fk ON track(root_folder_id);
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE IF NOT EXISTS user (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
name VARCHAR(64) NOT NULL, name VARCHAR(64) NOT NULL,
mail VARCHAR(256), mail VARCHAR(256),
password CHAR(40) NOT NULL, password CHAR(40) NOT NULL,
@ -59,13 +59,13 @@ CREATE TABLE IF NOT EXISTS user (
jukebox BOOLEAN NOT NULL, jukebox BOOLEAN NOT NULL,
lastfm_session CHAR(32), lastfm_session CHAR(32),
lastfm_status BOOLEAN NOT NULL, lastfm_status BOOLEAN NOT NULL,
last_play_id BINARY(16) REFERENCES track(id), last_play_id CHAR(32) REFERENCES track(id),
last_play_date DATETIME last_play_date DATETIME
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX index_user_last_play_id_fk ON user(last_play_id); CREATE INDEX index_user_last_play_id_fk ON user(last_play_id);
CREATE TABLE IF NOT EXISTS client_prefs ( CREATE TABLE IF NOT EXISTS client_prefs (
user_id BINARY(16) NOT NULL, user_id CHAR(32) NOT NULL,
client_name VARCHAR(32) NOT NULL, client_name VARCHAR(32) NOT NULL,
format VARCHAR(8), format VARCHAR(8),
bitrate INTEGER, bitrate INTEGER,
@ -73,7 +73,7 @@ CREATE TABLE IF NOT EXISTS client_prefs (
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS starred_folder ( CREATE TABLE IF NOT EXISTS starred_folder (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
starred_id INTEGER NOT NULL REFERENCES folder(id), starred_id INTEGER NOT NULL REFERENCES folder(id),
date DATETIME NOT NULL, date DATETIME NOT NULL,
PRIMARY KEY (user_id, starred_id) PRIMARY KEY (user_id, starred_id)
@ -82,8 +82,8 @@ CREATE INDEX index_starred_folder_user_id_fk ON starred_folder(user_id);
CREATE INDEX index_starred_folder_starred_id_fk ON starred_folder(starred_id); CREATE INDEX index_starred_folder_starred_id_fk ON starred_folder(starred_id);
CREATE TABLE IF NOT EXISTS starred_artist ( CREATE TABLE IF NOT EXISTS starred_artist (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
starred_id BINARY(16) NOT NULL REFERENCES artist(id), starred_id CHAR(32) NOT NULL REFERENCES artist(id),
date DATETIME NOT NULL, date DATETIME NOT NULL,
PRIMARY KEY (user_id, starred_id) PRIMARY KEY (user_id, starred_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -91,8 +91,8 @@ CREATE INDEX index_starred_artist_user_id_fk ON starred_artist(user_id);
CREATE INDEX index_starred_artist_starred_id_fk ON starred_artist(starred_id); CREATE INDEX index_starred_artist_starred_id_fk ON starred_artist(starred_id);
CREATE TABLE IF NOT EXISTS starred_album ( CREATE TABLE IF NOT EXISTS starred_album (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
starred_id BINARY(16) NOT NULL REFERENCES album(id), starred_id CHAR(32) NOT NULL REFERENCES album(id),
date DATETIME NOT NULL, date DATETIME NOT NULL,
PRIMARY KEY (user_id, starred_id) PRIMARY KEY (user_id, starred_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -100,8 +100,8 @@ CREATE INDEX index_starred_album_user_id_fk ON starred_album(user_id);
CREATE INDEX index_starred_album_starred_id_fk ON starred_album(starred_id); CREATE INDEX index_starred_album_starred_id_fk ON starred_album(starred_id);
CREATE TABLE IF NOT EXISTS starred_track ( CREATE TABLE IF NOT EXISTS starred_track (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
starred_id BINARY(16) NOT NULL REFERENCES track(id), starred_id CHAR(32) NOT NULL REFERENCES track(id),
date DATETIME NOT NULL, date DATETIME NOT NULL,
PRIMARY KEY (user_id, starred_id) PRIMARY KEY (user_id, starred_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -109,7 +109,7 @@ CREATE INDEX index_starred_track_user_id_fk ON starred_track(user_id);
CREATE INDEX index_starred_track_starred_id_fk ON starred_track(starred_id); CREATE INDEX index_starred_track_starred_id_fk ON starred_track(starred_id);
CREATE TABLE IF NOT EXISTS rating_folder ( CREATE TABLE IF NOT EXISTS rating_folder (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
rated_id INTEGER NOT NULL REFERENCES folder(id), rated_id INTEGER NOT NULL REFERENCES folder(id),
rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5),
PRIMARY KEY (user_id, rated_id) PRIMARY KEY (user_id, rated_id)
@ -118,8 +118,8 @@ CREATE INDEX index_rating_folder_user_id_fk ON rating_folder(user_id);
CREATE INDEX index_rating_folder_rated_id_fk ON rating_folder(rated_id); CREATE INDEX index_rating_folder_rated_id_fk ON rating_folder(rated_id);
CREATE TABLE IF NOT EXISTS rating_track ( CREATE TABLE IF NOT EXISTS rating_track (
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
rated_id BINARY(16) NOT NULL REFERENCES track(id), rated_id CHAR(32) NOT NULL REFERENCES track(id),
rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5),
PRIMARY KEY (user_id, rated_id) PRIMARY KEY (user_id, rated_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -127,16 +127,16 @@ CREATE INDEX index_rating_track_user_id_fk ON rating_track(user_id);
CREATE INDEX index_rating_track_rated_id_fk ON rating_track(rated_id); CREATE INDEX index_rating_track_rated_id_fk ON rating_track(rated_id);
CREATE TABLE IF NOT EXISTS chat_message ( CREATE TABLE IF NOT EXISTS chat_message (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
time INTEGER NOT NULL, time INTEGER NOT NULL,
message VARCHAR(512) NOT NULL message VARCHAR(512) NOT NULL
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX index_chat_message_user_id_fk ON chat_message(user_id); CREATE INDEX index_chat_message_user_id_fk ON chat_message(user_id);
CREATE TABLE IF NOT EXISTS playlist ( CREATE TABLE IF NOT EXISTS playlist (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
user_id BINARY(16) NOT NULL REFERENCES user(id), user_id CHAR(32) NOT NULL REFERENCES user(id),
name VARCHAR(256) NOT NULL, name VARCHAR(256) NOT NULL,
comment VARCHAR(256), comment VARCHAR(256),
public BOOLEAN NOT NULL, public BOOLEAN NOT NULL,
@ -151,7 +151,7 @@ CREATE TABLE meta (
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS radio_station ( CREATE TABLE IF NOT EXISTS radio_station (
id BINARY(16) PRIMARY KEY, id CHAR(32) PRIMARY KEY,
stream_url VARCHAR(256) NOT NULL, stream_url VARCHAR(256) NOT NULL,
name VARCHAR(256) NOT NULL, name VARCHAR(256) NOT NULL,
homepage_url VARCHAR(256), homepage_url VARCHAR(256),