Lots of changes in the database model. Warning: only lightly tested for
[emufront] / src / db / dbmediaimagecontainer.cpp
1 // EmuFront
2 // Copyright 2010 Mikko Keinänen
3 //
4 // This file is part of EmuFront.
5 //
6 //
7 // EmuFront is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2 as published by
9 // the Free Software Foundation and appearing in the file gpl.txt included in the
10 // packaging of this file.
11 //
12 // EmuFront is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with EmuFront.  If not, see <http://www.gnu.org/licenses/>.
19
20 #include <QDebug>
21 #include <QSqlRecord>
22 #include <QSqlQuery>
23 #include <QSqlRelationalTableModel>
24 #include <QSqlError>
25 #include "dbmediaimagecontainer.h"
26 #include "dbmediaimage.h"
27 //#include "dbsetup.h"
28 #include "dbfilepath.h"
29
30
31 DbMediaImageContainer::DbMediaImageContainer(QObject *parent)
32     : DbFile(parent) // DbQueryModelManager(parent)
33 {
34     dbMediaImage = new DbMediaImage(parent);
35     dbFilePath = new DbFilePath(parent);
36     tableName = DbMediaImageContainer::DB_TABLE_MEDIAIMAGECONTAINER;
37     //dbFile = new DbFile(parent);
38 }
39
40 bool DbMediaImageContainer::updateDataObjectToModel(const EmuFrontObject *efo)
41 {
42     // TODO
43     return false;
44 }
45
46
47 int DbMediaImageContainer::storeMediaImageContainer(EmuFrontObject *efo)
48 {
49     MediaImageContainer *mic
50         = dynamic_cast<MediaImageContainer *>(efo);
51
52     if (!mic->getFilePath())
53         throw new EmuFrontException("Cannot install media image "
54             "container to database without a file path object!");
55
56     // check if this media image container is already in the database
57
58     // multiple media image containers with matching checksum will be stored
59     //       if each instance is in a different file path
60
61     EmuFrontObject *o = getMediaImageContainerByChecksum(mic->getCheckSum());
62     int fileId = o ? o->getId() : -1;
63     if (fileId >= 0) {
64         qDebug() << "Media image container already in db with id " << fileId << ".";
65         // ok, we have a matching file
66         MediaImageContainer *tmpMic = dynamic_cast<MediaImageContainer*>(o);
67         // this will test if media image container is already in given path (media image container has a path spesific name!)
68         QString name = getMediaImageContainerName(mic->getFilePath()->getId(), mic->getId());
69         if (name.isEmpty()) {
70             // if file path differs, link the existing media image container to this file path with mic->getName() name
71             linkMediaImageContainerToPath(mic);
72         }
73         else if (name != mic->getName()) {
74             // if the file path is same but the file name differs update the mediaimagecontainer_filepath table entry
75             updateMediaImageContainerToPath(mic);
76         }
77         delete tmpMic;
78         return fileId;
79    }
80
81     QMap<QString, EmuFrontObject*> images = mic->getMediaImages();
82     QList<int> ids = dbMediaImage->storeMediaImages(images);
83
84     qDebug() << "Stored " << ids.count() << " media images.";
85
86     if (ids.count() <= 0)
87         return -1;
88
89     /* Contained Media images successfully stored to db,
90         storing media image container also */
91
92     try {
93
94         // Insert MediaImageContainer first as a EmuFrontFile object to file table.
95
96         // File id is used to store the media image container instance to database,
97         // file id is also the media image container id
98
99         fileId = insertDataObjectToModel(mic);
100
101         qDebug() << "Inserted media image container to file table with id " << fileId << ".";
102
103         if (fileId < 0) {
104             // TODO: note we most surely need to catch the exception
105             // in the calling code block and clean
106             // all the media image and ...containers from
107             // the memory!
108             throw new EmuFrontException(
109                     QString(tr("Inserting media image container %1 to file database failed"))
110                     .arg(mic->getName()));
111         }
112
113         mic->setId(fileId);
114
115         if (!linkMediaImageContainerToPath(mic)){
116             DbFile::deleteDataObject(fileId);
117             throw new EmuFrontException("Failed inserting media image to database!");
118         }
119         qDebug() << "Inserted media image container " << fileId << " to mediaimagecontainer table.";
120         linkMediaImagesWithContainer(fileId, images.values());
121         qDebug() << "Linked media image container with media images.";
122     } catch (EmuFrontException e) {
123         dbMediaImage->removeOrphanedMediaImages(ids);
124         throw e;
125     }
126
127     return fileId;
128 }
129
130 bool DbMediaImageContainer::deleteDataObjectFromModel(QModelIndex *i)
131 {
132     // TODO
133     return false;
134 }
135
136 QString DbMediaImageContainer::constructSelect(QString whereClause) const
137 {
138     // TODO, for a usual search we need a "light" version of this select
139     // and MediaImageContainer (only id, name)
140     QString select = QString("SELECT file.id, mediaimagecontainer_filepath.mediaimagecontainername, file.checksum, file.size, "
141                 "        filepath.id, filepath.name, "
142                 "        setup.id, "
143                 "        platform.id, platform.name, "
144                 "        mediatype.id, mediatype.name "
145                 "FROM file "
146                 "INNER JOIN mediaimagecontainer_filepath ON mediaimagecontainer_filepath.fileid = file.id "
147                 "INNER JOIN filepath ON mediaimagecontainer_filepath.filepathid = filepath.id "
148                 "INNER JOIN setup ON filepath.setupid = setup.id "
149                 "INNER JOIN platform ON setup.platformid = platform.id "
150                 "INNER JOIN mediatype ON setup.mediatypeid = mediatype.id "
151                 "%1 "
152                 "ORDER BY file.name").arg(whereClause);
153     qDebug() << select;
154     return select;
155 }
156
157 QString DbMediaImageContainer::constructFilterById(int id) const
158 {
159     return QString("file.id = %1").arg(id);
160 }
161
162 QString DbMediaImageContainer::constructSelectById(int id) const
163 {
164     return constructSelect(
165         QString("WHERE %1").arg(constructFilterById(id))
166         );
167 }
168
169 EmuFrontObject* DbMediaImageContainer::recordToDataObject(const QSqlRecord *rec)
170 {
171     // TODO: checks!
172     MediaImageContainer *mic = 0;
173     if (!rec) return mic;
174     int id = rec->value(MIC_FileId).toInt();
175     QString name = rec->value(MIC_FileName).toString();
176     QString checksum = rec->value(MIC_FileCheckSum).toString();
177     int size = rec->value(MIC_FileSize).toInt();
178     int fpId = rec->value(MIC_FilePathId).toInt();
179     FilePathObject *fpo
180         = dynamic_cast<FilePathObject*>(dbFilePath->getDataObject(fpId));
181     //int supId = rec->value(MIC_SetupId).toInt();
182     //Setup *sup = dbSetup->getDataObject(supId)
183     QMap<QString, EmuFrontObject*> images = dbMediaImage->getMediaImages(id);
184
185     mic = new MediaImageContainer(
186        id, name, checksum, size, images, fpo
187     );
188     return mic;
189 }
190
191 QSqlQueryModel* DbMediaImageContainer::getData()
192 {
193     QSqlQueryModel *model = new QSqlQueryModel(this);
194     if (sqlTableModel){
195         model->setQuery(sqlTableModel->query());
196     }
197     else
198         model->setQuery(constructSelect());
199     model->setHeaderData(MIC_FileId, Qt::Horizontal, tr("File id"));
200     model->setHeaderData(MIC_FileName, Qt::Horizontal, tr("File Name"));
201     model->setHeaderData(MIC_FileCheckSum, Qt::Horizontal, tr("File checksum"));
202     model->setHeaderData(MIC_FileSize, Qt::Horizontal, tr("File Size"));
203     model->setHeaderData(MIC_FilePathId, Qt::Horizontal, tr("File path id"));
204     model->setHeaderData(MIC_FilePathName, Qt::Horizontal, tr("File path name"));
205     model->setHeaderData(MIC_SetupId, Qt::Horizontal, tr("Setup id"));
206     model->setHeaderData(MIC_PlatformId, Qt::Horizontal, tr("Platform id"));
207     model->setHeaderData(MIC_PlatformName, Qt::Horizontal, tr("Platform name"));
208     model->setHeaderData(MIC_MediaTypeId, Qt::Horizontal, tr("Media type id"));
209     model->setHeaderData(MIC_MediaTypeName, Qt::Horizontal, tr("Media type name"));
210     return model;
211 }
212
213 /* Returns the id of a media image container with a given cheksum or -1 if not found */
214 int DbMediaImageContainer::getMediaImageContainer(QString checksum) const
215 {
216     QSqlQuery q;
217     q.prepare("SELECT id FROM file WHERE checksum=:checksum");
218     q.bindValue(":checksum", checksum);
219     int id = -1;
220     if (q.next())
221         id = q.value(0).toInt();
222     return id;
223 }
224
225
226 /**
227 * Stores media image containers, including the media images included
228 * to database.
229 */
230 void DbMediaImageContainer::storeContainers(QList<MediaImageContainer *> lst, FilePathObject *fpo)
231 {
232     qDebug() << "Storing media image containers to database.";
233     foreach(MediaImageContainer *mic, lst)
234     {
235         qDebug() << "Media image container " << mic->getName();
236         int micFileId = storeMediaImageContainer(mic);
237     }
238 }
239
240 void DbMediaImageContainer::linkMediaImagesWithContainer(int micId, QList<EmuFrontObject*> mediaImages)
241 {
242     if (micId < 0 || mediaImages.count() <= 0)
243         return;
244
245     MediaImage *mi = 0;
246     foreach(EmuFrontObject *efo, mediaImages) {
247         mi = dynamic_cast<MediaImage*>(efo);
248         qDebug() << "Linking media image container " << micId
249             << " to media image " << mi->getId()  << ", " << mi->getName() << ".";
250         QString name = getMediaImageContainerName(micId, mi->getId());
251         if (name.isEmpty() && !linkMediaImageToMediaImageContainer(mi, micId)) {
252                 throw new EmuFrontException(QString("Failed linking media "
253                                                     "image container %1 to a media image %2").arg(micId).arg(mi->getId()));
254         }
255         else if (name != mi->getName() && !updateMediaImageToMediaImageContainer(mi, micId)) {
256                 throw new EmuFrontException(QString("Failed updating media "
257                                                     "image container %1 to a media image %2").arg(micId).arg(mi->getId()));
258         }
259         // else already linked and name is up to date.
260     }
261 }
262
263 void DbMediaImageContainer::filter(int mediaTypeId, int platformId)
264 {
265     qDebug() << "Filtering media images with media type " << mediaTypeId
266         << " and platform " << platformId;
267     QList<QString> filters;
268     if (mediaTypeId >= 0)
269         filters.append(QString("mediatype.id=%1").arg(mediaTypeId));
270     if (platformId >= 0)
271         filters.append(QString("platform.id=%1").arg(platformId));
272     filterDataObjects(filters);
273 }
274
275 QString DbMediaImageContainer::getCountRefsSelect(int id) const
276 {
277     /* we need to count file references to give media image container */
278     /* example:
279         select count(*) from mediaimagecontainer
280         INNER JOIN mediaimagecontainer_mediaimage
281         ON mediaimagecontainer_mediaimage.mediaimagecontainerid
282         = mediaimagecontainer.fileid
283         WHERE mediaimagecontainer.fileid=589;
284     */
285     return QString("SELECT count(*) FROM mediaimagecontainer "
286               "INNER JOIN mediaimagecontainer_mediaimage "
287               "ON mediaimagecontainer_mediaimage.mediaimagecontainerid "
288               "    =mediaimagecontainer.fileid "
289               "WHERE mediaimagecontainer.fileid=%1").arg(id);
290 }
291
292 QString DbMediaImageContainer::getDeleteObjectSql() const
293 {
294        // The trigger will take care of deleting
295        // the reference from the mediaimagecontainer
296        // and mediaimage_mediaimagecontainer tables.
297        // there is also a trigger that will delete
298        // all the files linked to mediaimagecontainer
299        // using mediaimage_mediaimagecontainer (the actual
300        // mediaimages).
301        return QString("DELETE FROM file WHERE id=:id");
302 }
303
304 EmuFrontObject* DbMediaImageContainer::getMediaImageContainerByChecksum(QString checksum)
305 {
306     return getDataObject(QString("file.checksum LIKE '%1'").arg(checksum));
307 }
308
309 bool DbMediaImageContainer::linkMediaImageContainerToPath(const MediaImageContainer *mic) const
310 {
311     QSqlQuery q;
312     q.prepare("INSERT INTO mediaimagecontainer_filepath "
313               "(fileid, filepathid, mediaimagecontainername, updatetime) "
314               "VALUES (:fileid, :filepathid, :mediaimagecontainername, :updatetime)");
315     q.bindValue(":fileid", mic->getId());
316     q.bindValue(":filepathid", mic->getFilePath()->getId());
317     q.bindValue(":mediaimagecontainername", mic->getName());
318     q.bindValue(":updatetime", DatabaseManager::getCurrentTimeStamp());
319     return q.exec();
320 }
321
322 bool DbMediaImageContainer::updateMediaImageContainerToPath(const MediaImageContainer *mic) const
323 {
324     QSqlQuery q;
325     q.prepare("UPDATE mediaimagecontainer_filepath "
326                 "SET mediaimagecontainername=:mediaimagecontainername, "
327                 "updatetime=:updatetime "
328                 "WHERE fileid=:fileid AND filepathid=:filepathid");
329     q.bindValue(":fileid", mic->getId());
330     q.bindValue(":filepathid", mic->getFilePath()->getId());
331     q.bindValue(":mediaimagecontainername", mic->getName());
332     q.bindValue(":updatetime", DatabaseManager::getCurrentTimeStamp());
333     return q.exec();
334 }
335
336 QString DbMediaImageContainer::getMediaImageContainerName(int filePathId, int micId) const
337 {
338     QString name = "";
339     QSqlQuery q;
340     q.prepare("SELECT mediaimagecontainername FROM mediaimagecontainer_filepath "
341         "WHERE fileid=:fileid AND filepathid=:filepathid");
342     q.bindValue(":fileid", micId);
343     q.bindValue(":filepathid", filePathId);
344     q.exec();
345     if (q.next())
346         name = q.value(0).toString();
347     return name;
348 }
349
350 bool DbMediaImageContainer::linkMediaImageToMediaImageContainer(const MediaImage *mi, int micId) const
351 {
352     QSqlQuery q;
353     q.prepare("INSERT INTO mediaimagecontainer_mediaimage "
354         "(mediaimagecontainerid, mediaimageid, mediaimagename) "
355         "VALUES (:micid, :miid, :miname) ");
356     q.bindValue(":micid", micId);
357     q.bindValue(":miid", mi->getId());
358     q.bindValue(":miname", mi->getName());
359     return q.exec();
360 }
361
362 bool DbMediaImageContainer::updateMediaImageToMediaImageContainer(const MediaImage *mi, int micId) const
363 {
364     QSqlQuery q;
365     q.prepare("UPDATE mediaimagecontainer_mediaimage "
366         " SET mediaimagename=:miname "
367         " WHERE mediaimagecontainerid=:micid AND mediaimageid=:miid");
368     q.bindValue(":micid", micId);
369     q.bindValue(":miid", mi->getId());
370     q.bindValue(":miname", mi->getName());
371     return q.exec();
372 }