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 "SQLiteModDbi.h"
23
24 #include <QCoreApplication>
25
26 #include <U2Core/U2OpStatusUtils.h>
27 #include <U2Core/U2SafePoints.h>
28 #include <U2Core/U2SqlHelpers.h>
29
30 #include <U2Formats/SQLiteObjectDbi.h>
31
32 namespace U2 {
33
34 /************************************************************************/
35 /* U2UseCommonMultiModStep */
36 /************************************************************************/
U2UseCommonMultiModStep(SQLiteDbi * _sqliteDbi,const U2DataId & _masterObjId,U2OpStatus & os)37 U2UseCommonMultiModStep::U2UseCommonMultiModStep(SQLiteDbi *_sqliteDbi, const U2DataId &_masterObjId, U2OpStatus &os)
38 : sqliteDbi(_sqliteDbi),
39 valid(false),
40 masterObjId(_masterObjId) {
41 SAFE_POINT(nullptr != sqliteDbi, "NULL sqliteDbi!", );
42 QMutexLocker m(&sqliteDbi->getDbRef()->lock);
43
44 sqliteDbi->getSQLiteModDbi()->startCommonMultiModStep(masterObjId, os);
45 if (!os.hasError()) {
46 valid = true;
47 }
48 }
49
~U2UseCommonMultiModStep()50 U2UseCommonMultiModStep::~U2UseCommonMultiModStep() {
51 SAFE_POINT(nullptr != sqliteDbi, "NULL sqliteDbi!", );
52 QMutexLocker m(&sqliteDbi->getDbRef()->lock);
53 if (valid) {
54 U2OpStatus2Log os;
55 sqliteDbi->getSQLiteModDbi()->endCommonMultiModStep(masterObjId, os);
56 }
57 }
58
59 /************************************************************************/
60 /* ModStepsDescriptor */
61 /************************************************************************/
62
ModStepsDescriptor()63 ModStepsDescriptor::ModStepsDescriptor()
64 : userModStepId(-1),
65 multiModStepId(-1),
66 removeUserStepWithMulti(false) {
67 }
68
69 /************************************************************************/
70 /* SQLiteModDbi */
71 /************************************************************************/
72 QMap<U2DataId, ModStepsDescriptor> SQLiteModDbi::modStepsByObject;
73
SQLiteModDbi(SQLiteDbi * dbi)74 SQLiteModDbi::SQLiteModDbi(SQLiteDbi *dbi)
75 : U2ModDbi(dbi), SQLiteChildDBICommon(dbi) {
76 }
77
initSqlSchema(U2OpStatus & os)78 void SQLiteModDbi::initSqlSchema(U2OpStatus &os) {
79 if (os.hasError()) {
80 return;
81 }
82
83 // UserModStep - user modification steps
84 // id - id of the user modifications step
85 // object, otype, oextra - data id of the master object (i.e. object for which "undo/redo" was initiated)
86 // version - master object was modified from this version
87 SQLiteWriteQuery("CREATE TABLE UserModStep (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
88 " object INTEGER NOT NULL,"
89 " otype INTEGER NOT NULL,"
90 " oextra BLOB NOT NULL,"
91 " version INTEGER NOT NULL, "
92 " FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE)",
93 db,
94 os)
95 .execute();
96
97 // MultiModStep - multiple modifications step with reference to a user modifications step
98 // id - id of the multiple modifications step
99 // userStepId - id of the user modifications step
100 SQLiteWriteQuery("CREATE TABLE MultiModStep (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
101 " userStepId INTEGER NOT NULL,"
102 " FOREIGN KEY(userStepId) REFERENCES UserModStep(id) ON DELETE CASCADE)",
103 db,
104 os)
105 .execute();
106
107 // SingleModStep - single modification of a dbi object
108 // id - id of the modification
109 // object, otype, oextra - data id of the object that was modified
110 // version - this is a modification from 'version' to 'version + 1' of the object
111 // modType - type of the object modification
112 // details - detailed description of the object modification
113 // multiStepId - id of the multiModStep
114 SQLiteWriteQuery("CREATE TABLE SingleModStep (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
115 " object INTEGER NOT NULL,"
116 " otype INTEGER NOT NULL,"
117 " oextra BLOB NOT NULL,"
118 " version INTEGER NOT NULL,"
119 " modType INTEGER NOT NULL,"
120 " details TEXT NOT NULL,"
121 " multiStepId INTEGER NOT NULL, "
122 " FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE, "
123 " FOREIGN KEY(multiStepId) REFERENCES MultiModStep(id) ON DELETE CASCADE)",
124 db,
125 os)
126 .execute();
127 SQLiteWriteQuery("CREATE INDEX SingleModStep_object ON SingleModStep(object)", db, os).execute();
128 SQLiteWriteQuery("CREATE INDEX SingleModStep_object_version ON SingleModStep(object, version)", db, os).execute();
129 }
130
getModStep(const U2DataId & objectId,qint64 trackVersion,U2OpStatus & os)131 U2SingleModStep SQLiteModDbi::getModStep(const U2DataId &objectId, qint64 trackVersion, U2OpStatus &os) {
132 U2SingleModStep res;
133 SQLiteReadQuery q("SELECT id, object, otype, oextra, version, modType, details, multiStepId FROM SingleModStep WHERE object = ?1 AND version = ?2 ORDER BY id", db, os);
134 SAFE_POINT_OP(os, res);
135
136 q.bindDataId(1, objectId);
137 q.bindInt64(2, trackVersion);
138
139 if (q.step()) {
140 res.id = q.getInt64(0);
141 res.objectId = q.getDataIdExt(1);
142 res.version = q.getInt64(4);
143 res.modType = q.getInt64(5);
144 res.details = q.getBlob(6);
145 q.ensureDone();
146 } else if (!os.hasError()) {
147 os.setError(U2DbiL10n::tr("An object single modification step not found!"));
148 }
149
150 return res;
151 }
152
getNearestUserModStepVersion(const U2DataId & masterObjId,qint64 version,U2OpStatus & os)153 qint64 SQLiteModDbi::getNearestUserModStepVersion(const U2DataId &masterObjId, qint64 version, U2OpStatus &os) {
154 SQLiteReadQuery qVersion("SELECT MAX(version) FROM UserModStep WHERE object = ?1 AND version <= ?2", db, os);
155 qVersion.bindDataId(1, masterObjId);
156 qVersion.bindInt64(2, version);
157
158 qint64 userStepVersion = version;
159 if (qVersion.step()) {
160 userStepVersion = qVersion.getInt64(0);
161 }
162 SAFE_POINT_OP(os, userStepVersion);
163 return userStepVersion;
164 }
165
getModSteps(const U2DataId & masterObjId,qint64 version,U2OpStatus & os)166 QList<QList<U2SingleModStep>> SQLiteModDbi::getModSteps(const U2DataId &masterObjId, qint64 version, U2OpStatus &os) {
167 QList<QList<U2SingleModStep>> steps;
168 SQLiteTransaction t(db, os);
169
170 qint64 userStepId = -1;
171 SQLiteWriteQuery qGetUserStepId("SELECT id FROM UserModStep WHERE object = ?1 AND version = ?2", db, os);
172 SAFE_POINT_OP(os, QList<QList<U2SingleModStep>>());
173
174 qGetUserStepId.bindDataId(1, masterObjId);
175 qGetUserStepId.bindInt64(2, version);
176
177 if (qGetUserStepId.step()) {
178 userStepId = qGetUserStepId.getInt64(0);
179 qGetUserStepId.ensureDone();
180 } else if (!os.hasError()) {
181 os.setError("Failed to find user step ID!");
182 return steps;
183 }
184
185 SQLiteReadQuery qMultiStepId("SELECT id FROM MultiModStep WHERE userStepId = ?1", db, os);
186 qMultiStepId.bindInt64(1, userStepId);
187
188 SQLiteReadQuery qSingleStep("SELECT id, object, otype, oextra, version, modType, details, multiStepId FROM SingleModStep WHERE multiStepId = ?1", db, os);
189 while (qMultiStepId.step()) {
190 qint64 multiStepId = qMultiStepId.getInt64(0);
191
192 qSingleStep.reset();
193 qSingleStep.bindInt64(1, multiStepId);
194
195 QList<U2SingleModStep> currentMultiStepSingleSteps;
196
197 while (qSingleStep.step()) {
198 U2SingleModStep step;
199 step.id = qSingleStep.getInt64(0);
200 step.objectId = qSingleStep.getDataIdExt(1);
201 step.version = qSingleStep.getInt64(4);
202 step.modType = qSingleStep.getInt64(5);
203 step.details = qSingleStep.getBlob(6);
204
205 SAFE_POINT_OP(os, QList<QList<U2SingleModStep>>());
206 currentMultiStepSingleSteps.append(step);
207 }
208 steps.append(currentMultiStepSingleSteps);
209 }
210 return steps;
211 }
212
createModStep(const U2DataId & masterObjId,U2SingleModStep & step,U2OpStatus & os)213 void SQLiteModDbi::createModStep(const U2DataId &masterObjId, U2SingleModStep &step, U2OpStatus &os) {
214 SQLiteTransaction t(db, os);
215 bool closeMultiStep = false;
216 if (!isMultiStepStarted(masterObjId)) {
217 startCommonMultiModStep(masterObjId, os);
218 SAFE_POINT_OP(os, );
219 SAFE_POINT(isMultiStepStarted(masterObjId), "A multiple modifications step must have been started!", );
220 closeMultiStep = true;
221 }
222
223 SQLiteWriteQuery qSingle("INSERT INTO SingleModStep(object, otype, oextra, version, modType, details, multiStepId) VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7) ", db, os);
224 SAFE_POINT_OP(os, );
225
226 qSingle.bindDataId(1, step.objectId);
227 qSingle.bindType(2, U2DbiUtils::toType(step.objectId));
228 qSingle.bindBlob(3, U2DbiUtils::toDbExtra(step.objectId));
229 qSingle.bindInt64(4, step.version);
230 qSingle.bindInt64(5, step.modType);
231 qSingle.bindBlob(6, step.details);
232 qSingle.bindInt64(7, modStepsByObject[masterObjId].multiModStepId);
233
234 step.id = qSingle.insert();
235 step.multiStepId = modStepsByObject[masterObjId].multiModStepId;
236
237 if (closeMultiStep) {
238 endCommonMultiModStep(masterObjId, os);
239 }
240 }
241
removeModsWithGreaterVersion(const U2DataId & masterObjId,qint64 masterObjVersion,U2OpStatus & os)242 void SQLiteModDbi::removeModsWithGreaterVersion(const U2DataId &masterObjId, qint64 masterObjVersion, U2OpStatus &os) {
243 SQLiteTransaction t(db, os);
244
245 // Get user step IDs
246 QList<qint64> userStepIds;
247 SQLiteReadQuery qSelectUserSteps("SELECT id FROM UserModStep WHERE object = ?1 AND version >= ?2", db, os);
248 SAFE_POINT_OP(os, );
249
250 qSelectUserSteps.bindDataId(1, masterObjId);
251 qSelectUserSteps.bindInt64(2, masterObjVersion);
252
253 while (qSelectUserSteps.step()) {
254 qint64 userStepId = qSelectUserSteps.getInt64(0);
255 userStepIds.append(userStepId);
256 }
257 SAFE_POINT_OP(os, );
258
259 // Remove all affected steps (user, multi, single)
260 removeSteps(userStepIds, os);
261 SAFE_POINT_OP(os, );
262 }
263
removeDuplicateUserStep(const U2DataId & masterObjId,qint64 masterObjVersion,U2OpStatus & os)264 void SQLiteModDbi::removeDuplicateUserStep(const U2DataId &masterObjId, qint64 masterObjVersion, U2OpStatus &os) {
265 SQLiteTransaction t(db, os);
266
267 // Get user step IDs
268 QList<qint64> userStepIds;
269 SQLiteReadQuery qSelect("SELECT id FROM UserModStep WHERE object = ?1 AND version = ?2", db, os);
270 SAFE_POINT_OP(os, );
271
272 qSelect.bindDataId(1, masterObjId);
273 qSelect.bindInt64(2, masterObjVersion);
274
275 while (qSelect.step()) {
276 qint64 id = qSelect.getInt64(0);
277 userStepIds.append(id);
278 }
279 SAFE_POINT_OP(os, );
280
281 if (userStepIds.count() < 2) {
282 return;
283 }
284
285 assert(2 == userStepIds.count());
286
287 // Don't take into account user step with the greatest id
288 userStepIds.removeLast();
289
290 // Remove user step with lower ID
291 removeSteps(userStepIds, os);
292 }
293
removeSteps(QList<qint64> userStepIds,U2OpStatus & os)294 void SQLiteModDbi::removeSteps(QList<qint64> userStepIds, U2OpStatus &os) {
295 if (userStepIds.isEmpty()) {
296 return;
297 }
298
299 SQLiteTransaction t(db, os);
300
301 // Get multiple steps IDs
302 QList<qint64> multiStepIds;
303 SQLiteReadQuery qSelectMultiSteps("SELECT id FROM MultiModStep WHERE userStepId = ?1", db, os);
304 SAFE_POINT_OP(os, );
305 foreach (qint64 userStepId, userStepIds) {
306 qSelectMultiSteps.reset();
307 qSelectMultiSteps.bindInt64(1, userStepId);
308
309 while (qSelectMultiSteps.step()) {
310 qint64 multiStepId = qSelectMultiSteps.getInt64(0);
311 multiStepIds.append(multiStepId);
312 }
313 }
314
315 // Remove single steps
316 SQLiteWriteQuery qDeleteSingleSteps("DELETE FROM SingleModStep WHERE multiStepId = ?1", db, os);
317 SAFE_POINT_OP(os, );
318 foreach (qint64 multiStepId, multiStepIds) {
319 qDeleteSingleSteps.reset();
320 qDeleteSingleSteps.bindInt64(1, multiStepId);
321 qDeleteSingleSteps.execute();
322 }
323 SAFE_POINT_OP(os, );
324
325 // Remove multiple steps
326 SQLiteWriteQuery qDeleteMultiSteps("DELETE FROM MultiModStep WHERE id = ?1", db, os);
327 SAFE_POINT_OP(os, );
328 foreach (qint64 multiStepId, multiStepIds) {
329 qDeleteMultiSteps.reset();
330 qDeleteMultiSteps.bindInt64(1, multiStepId);
331 qDeleteMultiSteps.execute();
332 }
333
334 // Remove user steps
335 SQLiteWriteQuery qDeleteUserSteps("DELETE FROM UserModStep WHERE id = ?1", db, os);
336 foreach (qint64 userStepId, userStepIds) {
337 qDeleteUserSteps.reset();
338 qDeleteUserSteps.bindInt64(1, userStepId);
339 qDeleteUserSteps.execute();
340 }
341 }
342
removeObjectMods(const U2DataId & objectId,U2OpStatus & os)343 void SQLiteModDbi::removeObjectMods(const U2DataId &objectId, U2OpStatus &os) {
344 SQLiteTransaction t(db, os);
345
346 // Get user step IDs
347 QList<qint64> userStepIds;
348 SQLiteReadQuery qSelectUserSteps("SELECT id FROM UserModStep WHERE object = ?1", db, os);
349 SAFE_POINT_OP(os, );
350
351 qSelectUserSteps.bindDataId(1, objectId);
352
353 while (qSelectUserSteps.step()) {
354 qint64 userStepId = qSelectUserSteps.getInt64(0);
355 userStepIds.append(userStepId);
356 }
357 SAFE_POINT_OP(os, );
358
359 // Remove all affected steps (user, multi, single)
360 removeSteps(userStepIds, os);
361 SAFE_POINT_OP(os, );
362 }
363
cleanUpAllStepsOnError()364 void SQLiteModDbi::cleanUpAllStepsOnError() {
365 U2OpStatus2Log os;
366 SQLiteTransaction t(db, os);
367
368 SQLiteWriteQuery("DELETE FROM SingleModStep", db, os).execute();
369 SQLiteWriteQuery("DELETE FROM MultiModStep", db, os).execute();
370 SQLiteWriteQuery("DELETE FROM UserModStep", db, os).execute();
371 }
372
checkMainThread(U2OpStatus & os)373 static void checkMainThread(U2OpStatus &os) {
374 QThread *mainThread = QCoreApplication::instance()->thread();
375 QThread *thisThread = QThread::currentThread();
376
377 if (mainThread != thisThread) {
378 os.setError("Not main thread");
379 }
380 }
381
startCommonUserModStep(const U2DataId & masterObjId,U2OpStatus & os)382 void SQLiteModDbi::startCommonUserModStep(const U2DataId &masterObjId, U2OpStatus &os) {
383 checkMainThread(os);
384 CHECK_OP(os, );
385 SQLiteTransaction t(db, os);
386
387 // Only one common step at a time
388 if (isUserStepStarted(masterObjId)) {
389 os.setError("Can't create a common user modifications step, previous one is not complete!");
390 return;
391 }
392
393 if (!modStepsByObject.contains(masterObjId)) {
394 modStepsByObject[masterObjId] = ModStepsDescriptor();
395 }
396
397 // Create a new user modifications step in the database
398 createUserModStep(masterObjId, os);
399 SAFE_POINT_OP(os, );
400 }
401
endCommonUserModStep(const U2DataId & userMasterObjId,U2OpStatus & os)402 void SQLiteModDbi::endCommonUserModStep(const U2DataId &userMasterObjId, U2OpStatus &os) {
403 checkMainThread(os);
404 CHECK_OP(os, );
405 SAFE_POINT(modStepsByObject.contains(userMasterObjId), QString("There are not modification steps for object with id %1").arg(userMasterObjId.toLong()), );
406
407 qint64 userModStepId = modStepsByObject[userMasterObjId].userModStepId;
408 qint64 multiModStepId = modStepsByObject[userMasterObjId].multiModStepId;
409
410 modStepsByObject.remove(userMasterObjId);
411
412 if (-1 == multiModStepId) {
413 SQLiteTransaction t(db, os);
414
415 // Get multiple steps IDs
416 SQLiteReadQuery qSelectMultiSteps("SELECT id FROM MultiModStep WHERE userStepId = ?1", db, os);
417 SAFE_POINT_OP(os, );
418
419 qSelectMultiSteps.bindInt64(1, userModStepId);
420
421 // If user modification step doesn't contain any multi modification steps
422 if (!qSelectMultiSteps.step()) {
423 SQLiteWriteQuery qDeleteUserSteps("DELETE FROM UserModStep WHERE id = ?1", db, os);
424 qDeleteUserSteps.bindInt64(1, userModStepId);
425 qDeleteUserSteps.execute();
426 SAFE_POINT_OP(os, );
427 }
428 }
429 }
430
startCommonMultiModStep(const U2DataId & userMasterObjId,U2OpStatus & os)431 void SQLiteModDbi::startCommonMultiModStep(const U2DataId &userMasterObjId, U2OpStatus &os) {
432 SQLiteTransaction t(db, os);
433 if (!modStepsByObject.contains(userMasterObjId)) {
434 modStepsByObject[userMasterObjId] = ModStepsDescriptor();
435 }
436 if (!isUserStepStarted(userMasterObjId)) {
437 startCommonUserModStep(userMasterObjId, os);
438 SAFE_POINT_OP(os, );
439 SAFE_POINT(isUserStepStarted(userMasterObjId), "A user modifications step must have been started!", );
440 modStepsByObject[userMasterObjId].removeUserStepWithMulti = true;
441 } else {
442 modStepsByObject[userMasterObjId].removeUserStepWithMulti = false;
443 }
444
445 if (isMultiStepStarted(userMasterObjId)) {
446 os.setError("Can't create a common multiple modifications step, previous one is not complete!");
447 U2OpStatus2Log innerOs;
448 endCommonUserModStep(userMasterObjId, innerOs);
449 return;
450 }
451
452 // Create a new multiple modifications step in the database
453 createMultiModStep(userMasterObjId, os);
454 SAFE_POINT_OP(os, );
455 }
456
endCommonMultiModStep(const U2DataId & masterObjId,U2OpStatus & os)457 void SQLiteModDbi::endCommonMultiModStep(const U2DataId &masterObjId, U2OpStatus &os) {
458 if (modStepsByObject[masterObjId].removeUserStepWithMulti) {
459 endCommonUserModStep(masterObjId, os);
460 } else {
461 modStepsByObject[masterObjId].multiModStepId = -1;
462 }
463 }
464
createUserModStep(const U2DataId & masterObjId,U2OpStatus & os)465 void SQLiteModDbi::createUserModStep(const U2DataId &masterObjId, U2OpStatus &os) {
466 qint64 masterObjVersion = dbi->getSQLiteObjectDbi()->getObjectVersion(masterObjId, os);
467 SAFE_POINT_OP(os, );
468
469 SQLiteWriteQuery qUser("INSERT INTO UserModStep(object, otype, oextra, version) VALUES(?1, ?2, ?3, ?4)", db, os);
470 SAFE_POINT_OP(os, );
471
472 qUser.bindDataId(1, masterObjId);
473 qUser.bindType(2, U2DbiUtils::toType(masterObjId));
474 qUser.bindBlob(3, U2DbiUtils::toDbExtra(masterObjId));
475 qUser.bindInt64(4, masterObjVersion);
476
477 qint64 curUserModStepId = qUser.insert();
478
479 if (-1 == curUserModStepId) {
480 os.setError("Failed to create a common user modifications step!");
481 return;
482 } else {
483 modStepsByObject[masterObjId].userModStepId = curUserModStepId;
484 }
485 }
486
createMultiModStep(const U2DataId & masterObjId,U2OpStatus & os)487 void SQLiteModDbi::createMultiModStep(const U2DataId &masterObjId, U2OpStatus &os) {
488 SAFE_POINT(isUserStepStarted(masterObjId), "A user modifications step must have been started!", );
489
490 SQLiteWriteQuery qMulti("INSERT INTO MultiModStep(userStepId) VALUES(?1)", db, os);
491 SAFE_POINT_OP(os, );
492
493 qMulti.bindInt64(1, modStepsByObject[masterObjId].userModStepId);
494
495 qint64 curMultiModStepId = qMulti.insert();
496
497 if (-1 == curMultiModStepId) {
498 os.setError("Failed to create a common multiple modifications step!");
499 return;
500 } else {
501 modStepsByObject[masterObjId].multiModStepId = curMultiModStepId;
502 }
503 }
504
isUserStepStarted(const U2DataId & userMasterObjId)505 bool SQLiteModDbi::isUserStepStarted(const U2DataId &userMasterObjId) {
506 if (!modStepsByObject.contains(userMasterObjId)) {
507 return false;
508 }
509 return modStepsByObject[userMasterObjId].userModStepId != -1;
510 }
isMultiStepStarted(const U2DataId & userMasterObjId)511 bool SQLiteModDbi::isMultiStepStarted(const U2DataId &userMasterObjId) {
512 if (!modStepsByObject.contains(userMasterObjId)) {
513 return false;
514 }
515 return modStepsByObject[userMasterObjId].multiModStepId != -1;
516 }
517
canUndo(const U2DataId & objectId,U2OpStatus & os)518 bool SQLiteModDbi::canUndo(const U2DataId &objectId, U2OpStatus &os) {
519 SQLiteTransaction t(db, os);
520
521 // Get current object version
522 qint64 objVersion = dbi->getSQLiteObjectDbi()->getObjectVersion(objectId, os);
523 SAFE_POINT_OP(os, false);
524
525 // Verify if there are steps
526 SQLiteReadQuery q("SELECT id FROM UserModStep WHERE object = ?1 AND version < ?2", db, os);
527 SAFE_POINT_OP(os, false);
528
529 q.bindDataId(1, objectId);
530 q.bindInt64(2, objVersion);
531
532 if (q.step()) {
533 return true;
534 }
535
536 return false;
537 }
538
canRedo(const U2DataId & objectId,U2OpStatus & os)539 bool SQLiteModDbi::canRedo(const U2DataId &objectId, U2OpStatus &os) {
540 SQLiteTransaction t(db, os);
541
542 // Get current object version
543 qint64 objVersion = dbi->getSQLiteObjectDbi()->getObjectVersion(objectId, os);
544 SAFE_POINT_OP(os, false);
545
546 // Verify if there are steps
547 SQLiteReadQuery q("SELECT id FROM UserModStep WHERE object = ?1 AND version >= ?2", db, os);
548 SAFE_POINT_OP(os, false);
549
550 q.bindDataId(1, objectId);
551 q.bindInt64(2, objVersion);
552
553 if (q.step()) {
554 return true;
555 }
556
557 return false;
558 }
559
560 } // namespace U2
561