|
| 1 | +/* |
| 2 | + This file is part of libdjinterop. |
| 3 | +
|
| 4 | + libdjinterop is free software: you can redistribute it and/or modify |
| 5 | + it under the terms of the GNU Lesser General Public License as published by |
| 6 | + the Free Software Foundation, either version 3 of the License, or |
| 7 | + (at your option) any later version. |
| 8 | +
|
| 9 | + libdjinterop is distributed in the hope that it will be useful, |
| 10 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + GNU Lesser General Public License for more details. |
| 13 | +
|
| 14 | + You should have received a copy of the GNU Lesser General Public License |
| 15 | + along with libdjinterop. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + */ |
| 17 | + |
| 18 | +#include <sqlite_modern_cpp.h> |
| 19 | + |
| 20 | +#include "../../util/random.hpp" |
| 21 | +#include "schema_3_0_1.hpp" |
| 22 | + |
| 23 | +namespace djinterop::engine::schema |
| 24 | +{ |
| 25 | +void schema_3_0_1::create(sqlite::database& db) |
| 26 | +{ |
| 27 | + // Schema 3.0.1 modifies trigger definitions on the Track and |
| 28 | + // PerformanceData tables. |
| 29 | + db << "CREATE TABLE Information ( id INTEGER PRIMARY KEY AUTOINCREMENT, " |
| 30 | + " uuid TEXT, schemaVersionMajor INTEGER, schemaVersionMinor " |
| 31 | + "INTEGER, schemaVersionPatch INTEGER, " |
| 32 | + "currentPlayedIndiciator INTEGER, " |
| 33 | + "lastRekordBoxLibraryImportReadCounter INTEGER);"; |
| 34 | + db << "CREATE TABLE AlbumArt ( id INTEGER PRIMARY KEY AUTOINCREMENT, " |
| 35 | + "hash TEXT, albumArt BLOB );"; |
| 36 | + db << "CREATE TABLE Pack ( id INTEGER PRIMARY KEY AUTOINCREMENT, packId " |
| 37 | + "TEXT, changeLogDatabaseUuid TEXT, changeLogId INTEGER, " |
| 38 | + "lastPackTime DATETIME );"; |
| 39 | + db << "CREATE TABLE Playlist ( id INTEGER PRIMARY KEY AUTOINCREMENT, " |
| 40 | + "title TEXT, parentListId INTEGER, isPersisted BOOLEAN, " |
| 41 | + "nextListId INTEGER, lastEditTime DATETIME, isExplicitlyExported " |
| 42 | + "BOOLEAN, CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, " |
| 43 | + "parentListId), CONSTRAINT C_NEXT_LIST_ID_UNIQUE_FOR_PARENT UNIQUE " |
| 44 | + "(parentListId, nextListId) );"; |
| 45 | + db << "CREATE TABLE PlaylistEntity ( id INTEGER PRIMARY KEY " |
| 46 | + "AUTOINCREMENT, listId INTEGER, trackId INTEGER, " |
| 47 | + "databaseUuid TEXT, nextEntityId INTEGER, membershipReference " |
| 48 | + "INTEGER, CONSTRAINT C_NAME_UNIQUE_FOR_LIST UNIQUE (listId, " |
| 49 | + "databaseUuid, trackId), FOREIGN KEY (listId) REFERENCES Playlist " |
| 50 | + "(id) ON DELETE CASCADE );"; |
| 51 | + db << "CREATE TABLE Smartlist ( listUuid TEXT NOT NULL PRIMARY KEY, " |
| 52 | + " title TEXT, parentPlaylistPath TEXT, nextPlaylistPath TEXT, " |
| 53 | + " nextListUuid TEXT, rules TEXT, lastEditTime DATETIME, " |
| 54 | + "CONSTRAINT C_NAME_UNIQUE_FOR_PARENT UNIQUE (title, " |
| 55 | + "parentPlaylistPath), CONSTRAINT C_NEXT_LIST_UNIQUE_FOR_PARENT " |
| 56 | + "UNIQUE (parentPlaylistPath, nextPlaylistPath, nextListUuid) );"; |
| 57 | + db << "CREATE TABLE Track ( id INTEGER PRIMARY KEY AUTOINCREMENT, " |
| 58 | + "playOrder INTEGER, length INTEGER, bpm INTEGER, year " |
| 59 | + "INTEGER, path TEXT, filename TEXT, bitrate INTEGER, " |
| 60 | + "bpmAnalyzed REAL, albumArtId INTEGER, fileBytes INTEGER, " |
| 61 | + "title TEXT, artist TEXT, album TEXT, genre TEXT, " |
| 62 | + "comment TEXT, label TEXT, composer TEXT, remixer TEXT, " |
| 63 | + "key INTEGER, rating INTEGER, albumArt TEXT, timeLastPlayed " |
| 64 | + "DATETIME, isPlayed BOOLEAN, fileType TEXT, isAnalyzed " |
| 65 | + "BOOLEAN, dateCreated DATETIME, dateAdded DATETIME, " |
| 66 | + "isAvailable BOOLEAN, isMetadataOfPackedTrackChanged BOOLEAN, " |
| 67 | + " isPerfomanceDataOfPackedTrackChanged BOOLEAN, playedIndicator " |
| 68 | + "INTEGER, isMetadataImported BOOLEAN, pdbImportKey INTEGER, " |
| 69 | + " streamingSource TEXT, uri TEXT, isBeatGridLocked BOOLEAN, " |
| 70 | + "originDatabaseUuid TEXT, originTrackId INTEGER, streamingFlags " |
| 71 | + "INTEGER, explicitLyrics BOOLEAN, lastEditTime DATETIME, " |
| 72 | + "CONSTRAINT C_originDatabaseUuid_originTrackId UNIQUE " |
| 73 | + "(originDatabaseUuid, originTrackId), CONSTRAINT C_path UNIQUE " |
| 74 | + "(path), FOREIGN KEY (albumArtId) REFERENCES AlbumArt (id) ON " |
| 75 | + "DELETE RESTRICT );"; |
| 76 | + db << "CREATE TABLE PerformanceData ( trackId INTEGER PRIMARY KEY, " |
| 77 | + "trackData BLOB, overviewWaveFormData BLOB, beatData BLOB, " |
| 78 | + "quickCues BLOB, loops BLOB, thirdPartySourceId INTEGER, " |
| 79 | + "activeOnLoadLoops INTEGER, FOREIGN KEY(trackId) REFERENCES " |
| 80 | + "Track(id) ON DELETE CASCADE ON UPDATE CASCADE );"; |
| 81 | + db << "CREATE TABLE PreparelistEntity ( id INTEGER PRIMARY KEY " |
| 82 | + "AUTOINCREMENT, trackId INTEGER, trackNumber INTEGER, " |
| 83 | + "FOREIGN KEY (trackId) REFERENCES Track (id) ON DELETE CASCADE );"; |
| 84 | + db << "DELETE FROM sqlite_sequence;"; |
| 85 | + db << "CREATE INDEX index_AlbumArt_hash ON AlbumArt (hash);"; |
| 86 | + db << "CREATE INDEX index_PlaylistEntity_nextEntityId_listId ON " |
| 87 | + "PlaylistEntity(nextEntityId, listId);"; |
| 88 | + db << "CREATE TRIGGER trigger_after_insert_Pack_timestamp AFTER INSERT ON " |
| 89 | + "Pack FOR EACH ROW WHEN NEW.lastPackTime IS NULL BEGIN UPDATE " |
| 90 | + "Pack SET lastPackTime = strftime('%s') WHERE ROWID = NEW.ROWID; " |
| 91 | + "END;"; |
| 92 | + db << "CREATE TRIGGER trigger_after_insert_Pack_changeLogId AFTER INSERT " |
| 93 | + "ON Pack FOR EACH ROW WHEN NEW.changeLogId = 0 BEGIN UPDATE Pack " |
| 94 | + "SET changeLogId = 1 WHERE ROWID = NEW.ROWID; END;"; |
| 95 | + db << "CREATE VIEW ChangeLog (id, trackId) AS SELECT 0, 0 WHERE FALSE;"; |
| 96 | + db << "CREATE TRIGGER trigger_before_insert_List BEFORE INSERT ON Playlist " |
| 97 | + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = -(1 + " |
| 98 | + "nextListId) WHERE nextListId = NEW.nextListId AND parentListId = " |
| 99 | + "NEW.parentListId; END;"; |
| 100 | + db << "CREATE TRIGGER trigger_after_insert_List AFTER INSERT ON Playlist " |
| 101 | + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = " |
| 102 | + "NEW.id WHERE nextListId = -(1 + NEW.nextListId) AND " |
| 103 | + "parentListId = NEW.parentListId; END;"; |
| 104 | + db << "CREATE TRIGGER trigger_after_delete_List AFTER DELETE ON Playlist " |
| 105 | + "FOR EACH ROW BEGIN UPDATE Playlist SET nextListId = " |
| 106 | + "OLD.nextListId WHERE nextListId = OLD.id; DELETE FROM Playlist " |
| 107 | + " WHERE parentListId = OLD.id; END;"; |
| 108 | + db << "CREATE TRIGGER trigger_after_update_isPersistParent AFTER UPDATE ON " |
| 109 | + "Playlist WHEN (old.isPersisted = 0 AND new.isPersisted = 1) " |
| 110 | + " OR (old.parentListId != new.parentListId AND new.isPersisted = " |
| 111 | + "1) BEGIN UPDATE Playlist SET isPersisted = 1 WHERE " |
| 112 | + "id IN (SELECT parentListId FROM PlaylistAllParent WHERE id=new.id); " |
| 113 | + "END;"; |
| 114 | + db << "CREATE TRIGGER trigger_after_update_isPersistChild AFTER UPDATE ON " |
| 115 | + "Playlist WHEN old.isPersisted = 1 AND new.isPersisted = 0 " |
| 116 | + "BEGIN UPDATE Playlist SET isPersisted = 0 WHERE id " |
| 117 | + "IN (SELECT childListId FROM PlaylistAllChildren WHERE id=new.id); " |
| 118 | + "END;"; |
| 119 | + db << "CREATE TRIGGER trigger_after_insert_isPersist AFTER INSERT ON " |
| 120 | + "Playlist WHEN new.isPersisted = 1 BEGIN UPDATE Playlist SET " |
| 121 | + " isPersisted = 1 WHERE id IN (SELECT parentListId FROM " |
| 122 | + "PlaylistAllParent WHERE id=new.id); END;"; |
| 123 | + db << "CREATE VIEW PlaylistAllParent AS WITH FindAllParent AS ( SELECT " |
| 124 | + "id, parentListId FROM Playlist UNION ALL SELECT " |
| 125 | + "recursiveCTE.id, Plist.parentListId FROM Playlist Plist INNER JOIN " |
| 126 | + "FindAllParent recursiveCTE ON recursiveCTE.parentListId = " |
| 127 | + "Plist.id ) SELECT * FROM FindAllParent;"; |
| 128 | + db << "CREATE VIEW PlaylistAllChildren AS WITH FindAllChild AS ( SELECT " |
| 129 | + "id, id as childListId FROM Playlist UNION ALL SELECT " |
| 130 | + "recursiveCTE.id, Plist.id FROM Playlist Plist INNER JOIN " |
| 131 | + "FindAllChild recursiveCTE ON recursiveCTE.childListId = " |
| 132 | + "Plist.parentListId ) SELECT * FROM FindAllChild WHERE id <> " |
| 133 | + "childListId;"; |
| 134 | + db << "CREATE VIEW PlaylistPath AS WITH RECURSIVE Heirarchy AS ( SELECT " |
| 135 | + "id AS child, parentListId AS parent, title AS name, 1 AS depth FROM " |
| 136 | + "Playlist UNION ALL SELECT child, parentListId AS parent, " |
| 137 | + "title AS name, h.depth + 1 AS depth FROM Playlist c JOIN Heirarchy " |
| 138 | + "h ON h.parent = c.id ORDER BY depth DESC ), OrderedList AS ( " |
| 139 | + " SELECT id , nextListId, 1 AS position FROM Playlist WHERE " |
| 140 | + "nextListId = 0 UNION ALL SELECT c.id , c.nextListId , " |
| 141 | + "l.position + 1 FROM Playlist c INNER JOIN OrderedList l ON " |
| 142 | + "c.nextListId = l.id ), NameConcat AS ( SELECT child AS id, " |
| 143 | + " GROUP_CONCAT(name ,';') || ';' AS path FROM ( SELECT " |
| 144 | + "child, name FROM Heirarchy ORDER BY depth DESC ) " |
| 145 | + "GROUP BY child ) SELECT id, path, ROW_NUMBER() OVER ( " |
| 146 | + " ORDER BY (SELECT COUNT(*) FROM (SELECT * FROM Heirarchy " |
| 147 | + "WHERE child = id) ) DESC, (SELECT position FROM OrderedList " |
| 148 | + "ol WHERE ol.id = c.id) ASC ) AS position FROM Playlist c LEFT " |
| 149 | + "JOIN NameConcat g USING (id);"; |
| 150 | + db << "CREATE TRIGGER trigger_before_delete_PlaylistEntity BEFORE DELETE " |
| 151 | + "ON PlaylistEntity WHEN OLD.trackId > 0 BEGIN UPDATE " |
| 152 | + "PlaylistEntity SET nextEntityId = OLD.nextEntityId WHERE " |
| 153 | + "nextEntityId = OLD.id AND listId = OLD.listId; END;"; |
| 154 | + db << "CREATE INDEX index_Track_filename ON Track (filename);"; |
| 155 | + db << "CREATE INDEX index_Track_albumArtId ON Track (albumArtId);"; |
| 156 | + db << "CREATE INDEX index_Track_uri ON Track (uri);"; |
| 157 | + db << "CREATE INDEX index_Track_title ON Track(title);"; |
| 158 | + db << "CREATE INDEX index_Track_length ON Track(length);"; |
| 159 | + db << "CREATE INDEX index_Track_rating ON Track(rating);"; |
| 160 | + db << "CREATE INDEX index_Track_year ON Track(year);"; |
| 161 | + db << "CREATE INDEX index_Track_dateAdded ON Track(dateAdded);"; |
| 162 | + db << "CREATE INDEX index_Track_genre ON Track(genre);"; |
| 163 | + db << "CREATE INDEX index_Track_artist ON Track(artist);"; |
| 164 | + db << "CREATE INDEX index_Track_album ON Track(album);"; |
| 165 | + db << "CREATE INDEX index_Track_key ON Track(key);"; |
| 166 | + db << "CREATE INDEX index_Track_bpmAnalyzed ON Track(CAST(bpmAnalyzed + " |
| 167 | + "0.5 AS int));"; |
| 168 | + db << "CREATE TRIGGER trigger_after_insert_Track_check_id AFTER INSERT ON " |
| 169 | + "Track WHEN NEW.id <= (SELECT seq FROM sqlite_sequence WHERE name " |
| 170 | + "= 'Track') BEGIN SELECT RAISE(ABORT, 'Recycling deleted track " |
| 171 | + "id''s are not allowed'); END;"; |
| 172 | + db << "CREATE TRIGGER trigger_after_update_Track_check_Id BEFORE UPDATE ON " |
| 173 | + "Track WHEN NEW.id <> OLD.id BEGIN SELECT RAISE(ABORT, " |
| 174 | + "'Changing track id''s are not allowed'); END;"; |
| 175 | + db << "CREATE TRIGGER trigger_after_insert_Track_fix_origin AFTER INSERT " |
| 176 | + "ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR " |
| 177 | + "IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET " |
| 178 | + " originTrackId = NEW.id, originDatabaseUuid = (SELECT " |
| 179 | + "uuid FROM Information) WHERE track.id = NEW.id; END;"; |
| 180 | + db << "CREATE TRIGGER trigger_after_update_Track_fix_origin AFTER UPDATE " |
| 181 | + "ON Track WHEN IFNULL(NEW.originTrackId, 0) = 0 OR " |
| 182 | + "IFNULL(NEW.originDatabaseUuid, '') = '' BEGIN UPDATE Track SET " |
| 183 | + " originTrackId = NEW.id, originDatabaseUuid = (SELECT " |
| 184 | + "uuid FROM Information) WHERE track.id = NEW.id; END;"; |
| 185 | + db << "CREATE TRIGGER trigger_after_update_only_Track_timestamp " |
| 186 | + "AFTER UPDATE OF length," |
| 187 | + "bpm, year, filename, bitrate, bpmAnalyzed, albumArtId, title, " |
| 188 | + "artist," |
| 189 | + "album, genre, comment, label, composer, remixer, key, rating, " |
| 190 | + "albumArt," |
| 191 | + "fileType, isAnalyzed, isBeatgridLocked," |
| 192 | + "explicitLyrics ON Track FOR EACH ROW BEGIN UPDATE Track SET" |
| 193 | + " lastEditTime = strftime('%s') WHERE ROWID = NEW.ROWID;" |
| 194 | + "END;"; |
| 195 | + db << "CREATE TRIGGER trigger_PerformanceData_after_update_Track_timestamp " |
| 196 | + "AFTER UPDATE OF trackData, isAnalyzed, overviewWaveFormData, " |
| 197 | + "beatData, quickCues, loops, activeOnLoadLoops ON PerformanceData " |
| 198 | + "FOR EACH ROW BEGIN UPDATE Track SET lastEditTime = strftime('%s') " |
| 199 | + "WHERE id = NEW.trackId; " |
| 200 | + "END;"; |
| 201 | + db << "CREATE TRIGGER trigger_after_insert_Track_insert_performance_data " |
| 202 | + "AFTER INSERT ON Track BEGIN INSERT INTO " |
| 203 | + "PerformanceData(trackId) " |
| 204 | + "VALUES(NEW.id); END;"; |
| 205 | + db << "CREATE INDEX index_PreparelistEntity_trackId ON PreparelistEntity " |
| 206 | + "(trackId);"; |
| 207 | + |
| 208 | + // Generate UUID for the Information table. |
| 209 | + auto uuid_str = djinterop::util::generate_random_uuid(); |
| 210 | + |
| 211 | + // Not yet sure how the "currentPlayedIndiciator" (typo deliberate) value |
| 212 | + // is formed. |
| 213 | + auto current_played_indicator_fake_value = |
| 214 | + djinterop::util::generate_random_int64(); |
| 215 | + |
| 216 | + // Insert row into Information |
| 217 | + db << "INSERT INTO Information ([uuid], [schemaVersionMajor], " |
| 218 | + "[schemaVersionMinor], [schemaVersionPatch], " |
| 219 | + "[currentPlayedIndiciator], [lastRekordBoxLibraryImportReadCounter]) " |
| 220 | + "VALUES (?, ?, ?, ?, ?, ?)" |
| 221 | + << uuid_str << schema_version.maj << schema_version.min |
| 222 | + << schema_version.pat << current_played_indicator_fake_value << 0; |
| 223 | + |
| 224 | + // Insert default album art entry |
| 225 | + db << "INSERT INTO AlbumArt VALUES (1, '', NULL)"; |
| 226 | +} |
| 227 | + |
| 228 | +} // namespace djinterop::engine::schema |
0 commit comments