1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "SQLiteObjectDbi.h"
23 
24 #include <U2Core/U2AttributeDbi.h>
25 #include <U2Core/U2DbiPackUtils.h>
26 #include <U2Core/U2FeatureUtils.h>
27 #include <U2Core/U2SafePoints.h>
28 #include <U2Core/U2SqlHelpers.h>
29 
30 #include <U2Formats/SQLiteModDbi.h>
31 #include <U2Formats/SQLiteSequenceDbi.h>
32 
33 #include "SQLiteFeatureDbi.h"
34 #include "SQLiteMsaDbi.h"
35 #include "SQLiteUdrDbi.h"
36 
37 namespace U2 {
38 
SQLiteObjectDbi(SQLiteDbi * dbi)39 SQLiteObjectDbi::SQLiteObjectDbi(SQLiteDbi *dbi)
40     : U2ObjectDbi(dbi), SQLiteChildDBICommon(dbi) {
41 }
42 
initSqlSchema(U2OpStatus & os)43 void SQLiteObjectDbi::initSqlSchema(U2OpStatus &os) {
44     // objects table - stores IDs and types for all objects. It also stores 'top_level' flag to simplify queries
45     // rank: see U2DbiObjectRank
46     // name is a visual name of the object shown to user.
47     SQLiteWriteQuery("CREATE TABLE Object (id INTEGER PRIMARY KEY AUTOINCREMENT, type INTEGER NOT NULL, "
48                      "version INTEGER NOT NULL DEFAULT 1, rank INTEGER NOT NULL, "
49                      "name TEXT NOT NULL, trackMod INTEGER NOT NULL DEFAULT 0)",
50                      db,
51                      os)
52         .execute();
53     CHECK_OP(os, );
54 
55     // parent-child object relation
56     SQLiteWriteQuery("CREATE TABLE Parent (parent INTEGER, child INTEGER, "
57                      "PRIMARY KEY (parent, child), "
58                      "FOREIGN KEY(parent) REFERENCES Object(id) ON DELETE CASCADE, "
59                      "FOREIGN KEY(child) REFERENCES Object(id) ON DELETE CASCADE)",
60                      db,
61                      os)
62         .execute();
63     SQLiteWriteQuery("CREATE INDEX Parent_parent_child on Parent(parent, child)", db, os).execute();
64     SQLiteWriteQuery("CREATE INDEX Parent_child on Parent(child)", db, os).execute();
65     CHECK_OP(os, );
66 
67     // folders
68     SQLiteWriteQuery("CREATE TABLE Folder (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT UNIQUE NOT NULL, "
69                      "vlocal INTEGER NOT NULL DEFAULT 1, vglobal INTEGER NOT NULL DEFAULT 1 )",
70                      db,
71                      os)
72         .execute();
73     CHECK_OP(os, );
74 
75     // folder-object relation
76     SQLiteWriteQuery("CREATE TABLE FolderContent (folder INTEGER, object INTEGER, "
77                      "PRIMARY KEY (folder, object), "
78                      "FOREIGN KEY(folder) REFERENCES Folder(id) ON DELETE CASCADE,"
79                      "FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE)",
80                      db,
81                      os)
82         .execute();
83     CHECK_OP(os, );
84     SQLiteWriteQuery("CREATE INDEX FolderContent_object on FolderContent(object)", db, os).execute();
85     CHECK_OP(os, );
86 
87     createFolder(U2ObjectDbi::ROOT_FOLDER, os);
88 }
89 
90 //////////////////////////////////////////////////////////////////////////
91 // Read methods for objects
92 #define TOP_LEVEL_FILTER ("rank = " + QString::number(U2DbiObjectRank_TopLevel))
93 
countObjects(U2OpStatus & os)94 qint64 SQLiteObjectDbi::countObjects(U2OpStatus &os) {
95     return SQLiteReadQuery("SELECT COUNT (*) FROM Object WHERE " + TOP_LEVEL_FILTER, db, os).selectInt64();
96 }
97 
countObjects(U2DataType type,U2OpStatus & os)98 qint64 SQLiteObjectDbi::countObjects(U2DataType type, U2OpStatus &os) {
99     SQLiteReadQuery q("SELECT COUNT (*) FROM Object WHERE " + TOP_LEVEL_FILTER + " AND type = ?1", db, os);
100     q.bindType(1, type);
101     return q.selectInt64();
102 }
103 
getObjects(qint64 offset,qint64 count,U2OpStatus & os)104 QList<U2DataId> SQLiteObjectDbi::getObjects(qint64 offset, qint64 count, U2OpStatus &os) {
105     return SQLiteReadQuery("SELECT id, type FROM Object WHERE " + TOP_LEVEL_FILTER, offset, count, db, os).selectDataIdsExt();
106 }
107 
getObjects(U2DataType type,qint64 offset,qint64 count,U2OpStatus & os)108 QList<U2DataId> SQLiteObjectDbi::getObjects(U2DataType type, qint64 offset, qint64 count, U2OpStatus &os) {
109     SQLiteReadQuery q("SELECT id, type FROM Object WHERE " + TOP_LEVEL_FILTER + " AND type = ?1", offset, count, db, os);
110     q.bindType(1, type);
111     return q.selectDataIdsExt();
112 }
113 
getParents(const U2DataId & entityId,U2OpStatus & os)114 QList<U2DataId> SQLiteObjectDbi::getParents(const U2DataId &entityId, U2OpStatus &os) {
115     SQLiteReadQuery q("SELECT o.id AS id, o.type AS type FROM Parent AS p, Object AS o WHERE p.parent = o.id AND p.child = ?1", db, os);
116     q.bindDataId(1, entityId);
117     return q.selectDataIdsExt();
118 }
119 
getObjectsByVisualName(const QString & visualName,U2DataType type,U2OpStatus & os)120 U2DbiIterator<U2DataId> *SQLiteObjectDbi::getObjectsByVisualName(const QString &visualName, U2DataType type, U2OpStatus &os) {
121     SQLiteTransaction t(db, os);
122     bool checkType = (type != U2Type::Unknown);
123     QString query = "SELECT id, type FROM Object WHERE " + TOP_LEVEL_FILTER + " AND name = ?1 " + (checkType ? "AND type = ?2" : "" + QString(" ORDER BY id"));
124     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(query, db, os);
125     q->bindString(1, visualName);
126     if (checkType) {
127         q->bindType(2, type);
128     }
129     return new SQLiteResultSetIterator<U2DataId>(q, new SQLiteDataIdResultSetLoaderEx(), nullptr, U2DataId(), os);
130 }
131 
132 //////////////////////////////////////////////////////////////////////////
133 // Write methods for objects
134 
removeObject(const U2DataId & dataId,bool,U2OpStatus & os)135 bool SQLiteObjectDbi::removeObject(const U2DataId &dataId, bool /*force*/, U2OpStatus &os) {
136     bool result = removeObjectImpl(dataId, os);
137     CHECK_OP(os, result);
138     if (result) {
139         onFolderUpdated("");
140     }
141     return result;
142 }
143 
removeObject(const U2DataId & dataId,U2OpStatus & os)144 bool SQLiteObjectDbi::removeObject(const U2DataId &dataId, U2OpStatus &os) {
145     return removeObject(dataId, false, os);
146 }
147 
148 namespace {
149 
createDeleteObjectQueryStr(int objectCount)150 QString createDeleteObjectQueryStr(int objectCount) {
151     static const QString queryStartStr("DELETE FROM Object WHERE id IN (");
152     static const int bindingStrLength = 5;  // five characters for ",?nnn"
153 
154     QString result(queryStartStr);
155     result.reserve(result.length() + bindingStrLength * objectCount);
156     for (int placeholderCount = 0; placeholderCount < objectCount;) {
157         const QString bindingStr = QString("?%1,").arg(++placeholderCount);
158         result.append(bindingStr);
159     }
160     result.replace(result.length() - 1, 1, ')');
161     return result;
162 }
163 
164 }  // namespace
165 
removeObjects(const QList<U2DataId> & dataIds,bool,U2OpStatus & os)166 bool SQLiteObjectDbi::removeObjects(const QList<U2DataId> &dataIds, bool /*force*/, U2OpStatus &os) {
167     CHECK(!dataIds.isEmpty(), true);
168 
169     SQLiteTransaction t(db, os);
170     Q_UNUSED(t);
171 
172     // remove specific objects' data first
173     foreach (const U2DataId &objectId, dataIds) {
174         removeObjectSpecificData(objectId, os);
175         CHECK_OP(os, false);
176     }
177 
178     // then remove general ones
179     const int idsCount = dataIds.count();
180     const int residualBindQueryCount = idsCount % SQLiteDbi::BIND_PARAMETERS_LIMIT;
181     const int fullBindQueryCount = idsCount / SQLiteDbi::BIND_PARAMETERS_LIMIT;
182 
183     QString fullQueryStr;
184     QString residualQueryStr;
185     residualQueryStr = createDeleteObjectQueryStr(residualBindQueryCount);
186     if (fullBindQueryCount > 0) {
187         fullQueryStr = createDeleteObjectQueryStr(SQLiteDbi::BIND_PARAMETERS_LIMIT);
188     }
189 
190     // execute deletion of residual objects
191     SQLiteWriteQuery residualDeletionQuery(residualQueryStr, db, os);
192     for (int i = 0; i < residualBindQueryCount; ++i) {
193         residualDeletionQuery.bindDataId(i + 1, dataIds.at(i));
194     }
195     residualDeletionQuery.update(residualBindQueryCount);
196     CHECK_OP(os, false);
197 
198     // execute deletion of objects by parts of `SQLiteDbi::BIND_PARAMETERS_LIMIT` in size
199     if (fullBindQueryCount > 0) {
200         SQLiteWriteQuery fullDeletionQuery(fullQueryStr, db, os);
201         for (int currentFullQuery = 0; currentFullQuery < fullBindQueryCount; ++currentFullQuery) {
202             const int firstBindingPos = residualBindQueryCount + currentFullQuery * SQLiteDbi::BIND_PARAMETERS_LIMIT;
203             const int lastBindingPos = residualBindQueryCount + (currentFullQuery + 1) * SQLiteDbi::BIND_PARAMETERS_LIMIT;
204             for (int idNum = firstBindingPos, paramNum = 1; idNum < lastBindingPos; ++idNum, ++paramNum) {
205                 fullDeletionQuery.bindDataId(paramNum, dataIds.at(idNum));
206             }
207             fullDeletionQuery.update(SQLiteDbi::BIND_PARAMETERS_LIMIT);
208             CHECK_OP(os, false);
209             fullDeletionQuery.reset();
210         }
211     }
212 
213     onFolderUpdated("");
214     return !os.hasError();
215 }
216 
removeObjects(const QList<U2DataId> & dataIds,U2OpStatus & os)217 bool SQLiteObjectDbi::removeObjects(const QList<U2DataId> &dataIds, U2OpStatus &os) {
218     return removeObjects(dataIds, false, os);
219 }
220 
renameObject(const U2DataId & id,const QString & newName,U2OpStatus & os)221 void SQLiteObjectDbi::renameObject(const U2DataId &id, const QString &newName, U2OpStatus &os) {
222     SQLiteTransaction t(db, os);
223     static const QString queryString("UPDATE Object SET name = ?1 WHERE id = ?2");
224     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
225     SAFE_POINT_OP(os, );
226     q->bindString(1, newName);
227     q->bindDataId(2, id);
228     q->execute();
229     CHECK_OP(os, );
230 
231     incrementVersion(id, os);
232 }
233 
updateObjectCore(U2Object & obj,U2OpStatus & os)234 void SQLiteObjectDbi::updateObjectCore(U2Object &obj, U2OpStatus &os) {
235     SQLiteTransaction t(db, os);
236     static const QString queryString("UPDATE Object SET name = ?1, version = version WHERE id = ?2");
237     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
238     SAFE_POINT_OP(os, );
239     q->bindString(1, obj.visualName);
240     q->bindDataId(2, obj.id);
241     q->execute();
242 }
243 
removeObjectFromFolder(const U2DataId & id,const QString & folder,U2OpStatus & os)244 void SQLiteObjectDbi::removeObjectFromFolder(const U2DataId &id, const QString &folder, U2OpStatus &os) {
245     const qint64 folderId = getFolderId(folder, true, db, os);
246     CHECK_OP(os, );
247 
248     static const QString deleteString = "DELETE FROM FolderContent WHERE folder = ?1 AND object = ?2";
249     SQLiteWriteQuery deleteQ(deleteString, db, os);
250     CHECK_OP(os, );
251     deleteQ.bindInt64(1, folderId);
252     deleteQ.bindDataId(2, id);
253     deleteQ.execute();
254 }
255 
removeObjectFromAllFolders(const U2DataId & id,U2OpStatus & os)256 void SQLiteObjectDbi::removeObjectFromAllFolders(const U2DataId &id, U2OpStatus &os) {
257     static const QString deleteString("DELETE FROM FolderContent WHERE object = ?1");
258     SQLiteWriteQuery deleteQ(deleteString, db, os);
259     CHECK_OP(os, );
260     deleteQ.bindDataId(1, id);
261     deleteQ.update();
262 }
263 
removeObjectImpl(const U2DataId & objectId,U2OpStatus & os)264 bool SQLiteObjectDbi::removeObjectImpl(const U2DataId &objectId, U2OpStatus &os) {
265     SQLiteTransaction t(db, os);
266     Q_UNUSED(t);
267 
268     removeObjectSpecificData(objectId, os);
269     CHECK_OP(os, false);
270 
271     SQLiteUtils::remove("Object", "id", objectId, 1, db, os);
272     return !os.hasError();
273 }
274 
removeObjectSpecificData(const U2DataId & objectId,U2OpStatus & os)275 void SQLiteObjectDbi::removeObjectSpecificData(const U2DataId &objectId, U2OpStatus &os) {
276     U2DataType type = getRootDbi()->getEntityTypeById(objectId);
277     if (!U2Type::isObjectType(type)) {
278         os.setError(U2DbiL10n::tr("Not an object! Id: %1, type: %2").arg(U2DbiUtils::text(objectId)).arg(type));
279         return;
280     }
281 
282     switch (type) {
283         case U2Type::Sequence:
284         case U2Type::VariantTrack:
285             // nothing has to be done for object of these types
286             break;
287         case U2Type::Mca:
288         case U2Type::Msa:
289             dbi->getSQLiteMsaDbi()->deleteRowsData(objectId, os);
290             break;
291         case U2Type::AnnotationTable:
292             dbi->getSQLiteFeatureDbi()->removeAnnotationTableData(objectId, os);
293             break;
294         case U2Type::Assembly:
295             dbi->getAssemblyDbi()->removeAssemblyData(objectId, os);
296             break;
297         case U2Type::CrossDatabaseReference:
298             dbi->getCrossDatabaseReferenceDbi()->removeCrossReferenceData(objectId, os);
299             break;
300         default:
301             if (!U2Type::isUdrObjectType(type)) {
302                 os.setError(U2DbiL10n::tr("Unknown object type! Id: %1, type: %2").arg(U2DbiUtils::text(objectId)).arg(type));
303             }
304     }
305     CHECK_OP(os, );
306 }
307 
removeObjectAttributes(const U2DataId & id,U2OpStatus & os)308 void SQLiteObjectDbi::removeObjectAttributes(const U2DataId &id, U2OpStatus &os) {
309     U2AttributeDbi *attributeDbi = dbi->getAttributeDbi();
310     attributeDbi->removeObjectAttributes(id, os);
311 }
312 
removeObjectModHistory(const U2DataId & id,U2OpStatus & os)313 void SQLiteObjectDbi::removeObjectModHistory(const U2DataId &id, U2OpStatus &os) {
314     U2ModDbi *modDbi = dbi->getModDbi();
315     SAFE_POINT(nullptr != modDbi, "NULL Mod Dbi!", );
316 
317     modDbi->removeObjectMods(id, os);
318 }
319 
320 //////////////////////////////////////////////////////////////////////////
321 // Read methods for folders
322 
getFolders(U2OpStatus & os)323 QStringList SQLiteObjectDbi::getFolders(U2OpStatus &os) {
324     // Comparison is case sensitive by default
325     return SQLiteReadQuery("SELECT path FROM Folder ORDER BY path", db, os).selectStrings();
326 }
327 
getObjectFolders(U2OpStatus & os)328 QHash<U2Object, QString> SQLiteObjectDbi::getObjectFolders(U2OpStatus &os) {
329     QHash<U2Object, QString> result;
330 
331     static const QString queryString =
332         "SELECT o.id, o.type, o.version, o.name, o.trackMod, f.path "
333         "FROM Object AS o, FolderContent AS fc, Folder AS f WHERE fc.object=o.id AND "
334         "fc.folder=f.id AND " +
335         TOP_LEVEL_FILTER;
336     SQLiteReadQuery q(queryString, db, os);
337     CHECK_OP(os, result);
338 
339     const QString dbId = dbi->getDbiId();
340 
341     while (q.step()) {
342         U2Object object;
343         const U2DataType type = q.getDataType(1);
344         object.id = q.getDataId(0, type);
345         object.version = q.getInt64(2);
346         object.visualName = q.getString(3);
347         object.trackModType = static_cast<U2TrackModType>(q.getInt32(4));
348         const QString path = q.getString(5);
349         object.dbiId = dbId;
350         result[object] = path;
351     }
352     return result;
353 }
354 
renameFolder(const QString & oldPath,const QString & newPath,U2OpStatus & os)355 void SQLiteObjectDbi::renameFolder(const QString &oldPath, const QString &newPath, U2OpStatus &os) {
356     QString oldCPath = U2DbiUtils::makeFolderCanonical(oldPath);
357     QString newCPath = U2DbiUtils::makeFolderCanonical(newPath);
358 
359     QStringList allFolders = getFolders(os);
360     CHECK_OP(os, );
361 
362     static const QString renameFolderQueryStr = "UPDATE Folder SET path = ?1 where path = ?2";
363     if (allFolders.contains(oldCPath)) {
364         SQLiteWriteQuery q(renameFolderQueryStr, db, os);
365         q.bindString(1, newCPath);
366         q.bindString(2, oldPath);
367         q.update();
368         CHECK_OP(os, );
369     }
370 
371     QString parent = oldCPath + PATH_SEP;
372     QString newParent = newCPath + PATH_SEP;
373     for (const QString &path : qAsConst(allFolders)) {
374         if (path.startsWith(parent)) {
375             QString newParentPath = newParent + path.mid(parent.size());
376             SQLiteWriteQuery q(renameFolderQueryStr, db, os);
377             q.bindString(1, newParentPath);
378             q.bindString(2, path);
379             q.update();
380             CHECK_OP(os, );
381         }
382     }
383 }
384 
getFolderPreviousPath(const QString &,U2OpStatus & os)385 QString SQLiteObjectDbi::getFolderPreviousPath(const QString & /*currentPath*/, U2OpStatus &os) {
386     os.setError("Operation is not supported");
387     return "";
388 }
389 
countObjects(const QString & folder,U2OpStatus & os)390 qint64 SQLiteObjectDbi::countObjects(const QString &folder, U2OpStatus &os) {
391     SQLiteReadQuery q("SELECT COUNT(fc.*) FROM FolderContent AS fc, Folder AS f WHERE f.path = ?1 AND fc.folder = f.id", db, os);
392     q.bindString(1, folder);
393     return q.selectInt64();
394 }
395 
getObjects(const QString & folder,qint64,qint64,U2OpStatus & os)396 QList<U2DataId> SQLiteObjectDbi::getObjects(const QString &folder, qint64, qint64, U2OpStatus &os) {
397     SQLiteReadQuery q("SELECT o.id, o.type FROM Object AS o, FolderContent AS fc, Folder AS f WHERE f.path = ?1 AND fc.folder = f.id AND fc.object=o.id AND o." + TOP_LEVEL_FILTER, db, os);
398     q.bindString(1, folder);
399     return q.selectDataIdsExt();
400 }
401 
getObjectFolders(const U2DataId & objectId,U2OpStatus & os)402 QStringList SQLiteObjectDbi::getObjectFolders(const U2DataId &objectId, U2OpStatus &os) {
403     SQLiteReadQuery q("SELECT f.path FROM FolderContent AS fc, Folder AS f WHERE fc.object = ?1 AND fc.folder = f.id", db, os);
404     q.bindDataId(1, objectId);
405     return q.selectStrings();
406 }
407 
408 //////////////////////////////////////////////////////////////////////////
409 // Write methods for  folders
410 
createFolder(const QString & path,U2OpStatus & os)411 void SQLiteObjectDbi::createFolder(const QString &path, U2OpStatus &os) {
412     SQLiteTransaction t(db, os);
413     Q_UNUSED(t);
414     CHECK_OP(os, );
415 
416     const QString canonicalPath = U2DbiUtils::makeFolderCanonical(path);
417 
418     qint64 folderId = getFolderId(canonicalPath, false, db, os);
419     CHECK_OP(os, );
420     CHECK(-1 == folderId, );
421 
422     QString parentFolder = canonicalPath;
423     if (U2ObjectDbi::ROOT_FOLDER != parentFolder) {
424         parentFolder.truncate(parentFolder.lastIndexOf(U2ObjectDbi::PATH_SEP));
425         if (parentFolder.isEmpty()) {
426             parentFolder = U2ObjectDbi::ROOT_FOLDER;
427         }
428         createFolder(parentFolder, os);
429     }
430 
431     SQLiteWriteQuery q("INSERT INTO Folder(path) VALUES(?1)", db, os);
432     q.bindString(1, canonicalPath);
433     q.execute();
434 
435     if (!os.hasError()) {
436         onFolderUpdated(path);
437     }
438 }
439 
removeFolder(const QString & folder,U2OpStatus & os)440 bool SQLiteObjectDbi::removeFolder(const QString &folder, U2OpStatus &os) {
441     // remove subfolders first
442     SQLiteReadQuery q("SELECT path FROM Folder WHERE path LIKE ?1 ORDER BY LENGTH(path) DESC", db, os);
443     q.bindString(1, folder + "/%");
444     QStringList subfolders = q.selectStrings();
445     CHECK_OP(os, false);
446 
447     bool result = true;
448     foreach (const QString &subfolder, subfolders) {
449         result = removeFolder(subfolder, os);
450         CHECK_OP(os, false);
451     }
452 
453     // remove all objects from folder
454     qint64 nObjects = countObjects(folder, os);
455     CHECK_OP(os, false);
456 
457     int nObjectsPerIteration = 1000;
458     for (int i = 0; i < nObjects; i += nObjectsPerIteration) {
459         QList<U2DataId> objects = getObjects(folder, i, nObjectsPerIteration, os);
460         CHECK_OP(os, false);
461 
462         // Remove all objects in the folder
463         if (!objects.isEmpty()) {
464             bool deleted = removeObjects(objects, false, os);
465             CHECK_OP(os, false);
466             if (result && !deleted) {
467                 result = false;
468             }
469         }
470     }
471 
472     if (result) {
473         // remove folder record
474         SQLiteWriteQuery dq("DELETE FROM Folder WHERE path = ?1", db, os);
475         dq.bindString(1, folder);
476         dq.execute();
477         CHECK_OP(os, false);
478 
479         onFolderUpdated(folder);
480     }
481     return result;
482 }
483 
addObjectsToFolder(const QList<U2DataId> & objectIds,const QString & folder,U2OpStatus & os)484 void SQLiteObjectDbi::addObjectsToFolder(const QList<U2DataId> &objectIds, const QString &folder, U2OpStatus &os) {
485     qint64 folderId = getFolderId(folder, true, db, os);
486     if (os.hasError()) {
487         return;
488     }
489     QList<U2DataId> addedObjects;
490     SQLiteReadQuery countQ("SELECT count(object) FROM FolderContent WHERE folder = ?1", db, os);
491     SQLiteWriteQuery insertQ("INSERT INTO FolderContent(folder, object) VALUES(?1, ?2)", db, os);
492     SQLiteWriteQuery toplevelQ("UPDATE Object SET rank = " + QString::number(U2DbiObjectRank_TopLevel) + " WHERE id = ?1", db, os);
493 
494     foreach (const U2DataId &objectId, objectIds) {
495         countQ.reset();
496         countQ.bindInt64(1, folderId);
497         int c = countQ.selectInt64();
498         if (c != 0) {
499             continue;  // object is already in folder, skip it
500         }
501         insertQ.reset();
502         insertQ.bindInt64(1, folderId);
503         insertQ.bindDataId(2, objectId);
504         insertQ.execute();
505 
506         toplevelQ.reset();
507         toplevelQ.bindDataId(1, objectId);
508         toplevelQ.execute();
509 
510         if (os.hasError()) {
511             break;
512         }
513 
514         addedObjects.append(objectId);
515     }
516     onFolderUpdated(folder);
517 }
518 
moveObjects(const QList<U2DataId> & objectIds,const QString & fromFolder,const QString & toFolder,U2OpStatus & os,bool saveFromFolder)519 void SQLiteObjectDbi::moveObjects(const QList<U2DataId> &objectIds, const QString &fromFolder, const QString &toFolder, U2OpStatus &os, bool saveFromFolder) {
520     const QString canonicalFromFolder = U2DbiUtils::makeFolderCanonical(fromFolder);
521     const QString canonicalToFolder = U2DbiUtils::makeFolderCanonical(toFolder);
522 
523     CHECK(canonicalFromFolder != canonicalToFolder, );
524 
525     addObjectsToFolder(objectIds, toFolder, os);
526     CHECK_OP(os, );
527 
528     removeObjects(objectIds, false, os);
529 
530     if (saveFromFolder) {
531         CHECK_OP(os, );
532 
533         U2AttributeDbi *attrDbi = dbi->getAttributeDbi();
534         foreach (const U2DataId &id, objectIds) {
535             const QList<U2DataId> attributes = attrDbi->getObjectAttributes(id,
536                                                                             PREV_OBJ_PATH_ATTR_NAME,
537                                                                             os);
538             CHECK_OP(os, );
539 
540             CHECK_EXT(attributes.size() <= 1,
541                       os.setError("Multiple attribute definition detected!"), );
542 
543             if (!attributes.isEmpty()) {
544                 attrDbi->removeAttributes(attributes, os);
545                 CHECK_OP(os, );
546             }
547 
548             U2StringAttribute prevPath(id, PREV_OBJ_PATH_ATTR_NAME, fromFolder);
549             attrDbi->createStringAttribute(prevPath, os);
550         }
551     }
552 }
553 
restoreObjects(const QList<U2DataId> & objectIds,U2OpStatus & os)554 QStringList SQLiteObjectDbi::restoreObjects(const QList<U2DataId> &objectIds, U2OpStatus &os) {
555     U2AttributeDbi *attrDbi = dbi->getAttributeDbi();
556     QList<U2DataId> attributesWithStoredPath;
557     QStringList result;
558     foreach (const U2DataId &objId, objectIds) {
559         const QList<U2DataId> attributes = attrDbi->getObjectAttributes(objId,
560                                                                         PREV_OBJ_PATH_ATTR_NAME,
561                                                                         os);
562         CHECK_OP(os, result);
563 
564         CHECK_EXT(attributes.size() == 1,
565                   os.setError("Stored folder path not found!"),
566                   result);
567 
568         const U2DataId attrId = attributes.first();
569 
570         const U2StringAttribute storedPath = attrDbi->getStringAttribute(attrId, os);
571         CHECK_OP(os, result);
572         attributesWithStoredPath.append(attrId);
573         result.append(storedPath.value);
574 
575         const QStringList folders = getObjectFolders(objId, os);
576         CHECK_EXT(1 == folders.size(),
577                   os.setError("Multiple reference to object from folders found!"),
578                   result);
579         moveObjects(QList<U2DataId>() << objId, folders.first(), storedPath.value, os);
580         CHECK_OP(os, result);
581     }
582 
583     attrDbi->removeAttributes(attributesWithStoredPath, os);
584     return result;
585 }
586 
incrementVersion(const U2DataId & id,U2OpStatus & os)587 void SQLiteObjectDbi::incrementVersion(const U2DataId &id, U2OpStatus &os) {
588     SQLiteWriteQuery q("UPDATE Object SET version = version + 1 WHERE id = ?1", db, os);
589     CHECK_OP(os, );
590 
591     q.bindDataId(1, id);
592     q.update(1);
593 }
594 
setVersion(const U2DataId & id,qint64 version,U2OpStatus & os)595 void SQLiteObjectDbi::setVersion(const U2DataId &id, qint64 version, U2OpStatus &os) {
596     SQLiteWriteQuery q("UPDATE Object SET version = ?1 WHERE id = ?2", db, os);
597     SAFE_POINT_OP(os, );
598 
599     q.bindInt64(1, version);
600     q.bindDataId(2, id);
601     q.update(1);
602 }
603 
canUndo(const U2DataId & objId,U2OpStatus & os)604 bool SQLiteObjectDbi::canUndo(const U2DataId &objId, U2OpStatus &os) {
605     return dbi->getSQLiteModDbi()->canUndo(objId, os);
606 }
607 
canRedo(const U2DataId & objId,U2OpStatus & os)608 bool SQLiteObjectDbi::canRedo(const U2DataId &objId, U2OpStatus &os) {
609     return dbi->getSQLiteModDbi()->canRedo(objId, os);
610 }
611 
undo(const U2DataId & objId,U2OpStatus & os)612 void SQLiteObjectDbi::undo(const U2DataId &objId, U2OpStatus &os) {
613     SQLiteTransaction t(db, os);
614     Q_UNUSED(t);
615 
616     QString errorDescr = U2DbiL10n::tr("Can't undo an operation for the object!");
617 
618     // Get the object
619     U2Object obj;
620     getObject(obj, objId, os);
621     if (os.hasError()) {
622         coreLog.trace("Error getting an object: " + os.getError());
623         os.setError(errorDescr);
624         return;
625     }
626 
627     // Verify that modifications tracking is enabled for the object
628     if (TrackOnUpdate != obj.trackModType) {
629         coreLog.trace("Called 'undo' for an object without modifications tracking enabled!");
630         os.setError(errorDescr);
631         return;
632     }
633 
634     // Get all single modifications steps
635     qint64 userModStepVersion = dbi->getSQLiteModDbi()->getNearestUserModStepVersion(objId, obj.version - 1, os);
636     if (os.hasError()) {
637         coreLog.trace("Error getting the nearest userModStep version: " + os.getError());
638         os.setError(errorDescr);
639         return;
640     }
641 
642     QList<QList<U2SingleModStep>> modSteps = dbi->getSQLiteModDbi()->getModSteps(objId, userModStepVersion, os);
643     if (os.hasError()) {
644         coreLog.trace("Error getting modSteps for an object: " + os.getError());
645         os.setError(errorDescr);
646         return;
647     }
648 
649     QList<QList<U2SingleModStep>>::const_iterator multiIt = modSteps.end();
650     while (multiIt != modSteps.begin()) {
651         --multiIt;
652         QList<U2SingleModStep> multiStepSingleSteps = *multiIt;
653 
654         foreach (U2SingleModStep modStep, multiStepSingleSteps) {
655             // Call an appropriate "undo" depending on the object type
656             if (U2ModType::isUdrModType(modStep.modType)) {
657                 dbi->getSQLiteUdrDbi()->undo(modStep, os);
658             } else if (U2ModType::isMsaModType(modStep.modType)) {
659                 dbi->getSQLiteMsaDbi()->undo(modStep.objectId, modStep.modType, modStep.details, os);
660             } else if (U2ModType::isSequenceModType(modStep.modType)) {
661                 dbi->getSQLiteSequenceDbi()->undo(modStep.objectId, modStep.modType, modStep.details, os);
662             } else if (U2ModType::isObjectModType(modStep.modType)) {
663                 if (U2ModType::objUpdatedName == modStep.modType) {
664                     undoUpdateObjectName(modStep.objectId, modStep.details, os);
665                     CHECK_OP(os, );
666                 } else {
667                     coreLog.trace(QString("Can't undo an unknown operation: '%1'!").arg(QString::number(modStep.modType)));
668                     os.setError(errorDescr);
669                     return;
670                 }
671             }
672             if (os.hasError()) {
673                 coreLog.trace(QString("Can't undo a single step: '%1'!").arg(os.getError()));
674                 os.setError(errorDescr);
675                 return;
676             }
677 
678             setVersion(modStep.objectId, modStep.version, os);
679             if (os.hasError()) {
680                 coreLog.trace("Failed to set an object version: " + os.getError());
681                 os.setError(errorDescr);
682                 return;
683             }
684         }
685     }
686 
687     setVersion(objId, userModStepVersion, os);
688     if (os.hasError()) {
689         coreLog.trace("Failed to set an object version: " + os.getError());
690         os.setError(errorDescr);
691         return;
692     }
693 }
694 
redo(const U2DataId & objId,U2OpStatus & os)695 void SQLiteObjectDbi::redo(const U2DataId &objId, U2OpStatus &os) {
696     SQLiteTransaction t(db, os);
697     Q_UNUSED(t);
698 
699     QString errorDescr = U2DbiL10n::tr("Can't redo an operation for the object!");
700 
701     // Get the object
702     U2Object obj;
703     getObject(obj, objId, os);
704     if (os.hasError()) {
705         coreLog.trace("Error getting an object: " + os.getError());
706         os.setError(errorDescr);
707         return;
708     }
709 
710     // Verify that modifications tracking is enabled for the object
711     if (TrackOnUpdate != obj.trackModType) {
712         coreLog.trace("Called 'redo' for an object without modifications tracking enabled!");
713         os.setError(errorDescr);
714         return;
715     }
716 
717     // Get all single modification steps
718     QList<QList<U2SingleModStep>> modSteps = dbi->getSQLiteModDbi()->getModSteps(objId, obj.version, os);
719     if (os.hasError()) {
720         coreLog.trace("Error getting modSteps for an object: " + os.getError());
721         os.setError(errorDescr);
722         return;
723     }
724 
725     for (const QList<U2SingleModStep> &multiStepSingleSteps : qAsConst(modSteps)) {
726         QSet<U2DataId> modStepObjectIds;
727         for (const U2SingleModStep &modStep : qAsConst(multiStepSingleSteps)) {
728             if (U2ModType::isUdrModType(modStep.modType)) {
729                 dbi->getSQLiteUdrDbi()->redo(modStep, os);
730             } else if (U2ModType::isMsaModType(modStep.modType)) {
731                 dbi->getSQLiteMsaDbi()->redo(modStep.objectId, modStep.modType, modStep.details, os);
732             } else if (U2ModType::isSequenceModType(modStep.modType)) {
733                 dbi->getSQLiteSequenceDbi()->redo(modStep.objectId, modStep.modType, modStep.details, os);
734             } else if (U2ModType::isObjectModType(modStep.modType)) {
735                 if (modStep.modType == U2ModType::objUpdatedName) {
736                     redoUpdateObjectName(modStep.objectId, modStep.details, os);
737                     CHECK_OP(os, );
738                 } else {
739                     coreLog.trace(QString("Can't redo an unknown operation: '%1'!").arg(QString::number(modStep.modType)));
740                     os.setError(errorDescr);
741                     return;
742                 }
743             }
744 
745             modStepObjectIds.insert(modStep.objectId);
746         }
747         modStepObjectIds.insert(objId);
748 
749         for (const U2DataId &modStepObjectId : qAsConst(modStepObjectIds)) {
750             incrementVersion(modStepObjectId, os);
751             if (os.hasError()) {
752                 coreLog.trace("Can't increment an object version!");
753                 os.setError(errorDescr);
754                 return;
755             }
756         }
757     }
758 }
759 
undoUpdateObjectName(const U2DataId & id,const QByteArray & modDetails,U2OpStatus & os)760 void SQLiteObjectDbi::undoUpdateObjectName(const U2DataId &id, const QByteArray &modDetails, U2OpStatus &os) {
761     // Parse the input
762     QString oldName;
763     QString newName;
764     bool ok = U2DbiPackUtils::unpackObjectNameDetails(modDetails, oldName, newName);
765     if (!ok) {
766         os.setError("An error occurred during updating an object name!");
767         return;
768     }
769 
770     // Update the value
771     SQLiteWriteQuery q("UPDATE Object SET name = ?1 WHERE id = ?2", db, os);
772     CHECK_OP(os, );
773 
774     q.bindString(1, oldName);
775     q.bindDataId(2, id);
776     q.update(1);
777 }
778 
redoUpdateObjectName(const U2DataId & id,const QByteArray & modDetails,U2OpStatus & os)779 void SQLiteObjectDbi::redoUpdateObjectName(const U2DataId &id, const QByteArray &modDetails, U2OpStatus &os) {
780     QString oldName;
781     QString newName;
782     bool ok = U2DbiPackUtils::unpackObjectNameDetails(modDetails, oldName, newName);
783     if (!ok) {
784         os.setError("An error occurred during updating an object name!");
785         return;
786     }
787 
788     U2Object obj;
789     getObject(obj, id, os);
790     CHECK_OP(os, );
791 
792     obj.visualName = newName;
793     updateObjectCore(obj, os);
794     CHECK_OP(os, );
795 }
796 
removeParent(const U2DataId & parentId,const U2DataId & childId,bool removeDeadChild,U2OpStatus & os)797 void SQLiteObjectDbi::removeParent(const U2DataId &parentId, const U2DataId &childId, bool removeDeadChild, U2OpStatus &os) {
798     SQLiteWriteQuery q("DELETE FROM Parent WHERE parent = ?1 AND child = ?2", db, os);
799     q.bindDataId(1, parentId);
800     q.bindDataId(2, childId);
801     /*qint64 res = */ q.update(1);
802     if (os.hasError()) {
803         return;
804     }
805     if (!removeDeadChild) {
806         return;
807     }
808     QList<U2DataId> parents = getParents(childId, os);
809     if (!parents.isEmpty() || os.hasError()) {
810         return;
811     }
812     QList<QString> folders = getObjectFolders(childId, os);
813     if (!folders.isEmpty() || os.hasError()) {
814         return;
815     }
816     removeObjects(QList<U2DataId>() << childId, "", os);
817 }
818 
setParent(const U2DataId & parentId,const U2DataId & childId,U2OpStatus & os)819 void SQLiteObjectDbi::setParent(const U2DataId &parentId, const U2DataId &childId, U2OpStatus &os) {
820     SQLiteWriteQuery insertQ("INSERT OR IGNORE INTO Parent (parent, child) VALUES (?1, ?2)", db, os);
821     insertQ.bindDataId(1, parentId);
822     insertQ.bindDataId(2, childId);
823     insertQ.execute();
824 }
825 
826 //////////////////////////////////////////////////////////////////////////
827 // Helper methods
828 
incrementVersion(const U2DataId & objectId,DbRef * db,U2OpStatus & os)829 void SQLiteObjectDbi::incrementVersion(const U2DataId &objectId, DbRef *db, U2OpStatus &os) {
830     SQLiteTransaction t(db, os);
831     static const QString queryString("UPDATE Object SET version = version + 1 WHERE id = ?1");
832     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
833     CHECK_OP(os, );
834     q->bindDataId(1, objectId);
835     q->update(1);
836 }
837 
getObjectVersion(const U2DataId & objectId,U2OpStatus & os)838 qint64 SQLiteObjectDbi::getObjectVersion(const U2DataId &objectId, U2OpStatus &os) {
839     SQLiteTransaction t(db, os);
840     static const QString queryString("SELECT version FROM Object WHERE id = ?1");
841     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
842     CHECK_OP(os, -1);
843     q->bindDataId(1, objectId);
844     return q->selectInt64();
845 }
846 
setTrackModType(const U2DataId & objectId,U2TrackModType trackModType,U2OpStatus & os)847 void SQLiteObjectDbi::setTrackModType(const U2DataId &objectId, U2TrackModType trackModType, U2OpStatus &os) {
848     SQLiteWriteQuery q("UPDATE Object SET trackMod = ?1 WHERE id IN "
849                        "(SELECT o.id FROM Object o, Parent p WHERE p.parent = ?2 AND p.child = o.id) OR id = ?2",
850                        db,
851                        os);
852     CHECK_OP(os, );
853     q.bindInt32(1, trackModType);
854     q.bindDataId(2, objectId);
855     q.update(1);
856 }
857 
getTrackModType(const U2DataId & objectId,U2OpStatus & os)858 U2TrackModType SQLiteObjectDbi::getTrackModType(const U2DataId &objectId, U2OpStatus &os) {
859     SQLiteReadQuery q("SELECT trackMod FROM Object WHERE id = ?1", db, os);
860     CHECK_OP(os, NoTrack);
861 
862     q.bindDataId(1, objectId);
863     if (q.step()) {
864         int res = q.getInt32(0);
865         SAFE_POINT(res >= 0 && res < TRACK_MOD_TYPE_NR_ITEMS, "Incorrect trackMod value!", NoTrack);
866         q.ensureDone();
867         return (U2TrackModType)res;
868     } else if (!os.hasError()) {
869         os.setError(U2DbiL10n::tr("Object not found!"));
870         return NoTrack;
871     }
872 
873     return NoTrack;
874 }
875 
createObject(U2Object & object,const QString & folder,U2DbiObjectRank rank,U2OpStatus & os)876 U2DataId SQLiteObjectDbi::createObject(U2Object &object, const QString &folder, U2DbiObjectRank rank, U2OpStatus &os) {
877     SQLiteTransaction t(db, os);
878     U2DataType type = object.getType();
879     const QString &vname = object.visualName;
880     int trackMod = object.trackModType;
881     static const QString i1String("INSERT INTO Object(type, rank, name, trackMod) VALUES(?1, ?2, ?3, ?4)");
882     QSharedPointer<SQLiteQuery> i1 = t.getPreparedQuery(i1String, db, os);
883     CHECK_OP(os, U2DataId());
884     i1->bindType(1, type);
885     i1->bindInt32(2, rank);
886     i1->bindString(3, vname);
887     i1->bindInt32(4, trackMod);
888     U2DataId res = i1->insert(type);
889     CHECK_OP(os, res);
890 
891     if (U2DbiObjectRank_TopLevel == rank) {
892         const QString canonicalFolder = U2DbiUtils::makeFolderCanonical(folder);
893         qint64 folderId = getFolderId(canonicalFolder, true, db, os);
894         CHECK_OP(os, res);
895 
896         static const QString i2String("INSERT INTO FolderContent(folder, object) VALUES(?1, ?2)");
897         QSharedPointer<SQLiteQuery> i2 = t.getPreparedQuery(i2String, db, os);
898         CHECK_OP(os, res);
899         i2->bindInt64(1, folderId);
900         i2->bindDataId(2, res);
901         i2->execute();
902         CHECK_OP(os, res);
903     }
904 
905     object.id = res;
906     object.dbiId = dbi->getDbiId();
907     object.version = getObjectVersion(object.id, os);
908     SAFE_POINT_OP(os, res);
909     return res;
910 }
911 
getObject(U2Object & object,const U2DataId & id,U2OpStatus & os)912 void SQLiteObjectDbi::getObject(U2Object &object, const U2DataId &id, U2OpStatus &os) {
913     SQLiteReadQuery q("SELECT name, version, trackMod FROM Object WHERE id = ?1", db, os);
914     q.bindDataId(1, id);
915     if (q.step()) {
916         object.id = id;
917         object.dbiId = dbi->getDbiId();
918         object.visualName = q.getString(0);
919         object.version = q.getInt64(1);
920         int trackMod = q.getInt32(2);
921         if (trackMod >= 0 && trackMod < TRACK_MOD_TYPE_NR_ITEMS) {
922             object.trackModType = (U2TrackModType)trackMod;
923         } else {
924             os.setError("Incorrect trackMod value in an object!");
925             object.trackModType = NoTrack;
926         }
927         q.ensureDone();
928     } else if (!os.hasError()) {
929         os.setError(U2DbiL10n::tr("Object not found."));
930     }
931 }
932 
getObject(qint64 objectId,U2OpStatus & os)933 U2DataId SQLiteObjectDbi::getObject(qint64 objectId, U2OpStatus &os) {
934     SQLiteReadQuery q("SELECT id, type FROM Object WHERE id = ?1", db, os);
935     q.bindInt64(1, objectId);
936     if (q.step()) {
937         U2DataId result = q.getDataIdExt(0);
938         q.ensureDone();
939         return result;
940     } else if (!os.hasError()) {
941         os.setError(U2DbiL10n::tr("Object not found."));
942     }
943     return U2DataId();
944 }
945 
getObjectNames(qint64 offset,qint64 count,U2OpStatus & os)946 QHash<U2DataId, QString> SQLiteObjectDbi::getObjectNames(qint64 offset, qint64 count, U2OpStatus &os) {
947     QHash<U2DataId, QString> result;
948 
949     static const QString queryString = "SELECT id, type, name FROM Object WHERE " + TOP_LEVEL_FILTER;
950 
951     SQLiteReadQuery q(queryString, offset, count, db, os);
952     CHECK_OP(os, result);
953     while (q.step()) {
954         const U2DataType type = q.getDataType(1);
955         const U2DataId id = q.getDataId(0, type);
956         const QString name = q.getString(2);
957         result.insert(id, name);
958     }
959     return result;
960 }
961 
updateObject(U2Object & obj,U2OpStatus & os)962 void SQLiteObjectDbi::updateObject(U2Object &obj, U2OpStatus &os) {
963     updateObjectCore(obj, os);
964     SAFE_POINT_OP(os, );
965 
966     obj.version = getObjectVersion(obj.id, os);
967 }
968 
getFolderId(const QString & path,bool mustExist,DbRef * db,U2OpStatus & os)969 qint64 SQLiteObjectDbi::getFolderId(const QString &path, bool mustExist, DbRef *db, U2OpStatus &os) {
970     static const QString queryString("SELECT id FROM Folder WHERE path = ?1");
971     SQLiteReadQuery q(queryString, db, os);
972     q.bindString(1, path);
973     qint64 res = q.selectInt64();
974     if (os.hasError()) {
975         return -1;
976     }
977     if (mustExist && res == -1) {
978         os.setError(U2DbiL10n::tr("Folder not found: %1").arg(path));
979     }
980     return res;
981 }
982 
getFolderLocalVersion(const QString & folder,U2OpStatus & os)983 qint64 SQLiteObjectDbi::getFolderLocalVersion(const QString &folder, U2OpStatus &os) {
984     SQLiteReadQuery q("SELECT vlocal FROM Folder WHERE path = ?1", db, os);
985     q.bindString(1, folder);
986     return q.selectInt64();
987 }
988 
getFolderGlobalVersion(const QString & folder,U2OpStatus & os)989 qint64 SQLiteObjectDbi::getFolderGlobalVersion(const QString &folder, U2OpStatus &os) {
990     SQLiteReadQuery q("SELECT vglobal FROM Folder WHERE path = ?1", db, os);
991     q.bindString(1, folder);
992     return q.selectInt64();
993 }
994 
setObjectRank(const U2DataId & objectId,U2DbiObjectRank newRank,U2OpStatus & os)995 void SQLiteObjectDbi::setObjectRank(const U2DataId &objectId, U2DbiObjectRank newRank, U2OpStatus &os) {
996     SQLiteTransaction t(db, os);
997     static const QString queryStr("UPDATE Object SET rank = ?1 WHERE id = ?2");
998     QSharedPointer<SQLiteQuery> query = t.getPreparedQuery(queryStr, db, os);
999     CHECK_OP(os, );
1000     query->bindInt32(1, newRank);
1001     query->bindDataId(2, objectId);
1002     const int affectedRowsCount = query->update(-1);
1003     SAFE_POINT_EXT(-1 == affectedRowsCount || 0 == affectedRowsCount || affectedRowsCount == 1, os.setError(U2DbiL10n::tr("Unexpected row count. Query: '%1', rows: %2").arg(query->getQueryText()).arg(affectedRowsCount)), );
1004 }
1005 
getObjectRank(const U2DataId & objectId,U2OpStatus & os)1006 U2DbiObjectRank SQLiteObjectDbi::getObjectRank(const U2DataId &objectId, U2OpStatus &os) {
1007     SQLiteReadQuery q("SELECT rank FROM Object WHERE id = ?1", db, os);
1008     q.bindDataId(1, objectId);
1009     return static_cast<U2DbiObjectRank>(q.selectInt32());
1010 }
1011 
onFolderUpdated(const QString &)1012 void SQLiteObjectDbi::onFolderUpdated(const QString &) {
1013     // TODO: update local version of the given folder & global for all parents
1014 }
1015 
1016 //////////////////////////////////////////////////////////////////////////
1017 // cross references dbi
1018 
SQLiteCrossDatabaseReferenceDbi(SQLiteDbi * dbi)1019 SQLiteCrossDatabaseReferenceDbi::SQLiteCrossDatabaseReferenceDbi(SQLiteDbi *dbi)
1020     : U2CrossDatabaseReferenceDbi(dbi), SQLiteChildDBICommon(dbi) {
1021 }
1022 
initSqlSchema(U2OpStatus & os)1023 void SQLiteCrossDatabaseReferenceDbi::initSqlSchema(U2OpStatus &os) {
1024     if (os.hasError()) {
1025         return;
1026     }
1027     // cross database reference object
1028     // factory - remote dbi factory
1029     // dbi - remote dbi id (url)
1030     // rid  - remote object id
1031     // version - remove object version
1032     SQLiteWriteQuery("CREATE TABLE CrossDatabaseReference (object INTEGER, factory TEXT NOT NULL, dbi TEXT NOT NULL, "
1033                      "rid BLOB NOT NULL, version INTEGER NOT NULL, "
1034                      " FOREIGN KEY(object) REFERENCES Object(id) )",
1035                      db,
1036                      os)
1037         .execute();
1038 }
1039 
createCrossReference(U2CrossDatabaseReference & reference,const QString & folder,U2OpStatus & os)1040 void SQLiteCrossDatabaseReferenceDbi::createCrossReference(U2CrossDatabaseReference &reference, const QString &folder, U2OpStatus &os) {
1041     dbi->getSQLiteObjectDbi()->createObject(reference, folder, U2DbiObjectRank_TopLevel, os);
1042     if (os.hasError()) {
1043         return;
1044     }
1045 
1046     SQLiteWriteQuery q("INSERT INTO CrossDatabaseReference(object, factory, dbi, rid, version) VALUES(?1, ?2, ?3, ?4, ?5)", db, os);
1047     q.bindDataId(1, reference.id);
1048     q.bindString(2, reference.dataRef.dbiRef.dbiFactoryId);
1049     q.bindString(3, reference.dataRef.dbiRef.dbiId);
1050     q.bindBlob(4, reference.dataRef.entityId);
1051     q.bindInt64(5, reference.dataRef.version);
1052     q.execute();
1053 }
1054 
removeCrossReferenceData(const U2DataId & referenceId,U2OpStatus & os)1055 void SQLiteCrossDatabaseReferenceDbi::removeCrossReferenceData(const U2DataId &referenceId, U2OpStatus &os) {
1056     static const QString queryString = "DELETE FROM CrossDatabaseReference WHERE object = ?1";
1057     SQLiteWriteQuery q(queryString, db, os);
1058     q.bindDataId(1, referenceId);
1059     q.execute();
1060 }
1061 
getCrossReference(const U2DataId & objectId,U2OpStatus & os)1062 U2CrossDatabaseReference SQLiteCrossDatabaseReferenceDbi::getCrossReference(const U2DataId &objectId, U2OpStatus &os) {
1063     U2CrossDatabaseReference res(objectId, dbi->getDbiId(), 0);
1064     SQLiteReadQuery q("SELECT r.factory, r.dbi, r.rid, r.version, o.name, o.version FROM CrossDatabaseReference AS r, Object AS o "
1065                       " WHERE o.id = ?1 AND r.object = o.id",
1066                       db,
1067                       os);
1068     q.bindDataId(1, objectId);
1069     if (q.step()) {
1070         res.dataRef.dbiRef.dbiFactoryId = q.getString(0);
1071         res.dataRef.dbiRef.dbiId = q.getString(1);
1072         res.dataRef.entityId = q.getBlob(2);
1073         res.dataRef.version = q.getInt64(3);
1074         res.visualName = q.getString(4);
1075         res.version = q.getInt64(5);
1076         q.ensureDone();
1077     }
1078     return res;
1079 }
1080 
updateCrossReference(const U2CrossDatabaseReference & reference,U2OpStatus & os)1081 void SQLiteCrossDatabaseReferenceDbi::updateCrossReference(const U2CrossDatabaseReference &reference, U2OpStatus &os) {
1082     SQLiteWriteQuery q("UPDATE CrossDatabaseReference SET factory = ?1, dbi = ?2, rid = ?3, version = ?4 WHERE object = ?5", db, os);
1083     q.bindString(1, reference.dataRef.dbiRef.dbiFactoryId);
1084     q.bindString(2, reference.dataRef.dbiRef.dbiId);
1085     q.bindBlob(3, reference.dataRef.entityId);
1086     q.bindInt64(4, reference.dataRef.version);
1087     q.bindDataId(5, reference.id);
1088     q.execute();
1089 }
1090 
SQLiteModificationAction(SQLiteDbi * _dbi,const U2DataId & _masterObjId)1091 SQLiteModificationAction::SQLiteModificationAction(SQLiteDbi *_dbi, const U2DataId &_masterObjId)
1092     : ModificationAction(_dbi, _masterObjId) {
1093 }
1094 
prepare(U2OpStatus & os)1095 U2TrackModType SQLiteModificationAction::prepare(U2OpStatus &os) {
1096     trackMod = dbi->getObjectDbi()->getTrackModType(masterObjId, os);
1097     if (os.hasError()) {
1098         trackMod = NoTrack;
1099         FAIL("Failed to get trackMod!", NoTrack);
1100     }
1101 
1102     if (TrackOnUpdate == trackMod) {
1103         qint64 masterObjVersionToTrack = dbi->getObjectDbi()->getObjectVersion(masterObjId, os);
1104         SAFE_POINT_OP(os, trackMod);
1105 
1106         // If a user mod step has already been created for this action
1107         // then it can not be deleted. The version must be incremented.
1108         // Obsolete duplicate step must be deleted
1109         if (getDbi()->getSQLiteModDbi()->isUserStepStarted(masterObjId)) {
1110             getDbi()->getSQLiteModDbi()->removeDuplicateUserStep(masterObjId, masterObjVersionToTrack, os);
1111 
1112             // Increment the object version
1113             masterObjVersionToTrack++;
1114         }
1115 
1116         // A user pressed "Undo" (maybe several times), did another action => there is no more "Redo" history
1117         getDbi()->getSQLiteModDbi()->removeModsWithGreaterVersion(masterObjId, masterObjVersionToTrack, os);
1118         if (os.hasError()) {
1119             getDbi()->getSQLiteModDbi()->cleanUpAllStepsOnError();
1120             return trackMod;
1121         }
1122     }
1123 
1124     return trackMod;
1125 }
1126 
addModification(const U2DataId & objId,qint64 modType,const QByteArray & modDetails,U2OpStatus & os)1127 void SQLiteModificationAction::addModification(const U2DataId &objId, qint64 modType, const QByteArray &modDetails, U2OpStatus &os) {
1128     objIds.insert(objId);
1129 
1130     if (TrackOnUpdate == trackMod) {
1131         SAFE_POINT(!modDetails.isEmpty(), "Empty modification details!", );
1132 
1133         qint64 objVersion = dbi->getObjectDbi()->getObjectVersion(objId, os);
1134         SAFE_POINT_OP(os, );
1135 
1136         if ((objId == masterObjId) && (getDbi()->getSQLiteModDbi()->isUserStepStarted(masterObjId))) {
1137             objVersion++;
1138         }
1139 
1140         U2SingleModStep singleModStep;
1141         singleModStep.objectId = objId;
1142         singleModStep.version = objVersion;
1143         singleModStep.modType = modType;
1144         singleModStep.details = modDetails;
1145 
1146         singleSteps.append(singleModStep);
1147     }
1148 }
1149 
complete(U2OpStatus & os)1150 void SQLiteModificationAction::complete(U2OpStatus &os) {
1151     // Save modification tracks, if required
1152     if (TrackOnUpdate == trackMod) {
1153         if (0 == singleSteps.size()) {
1154             // do nothing
1155         } else if (1 == singleSteps.size()) {
1156             getDbi()->getSQLiteModDbi()->createModStep(masterObjId, singleSteps.first(), os);
1157             SAFE_POINT_OP(os, );
1158         } else {
1159             U2UseCommonMultiModStep multi(getDbi(), masterObjId, os);
1160             SAFE_POINT_OP(os, );
1161 
1162             foreach (U2SingleModStep singleStep, singleSteps) {
1163                 getDbi()->getSQLiteModDbi()->createModStep(masterObjId, singleStep, os);
1164                 SAFE_POINT_OP(os, );
1165             }
1166         }
1167     }
1168 
1169     // Increment versions of all objects
1170     foreach (const U2DataId &objId, objIds) {
1171         SQLiteObjectDbi::incrementVersion(objId, getDbi()->getDbRef(), os);
1172         SAFE_POINT_OP(os, );
1173     }
1174 }
1175 
getDbi() const1176 SQLiteDbi *SQLiteModificationAction::getDbi() const {
1177     return static_cast<SQLiteDbi *>(dbi);
1178 }
1179 
1180 /************************************************************************/
1181 /* SQLiteObjectDbiUtils */
1182 /************************************************************************/
renameObject(SQLiteDbi * dbi,U2Object & object,const QString & newName,U2OpStatus & os)1183 void SQLiteObjectDbiUtils::renameObject(SQLiteDbi *dbi, U2Object &object, const QString &newName, U2OpStatus &os) {
1184     SAFE_POINT(nullptr != dbi, "NULL dbi!", );
1185     SQLiteTransaction t(dbi->getDbRef(), os);
1186     Q_UNUSED(t);
1187 
1188     SQLiteModificationAction updateAction(dbi, object.id);
1189     updateAction.prepare(os);
1190     SAFE_POINT_OP(os, );
1191 
1192     renameObject(updateAction, dbi, object, newName, os);
1193     SAFE_POINT_OP(os, );
1194 
1195     // Increment version; track the modification, if required
1196     updateAction.complete(os);
1197     SAFE_POINT_OP(os, );
1198 }
1199 
renameObject(SQLiteModificationAction & updateAction,SQLiteDbi * dbi,U2Object & object,const QString & newName,U2OpStatus & os)1200 void SQLiteObjectDbiUtils::renameObject(SQLiteModificationAction &updateAction, SQLiteDbi *dbi, U2Object &object, const QString &newName, U2OpStatus &os) {
1201     SAFE_POINT(nullptr != dbi, "NULL dbi!", );
1202     SQLiteTransaction t(dbi->getDbRef(), os);
1203     Q_UNUSED(t);
1204 
1205     QByteArray modDetails;
1206     if (TrackOnUpdate == updateAction.getTrackModType()) {
1207         modDetails = U2DbiPackUtils::packObjectNameDetails(object.visualName, newName);
1208     }
1209 
1210     object.visualName = newName;
1211     dbi->getSQLiteObjectDbi()->updateObject(object, os);
1212     SAFE_POINT_OP(os, );
1213 
1214     updateAction.addModification(object.id, U2ModType::objUpdatedName, modDetails, os);
1215     SAFE_POINT_OP(os, );
1216 }
1217 
1218 }  // namespace U2
1219