diff --git a/etiquette/constants.py b/etiquette/constants.py index ccaa937..87257dc 100644 --- a/etiquette/constants.py +++ b/etiquette/constants.py @@ -21,7 +21,7 @@ FILENAME_BADCHARS = '\\/:*?<>|"' # Note: Setting user_version pragma in init sequence is safe because it only # happens after the out-of-date check occurs, so no chance of accidentally # overwriting it. -DATABASE_VERSION = 10 +DATABASE_VERSION = 11 DB_INIT = ''' PRAGMA cache_size = 10000; PRAGMA count_changes = OFF; @@ -30,32 +30,33 @@ PRAGMA user_version = {user_version}; ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS users( - id TEXT, - username TEXT COLLATE NOCASE, - password BLOB, + id TEXT PRIMARY KEY NOT NULL, + username TEXT NOT NULL COLLATE NOCASE, + password BLOB NOT NULL, created INT ); CREATE INDEX IF NOT EXISTS index_users_id on users(id); CREATE INDEX IF NOT EXISTS index_users_username on users(username COLLATE NOCASE); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS albums( - id TEXT, + id TEXT PRIMARY KEY NOT NULL, title TEXT, description TEXT ); CREATE INDEX IF NOT EXISTS index_albums_id on albums(id); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS bookmarks( - id TEXT, + id TEXT PRIMARY KEY NOT NULL, title TEXT, url TEXT, - author_id TEXT + author_id TEXT, + FOREIGN KEY(author_id) REFERENCES users(id) ); CREATE INDEX IF NOT EXISTS index_bookmarks_id on bookmarks(id); CREATE INDEX IF NOT EXISTS index_bookmarks_author on bookmarks(author_id); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS photos( - id TEXT, + id TEXT PRIMARY KEY NOT NULL, filepath TEXT COLLATE NOCASE, override_filename TEXT COLLATE NOCASE, extension TEXT, @@ -69,7 +70,8 @@ CREATE TABLE IF NOT EXISTS photos( thumbnail TEXT, tagged_at INT, author_id TEXT, - searchhidden INT + searchhidden INT, + FOREIGN KEY(author_id) REFERENCES users(id) ); CREATE INDEX IF NOT EXISTS index_photos_id on photos(id); CREATE INDEX IF NOT EXISTS index_photos_filepath on photos(filepath COLLATE NOCASE); @@ -81,8 +83,8 @@ CREATE INDEX IF NOT EXISTS index_photos_author_id on photos(author_id); CREATE INDEX IF NOT EXISTS index_photos_searchhidden on photos(searchhidden); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS tags( - id TEXT, - name TEXT, + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, description TEXT ); CREATE INDEX IF NOT EXISTS index_tags_id on tags(id); @@ -92,8 +94,9 @@ CREATE INDEX IF NOT EXISTS index_tags_name on tags(name); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS album_associated_directories( - albumid TEXT, - directory TEXT COLLATE NOCASE + albumid TEXT NOT NULL, + directory TEXT NOT NULL COLLATE NOCASE, + FOREIGN KEY(albumid) REFERENCES albums(id) ); CREATE INDEX IF NOT EXISTS index_album_associated_directories_albumid on album_associated_directories(albumid); @@ -101,40 +104,49 @@ CREATE INDEX IF NOT EXISTS index_album_associated_directories_directory on album_associated_directories(directory); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS album_group_rel( - parentid TEXT, - memberid TEXT + parentid TEXT NOT NULL, + memberid TEXT NOT NULL, + FOREIGN KEY(parentid) REFERENCES albums(id), + FOREIGN KEY(memberid) REFERENCES albums(id) ); CREATE INDEX IF NOT EXISTS index_album_group_rel_parentid on album_group_rel(parentid); CREATE INDEX IF NOT EXISTS index_album_group_rel_memberid on album_group_rel(memberid); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS album_photo_rel( - albumid TEXT, - photoid TEXT + albumid TEXT NOT NULL, + photoid TEXT NOT NULL, + FOREIGN KEY(albumid) REFERENCES albums(id), + FOREIGN KEY(photoid) REFERENCES photos(id) ); CREATE INDEX IF NOT EXISTS index_album_photo_rel_albumid on album_photo_rel(albumid); CREATE INDEX IF NOT EXISTS index_album_photo_rel_photoid on album_photo_rel(photoid); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS id_numbers( - tab TEXT, - last_id TEXT + tab TEXT NOT NULL, + last_id TEXT NOT NULL ); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS photo_tag_rel( - photoid TEXT, - tagid TEXT + photoid TEXT NOT NULL, + tagid TEXT NOT NULL, + FOREIGN KEY(photoid) REFERENCES photos(id), + FOREIGN KEY(tagid) REFERENCES tags(id) ); CREATE INDEX IF NOT EXISTS index_photo_tag_rel_photoid on photo_tag_rel(photoid); CREATE INDEX IF NOT EXISTS index_photo_tag_rel_tagid on photo_tag_rel(tagid); +---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS tag_group_rel( - parentid TEXT, - memberid TEXT + parentid TEXT NOT NULL, + memberid TEXT NOT NULL, + FOREIGN KEY(parentid) REFERENCES tags(id), + FOREIGN KEY(memberid) REFERENCES tags(id) ); CREATE INDEX IF NOT EXISTS index_tag_group_rel_parentid on tag_group_rel(parentid); CREATE INDEX IF NOT EXISTS index_tag_group_rel_memberid on tag_group_rel(memberid); ---------------------------------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS tag_synonyms( - name TEXT, - mastername TEXT + name TEXT NOT NULL, + mastername TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS index_tag_synonyms_name on tag_synonyms(name); ---------------------------------------------------------------------------------------------------- @@ -149,6 +161,7 @@ for statement in DB_INIT.split(';'): column_names = statement.split('(')[1].rsplit(')', 1)[0] column_names = column_names.split(',') column_names = [x.strip().split(' ')[0] for x in column_names] + column_names = [c for c in column_names if c.lower() != 'foreign'] SQL_COLUMNS[table_name] = column_names def _sql_dictify(columns): diff --git a/utilities/database_upgrader.py b/utilities/database_upgrader.py index c67525c..6bc424a 100644 --- a/utilities/database_upgrader.py +++ b/utilities/database_upgrader.py @@ -5,6 +5,8 @@ import sys import etiquette +import old_inits + def upgrade_1_to_2(photodb): ''' In this version, a column `tagged_at` was added to the Photos table, to keep @@ -174,6 +176,57 @@ def upgrade_9_to_10(photodb): new_thumbnail_path = '.' + new_thumbnail_path.replace(thumbnail_dir, '') cur.execute('UPDATE photos SET thumbnail = ? WHERE id == ?', [new_thumbnail_path, photo.id]) +def upgrade_10_to_11(photodb): + ''' + Added Primary keys, Foreign keys, and NOT NULL constraints which cannot be + done in-place and requires the reconstruction of all the affected tables. + ''' + tables_to_delete = [ + 'users', + 'albums', + 'bookmarks', + 'photos', + 'tags', + 'album_associated_directories', + 'album_group_rel', + 'album_photo_rel', + 'id_numbers', + 'photo_tag_rel', + 'tag_group_rel', + 'tag_synonyms', + ] + cur = photodb.sql.cursor() + cur.execute('PRAGMA foreign_keys = OFF') + + print('Renaming existing tables.') + for table in tables_to_delete: + statement = 'ALTER TABLE %s RENAME TO %s_old' % (table, table) + cur.execute(statement) + + lines = [line.strip() for line in old_inits.V11.splitlines()] + lines = [line for line in lines if not line.startswith('--')] + statements = '\n'.join(lines).split(';') + statements = [x.strip() for x in statements] + create_tables = [x for x in statements if x.lower().startswith('create table')] + create_indices = [x for x in statements if x.lower().startswith('create index')] + + print('Recreating tables.') + for statement in create_tables: + cur.execute(statement) + + print('Migrating table data.') + for table in tables_to_delete: + statement = 'INSERT INTO %s SELECT * FROM %s_old' % (table, table) + cur.execute(statement) + statement = 'DROP TABLE %s_old' % table + cur.execute(statement) + + print('Recreating indices.') + for statement in create_indices: + cur.execute(statement) + + cur.execute('PRAGMA foreign_keys = ON') + def upgrade_all(data_directory): ''' Given the directory containing a phototagger database, apply all of the @@ -188,11 +241,11 @@ def upgrade_all(data_directory): needed_version = etiquette.constants.DATABASE_VERSION if current_version == needed_version: - print('Already up-to-date with version %d.' % needed_version) + print('Already up to date with version %d.' % needed_version) return for version_number in range(current_version + 1, needed_version + 1): - print('Upgrading from %d to %d' % (current_version, version_number)) + print('Upgrading from %d to %d.' % (current_version, version_number)) upgrade_function = 'upgrade_%d_to_%d' % (current_version, version_number) upgrade_function = eval(upgrade_function) upgrade_function(photodb) diff --git a/utilities/old_inits.py b/utilities/old_inits.py new file mode 100644 index 0000000..99e7f04 --- /dev/null +++ b/utilities/old_inits.py @@ -0,0 +1,129 @@ +V11 = ''' +PRAGMA cache_size = 10000; +PRAGMA count_changes = OFF; +PRAGMA foreign_keys = ON; +PRAGMA user_version = 11; + +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS users( + id TEXT PRIMARY KEY NOT NULL, + username TEXT NOT NULL COLLATE NOCASE, + password BLOB NOT NULL, + created INT +); +CREATE INDEX IF NOT EXISTS index_users_id on users(id); +CREATE INDEX IF NOT EXISTS index_users_username on users(username COLLATE NOCASE); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS albums( + id TEXT PRIMARY KEY NOT NULL, + title TEXT, + description TEXT +); +CREATE INDEX IF NOT EXISTS index_albums_id on albums(id); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS bookmarks( + id TEXT PRIMARY KEY NOT NULL, + title TEXT, + url TEXT, + author_id TEXT, + FOREIGN KEY(author_id) REFERENCES users(id) +); +CREATE INDEX IF NOT EXISTS index_bookmarks_id on bookmarks(id); +CREATE INDEX IF NOT EXISTS index_bookmarks_author on bookmarks(author_id); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS photos( + id TEXT PRIMARY KEY NOT NULL, + filepath TEXT COLLATE NOCASE, + override_filename TEXT COLLATE NOCASE, + extension TEXT, + width INT, + height INT, + ratio REAL, + area INT, + duration INT, + bytes INT, + created INT, + thumbnail TEXT, + tagged_at INT, + author_id TEXT, + searchhidden INT, + FOREIGN KEY(author_id) REFERENCES users(id) +); +CREATE INDEX IF NOT EXISTS index_photos_id on photos(id); +CREATE INDEX IF NOT EXISTS index_photos_filepath on photos(filepath COLLATE NOCASE); +CREATE INDEX IF NOT EXISTS index_photos_override_filename on + photos(override_filename COLLATE NOCASE); +CREATE INDEX IF NOT EXISTS index_photos_created on photos(created); +CREATE INDEX IF NOT EXISTS index_photos_extension on photos(extension); +CREATE INDEX IF NOT EXISTS index_photos_author_id on photos(author_id); +CREATE INDEX IF NOT EXISTS index_photos_searchhidden on photos(searchhidden); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS tags( + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + description TEXT +); +CREATE INDEX IF NOT EXISTS index_tags_id on tags(id); +CREATE INDEX IF NOT EXISTS index_tags_name on tags(name); +---------------------------------------------------------------------------------------------------- + + +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS album_associated_directories( + albumid TEXT NOT NULL, + directory TEXT NOT NULL COLLATE NOCASE, + FOREIGN KEY(albumid) REFERENCES albums(id) +); +CREATE INDEX IF NOT EXISTS index_album_associated_directories_albumid on + album_associated_directories(albumid); +CREATE INDEX IF NOT EXISTS index_album_associated_directories_directory on + album_associated_directories(directory); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS album_group_rel( + parentid TEXT NOT NULL, + memberid TEXT NOT NULL, + FOREIGN KEY(parentid) REFERENCES albums(id), + FOREIGN KEY(memberid) REFERENCES albums(id) +); +CREATE INDEX IF NOT EXISTS index_album_group_rel_parentid on album_group_rel(parentid); +CREATE INDEX IF NOT EXISTS index_album_group_rel_memberid on album_group_rel(memberid); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS album_photo_rel( + albumid TEXT NOT NULL, + photoid TEXT NOT NULL, + FOREIGN KEY(albumid) REFERENCES albums(id), + FOREIGN KEY(photoid) REFERENCES photos(id) +); +CREATE INDEX IF NOT EXISTS index_album_photo_rel_albumid on album_photo_rel(albumid); +CREATE INDEX IF NOT EXISTS index_album_photo_rel_photoid on album_photo_rel(photoid); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS id_numbers( + tab TEXT NOT NULL, + last_id TEXT NOT NULL +); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS photo_tag_rel( + photoid TEXT NOT NULL, + tagid TEXT NOT NULL, + FOREIGN KEY(photoid) REFERENCES photos(id), + FOREIGN KEY(tagid) REFERENCES tags(id) +); +CREATE INDEX IF NOT EXISTS index_photo_tag_rel_photoid on photo_tag_rel(photoid); +CREATE INDEX IF NOT EXISTS index_photo_tag_rel_tagid on photo_tag_rel(tagid); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS tag_group_rel( + parentid TEXT NOT NULL, + memberid TEXT NOT NULL, + FOREIGN KEY(parentid) REFERENCES tags(id), + FOREIGN KEY(memberid) REFERENCES tags(id) +); +CREATE INDEX IF NOT EXISTS index_tag_group_rel_parentid on tag_group_rel(parentid); +CREATE INDEX IF NOT EXISTS index_tag_group_rel_memberid on tag_group_rel(memberid); +---------------------------------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS tag_synonyms( + name TEXT NOT NULL, + mastername TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS index_tag_synonyms_name on tag_synonyms(name); +---------------------------------------------------------------------------------------------------- +'''