diff --git a/MANIFEST.in b/MANIFEST.in index 238b63e..7a5f63a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include cgi-bin/* include config.sample include README.md +include supysonic/schema/* recursive-include supysonic/templates * recursive-include supysonic/static * diff --git a/supysonic/db.py b/supysonic/db.py index 260a4f3..0489686 100644 --- a/supysonic/db.py +++ b/supysonic/db.py @@ -10,13 +10,15 @@ import time import mimetypes import os.path +import pkg_resources from datetime import datetime from hashlib import sha1 from pony.orm import Database, Required, Optional, Set, PrimaryKey, LongStr -from pony.orm import ObjectNotFound +from pony.orm import ObjectNotFound, DatabaseError from pony.orm import buffer from pony.orm import min, max, avg, sum, exists +from pony.orm import db_session from uuid import UUID, uuid4 from .py23 import dict, strtype @@ -488,7 +490,7 @@ def parse_uri(database_uri): elif path[0] == '/': path = path[1:] - return dict(provider = 'sqlite', filename = path, **args) + return dict(provider = 'sqlite', filename = path, create_db = True, **args) elif uri.scheme in ('postgres', 'postgresql'): return dict(provider = 'postgres', user = uri.username, password = uri.password, host = uri.hostname, dbname = uri.path[1:], **args) elif uri.scheme == 'mysql': @@ -496,9 +498,20 @@ def parse_uri(database_uri): return dict(provider = 'mysql', user = uri.username, passwd = uri.password, host = uri.hostname, db = uri.path[1:], **args) return dict() -def init_database(database_uri, create_tables = False): - db.bind(**parse_uri(database_uri)) - db.generate_mapping(create_tables = create_tables) +def init_database(database_uri): + settings = parse_uri(database_uri) + db.bind(**settings) + db.generate_mapping(check_tables = False) + + try: + db.check_tables() + except DatabaseError: + sql = pkg_resources.resource_string(__package__, 'schema/' + settings['provider'] + '.sql').decode('utf-8') + with db_session: + for statement in sql.split(';'): + statement = statement.strip() + if statement: + db.execute(statement) def release_database(): db.disconnect() diff --git a/schema/mysql.sql b/supysonic/schema/mysql.sql similarity index 85% rename from schema/mysql.sql rename to supysonic/schema/mysql.sql index edc54e2..7881946 100644 --- a/schema/mysql.sql +++ b/supysonic/schema/mysql.sql @@ -1,29 +1,27 @@ -CREATE TABLE folder ( +CREATE TABLE IF NOT EXISTS folder ( id BINARY(16) PRIMARY KEY, root BOOLEAN NOT NULL, name VARCHAR(256) NOT NULL, path VARCHAR(4096) NOT NULL, - path_hash BINARY(20) NOT NULL, + path_hash BINARY(20) UNIQUE NOT NULL, created DATETIME NOT NULL, cover_art VARCHAR(256), last_scan INTEGER NOT NULL, parent_id BINARY(16) REFERENCES folder ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE UNIQUE INDEX index_folder_path ON folder(path_hash); - -CREATE TABLE artist ( +CREATE TABLE IF NOT EXISTS artist ( id BINARY(16) PRIMARY KEY, name VARCHAR(256) NOT NULL ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE album ( +CREATE TABLE IF NOT EXISTS album ( id BINARY(16) PRIMARY KEY, name VARCHAR(256) NOT NULL, artist_id BINARY(16) NOT NULL REFERENCES artist ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE track ( +CREATE TABLE IF NOT EXISTS track ( id BINARY(16) PRIMARY KEY, disc INTEGER NOT NULL, number INTEGER NOT NULL, @@ -35,7 +33,7 @@ CREATE TABLE track ( artist_id BINARY(16) NOT NULL REFERENCES artist, bitrate INTEGER NOT NULL, path VARCHAR(4096) NOT NULL, - path_hash BINARY(20) NOT NULL, + path_hash BINARY(20) UNIQUE NOT NULL, content_type VARCHAR(32) NOT NULL, created DATETIME NOT NULL, last_modification INTEGER NOT NULL, @@ -45,9 +43,7 @@ CREATE TABLE track ( folder_id BINARY(16) NOT NULL REFERENCES folder ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE UNIQUE INDEX index_track_path ON track(path_hash); - -CREATE TABLE user ( +CREATE TABLE IF NOT EXISTS user ( id BINARY(16) PRIMARY KEY, name VARCHAR(64) NOT NULL, mail VARCHAR(256), @@ -60,7 +56,7 @@ CREATE TABLE user ( last_play_date DATETIME ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE client_prefs ( +CREATE TABLE IF NOT EXISTS client_prefs ( user_id BINARY(16) NOT NULL, client_name VARCHAR(32) NOT NULL, format VARCHAR(8), @@ -68,56 +64,56 @@ CREATE TABLE client_prefs ( PRIMARY KEY (user_id, client_name) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE starred_folder ( +CREATE TABLE IF NOT EXISTS starred_folder ( user_id BINARY(16) NOT NULL REFERENCES user, starred_id BINARY(16) NOT NULL REFERENCES folder, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE starred_artist ( +CREATE TABLE IF NOT EXISTS starred_artist ( user_id BINARY(16) NOT NULL REFERENCES user, starred_id BINARY(16) NOT NULL REFERENCES artist, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE starred_album ( +CREATE TABLE IF NOT EXISTS starred_album ( user_id BINARY(16) NOT NULL REFERENCES user, starred_id BINARY(16) NOT NULL REFERENCES album, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE starred_track ( +CREATE TABLE IF NOT EXISTS starred_track ( user_id BINARY(16) NOT NULL REFERENCES user, starred_id BINARY(16) NOT NULL REFERENCES track, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE rating_folder ( +CREATE TABLE IF NOT EXISTS rating_folder ( user_id BINARY(16) NOT NULL REFERENCES user, rated_id BINARY(16) NOT NULL REFERENCES folder, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE rating_track ( +CREATE TABLE IF NOT EXISTS rating_track ( user_id BINARY(16) NOT NULL REFERENCES user, rated_id BINARY(16) NOT NULL REFERENCES track, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE chat_message ( +CREATE TABLE IF NOT EXISTS chat_message ( id BINARY(16) PRIMARY KEY, user_id BINARY(16) NOT NULL REFERENCES user, time INTEGER NOT NULL, message VARCHAR(512) NOT NULL ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE TABLE playlist ( +CREATE TABLE IF NOT EXISTS playlist ( id BINARY(16) PRIMARY KEY, user_id BINARY(16) NOT NULL REFERENCES user, name VARCHAR(256) NOT NULL, diff --git a/schema/postgresql.sql b/supysonic/schema/postgres.sql similarity index 81% rename from schema/postgresql.sql rename to supysonic/schema/postgres.sql index 0c4f7a4..90c3156 100644 --- a/schema/postgresql.sql +++ b/supysonic/schema/postgres.sql @@ -1,29 +1,27 @@ -CREATE TABLE folder ( +CREATE TABLE IF NOT EXISTS folder ( id UUID PRIMARY KEY, root BOOLEAN NOT NULL, name CITEXT NOT NULL, path VARCHAR(4096) NOT NULL, - path_hash BYTEA NOT NULL, + path_hash BYTEA UNIQUE NOT NULL, created TIMESTAMP NOT NULL, cover_art VARCHAR(256), last_scan INTEGER NOT NULL, parent_id UUID REFERENCES folder ); -CREATE UNIQUE INDEX index_folder_path ON folder(path_hash); - -CREATE TABLE artist ( +CREATE TABLE IF NOT EXISTS artist ( id UUID PRIMARY KEY, name CITEXT NOT NULL ); -CREATE TABLE album ( +CREATE TABLE IF NOT EXISTS album ( id UUID PRIMARY KEY, name CITEXT NOT NULL, artist_id UUID NOT NULL REFERENCES artist ); -CREATE TABLE track ( +CREATE TABLE IF NOT EXISTS track ( id UUID PRIMARY KEY, disc INTEGER NOT NULL, number INTEGER NOT NULL, @@ -35,7 +33,7 @@ CREATE TABLE track ( artist_id UUID NOT NULL REFERENCES artist, bitrate INTEGER NOT NULL, path VARCHAR(4096) NOT NULL, - path_hash BYTEA NOT NULL, + path_hash BYTEA UNIQUE NOT NULL, content_type VARCHAR(32) NOT NULL, created TIMESTAMP NOT NULL, last_modification INTEGER NOT NULL, @@ -45,9 +43,7 @@ CREATE TABLE track ( folder_id UUID NOT NULL REFERENCES folder ); -CREATE UNIQUE INDEX index_track_path ON track(path_hash); - -CREATE TABLE "user" ( +CREATE TABLE IF NOT EXISTS "user" ( id UUID PRIMARY KEY, name VARCHAR(64) NOT NULL, mail VARCHAR(256), @@ -60,7 +56,7 @@ CREATE TABLE "user" ( last_play_date TIMESTAMP ); -CREATE TABLE client_prefs ( +CREATE TABLE IF NOT EXISTS client_prefs ( user_id UUID NOT NULL, client_name VARCHAR(32) NOT NULL, format VARCHAR(8), @@ -68,56 +64,56 @@ CREATE TABLE client_prefs ( PRIMARY KEY (user_id, client_name) ); -CREATE TABLE starred_folder ( +CREATE TABLE IF NOT EXISTS starred_folder ( user_id UUID NOT NULL REFERENCES "user", starred_id UUID NOT NULL REFERENCES folder, date TIMESTAMP NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_artist ( +CREATE TABLE IF NOT EXISTS starred_artist ( user_id UUID NOT NULL REFERENCES "user", starred_id UUID NOT NULL REFERENCES artist, date TIMESTAMP NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_album ( +CREATE TABLE IF NOT EXISTS starred_album ( user_id UUID NOT NULL REFERENCES "user", starred_id UUID NOT NULL REFERENCES album, date TIMESTAMP NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_track ( +CREATE TABLE IF NOT EXISTS starred_track ( user_id UUID NOT NULL REFERENCES "user", starred_id UUID NOT NULL REFERENCES track, date TIMESTAMP NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE rating_folder ( +CREATE TABLE IF NOT EXISTS rating_folder ( user_id UUID NOT NULL REFERENCES "user", rated_id UUID NOT NULL REFERENCES folder, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ); -CREATE TABLE rating_track ( +CREATE TABLE IF NOT EXISTS rating_track ( user_id UUID NOT NULL REFERENCES "user", rated_id UUID NOT NULL REFERENCES track, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ); -CREATE TABLE chat_message ( +CREATE TABLE IF NOT EXISTS chat_message ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES "user", time INTEGER NOT NULL, message VARCHAR(512) NOT NULL ); -CREATE TABLE playlist ( +CREATE TABLE IF NOT EXISTS playlist ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES "user", name VARCHAR(256) NOT NULL, diff --git a/schema/sqlite.sql b/supysonic/schema/sqlite.sql similarity index 81% rename from schema/sqlite.sql rename to supysonic/schema/sqlite.sql index 5ee370d..09ed1b4 100644 --- a/schema/sqlite.sql +++ b/supysonic/schema/sqlite.sql @@ -1,4 +1,4 @@ -CREATE TABLE folder ( +CREATE TABLE IF NOT EXISTS folder ( id CHAR(36) PRIMARY KEY, root BOOLEAN NOT NULL, name VARCHAR(256) NOT NULL COLLATE NOCASE, @@ -10,20 +10,20 @@ CREATE TABLE folder ( parent_id CHAR(36) REFERENCES folder ); -CREATE UNIQUE INDEX index_folder_path ON folder(path_hash); +CREATE UNIQUE INDEX IF NOT EXISTS index_folder_path ON folder(path_hash); -CREATE TABLE artist ( +CREATE TABLE IF NOT EXISTS artist ( id CHAR(36) PRIMARY KEY, name VARCHAR(256) NOT NULL COLLATE NOCASE ); -CREATE TABLE album ( +CREATE TABLE IF NOT EXISTS album ( id CHAR(36) PRIMARY KEY, name VARCHAR(256) NOT NULL COLLATE NOCASE, artist_id CHAR(36) NOT NULL REFERENCES artist ); -CREATE TABLE track ( +CREATE TABLE IF NOT EXISTS track ( id CHAR(36) PRIMARY KEY, disc INTEGER NOT NULL, number INTEGER NOT NULL, @@ -45,9 +45,9 @@ CREATE TABLE track ( folder_id CHAR(36) NOT NULL REFERENCES folder ); -CREATE UNIQUE INDEX index_track_path ON track(path_hash); +CREATE UNIQUE INDEX IF NOT EXISTS index_track_path ON track(path_hash); -CREATE TABLE user ( +CREATE TABLE IF NOT EXISTS user ( id CHAR(36) PRIMARY KEY, name VARCHAR(64) NOT NULL, mail VARCHAR(256), @@ -60,7 +60,7 @@ CREATE TABLE user ( last_play_date DATETIME ); -CREATE TABLE client_prefs ( +CREATE TABLE IF NOT EXISTS client_prefs ( user_id CHAR(36) NOT NULL, client_name VARCHAR(32) NOT NULL, format VARCHAR(8), @@ -68,56 +68,56 @@ CREATE TABLE client_prefs ( PRIMARY KEY (user_id, client_name) ); -CREATE TABLE starred_folder ( +CREATE TABLE IF NOT EXISTS starred_folder ( user_id CHAR(36) NOT NULL REFERENCES user, starred_id CHAR(36) NOT NULL REFERENCES folder, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_artist ( +CREATE TABLE IF NOT EXISTS starred_artist ( user_id CHAR(36) NOT NULL REFERENCES user, starred_id CHAR(36) NOT NULL REFERENCES artist, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_album ( +CREATE TABLE IF NOT EXISTS starred_album ( user_id CHAR(36) NOT NULL REFERENCES user, starred_id CHAR(36) NOT NULL REFERENCES album, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE starred_track ( +CREATE TABLE IF NOT EXISTS starred_track ( user_id CHAR(36) NOT NULL REFERENCES user, starred_id CHAR(36) NOT NULL REFERENCES track, date DATETIME NOT NULL, PRIMARY KEY (user_id, starred_id) ); -CREATE TABLE rating_folder ( +CREATE TABLE IF NOT EXISTS rating_folder ( user_id CHAR(36) NOT NULL REFERENCES user, rated_id CHAR(36) NOT NULL REFERENCES folder, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ); -CREATE TABLE rating_track ( +CREATE TABLE IF NOT EXISTS rating_track ( user_id CHAR(36) NOT NULL REFERENCES user, rated_id CHAR(36) NOT NULL REFERENCES track, rating INTEGER NOT NULL CHECK(rating BETWEEN 1 AND 5), PRIMARY KEY (user_id, rated_id) ); -CREATE TABLE chat_message ( +CREATE TABLE IF NOT EXISTS chat_message ( id CHAR(36) PRIMARY KEY, user_id CHAR(36) NOT NULL REFERENCES user, time INTEGER NOT NULL, message VARCHAR(512) NOT NULL ); -CREATE TABLE playlist ( +CREATE TABLE IF NOT EXISTS playlist ( id CHAR(36) PRIMARY KEY, user_id CHAR(36) NOT NULL REFERENCES user, name VARCHAR(256) NOT NULL COLLATE NOCASE, diff --git a/tests/base/test_cli.py b/tests/base/test_cli.py index b0e11d0..ab2f4f7 100644 --- a/tests/base/test_cli.py +++ b/tests/base/test_cli.py @@ -34,7 +34,7 @@ class CLITestCase(unittest.TestCase): conf = TestConfig(False, False) self.__dbfile = tempfile.mkstemp()[1] conf.BASE['database_uri'] = 'sqlite:///' + self.__dbfile - init_database(conf.BASE['database_uri'], True) + init_database(conf.BASE['database_uri']) self.__stdout = StringIO() self.__stderr = StringIO() diff --git a/tests/base/test_db.py b/tests/base/test_db.py index 8421f7f..dc53888 100644 --- a/tests/base/test_db.py +++ b/tests/base/test_db.py @@ -21,7 +21,7 @@ date_regex = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$') class DbTestCase(unittest.TestCase): def setUp(self): - db.init_database('sqlite:', True) + db.init_database('sqlite:') try: self.assertRegex diff --git a/tests/base/test_scanner.py b/tests/base/test_scanner.py index 74be660..5fc4cad 100644 --- a/tests/base/test_scanner.py +++ b/tests/base/test_scanner.py @@ -23,7 +23,7 @@ from supysonic.scanner import Scanner class ScannerTestCase(unittest.TestCase): def setUp(self): - db.init_database('sqlite:', True) + db.init_database('sqlite:') with db_session: folder = FolderManager.add('folder', os.path.abspath('tests/assets')) diff --git a/tests/base/test_watcher.py b/tests/base/test_watcher.py index b7d29d2..d8fef7a 100644 --- a/tests/base/test_watcher.py +++ b/tests/base/test_watcher.py @@ -42,7 +42,7 @@ class WatcherTestBase(unittest.TestCase): def setUp(self): self.__dbfile = tempfile.mkstemp()[1] dburi = 'sqlite:///' + self.__dbfile - init_database(dburi, True) + init_database(dburi) release_database() conf = WatcherTestConfig(dburi) diff --git a/tests/issue101.py b/tests/issue101.py index 98b68a5..92580f5 100644 --- a/tests/issue101.py +++ b/tests/issue101.py @@ -22,7 +22,7 @@ from supysonic.scanner import Scanner class Issue101TestCase(unittest.TestCase): def setUp(self): self.__dir = tempfile.mkdtemp() - init_database('sqlite:', True) + init_database('sqlite:') with db_session: FolderManager.add('folder', self.__dir) diff --git a/tests/managers/test_manager_folder.py b/tests/managers/test_manager_folder.py index 8430479..7070734 100644 --- a/tests/managers/test_manager_folder.py +++ b/tests/managers/test_manager_folder.py @@ -23,7 +23,7 @@ from pony.orm import db_session, ObjectNotFound class FolderManagerTestCase(unittest.TestCase): def setUp(self): # Create an empty sqlite database in memory - db.init_database('sqlite:', True) + db.init_database('sqlite:') # Create some temporary directories self.media_dir = tempfile.mkdtemp() diff --git a/tests/managers/test_manager_user.py b/tests/managers/test_manager_user.py index 082cdb2..dcb52ee 100644 --- a/tests/managers/test_manager_user.py +++ b/tests/managers/test_manager_user.py @@ -23,7 +23,7 @@ from pony.orm import ObjectNotFound class UserManagerTestCase(unittest.TestCase): def setUp(self): # Create an empty sqlite database in memory - db.init_database('sqlite:', True) + db.init_database('sqlite:') def tearDown(self): db.release_database() diff --git a/tests/testbase.py b/tests/testbase.py index d93b477..b6784c2 100644 --- a/tests/testbase.py +++ b/tests/testbase.py @@ -90,7 +90,7 @@ class TestBase(unittest.TestCase): config.BASE['database_uri'] = 'sqlite:///' + self.__dbfile config.WEBAPP['cache_dir'] = self.__dir - init_database(config.BASE['database_uri'], True) + init_database(config.BASE['database_uri']) release_database() self.__app = create_application(config)