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 "SQLiteMsaDbi.h"
23 
24 #include <U2Core/U2DbiPackUtils.h>
25 #include <U2Core/U2SafePoints.h>
26 #include <U2Core/U2SqlHelpers.h>
27 
28 #include "SQLiteModDbi.h"
29 #include "SQLiteSequenceDbi.h"
30 
31 namespace U2 {
32 
SQLiteMsaDbi(SQLiteDbi * dbi)33 SQLiteMsaDbi::SQLiteMsaDbi(SQLiteDbi *dbi)
34     : U2MsaDbi(dbi), SQLiteChildDBICommon(dbi) {
35 }
36 
initSqlSchema(U2OpStatus & os)37 void SQLiteMsaDbi::initSqlSchema(U2OpStatus &os) {
38     if (os.hasError()) {
39         return;
40     }
41 
42     // MSA object
43     SQLiteWriteQuery("CREATE TABLE Msa (object INTEGER PRIMARY KEY, length INTEGER NOT NULL, "
44                      "alphabet TEXT NOT NULL, numOfRows INTEGER NOT NULL, "
45                      "FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE)",
46                      db,
47                      os)
48         .execute();
49 
50     // MSA object row
51     //   msa      - msa object id
52     //   rowId    - id of the row in the msa
53     //   sequence - sequence object id
54     //   pos      - positional number of a row in the msa (initially, equals 'id', but can be changed, e.g. in GUI by moving rows)
55     //   gstart   - offset of the first element in the sequence
56     //   gend     - offset of the last element in the sequence (non-inclusive)
57     //   length   - sequence and gaps length (trailing gap are not taken into account)
58     SQLiteWriteQuery("CREATE TABLE MsaRow (msa INTEGER NOT NULL, rowId INTEGER NOT NULL, sequence INTEGER NOT NULL,"
59                      " pos INTEGER NOT NULL, gstart INTEGER NOT NULL, gend INTEGER NOT NULL, length INTEGER NOT NULL,"
60                      " PRIMARY KEY(msa, rowId),"
61                      " FOREIGN KEY(msa) REFERENCES Msa(object) ON DELETE CASCADE, "
62                      " FOREIGN KEY(sequence) REFERENCES Sequence(object) ON DELETE CASCADE)",
63                      db,
64                      os)
65         .execute();
66     SQLiteWriteQuery("CREATE INDEX MsaRow_msa_rowId ON MsaRow(msa, rowId)", db, os).execute();
67     SQLiteWriteQuery("CREATE INDEX MsaRow_length ON MsaRow(length)", db, os).execute();
68     SQLiteWriteQuery("CREATE INDEX MsaRow_sequence ON MsaRow(sequence)", db, os).execute();
69 
70     // Gap info for a MSA row:
71     //   msa       - msa object id
72     //   rowId     - id of the row in the msa
73     //   gapStart  - start of the gap, the coordinate is relative to the gstart coordinate of the row
74     //   gapEnd    - end of the gap, the coordinate is relative to the gstart coordinate of the row (non-inclusive)
75     // Note! there is invariant: gend - gstart (of the row) == gapEnd - gapStart
76     SQLiteWriteQuery("CREATE TABLE MsaRowGap (msa INTEGER NOT NULL, rowId INTEGER NOT NULL, "
77                      "gapStart INTEGER NOT NULL, gapEnd INTEGER NOT NULL, "
78                      "FOREIGN KEY(msa, rowId) REFERENCES MsaRow(msa, rowId) ON DELETE CASCADE)",
79                      db,
80                      os)
81         .execute();
82     SQLiteWriteQuery("CREATE INDEX MsaRowGap_msa_rowId ON MsaRowGap(msa, rowId)", db, os).execute();
83 }
84 
createMsaObject(const QString & folder,const QString & name,const U2AlphabetId & alphabet,U2OpStatus & os)85 U2DataId SQLiteMsaDbi::createMsaObject(const QString &folder, const QString &name, const U2AlphabetId &alphabet, U2OpStatus &os) {
86     return createMsaObject(folder, name, alphabet, 0, os);
87 }
88 
createMsaObject(const QString & folder,const QString & name,const U2AlphabetId & alphabet,int length,U2OpStatus & os)89 U2DataId SQLiteMsaDbi::createMsaObject(const QString &folder, const QString &name, const U2AlphabetId &alphabet, int length, U2OpStatus &os) {
90     SQLiteTransaction t(db, os);
91     U2Msa msa;
92     msa.visualName = name;
93     msa.alphabet = alphabet;
94     msa.length = length;
95 
96     // Create the object
97     dbi->getSQLiteObjectDbi()->createObject(msa, folder, U2DbiObjectRank_TopLevel, os);
98     CHECK_OP(os, U2DataId());
99 
100     // Create a record in the Msa table
101     SQLiteWriteQuery q("INSERT INTO Msa(object, length, alphabet, numOfRows) VALUES(?1, ?2, ?3, ?4)", db, os);
102     CHECK_OP(os, U2DataId());
103 
104     q.bindDataId(1, msa.id);
105     q.bindInt64(2, msa.length);
106     q.bindString(3, msa.alphabet.id);
107     q.bindInt64(4, 0);  // no rows
108     q.insert();
109 
110     return msa.id;
111 }
112 
updateMsaName(const U2DataId & msaId,const QString & name,U2OpStatus & os)113 void SQLiteMsaDbi::updateMsaName(const U2DataId &msaId, const QString &name, U2OpStatus &os) {
114     SQLiteTransaction t(db, os);
115     U2Object msaObj;
116     dbi->getSQLiteObjectDbi()->getObject(msaObj, msaId, os);
117     CHECK_OP(os, );
118 
119     SQLiteObjectDbiUtils::renameObject(dbi, msaObj, name, os);
120 }
121 
updateMsaAlphabet(const U2DataId & msaId,const U2AlphabetId & alphabet,U2OpStatus & os)122 void SQLiteMsaDbi::updateMsaAlphabet(const U2DataId &msaId, const U2AlphabetId &alphabet, U2OpStatus &os) {
123     SQLiteTransaction t(db, os);
124     SQLiteModificationAction updateAction(dbi, msaId);
125     U2TrackModType trackMod = updateAction.prepare(os);
126     CHECK_OP(os, );
127 
128     // Get modDetails, if required
129     QByteArray modDetails;
130     if (TrackOnUpdate == trackMod) {
131         U2Msa msaObj = getMsaObject(msaId, os);
132         CHECK_OP(os, );
133         modDetails = U2DbiPackUtils::packAlphabetDetails(msaObj.alphabet, alphabet);
134     }
135 
136     // Update the alphabet
137     SQLiteWriteQuery q("UPDATE Msa SET alphabet = ?1 WHERE object = ?2", db, os);
138     CHECK_OP(os, );
139 
140     q.bindString(1, alphabet.id);
141     q.bindDataId(2, msaId);
142     q.update(1);
143 
144     // Increment version; track the modification, if required
145     updateAction.addModification(msaId, U2ModType::msaUpdatedAlphabet, modDetails, os);
146     SAFE_POINT_OP(os, );
147 
148     updateAction.complete(os);
149     SAFE_POINT_OP(os, );
150 }
151 
createMsaRow(const U2DataId & msaId,qint64 posInMsa,U2MsaRow & msaRow,U2OpStatus & os)152 void SQLiteMsaDbi::createMsaRow(const U2DataId &msaId, qint64 posInMsa, U2MsaRow &msaRow, U2OpStatus &os) {
153     assert(posInMsa >= 0);
154 
155     // Calculate the row length
156     qint64 rowLength = calculateRowLength(msaRow.gend - msaRow.gstart, msaRow.gaps);
157 
158     // Insert the data
159     SQLiteWriteQuery q("INSERT INTO MsaRow(msa, rowId, sequence, pos, gstart, gend, length)"
160                        " VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)",
161                        db,
162                        os);
163     CHECK_OP(os, );
164 
165     q.bindDataId(1, msaId);
166     q.bindInt64(2, msaRow.rowId);
167     q.bindDataId(3, msaRow.sequenceId);
168     q.bindInt64(4, posInMsa);
169     q.bindInt64(5, msaRow.gstart);
170     q.bindInt64(6, msaRow.gend);
171     q.bindInt64(7, rowLength);
172     q.insert();
173 }
174 
createMsaRowGap(const U2DataId & msaId,qint64 msaRowId,const U2MsaGap & msaGap,U2OpStatus & os)175 void SQLiteMsaDbi::createMsaRowGap(const U2DataId &msaId, qint64 msaRowId, const U2MsaGap &msaGap, U2OpStatus &os) {
176     SQLiteTransaction t(db, os);
177 
178     static const QString queryString("INSERT INTO MsaRowGap(msa, rowId, gapStart, gapEnd) VALUES(?1, ?2, ?3, ?4)");
179     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
180     CHECK_OP(os, );
181 
182     q->bindDataId(1, msaId);
183     q->bindInt64(2, msaRowId);
184     q->bindInt64(3, msaGap.offset);
185     q->bindInt64(4, msaGap.offset + msaGap.gap);
186     q->insert();
187 }
188 
addMsaRowAndGaps(const U2DataId & msaId,qint64 posInMsa,U2MsaRow & row,U2OpStatus & os)189 void SQLiteMsaDbi::addMsaRowAndGaps(const U2DataId &msaId, qint64 posInMsa, U2MsaRow &row, U2OpStatus &os) {
190     createMsaRow(msaId, posInMsa, row, os);
191     CHECK_OP(os, );
192 
193     foreach (const U2MsaGap &gap, row.gaps) {
194         createMsaRowGap(msaId, row.rowId, gap, os);
195         CHECK_OP(os, );
196     }
197 
198     dbi->getSQLiteObjectDbi()->setParent(msaId, row.sequenceId, os);
199 }
200 
addRow(const U2DataId & msaId,int insertRowIndex,U2MsaRow & row,U2OpStatus & os)201 void SQLiteMsaDbi::addRow(const U2DataId &msaId, int insertRowIndex, U2MsaRow &row, U2OpStatus &os) {
202     SQLiteModificationAction updateAction(dbi, msaId);
203     U2TrackModType trackMod = updateAction.prepare(os);
204     CHECK_OP(os, );
205 
206     row.rowId = getMaximumRowId(msaId, os) + 1;
207     CHECK_OP(os, );
208 
209     // TODO: allow core method to change insertRowIndex value to store the correct value on tracking
210     addRowCore(msaId, insertRowIndex, row, os);
211     CHECK_OP(os, );
212 
213     QByteArray modDetails;
214     if (trackMod == TrackOnUpdate) {
215         modDetails = U2DbiPackUtils::packRow(insertRowIndex, row);
216     }
217     if (row.length > getMsaLength(msaId, os)) {
218         updateMsaLength(updateAction, msaId, row.length, os);
219     }
220 
221     // Update track mod type for child sequence object
222     if (trackMod == TrackOnUpdate) {
223         dbi->getObjectDbi()->setTrackModType(row.sequenceId, TrackOnUpdate, os);
224         CHECK_OP(os, );
225     }
226 
227     // Increment version; track the modification, if required
228     updateAction.addModification(msaId, U2ModType::msaAddedRow, modDetails, os);
229     SAFE_POINT_OP(os, );
230 
231     updateAction.complete(os);
232     SAFE_POINT_OP(os, );
233 }
234 
addRows(const U2DataId & msaId,QList<U2MsaRow> & rows,int insertRowIndex,U2OpStatus & os)235 void SQLiteMsaDbi::addRows(const U2DataId &msaId, QList<U2MsaRow> &rows, int insertRowIndex, U2OpStatus &os) {
236     SQLiteTransaction t(db, os);
237 
238     SQLiteModificationAction updateAction(dbi, msaId);
239     U2TrackModType trackMod = updateAction.prepare(os);
240     CHECK_OP(os, );
241 
242     // Add the rows
243     qint64 numOfRows = getNumOfRows(msaId, os);
244     CHECK_OP(os, );
245 
246     qint64 insertStartIndex = insertRowIndex < 0 || insertRowIndex >= numOfRows ? numOfRows : insertRowIndex;
247     QList<int> insertRowIndexes;
248     for (int i = 0; i < rows.count(); i++) {
249         insertRowIndexes << i + insertStartIndex;
250     }
251 
252     qint64 maxRowId = getMaximumRowId(msaId, os);
253     for (int i = 0; i < rows.count(); ++i) {
254         rows[i].rowId = maxRowId + i + 1;
255     }
256 
257     QByteArray modDetails;
258     if (trackMod == TrackOnUpdate) {
259         modDetails = U2DbiPackUtils::packRows(insertRowIndexes, rows);
260     }
261 
262     addRowsCore(msaId, insertRowIndexes, rows, os);
263     CHECK_OP(os, );
264 
265     // Update msa length
266     qint64 maxLength = 0;
267     foreach (const U2MsaRow &row, rows) {
268         maxLength = qMax(maxLength, row.length);
269     }
270     qint64 currentMsaLength = getMsaLength(msaId, os);
271     if (maxLength > currentMsaLength) {
272         updateMsaLength(updateAction, msaId, maxLength, os);
273         CHECK_OP(os, );
274     }
275 
276     // Update track mod type for child sequence object
277     if (trackMod == TrackOnUpdate) {
278         foreach (const U2MsaRow &row, rows) {
279             dbi->getObjectDbi()->setTrackModType(row.sequenceId, TrackOnUpdate, os);
280             CHECK_OP(os, );
281         }
282     }
283 
284     // Increment version; track the modification, if required
285     updateAction.addModification(msaId, U2ModType::msaAddedRows, modDetails, os);
286     SAFE_POINT_OP(os, );
287 
288     updateAction.complete(os);
289     SAFE_POINT_OP(os, );
290 }
291 
updateRowName(const U2DataId & msaId,qint64 rowId,const QString & newName,U2OpStatus & os)292 void SQLiteMsaDbi::updateRowName(const U2DataId &msaId, qint64 rowId, const QString &newName, U2OpStatus &os) {
293     SQLiteTransaction t(db, os);
294 
295     SQLiteModificationAction updateAction(dbi, msaId);
296     updateAction.prepare(os);
297     SAFE_POINT_OP(os, );
298 
299     U2DataId sequenceId = getSequenceIdByRowId(msaId, rowId, os);
300     SAFE_POINT_OP(os, );
301 
302     U2Sequence seqObject = dbi->getSequenceDbi()->getSequenceObject(sequenceId, os);
303     SAFE_POINT_OP(os, );
304 
305     SQLiteObjectDbiUtils::renameObject(updateAction, dbi, seqObject, newName, os);
306     SAFE_POINT_OP(os, );
307 
308     updateAction.complete(os);
309     SAFE_POINT_OP(os, );
310 }
311 
updateRowContent(const U2DataId & msaId,qint64 rowId,const QByteArray & seqBytes,const QList<U2MsaGap> & gaps,U2OpStatus & os)312 void SQLiteMsaDbi::updateRowContent(const U2DataId &msaId, qint64 rowId, const QByteArray &seqBytes, const QList<U2MsaGap> &gaps, U2OpStatus &os) {
313     SQLiteTransaction t(db, os);
314 
315     SQLiteModificationAction updateAction(dbi, msaId);
316     U2TrackModType trackMod = updateAction.prepare(os);
317     SAFE_POINT_OP(os, );
318     Q_UNUSED(trackMod);
319 
320     // Get the row object
321     U2MsaRow row = getRow(msaId, rowId, os);
322     SAFE_POINT_OP(os, );
323 
324     // Update the sequence data
325     QVariantMap hints;
326     dbi->getSQLiteSequenceDbi()->updateSequenceData(updateAction,
327                                                     row.sequenceId,
328                                                     U2_REGION_MAX,
329                                                     seqBytes,
330                                                     hints,
331                                                     os);
332     SAFE_POINT_OP(os, );
333 
334     // Update the row object
335     U2MsaRow newRow(row);
336     qint64 seqLength = seqBytes.length();
337     newRow.gstart = 0;
338     newRow.gend = seqLength;
339     newRow.length = calculateRowLength(seqLength, gaps);
340     updateRowInfo(updateAction, msaId, newRow, os);
341     SAFE_POINT_OP(os, );
342 
343     // Update the gap model
344     // WARNING: this update must go after the row info update to recalculate the msa length properly
345     updateGapModel(updateAction, msaId, rowId, gaps, os);
346     SAFE_POINT_OP(os, );
347 
348     // Save tracks, if required; increment versions
349     updateAction.complete(os);
350     SAFE_POINT_OP(os, );
351 }
352 
updateRowInfo(SQLiteModificationAction & updateAction,const U2DataId & msaId,const U2MsaRow & row,U2OpStatus & os)353 void SQLiteMsaDbi::updateRowInfo(SQLiteModificationAction &updateAction, const U2DataId &msaId, const U2MsaRow &row, U2OpStatus &os) {
354     QByteArray modDetails;
355     if (TrackOnUpdate == updateAction.getTrackModType()) {
356         U2MsaRow oldRow = getRow(msaId, row.rowId, os);
357         SAFE_POINT_OP(os, );
358 
359         modDetails = U2DbiPackUtils::packRowInfoDetails(oldRow, row);
360     }
361 
362     updateRowInfoCore(msaId, row, os);
363     SAFE_POINT_OP(os, );
364 
365     // Track the modification, if required; add the object to the list (versions of the objects will be incremented on the updateAction completion)
366     updateAction.addModification(msaId, U2ModType::msaUpdatedRowInfo, modDetails, os);
367     SAFE_POINT_OP(os, );
368 }
369 
getOrderedRowIds(const U2DataId & msaId,U2OpStatus & os)370 QList<qint64> SQLiteMsaDbi::getOrderedRowIds(const U2DataId &msaId, U2OpStatus &os) {
371     QList<qint64> res;
372     SQLiteReadQuery q("SELECT rowId FROM MsaRow WHERE msa = ?1 ORDER BY pos", db, os);
373     q.bindDataId(1, msaId);
374     qint64 rowId;
375     while (q.step()) {
376         rowId = q.getInt64(0);
377         res.append(rowId);
378     }
379     return res;
380 }
381 
getMsaAlphabet(const U2DataId & msaId,U2OpStatus & os)382 U2AlphabetId SQLiteMsaDbi::getMsaAlphabet(const U2DataId &msaId, U2OpStatus &os) {
383     QString alphabetName;
384     SQLiteReadQuery q("SELECT alphabet FROM Msa WHERE object = ?1", db, os);
385     q.bindDataId(1, msaId);
386     if (q.step()) {
387         alphabetName = q.getString(0);
388         q.ensureDone();
389     } else if (!os.hasError()) {
390         os.setError(U2DbiL10n::tr("Msa object not found"));
391     }
392 
393     return U2AlphabetId(alphabetName);
394 }
395 
setNewRowsOrder(const U2DataId & msaId,const QList<qint64> & rowIds,U2OpStatus & os)396 void SQLiteMsaDbi::setNewRowsOrder(const U2DataId &msaId, const QList<qint64> &rowIds, U2OpStatus &os) {
397     // Init track info
398     SQLiteTransaction t(db, os);
399     SQLiteModificationAction updateAction(dbi, msaId);
400     U2TrackModType trackMod = updateAction.prepare(os);
401     CHECK_OP(os, );
402 
403     QByteArray modDetails;
404     if (trackMod == TrackOnUpdate) {
405         QList<qint64> oldOrder = getOrderedRowIds(msaId, os);
406         CHECK_OP(os, );
407         modDetails = U2DbiPackUtils::packRowOrderDetails(oldOrder, rowIds);
408     }
409 
410     // Check that row IDs number is correct (if required, can be later removed for efficiency)
411     qint64 numOfRows = getNumOfRows(msaId, os);
412     CHECK_OP(os, );
413     SAFE_POINT(numOfRows == rowIds.count(), "Incorrect number of row IDs!", );
414 
415     // Set the new order
416     setNewRowsOrderCore(msaId, rowIds, os);
417     CHECK_OP(os, );
418 
419     // Increment version; track the modification, if required
420     updateAction.addModification(msaId, U2ModType::msaSetNewRowsOrder, modDetails, os);
421     SAFE_POINT_OP(os, );
422 
423     updateAction.complete(os);
424     SAFE_POINT_OP(os, );
425 }
426 
removeRecordFromMsaRow(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)427 void SQLiteMsaDbi::removeRecordFromMsaRow(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
428     SQLiteTransaction t(db, os);
429     static const QString queryString("DELETE FROM MsaRow WHERE msa = ?1 AND rowId = ?2");
430     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
431     CHECK_OP(os, );
432 
433     q->bindDataId(1, msaId);
434     q->bindInt64(2, rowId);
435     q->update(1);
436 }
437 
removeRecordsFromMsaRowGap(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)438 void SQLiteMsaDbi::removeRecordsFromMsaRowGap(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
439     SQLiteTransaction t(db, os);
440     static const QString queryString("DELETE FROM MsaRowGap WHERE msa = ?1 AND rowId = ?2");
441     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
442     CHECK_OP(os, );
443 
444     q->bindDataId(1, msaId);
445     q->bindInt64(2, rowId);
446     q->update();
447 }
448 
removeRow(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)449 void SQLiteMsaDbi::removeRow(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
450     SQLiteTransaction t(db, os);
451     SQLiteModificationAction updateAction(dbi, msaId);
452     U2TrackModType trackMod = updateAction.prepare(os);
453     CHECK_OP(os, );
454 
455     QByteArray modDetails;
456     if (TrackOnUpdate == trackMod) {
457         U2MsaRow removedRow = getRow(msaId, rowId, os);
458         CHECK_OP(os, );
459         qint64 posInMsa = getPosInMsa(msaId, rowId, os);
460         CHECK_OP(os, );
461         modDetails = U2DbiPackUtils::packRow(posInMsa, removedRow);
462     }
463 
464     bool removeSequence = (TrackOnUpdate != trackMod);
465     removeRowCore(msaId, rowId, removeSequence, os);
466     CHECK_OP(os, );
467 
468     // Increment version; track the modification, if required
469     updateAction.addModification(msaId, U2ModType::msaRemovedRow, modDetails, os);
470     SAFE_POINT_OP(os, );
471 
472     updateAction.complete(os);
473     SAFE_POINT_OP(os, );
474 }
475 
removeRows(const U2DataId & msaId,const QList<qint64> & rowIds,U2OpStatus & os)476 void SQLiteMsaDbi::removeRows(const U2DataId &msaId, const QList<qint64> &rowIds, U2OpStatus &os) {
477     SQLiteTransaction t(db, os);
478 
479     SQLiteModificationAction updateAction(dbi, msaId);
480     U2TrackModType trackMod = updateAction.prepare(os);
481     CHECK_OP(os, );
482 
483     QByteArray modDetails;
484     int numOfRows = getNumOfRows(msaId, os);
485     if (trackMod == TrackOnUpdate) {
486         QList<int> rowIndexes;
487         QList<U2MsaRow> rows;
488         for (qint64 rowId : qAsConst(rowIds)) {
489             rowIndexes << getPosInMsa(msaId, rowId, os);
490             CHECK_OP(os, );
491             rows << getRow(msaId, rowId, os);
492             CHECK_OP(os, );
493         }
494         modDetails = U2DbiPackUtils::packRows(rowIndexes, rows);
495     }
496 
497     bool removeSequence = trackMod != TrackOnUpdate;
498     removeRowsCore(msaId, rowIds, removeSequence, os);
499 
500     if (numOfRows == rowIds.count()) {
501         updateMsaLength(updateAction, msaId, 0, os);
502     }
503 
504     // Increment version; track the modification, if required
505     updateAction.addModification(msaId, U2ModType::msaRemovedRows, modDetails, os);
506     SAFE_POINT_OP(os, );
507 
508     updateAction.complete(os);
509     SAFE_POINT_OP(os, );
510 }
511 
removeMsaRowAndGaps(const U2DataId & msaId,qint64 rowId,bool removeSequence,U2OpStatus & os)512 void SQLiteMsaDbi::removeMsaRowAndGaps(const U2DataId &msaId, qint64 rowId, bool removeSequence, U2OpStatus &os) {
513     U2DataId sequenceId = getSequenceIdByRowId(msaId, rowId, os);
514     CHECK_OP(os, );
515 
516     removeRecordsFromMsaRowGap(msaId, rowId, os);
517     removeRecordFromMsaRow(msaId, rowId, os);
518 
519     dbi->getSQLiteObjectDbi()->removeParent(msaId, sequenceId, removeSequence, os);
520 }
521 
deleteRowsData(const U2DataId & msaId,U2OpStatus & os)522 void SQLiteMsaDbi::deleteRowsData(const U2DataId &msaId, U2OpStatus &os) {
523     static const QString deleteObjStr = "DELETE FROM Object WHERE id IN (SELECT sequence FROM MsaRow WHERE msa = ?1)";
524     SQLiteWriteQuery deleteObjQeury(deleteObjStr, db, os);
525     deleteObjQeury.bindDataId(1, msaId);
526     deleteObjQeury.execute();
527 }
528 
getMsaObject(const U2DataId & msaId,U2OpStatus & os)529 U2Msa SQLiteMsaDbi::getMsaObject(const U2DataId &msaId, U2OpStatus &os) {
530     U2Msa res;
531     dbi->getSQLiteObjectDbi()->getObject(res, msaId, os);
532 
533     SAFE_POINT_OP(os, res);
534 
535     SQLiteReadQuery q("SELECT length, alphabet FROM Msa WHERE object = ?1", db, os);
536     q.bindDataId(1, msaId);
537     if (q.step()) {
538         res.length = q.getInt64(0);
539         res.alphabet = q.getString(1);
540         q.ensureDone();
541     } else if (!os.hasError()) {
542         os.setError(U2DbiL10n::tr("Msa object not found!"));
543     }
544     return res;
545 }
546 
getNumOfRows(const U2DataId & msaId,U2OpStatus & os)547 int SQLiteMsaDbi::getNumOfRows(const U2DataId &msaId, U2OpStatus &os) {
548     int res = 0;
549     SQLiteReadQuery q("SELECT numOfRows FROM Msa WHERE object = ?1", db, os);
550     CHECK_OP(os, res);
551 
552     q.bindDataId(1, msaId);
553     if (q.step()) {
554         res = q.getInt32(0);
555         q.ensureDone();
556     } else if (!os.hasError()) {
557         os.setError(U2DbiL10n::tr("Msa object not found!"));
558     }
559     return res;
560 }
561 
recalculateRowsPositions(const U2DataId & msaId,U2OpStatus & os)562 void SQLiteMsaDbi::recalculateRowsPositions(const U2DataId &msaId, U2OpStatus &os) {
563     QList<U2MsaRow> rows = getRows(msaId, os);
564     CHECK_OP(os, );
565 
566     SQLiteTransaction t(db, os);
567     SQLiteWriteQuery q("UPDATE MsaRow SET pos = ?1 WHERE msa = ?2 AND rowId = ?3", db, os);
568     CHECK_OP(os, );
569 
570     for (int i = 0, n = rows.count(); i < n; ++i) {
571         qint64 rowId = rows[i].rowId;
572         q.reset();
573         q.bindInt64(1, i);
574         q.bindDataId(2, msaId);
575         q.bindInt64(3, rowId);
576         q.execute();
577     }
578 }
579 
getMaximumRowId(const U2DataId & msaId,U2OpStatus & os)580 qint64 SQLiteMsaDbi::getMaximumRowId(const U2DataId &msaId, U2OpStatus &os) {
581     qint64 maxRowId = 0;
582     SQLiteReadQuery q("SELECT MAX(rowId) FROM MsaRow WHERE msa = ?1", db, os);
583     SAFE_POINT_OP(os, 0);
584 
585     q.bindDataId(1, msaId);
586     q.getInt64(1);
587     if (q.step()) {
588         maxRowId = q.getInt64(0);
589     }
590 
591     return maxRowId;
592 }
593 
getRows(const U2DataId & msaId,U2OpStatus & os)594 QList<U2MsaRow> SQLiteMsaDbi::getRows(const U2DataId &msaId, U2OpStatus &os) {
595     QList<U2MsaRow> res;
596     SQLiteReadQuery q("SELECT rowId, sequence, gstart, gend, length FROM MsaRow WHERE msa = ?1 ORDER BY pos", db, os);
597     q.bindDataId(1, msaId);
598 
599     SQLiteReadQuery gapQ("SELECT gapStart, gapEnd FROM MsaRowGap WHERE msa = ?1 AND rowId = ?2 ORDER BY gapStart", db, os);
600     while (q.step()) {
601         U2MsaRow row;
602         row.rowId = q.getInt64(0);
603         row.sequenceId = q.getDataId(1, U2Type::Sequence);
604         row.gstart = q.getInt64(2);
605         row.gend = q.getInt64(3);
606         row.length = q.getInt64(4);
607 
608         gapQ.reset();
609         gapQ.bindDataId(1, msaId);
610         gapQ.bindInt64(2, row.rowId);
611         while (gapQ.step()) {
612             U2MsaGap gap;
613             gap.offset = gapQ.getInt64(0);
614             gap.gap = gapQ.getInt64(1) - gap.offset;
615             SAFE_POINT_EXT(gap.isValid(), os.setError("An invalid gap is stored in the database"), res);
616             row.gaps.append(gap);
617         }
618 
619         SAFE_POINT_OP(os, res);
620         res.append(row);
621     }
622     return res;
623 }
624 
getRow(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)625 U2MsaRow SQLiteMsaDbi::getRow(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
626     U2MsaRow res;
627     SQLiteReadQuery q("SELECT sequence, gstart, gend, length FROM MsaRow WHERE msa = ?1 AND rowId = ?2", db, os);
628     SAFE_POINT_OP(os, res);
629 
630     q.bindDataId(1, msaId);
631     q.bindInt64(2, rowId);
632     if (q.step()) {
633         res.rowId = rowId;
634         res.sequenceId = q.getDataId(0, U2Type::Sequence);
635         res.gstart = q.getInt64(1);
636         res.gend = q.getInt64(2);
637         res.length = q.getInt64(3);
638         q.ensureDone();
639     } else if (!os.hasError()) {
640         os.setError(U2DbiL10n::tr("Msa row not found!"));
641         SAFE_POINT_OP(os, res);
642     }
643 
644     SQLiteReadQuery gapQ("SELECT gapStart, gapEnd FROM MsaRowGap WHERE msa = ?1 AND rowId = ?2 ORDER BY gapStart", db, os);
645     SAFE_POINT_OP(os, res);
646 
647     gapQ.bindDataId(1, msaId);
648     gapQ.bindInt64(2, rowId);
649     while (gapQ.step()) {
650         U2MsaGap gap;
651         gap.offset = gapQ.getInt64(0);
652         gap.gap = gapQ.getInt64(1) - gap.offset;
653         res.gaps.append(gap);
654     }
655 
656     return res;
657 }
658 
updateNumOfRows(const U2DataId & msaId,qint64 numOfRows,U2OpStatus & os)659 void SQLiteMsaDbi::updateNumOfRows(const U2DataId &msaId, qint64 numOfRows, U2OpStatus &os) {
660     SQLiteWriteQuery q("UPDATE Msa SET numOfRows = ?1 WHERE object = ?2", db, os);
661     SAFE_POINT_OP(os, );
662 
663     q.bindInt64(1, numOfRows);
664     q.bindDataId(2, msaId);
665     q.update(1);
666 }
667 
updateGapModel(const U2DataId & msaId,qint64 msaRowId,const QList<U2MsaGap> & gapModel,U2OpStatus & os)668 void SQLiteMsaDbi::updateGapModel(const U2DataId &msaId, qint64 msaRowId, const QList<U2MsaGap> &gapModel, U2OpStatus &os) {
669     SQLiteTransaction t(db, os);
670 
671     SQLiteModificationAction updateAction(dbi, msaId);
672     updateAction.prepare(os);
673     SAFE_POINT_OP(os, );
674 
675     updateGapModel(updateAction, msaId, msaRowId, gapModel, os);
676     SAFE_POINT_OP(os, );
677 
678     updateAction.complete(os);
679     SAFE_POINT_OP(os, );
680 }
681 
updateGapModel(SQLiteModificationAction & updateAction,const U2DataId & msaId,qint64 msaRowId,const QList<U2MsaGap> & gapModel,U2OpStatus & os)682 void SQLiteMsaDbi::updateGapModel(SQLiteModificationAction &updateAction, const U2DataId &msaId, qint64 msaRowId, const QList<U2MsaGap> &gapModel, U2OpStatus &os) {
683     QByteArray gapsDetails;
684     if (TrackOnUpdate == updateAction.getTrackModType()) {
685         U2MsaRow row = getRow(msaId, msaRowId, os);
686         SAFE_POINT_OP(os, );
687         gapsDetails = U2DbiPackUtils::packGapDetails(msaRowId, row.gaps, gapModel);
688     }
689 
690     updateGapModelCore(msaId, msaRowId, gapModel, os);
691     SAFE_POINT_OP(os, );
692 
693     qint64 len = 0;
694     foreach (const U2MsaGap &gap, gapModel) {
695         len += gap.gap;
696     }
697     len += getRowSequenceLength(msaId, msaRowId, os);
698     SAFE_POINT_OP(os, );
699     if (len > getMsaLength(msaId, os)) {
700         updateMsaLength(updateAction, msaId, len, os);
701     }
702     SAFE_POINT_OP(os, );
703 
704     // Track the modification, if required; add the object to the list (versions of the objects will be incremented on the updateAction completion)
705     updateAction.addModification(msaId, U2ModType::msaUpdatedGapModel, gapsDetails, os);
706     SAFE_POINT_OP(os, );
707 }
708 
updateMsaLength(const U2DataId & msaId,qint64 length,U2OpStatus & os)709 void SQLiteMsaDbi::updateMsaLength(const U2DataId &msaId, qint64 length, U2OpStatus &os) {
710     SQLiteTransaction t(db, os);
711 
712     SQLiteModificationAction updateAction(dbi, msaId);
713     updateAction.prepare(os);
714     SAFE_POINT_OP(os, );
715 
716     updateMsaLength(updateAction, msaId, length, os);
717 
718     updateAction.complete(os);
719     SAFE_POINT_OP(os, );
720 }
721 
updateMsaLength(SQLiteModificationAction & updateAction,const U2DataId & msaId,qint64 length,U2OpStatus & os)722 void SQLiteMsaDbi::updateMsaLength(SQLiteModificationAction &updateAction, const U2DataId &msaId, qint64 length, U2OpStatus &os) {
723     QByteArray modDetails;
724     if (TrackOnUpdate == updateAction.getTrackModType()) {
725         qint64 oldMsaLen = getMsaLength(msaId, os);
726         CHECK_OP(os, );
727         modDetails = U2DbiPackUtils::packAlignmentLength(oldMsaLen, length);
728     }
729 
730     updateMsaLengthCore(msaId, length, os);
731     SAFE_POINT_OP(os, )
732 
733     updateAction.addModification(msaId, U2ModType::msaLengthChanged, modDetails, os);
734     SAFE_POINT_OP(os, );
735 }
736 
getMsaLength(const U2DataId & msaId,U2OpStatus & os)737 qint64 SQLiteMsaDbi::getMsaLength(const U2DataId &msaId, U2OpStatus &os) {
738     qint64 res = 0;
739     SQLiteReadQuery q("SELECT length FROM Msa WHERE object = ?1", db, os);
740     CHECK_OP(os, res);
741 
742     q.bindDataId(1, msaId);
743     if (q.step()) {
744         res = q.getInt64(0);
745         q.ensureDone();
746     } else if (!os.hasError()) {
747         os.setError(U2DbiL10n::tr("Msa object not found!"));
748     }
749 
750     return res;
751 }
752 
createMcaObject(const QString & folder,const QString & name,const U2AlphabetId & alphabet,U2OpStatus & os)753 U2DataId SQLiteMsaDbi::createMcaObject(const QString &folder, const QString &name, const U2AlphabetId &alphabet, U2OpStatus &os) {
754     return createMcaObject(folder, name, alphabet, 0, os);
755 }
756 
createMcaObject(const QString & folder,const QString & name,const U2AlphabetId & alphabet,int length,U2OpStatus & os)757 U2DataId SQLiteMsaDbi::createMcaObject(const QString &folder, const QString &name, const U2AlphabetId &alphabet, int length, U2OpStatus &os) {
758     SQLiteTransaction t(db, os);
759 
760     U2Mca mca;
761     mca.visualName = name;
762     mca.alphabet = alphabet;
763     mca.length = length;
764 
765     // Create the object
766     dbi->getSQLiteObjectDbi()->createObject(mca, folder, U2DbiObjectRank_TopLevel, os);
767     CHECK_OP(os, U2DataId());
768 
769     // Create a record in the Msa table
770     SQLiteWriteQuery q("INSERT INTO Msa(object, length, alphabet, numOfRows) VALUES(?1, ?2, ?3, ?4)", db, os);
771     CHECK_OP(os, U2DataId());
772 
773     q.bindDataId(1, mca.id);
774     q.bindInt64(2, mca.length);
775     q.bindString(3, mca.alphabet.id);
776     q.bindInt64(4, 0);  // no rows
777     q.insert();
778 
779     return mca.id;
780 }
781 
calculateRowLength(qint64 seqLength,const QList<U2MsaGap> & gaps)782 qint64 SQLiteMsaDbi::calculateRowLength(qint64 seqLength, const QList<U2MsaGap> &gaps) {
783     qint64 res = seqLength;
784     foreach (const U2MsaGap &gap, gaps) {
785         if (gap.offset < res) {  // ignore trailing gaps
786             res += gap.gap;
787         }
788     }
789     return res;
790 }
791 
getRowSequenceLength(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)792 qint64 SQLiteMsaDbi::getRowSequenceLength(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
793     qint64 res = 0;
794     SQLiteReadQuery q("SELECT gstart, gend FROM MsaRow WHERE msa = ?1 AND rowId = ?2", db, os);
795     CHECK_OP(os, res);
796 
797     q.bindDataId(1, msaId);
798     q.bindInt64(2, rowId);
799     if (q.step()) {
800         qint64 startInSeq = q.getInt64(0);
801         qint64 endInSeq = q.getInt64(1);
802         res = endInSeq - startInSeq;
803         q.ensureDone();
804     } else if (!os.hasError()) {
805         os.setError(U2DbiL10n::tr("Msa row not found!"));
806     }
807 
808     return res;
809 }
810 
updateRowLength(const U2DataId & msaId,qint64 rowId,qint64 newLength,U2OpStatus & os)811 void SQLiteMsaDbi::updateRowLength(const U2DataId &msaId, qint64 rowId, qint64 newLength, U2OpStatus &os) {
812     SQLiteWriteQuery q("UPDATE MsaRow SET length = ?1 WHERE msa = ?2 AND rowId = ?3", db, os);
813     CHECK_OP(os, );
814 
815     q.bindInt64(1, newLength);
816     q.bindDataId(2, msaId);
817     q.bindInt64(3, rowId);
818     q.update(1);
819 }
820 
updateMsaLengthCore(const U2DataId & msaId,qint64 length,U2OpStatus & os)821 void SQLiteMsaDbi::updateMsaLengthCore(const U2DataId &msaId, qint64 length, U2OpStatus &os) {
822     SQLiteTransaction t(db, os);
823     SQLiteWriteQuery q("UPDATE Msa SET length = ?1 WHERE object = ?2", db, os);
824     CHECK_OP(os, );
825 
826     q.bindInt64(1, length);
827     q.bindDataId(2, msaId);
828     q.execute();
829 }
830 
getSequenceIdByRowId(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)831 U2DataId SQLiteMsaDbi::getSequenceIdByRowId(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
832     U2DataId res;
833     SQLiteReadQuery q("SELECT sequence FROM MsaRow WHERE msa = ?1 AND rowId = ?2", db, os);
834     CHECK_OP(os, res);
835 
836     q.bindDataId(1, msaId);
837     q.bindInt64(2, rowId);
838     if (q.step()) {
839         res = q.getDataId(0, U2Type::Sequence);
840         q.ensureDone();
841     } else if (!os.hasError()) {
842         os.setError(U2DbiL10n::tr("Msa row not found!"));
843     }
844 
845     return res;
846 }
847 
getRemovedRowDetails(const U2MsaRow & row)848 QByteArray SQLiteMsaDbi::getRemovedRowDetails(const U2MsaRow &row) {
849     QByteArray res;
850 
851     // Info about gaps
852     QByteArray gapsInfo;
853     for (int i = 0, n = row.gaps.count(); i < n; ++i) {
854         const U2MsaGap &gap = row.gaps[i];
855         gapsInfo += "offset=";
856         gapsInfo += QByteArray::number(gap.offset);
857         gapsInfo += "&gap=";
858         gapsInfo += QByteArray::number(gap.gap);
859 
860         if (i > 0 && i < n - 1) {
861             gapsInfo += "&";
862         }
863     }
864 
865     res = QByteArray("rowId=") + QByteArray::number(row.rowId) +
866           QByteArray("&sequenceId=") + row.sequenceId.toHex() +
867           QByteArray("&gstart=") + QByteArray::number(row.gstart) +
868           QByteArray("&gend=") + QByteArray::number(row.gend) +
869           QByteArray("&gaps=\"") + gapsInfo + QByteArray("\"") +
870           QByteArray("&length=") + QByteArray::number(row.length);
871 
872     return res;
873 }
874 
getPosInMsa(const U2DataId & msaId,qint64 rowId,U2OpStatus & os)875 qint64 SQLiteMsaDbi::getPosInMsa(const U2DataId &msaId, qint64 rowId, U2OpStatus &os) {
876     SQLiteReadQuery q("SELECT pos FROM MsaRow WHERE msa = ?1 AND rowId = ?2", db, os);
877     CHECK_OP(os, -1);
878     q.bindDataId(1, msaId);
879     q.bindInt64(2, rowId);
880     if (q.step()) {
881         qint64 result = q.getInt64(0);
882         q.ensureDone();
883         return result;
884     }
885     os.setError(QString("No row with id '%1' in msa '%2'").arg(QString::number(rowId)).arg(msaId.data()));
886     return -1;
887 }
888 
undo(const U2DataId & msaId,qint64 modType,const QByteArray & modDetails,U2OpStatus & os)889 void SQLiteMsaDbi::undo(const U2DataId &msaId, qint64 modType, const QByteArray &modDetails, U2OpStatus &os) {
890     if (U2ModType::msaUpdatedAlphabet == modType) {
891         undoUpdateMsaAlphabet(msaId, modDetails, os);
892     } else if (U2ModType::msaAddedRows == modType) {
893         undoAddRows(msaId, modDetails, os);
894     } else if (U2ModType::msaAddedRow == modType) {
895         undoAddRow(msaId, modDetails, os);
896     } else if (U2ModType::msaRemovedRows == modType) {
897         undoRemoveRows(msaId, modDetails, os);
898     } else if (U2ModType::msaRemovedRow == modType) {
899         undoRemoveRow(msaId, modDetails, os);
900     } else if (U2ModType::msaUpdatedRowInfo == modType) {
901         undoUpdateRowInfo(msaId, modDetails, os);
902     } else if (U2ModType::msaUpdatedGapModel == modType) {
903         undoUpdateGapModel(msaId, modDetails, os);
904     } else if (U2ModType::msaSetNewRowsOrder == modType) {
905         undoSetNewRowsOrder(msaId, modDetails, os);
906     } else if (U2ModType::msaLengthChanged == modType) {
907         undoMsaLengthChange(msaId, modDetails, os);
908     } else {
909         os.setError(QString("Unexpected modification type '%1'!").arg(QString::number(modType)));
910         return;
911     }
912 }
913 
redo(const U2DataId & msaId,qint64 modType,const QByteArray & modDetails,U2OpStatus & os)914 void SQLiteMsaDbi::redo(const U2DataId &msaId, qint64 modType, const QByteArray &modDetails, U2OpStatus &os) {
915     if (U2ModType::msaUpdatedAlphabet == modType) {
916         redoUpdateMsaAlphabet(msaId, modDetails, os);
917     } else if (U2ModType::msaAddedRows == modType) {
918         redoAddRows(msaId, modDetails, os);
919     } else if (U2ModType::msaAddedRow == modType) {
920         redoAddRow(msaId, modDetails, os);
921     } else if (U2ModType::msaRemovedRows == modType) {
922         redoRemoveRows(msaId, modDetails, os);
923     } else if (U2ModType::msaRemovedRow == modType) {
924         redoRemoveRow(msaId, modDetails, os);
925     } else if (U2ModType::msaUpdatedRowInfo == modType) {
926         redoUpdateRowInfo(msaId, modDetails, os);
927     } else if (U2ModType::msaUpdatedGapModel == modType) {
928         redoUpdateGapModel(msaId, modDetails, os);
929     } else if (U2ModType::msaSetNewRowsOrder == modType) {
930         redoSetNewRowsOrder(msaId, modDetails, os);
931     } else if (U2ModType::msaLengthChanged == modType) {
932         redoMsaLengthChange(msaId, modDetails, os);
933     } else {
934         os.setError(QString("Unexpected modification type '%1'!").arg(QString::number(modType)));
935         return;
936     }
937 }
938 
939 /************************************************************************/
940 /* Core methods                                                         */
941 /************************************************************************/
updateGapModelCore(const U2DataId & msaId,qint64 msaRowId,const QList<U2MsaGap> & gapModel,U2OpStatus & os)942 void SQLiteMsaDbi::updateGapModelCore(const U2DataId &msaId, qint64 msaRowId, const QList<U2MsaGap> &gapModel, U2OpStatus &os) {
943     SQLiteTransaction t(db, os);
944     // Remove obsolete gaps of the row
945     removeRecordsFromMsaRowGap(msaId, msaRowId, os);
946     CHECK_OP(os, );
947 
948     // Store the new gap model
949     foreach (const U2MsaGap &gap, gapModel) {
950         createMsaRowGap(msaId, msaRowId, gap, os);
951         CHECK_OP(os, );
952     }
953 
954     // Update the row length (without trailing gaps)
955     qint64 rowSequenceLength = getRowSequenceLength(msaId, msaRowId, os);
956     CHECK_OP(os, );
957 
958     qint64 newRowLength = calculateRowLength(rowSequenceLength, gapModel);
959     updateRowLength(msaId, msaRowId, newRowLength, os);
960     CHECK_OP(os, );
961 }
962 
addRowSubcore(const U2DataId & msaId,qint64 numOfRows,const QList<qint64> & rowsOrder,U2OpStatus & os)963 void SQLiteMsaDbi::addRowSubcore(const U2DataId &msaId, qint64 numOfRows, const QList<qint64> &rowsOrder, U2OpStatus &os) {
964     // Re-calculate position, if needed
965     setNewRowsOrderCore(msaId, rowsOrder, os);
966     CHECK_OP(os, );
967 
968     // Update the number of rows of the MSA
969     updateNumOfRows(msaId, numOfRows, os);
970 }
971 
addRowCore(const U2DataId & msaId,int insertRowIndex,U2MsaRow & row,U2OpStatus & os)972 void SQLiteMsaDbi::addRowCore(const U2DataId &msaId, int insertRowIndex, U2MsaRow &row, U2OpStatus &os) {
973     SQLiteTransaction t(db, os);
974 
975     // Append the row to the end, if "-1"
976     qint64 numOfRows = getNumOfRows(msaId, os);
977     CHECK_OP(os, );
978 
979     QList<qint64> orderedRowIds;
980     if (insertRowIndex == -1) {
981         insertRowIndex = numOfRows;
982     } else {
983         orderedRowIds = getOrderedRowIds(msaId, os);
984         CHECK_OP(os, );
985         SAFE_POINT(orderedRowIds.count() == numOfRows, "Incorrect number of rows!", );
986     }
987     SAFE_POINT(insertRowIndex >= 0 && insertRowIndex <= numOfRows, "Incorrect input position!", );
988 
989     // Create the row
990     addMsaRowAndGaps(msaId, insertRowIndex, row, os);
991     CHECK_OP(os, );
992 
993     // Update the alignment length
994     row.length = calculateRowLength(row.gend - row.gstart, row.gaps);
995     if (insertRowIndex != numOfRows) {
996         orderedRowIds.insert(insertRowIndex, row.rowId);
997     }
998     addRowSubcore(msaId, numOfRows + 1, orderedRowIds, os);
999 }
1000 
addRowsCore(const U2DataId & msaId,const QList<int> & insertRowIndexes,QList<U2MsaRow> & rows,U2OpStatus & os)1001 void SQLiteMsaDbi::addRowsCore(const U2DataId &msaId, const QList<int> &insertRowIndexes, QList<U2MsaRow> &rows, U2OpStatus &os) {
1002     SQLiteTransaction t(db, os);
1003 
1004     int numOfRows = getNumOfRows(msaId, os);
1005     CHECK_OP(os, );
1006 
1007     QList<qint64> orderedRowIds = getOrderedRowIds(msaId, os);
1008     CHECK_OP(os, );
1009     SAFE_POINT(numOfRows == orderedRowIds.count(), "Incorrect number of rows!", );
1010 
1011     // Add new rows
1012     QList<int>::ConstIterator insertIndexIt = insertRowIndexes.begin();
1013     QList<U2MsaRow>::Iterator rowIt = rows.begin();
1014     for (; rowIt != rows.end(); rowIt++, insertIndexIt++) {
1015         int insertRowIndex = *insertIndexIt;
1016         if (insertRowIndex < 0 || insertRowIndex > numOfRows) {
1017             insertRowIndex = numOfRows;
1018         }
1019         addMsaRowAndGaps(msaId, insertRowIndex, *rowIt, os);
1020         CHECK_OP(os, );
1021 
1022         rowIt->length = calculateRowLength(rowIt->gend - rowIt->gstart, rowIt->gaps);
1023         numOfRows++;
1024         orderedRowIds.insert(insertRowIndex, rowIt->rowId);
1025     }
1026 
1027     addRowSubcore(msaId, numOfRows, orderedRowIds, os);
1028 }
1029 
removeRowSubcore(const U2DataId & msaId,qint64 numOfRows,U2OpStatus & os)1030 void SQLiteMsaDbi::removeRowSubcore(const U2DataId &msaId, qint64 numOfRows, U2OpStatus &os) {
1031     // Update the number of rows
1032     updateNumOfRows(msaId, numOfRows, os);
1033     CHECK_OP(os, );
1034 
1035     // Re-calculate the rows positions
1036     recalculateRowsPositions(msaId, os);
1037 }
1038 
removeRowCore(const U2DataId & msaId,qint64 rowId,bool removeSequence,U2OpStatus & os)1039 void SQLiteMsaDbi::removeRowCore(const U2DataId &msaId, qint64 rowId, bool removeSequence, U2OpStatus &os) {
1040     SQLiteTransaction t(db, os);
1041     // Get and verify the number of rows
1042     qint64 numOfRows = getNumOfRows(msaId, os);
1043     CHECK_OP(os, );
1044     SAFE_POINT(numOfRows > 0, "Empty MSA!", );
1045 
1046     // Remove the row
1047     removeMsaRowAndGaps(msaId, rowId, removeSequence, os);
1048     CHECK_OP(os, );
1049 
1050     removeRowSubcore(msaId, numOfRows - 1, os);
1051 }
1052 
removeRowsCore(const U2DataId & msaId,const QList<qint64> & rowIds,bool removeSequence,U2OpStatus & os)1053 void SQLiteMsaDbi::removeRowsCore(const U2DataId &msaId, const QList<qint64> &rowIds, bool removeSequence, U2OpStatus &os) {
1054     SQLiteTransaction t(db, os);
1055     qint64 numOfRows = getNumOfRows(msaId, os);
1056     CHECK_OP(os, );
1057     SAFE_POINT(numOfRows >= rowIds.count(), "Incorrect rows to remove!", );
1058 
1059     for (int i = 0; i < rowIds.count(); ++i) {
1060         removeMsaRowAndGaps(msaId, rowIds[i], removeSequence, os);
1061         CHECK_OP(os, );
1062     }
1063 
1064     removeRowSubcore(msaId, numOfRows - rowIds.size(), os);
1065 }
1066 
setNewRowsOrderCore(const U2DataId & msaId,const QList<qint64> rowIds,U2OpStatus & os)1067 void SQLiteMsaDbi::setNewRowsOrderCore(const U2DataId &msaId, const QList<qint64> rowIds, U2OpStatus &os) {
1068     SQLiteTransaction t(db, os);
1069     SQLiteWriteQuery q("UPDATE MsaRow SET pos = ?1 WHERE msa = ?2 AND rowId = ?3", db, os);
1070     CHECK_OP(os, );
1071 
1072     for (int i = 0, n = rowIds.count(); i < n; ++i) {
1073         qint64 rowId = rowIds[i];
1074         q.reset();
1075         q.bindInt64(1, i);
1076         q.bindDataId(2, msaId);
1077         q.bindInt64(3, rowId);
1078         q.execute();
1079     }
1080 }
1081 
updateRowInfoCore(const U2DataId & msaId,const U2MsaRow & row,U2OpStatus & os)1082 void SQLiteMsaDbi::updateRowInfoCore(const U2DataId &msaId, const U2MsaRow &row, U2OpStatus &os) {
1083     SQLiteWriteQuery q("UPDATE MsaRow SET sequence = ?1, gstart = ?2, gend = ?3 WHERE msa = ?4 AND rowId = ?5", db, os);
1084     SAFE_POINT_OP(os, );
1085 
1086     q.bindDataId(1, row.sequenceId);
1087     q.bindInt64(2, row.gstart);
1088     q.bindInt64(3, row.gend);
1089     q.bindDataId(4, msaId);
1090     q.bindInt64(5, row.rowId);
1091     q.update(1);
1092 }
1093 
1094 /************************************************************************/
1095 /* Undo/redo methods */
1096 /************************************************************************/
undoUpdateMsaAlphabet(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1097 void SQLiteMsaDbi::undoUpdateMsaAlphabet(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1098     U2AlphabetId oldAlphabet;
1099     U2AlphabetId newAlphabet;
1100     bool ok = U2DbiPackUtils::unpackAlphabetDetails(modDetails, oldAlphabet, newAlphabet);
1101     if (!ok) {
1102         os.setError("An error occurred during updating an alignment alphabet!");
1103         return;
1104     }
1105 
1106     // Update the value
1107     SQLiteWriteQuery q("UPDATE Msa SET alphabet = ?1 WHERE object = ?2", db, os);
1108     CHECK_OP(os, );
1109 
1110     q.bindString(1, oldAlphabet.id);
1111     q.bindDataId(2, msaId);
1112     q.update(1);
1113 }
1114 
redoUpdateMsaAlphabet(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1115 void SQLiteMsaDbi::redoUpdateMsaAlphabet(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1116     U2AlphabetId oldAlphabet;
1117     U2AlphabetId newAlphabet;
1118     bool ok = U2DbiPackUtils::unpackAlphabetDetails(modDetails, oldAlphabet, newAlphabet);
1119     if (!ok) {
1120         os.setError("An error occurred during updating an alignment alphabet!");
1121         return;
1122     }
1123 
1124     // Redo the updating
1125     SQLiteWriteQuery q("UPDATE Msa SET alphabet = ?1 WHERE object = ?2", db, os);
1126     CHECK_OP(os, );
1127 
1128     q.bindString(1, newAlphabet.id);
1129     q.bindDataId(2, msaId);
1130     q.update(1);
1131 }
1132 
undoAddRows(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1133 void SQLiteMsaDbi::undoAddRows(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1134     QList<int> rowIndexes;
1135     QList<U2MsaRow> rows;
1136     bool ok = U2DbiPackUtils::unpackRows(modDetails, rowIndexes, rows);
1137     if (!ok) {
1138         os.setError("An error occurred during reverting adding of rows!");
1139         return;
1140     }
1141     QList<qint64> rowIds;
1142     for (const U2MsaRow &row : qAsConst(rows)) {
1143         rowIds << row.rowId;
1144     }
1145     removeRowsCore(msaId, rowIds, false, os);
1146 }
1147 
redoAddRows(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1148 void SQLiteMsaDbi::redoAddRows(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1149     QList<int> rowIndexes;
1150     QList<U2MsaRow> rows;
1151     bool ok = U2DbiPackUtils::unpackRows(modDetails, rowIndexes, rows);
1152     if (!ok) {
1153         os.setError("An error occurred during reverting adding of rows!");
1154         return;
1155     }
1156 
1157     addRowsCore(msaId, rowIndexes, rows, os);
1158 }
1159 
undoAddRow(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1160 void SQLiteMsaDbi::undoAddRow(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1161     U2MsaRow row;
1162     int rowIndex;
1163     bool ok = U2DbiPackUtils::unpackRow(modDetails, rowIndex, row);
1164     if (!ok) {
1165         os.setError("An error occurred during reverting addition of a row!");
1166         return;
1167     }
1168 
1169     removeRowCore(msaId, row.rowId, false, os);
1170 }
1171 
redoAddRow(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1172 void SQLiteMsaDbi::redoAddRow(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1173     U2MsaRow row;
1174     int rowIndex;
1175     bool ok = U2DbiPackUtils::unpackRow(modDetails, rowIndex, row);
1176     if (!ok) {
1177         os.setError("An error occurred during addition of a row!");
1178         return;
1179     }
1180 
1181     addRowCore(msaId, rowIndex, row, os);
1182 }
1183 
undoRemoveRows(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1184 void SQLiteMsaDbi::undoRemoveRows(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1185     QList<int> rowIndexes;
1186     QList<U2MsaRow> rows;
1187     bool ok = U2DbiPackUtils::unpackRows(modDetails, rowIndexes, rows);
1188     if (!ok) {
1189         os.setError("An error occurred during reverting removing of rows!");
1190         return;
1191     }
1192 
1193     addRowsCore(msaId, rowIndexes, rows, os);
1194 }
1195 
redoRemoveRows(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1196 void SQLiteMsaDbi::redoRemoveRows(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1197     QList<int> rowIndexes;
1198     QList<U2MsaRow> rows;
1199     bool ok = U2DbiPackUtils::unpackRows(modDetails, rowIndexes, rows);
1200     if (!ok) {
1201         os.setError("An error occurred during reverting removing of rows!");
1202         return;
1203     }
1204     QList<qint64> rowIds;
1205     for (const U2MsaRow &row : qAsConst(rows)) {
1206         rowIds << row.rowId;
1207     }
1208     removeRowsCore(msaId, rowIds, false, os);
1209 }
1210 
undoRemoveRow(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1211 void SQLiteMsaDbi::undoRemoveRow(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1212     U2MsaRow row;
1213     int rowIndex;
1214     bool ok = U2DbiPackUtils::unpackRow(modDetails, rowIndex, row);
1215     if (!ok) {
1216         os.setError("An error occurred during reverting removing of a row!");
1217         return;
1218     }
1219 
1220     addRowCore(msaId, rowIndex, row, os);
1221 }
1222 
redoRemoveRow(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1223 void SQLiteMsaDbi::redoRemoveRow(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1224     U2MsaRow row;
1225     int rowIndex;
1226     bool ok = U2DbiPackUtils::unpackRow(modDetails, rowIndex, row);
1227     if (!ok) {
1228         os.setError("An error occurred during reverting removing of a row!");
1229         return;
1230     }
1231 
1232     removeRowCore(msaId, row.rowId, false, os);
1233 }
1234 
undoUpdateGapModel(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1235 void SQLiteMsaDbi::undoUpdateGapModel(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1236     qint64 rowId = 0;
1237     QList<U2MsaGap> oldGaps;
1238     QList<U2MsaGap> newGaps;
1239     bool ok = U2DbiPackUtils::unpackGapDetails(modDetails, rowId, oldGaps, newGaps);
1240     if (!ok) {
1241         os.setError("An error occurred during updating an alignment gaps!");
1242         return;
1243     }
1244 
1245     updateGapModelCore(msaId, rowId, oldGaps, os);
1246 }
1247 
redoUpdateGapModel(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1248 void SQLiteMsaDbi::redoUpdateGapModel(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1249     qint64 rowId = 0;
1250     QList<U2MsaGap> oldGaps;
1251     QList<U2MsaGap> newGaps;
1252     bool ok = U2DbiPackUtils::unpackGapDetails(modDetails, rowId, oldGaps, newGaps);
1253     if (!ok) {
1254         os.setError("An error occurred during updating an alignment gaps!");
1255         return;
1256     }
1257 
1258     updateGapModelCore(msaId, rowId, newGaps, os);
1259 }
1260 
undoSetNewRowsOrder(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1261 void SQLiteMsaDbi::undoSetNewRowsOrder(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1262     QList<qint64> oldOrder;
1263     QList<qint64> newOrder;
1264     bool ok = U2DbiPackUtils::unpackRowOrderDetails(modDetails, oldOrder, newOrder);
1265     if (!ok) {
1266         os.setError("An error occurred during updating an alignment row order!");
1267         return;
1268     }
1269 
1270     // Set the old order
1271     setNewRowsOrderCore(msaId, oldOrder, os);
1272 }
1273 
redoSetNewRowsOrder(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1274 void SQLiteMsaDbi::redoSetNewRowsOrder(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1275     QList<qint64> oldOrder;
1276     QList<qint64> newOrder;
1277     bool ok = U2DbiPackUtils::unpackRowOrderDetails(modDetails, oldOrder, newOrder);
1278     if (!ok) {
1279         os.setError("An error occurred during updating an alignment row order!");
1280         return;
1281     }
1282 
1283     setNewRowsOrderCore(msaId, newOrder, os);
1284 }
1285 
undoUpdateRowInfo(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1286 void SQLiteMsaDbi::undoUpdateRowInfo(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1287     U2MsaRow oldRow;
1288     U2MsaRow newRow;
1289     bool ok = U2DbiPackUtils::unpackRowInfoDetails(modDetails, oldRow, newRow);
1290     if (!ok) {
1291         os.setError("An error occurred during updating a row info!");
1292         return;
1293     }
1294     SAFE_POINT(oldRow.rowId == newRow.rowId, "Incorrect rowId!", );
1295     SAFE_POINT(oldRow.sequenceId == newRow.sequenceId, "Incorrect sequenceId!", );
1296 
1297     updateRowInfoCore(msaId, oldRow, os);
1298 }
1299 
redoUpdateRowInfo(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1300 void SQLiteMsaDbi::redoUpdateRowInfo(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1301     U2MsaRow oldRow;
1302     U2MsaRow newRow;
1303     bool ok = U2DbiPackUtils::unpackRowInfoDetails(modDetails, oldRow, newRow);
1304     if (!ok) {
1305         os.setError("An error occurred during updating a row info!");
1306         return;
1307     }
1308     SAFE_POINT(oldRow.rowId == newRow.rowId, "Incorrect rowId!", );
1309     SAFE_POINT(oldRow.sequenceId == newRow.sequenceId, "Incorrect sequenceId!", );
1310 
1311     updateRowInfoCore(msaId, newRow, os);
1312 }
1313 
undoMsaLengthChange(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1314 void SQLiteMsaDbi::undoMsaLengthChange(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1315     qint64 oldLen;
1316     qint64 newLen;
1317 
1318     bool ok = U2DbiPackUtils::unpackAlignmentLength(modDetails, oldLen, newLen);
1319     CHECK_EXT(ok, os.setError(U2DbiL10n::tr("An error occurred during updating an msa length")), );
1320 
1321     updateMsaLengthCore(msaId, oldLen, os);
1322 }
1323 
redoMsaLengthChange(const U2DataId & msaId,const QByteArray & modDetails,U2OpStatus & os)1324 void SQLiteMsaDbi::redoMsaLengthChange(const U2DataId &msaId, const QByteArray &modDetails, U2OpStatus &os) {
1325     qint64 oldLen;
1326     qint64 newLen;
1327 
1328     bool ok = U2DbiPackUtils::unpackAlignmentLength(modDetails, oldLen, newLen);
1329     CHECK_EXT(ok, os.setError(U2DbiL10n::tr("An error occurred during updating an msa length")), );
1330 
1331     updateMsaLengthCore(msaId, newLen, os);
1332 }
1333 
1334 }  // namespace U2
1335