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:
parent
ba1fbf4b73
commit
4bbd7e94b0
@ -15,7 +15,6 @@ from datetime import datetime
|
||||
from hashlib import sha1
|
||||
from peewee import (
|
||||
AutoField,
|
||||
BinaryUUIDField,
|
||||
BlobField,
|
||||
BooleanField,
|
||||
CharField,
|
||||
@ -24,6 +23,7 @@ from peewee import (
|
||||
ForeignKeyField,
|
||||
IntegerField,
|
||||
TextField,
|
||||
UUIDField,
|
||||
)
|
||||
from peewee import CompositeKey, DatabaseProxy, Model, MySQLDatabase
|
||||
from peewee import fn
|
||||
@ -31,7 +31,7 @@ from playhouse.db_url import parseresult_to_dict, schemes
|
||||
from urllib.parse import urlparse
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
SCHEMA_VERSION = "20200607"
|
||||
SCHEMA_VERSION = "20230111"
|
||||
|
||||
|
||||
def now():
|
||||
@ -45,7 +45,7 @@ def random():
|
||||
|
||||
|
||||
def PrimaryKeyField(**kwargs):
|
||||
return BinaryUUIDField(primary_key=True, default=uuid4, **kwargs)
|
||||
return UUIDField(primary_key=True, default=uuid4, **kwargs)
|
||||
|
||||
|
||||
db = DatabaseProxy()
|
||||
|
75
supysonic/schema/migration/mysql/20230111.py
Normal file
75
supysonic/schema/migration/mysql/20230111.py
Normal 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()
|
50
supysonic/schema/migration/sqlite/20230111.py
Normal file
50
supysonic/schema/migration/sqlite/20230111.py
Normal 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",))
|
@ -12,19 +12,19 @@ CREATE TABLE IF NOT EXISTS folder (
|
||||
CREATE INDEX index_folder_parent_id_fk ON folder(parent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS artist (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
name VARCHAR(256) NOT NULL
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS album (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
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;
|
||||
CREATE INDEX index_album_artist_id_fk ON album(artist_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS track (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
disc INTEGER NOT NULL,
|
||||
number INTEGER NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
@ -32,8 +32,8 @@ CREATE TABLE IF NOT EXISTS track (
|
||||
genre VARCHAR(256),
|
||||
duration INTEGER NOT NULL,
|
||||
has_art BOOLEAN NOT NULL DEFAULT false,
|
||||
album_id BINARY(16) NOT NULL REFERENCES album(id),
|
||||
artist_id BINARY(16) NOT NULL REFERENCES artist(id),
|
||||
album_id CHAR(32) NOT NULL REFERENCES album(id),
|
||||
artist_id CHAR(32) NOT NULL REFERENCES artist(id),
|
||||
bitrate INTEGER NOT NULL,
|
||||
path VARCHAR(4096) 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 TABLE IF NOT EXISTS user (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
mail VARCHAR(256),
|
||||
password CHAR(40) NOT NULL,
|
||||
@ -59,13 +59,13 @@ CREATE TABLE IF NOT EXISTS user (
|
||||
jukebox BOOLEAN NOT NULL,
|
||||
lastfm_session CHAR(32),
|
||||
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
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX index_user_last_play_id_fk ON user(last_play_id);
|
||||
|
||||
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,
|
||||
format VARCHAR(8),
|
||||
bitrate INTEGER,
|
||||
@ -73,7 +73,7 @@ CREATE TABLE IF NOT EXISTS client_prefs (
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
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),
|
||||
date DATETIME NOT NULL,
|
||||
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 TABLE IF NOT EXISTS starred_artist (
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
starred_id BINARY(16) NOT NULL REFERENCES artist(id),
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
starred_id CHAR(32) NOT NULL REFERENCES artist(id),
|
||||
date DATETIME NOT NULL,
|
||||
PRIMARY KEY (user_id, starred_id)
|
||||
) 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 TABLE IF NOT EXISTS starred_album (
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
starred_id BINARY(16) NOT NULL REFERENCES album(id),
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
starred_id CHAR(32) NOT NULL REFERENCES album(id),
|
||||
date DATETIME NOT NULL,
|
||||
PRIMARY KEY (user_id, starred_id)
|
||||
) 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 TABLE IF NOT EXISTS starred_track (
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
starred_id BINARY(16) NOT NULL REFERENCES track(id),
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
starred_id CHAR(32) NOT NULL REFERENCES track(id),
|
||||
date DATETIME NOT NULL,
|
||||
PRIMARY KEY (user_id, starred_id)
|
||||
) 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 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),
|
||||
rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5),
|
||||
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 TABLE IF NOT EXISTS rating_track (
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
rated_id BINARY(16) NOT NULL REFERENCES track(id),
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
rated_id CHAR(32) NOT NULL REFERENCES track(id),
|
||||
rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5),
|
||||
PRIMARY KEY (user_id, rated_id)
|
||||
) 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 TABLE IF NOT EXISTS chat_message (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
time INTEGER NOT NULL,
|
||||
message VARCHAR(512) NOT NULL
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX index_chat_message_user_id_fk ON chat_message(user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS playlist (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
user_id BINARY(16) NOT NULL REFERENCES user(id),
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
user_id CHAR(32) NOT NULL REFERENCES user(id),
|
||||
name VARCHAR(256) NOT NULL,
|
||||
comment VARCHAR(256),
|
||||
public BOOLEAN NOT NULL,
|
||||
@ -151,7 +151,7 @@ CREATE TABLE meta (
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS radio_station (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
stream_url VARCHAR(256) NOT NULL,
|
||||
name VARCHAR(256) NOT NULL,
|
||||
homepage_url VARCHAR(256),
|
||||
|
Loading…
Reference in New Issue
Block a user