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