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 "MysqlAssemblyDbi.h"
23 
24 #include <QVarLengthArray>
25 
26 #include <U2Core/AppContext.h>
27 #include <U2Core/Timer.h>
28 #include <U2Core/U2AssemblyUtils.h>
29 #include <U2Core/U2OpStatusUtils.h>
30 #include <U2Core/U2SafePoints.h>
31 #include <U2Core/U2SqlHelpers.h>
32 
33 #include <U2Formats/BAMUtils.h>
34 
35 #include "MysqlObjectDbi.h"
36 #include "util/MysqlMultiTableAssemblyAdapter.h"
37 #include "util/MysqlSingleTableAssemblyAdapter.h"
38 
39 namespace U2 {
40 
MysqlAssemblyDbi(MysqlDbi * dbi)41 MysqlAssemblyDbi::MysqlAssemblyDbi(MysqlDbi *dbi)
42     : U2AssemblyDbi(dbi), MysqlChildDbiCommon(dbi) {
43 }
44 
~MysqlAssemblyDbi()45 MysqlAssemblyDbi::~MysqlAssemblyDbi() {
46     SAFE_POINT(adaptersById.isEmpty(), "Adapters list is not empty", );
47 }
48 
initSqlSchema(U2OpStatus & os)49 void MysqlAssemblyDbi::initSqlSchema(U2OpStatus &os) {
50     MysqlTransaction t(db, os);
51 
52     // assembly object
53     // reference            - reference sequence id
54     // imethod - indexing method - method used to handle read location
55     // cmethod - method used to handle compression of reads data
56     // idata - additional indexing method data
57     // cdata - additional compression method data
58     U2SqlQuery("CREATE TABLE Assembly (object BIGINT PRIMARY KEY, reference BIGINT, imethod LONGTEXT NOT NULL,"
59                " cmethod LONGTEXT NOT NULL, idata LONGBLOB, cdata LONGBLOB, "
60                " FOREIGN KEY(object) REFERENCES Object(id), "
61                " FOREIGN KEY(reference) REFERENCES Object(id) ON DELETE SET NULL ) "
62                " ENGINE=InnoDB DEFAULT CHARSET=utf8",
63                db,
64                os)
65         .execute();
66 }
67 
shutdown(U2OpStatus & os)68 void MysqlAssemblyDbi::shutdown(U2OpStatus &os) {
69     foreach (MysqlAssemblyAdapter *a, adaptersById.values()) {
70         a->shutdown(os);
71         delete a;
72     }
73 
74     adaptersById.clear();
75 }
76 
getAdapter(const U2DataId & assemblyId,U2OpStatus & os)77 MysqlAssemblyAdapter *MysqlAssemblyDbi::getAdapter(const U2DataId &assemblyId, U2OpStatus &os) {
78     qint64 dbiId = U2DbiUtils::toDbiId(assemblyId);
79     MysqlAssemblyAdapter *res = adaptersById.value(dbiId);
80     if (res != nullptr) {
81         return res;
82     }
83 
84     static const QString qString = "SELECT imethod FROM Assembly WHERE object = :object";
85     U2SqlQuery q(qString, db, os);
86     q.bindDataId(":object", assemblyId);
87     if (!q.step()) {
88         os.setError(U2DbiL10n::tr("There is no assembly object with the specified id."));
89         return nullptr;
90     }
91 
92     res = new MysqlMultiTableAssemblyAdapter(dbi, assemblyId, nullptr, db, os);
93     //    const QString indexMethod = q.getString(0);
94     //    if (indexMethod == MYSQL_DBI_ASSEMBLY_READ_ELEN_METHOD_SINGLE_TABLE) {
95     //        res = new SingleTableAssemblyAdapter(dbi, assemblyId, 'S', "", NULL, db, os);
96     //    } else if (indexMethod == MYSQL_DBI_ASSEMBLY_READ_ELEN_METHOD_MULTITABLE_V1) {
97     //    } else if (indexMethod == MYSQL_DBI_ASSEMBLY_READ_ELEN_METHOD_RTREE) {
98     //        res = new RTreeAssemblyAdapter(dbi, assemblyId, NULL, db, os);
99     //    } else {
100     //        os.setError(U2DbiL10n::tr("Unsupported reads storage type: %1").arg(indexMethod));
101     //        return NULL;
102     //    }
103 
104     adaptersById[dbiId] = res;
105     return res;
106 }
107 
getAssemblyObject(const U2DataId & assemblyId,U2OpStatus & os)108 U2Assembly MysqlAssemblyDbi::getAssemblyObject(const U2DataId &assemblyId, U2OpStatus &os) {
109     U2Assembly res;
110 
111     DBI_TYPE_CHECK(assemblyId, U2Type::Assembly, os, res);
112     dbi->getMysqlObjectDbi()->getObject(res, assemblyId, os);
113 
114     CHECK_OP(os, res);
115 
116     U2SqlQuery q("SELECT Assembly.reference, Object.type, '' FROM Assembly, Object "
117                  " WHERE Assembly.object = :object AND Object.id = Assembly.reference",
118                  db,
119                  os);
120     q.bindDataId(":object", assemblyId);
121     if (q.step()) {
122         res.referenceId = q.getDataIdExt(0);
123         q.ensureDone();
124     }
125 
126     return res;
127 }
128 
countReads(const U2DataId & assemblyId,const U2Region & r,U2OpStatus & os)129 qint64 MysqlAssemblyDbi::countReads(const U2DataId &assemblyId, const U2Region &r, U2OpStatus &os) {
130     GTIMER(c2, t2, "MysqlAssemblyDbi::countReadsAt");
131     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
132     if (a == nullptr) {
133         return -1;
134     }
135     return a->countReads(r, os);
136 }
137 
getReads(const U2DataId & assemblyId,const U2Region & r,U2OpStatus & os,bool sortedHint)138 U2DbiIterator<U2AssemblyRead> *MysqlAssemblyDbi::getReads(const U2DataId &assemblyId, const U2Region &r, U2OpStatus &os, bool sortedHint) {
139     GTIMER(c2, t2, "MysqlAssemblyDbi::getReadsAt");
140     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
141     if (a != nullptr) {
142         return a->getReads(r, os, sortedHint);
143     }
144     return nullptr;
145 }
146 
getReadsByRow(const U2DataId & assemblyId,const U2Region & r,qint64 minRow,qint64 maxRow,U2OpStatus & os)147 U2DbiIterator<U2AssemblyRead> *MysqlAssemblyDbi::getReadsByRow(const U2DataId &assemblyId, const U2Region &r, qint64 minRow, qint64 maxRow, U2OpStatus &os) {
148     GTIMER(c2, t2, "MysqlAssemblyDbi::getReadsAt");
149 
150     quint64 t0 = GTimer::currentTimeMicros();
151     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
152 
153     if (a == nullptr) {
154         return nullptr;
155     }
156 
157     U2DbiIterator<U2AssemblyRead> *res = a->getReadsByRow(r, minRow, maxRow, os);
158 
159     t2.stop();
160     perfLog.trace(QString("Assembly: reads 2D select time: %1 seconds").arg((GTimer::currentTimeMicros() - t0) / float(1000 * 1000)));
161 
162     return res;
163 }
164 
getReadsByName(const U2DataId & assemblyId,const QByteArray & name,U2OpStatus & os)165 U2DbiIterator<U2AssemblyRead> *MysqlAssemblyDbi::getReadsByName(const U2DataId &assemblyId, const QByteArray &name, U2OpStatus &os) {
166     GTIMER(c2, t2, "MysqlAssemblyDbi::getReadsByName");
167     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
168     if (a != nullptr) {
169         return a->getReadsByName(name, os);
170     }
171     return nullptr;
172 }
173 
getMaxPackedRow(const U2DataId & assemblyId,const U2Region & r,U2OpStatus & os)174 qint64 MysqlAssemblyDbi::getMaxPackedRow(const U2DataId &assemblyId, const U2Region &r, U2OpStatus &os) {
175     quint64 t0 = GTimer::currentTimeMicros();
176 
177     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
178 
179     if (a == nullptr) {
180         return -1;
181     }
182     qint64 res = a->getMaxPackedRow(r, os);
183 
184     perfLog.trace(QString("Assembly: get max packed row: %1 seconds").arg((GTimer::currentTimeMicros() - t0) / (1000 * 1000)));
185     return res;
186 }
187 
getMaxEndPos(const U2DataId & assemblyId,U2OpStatus & os)188 qint64 MysqlAssemblyDbi::getMaxEndPos(const U2DataId &assemblyId, U2OpStatus &os) {
189     quint64 t0 = GTimer::currentTimeMicros();
190 
191     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
192     if (a == nullptr) {
193         return -1;
194     }
195     quint64 res = a->getMaxEndPos(os);
196 
197     perfLog.trace(QString("Assembly: get max end pos: %1 seconds").arg((GTimer::currentTimeMicros() - t0) / (1000 * 1000)));
198     return res;
199 }
200 
createAssemblyObject(U2Assembly & assembly,const QString & folder,U2DbiIterator<U2AssemblyRead> * it,U2AssemblyReadsImportInfo & importInfo,U2OpStatus & os)201 void MysqlAssemblyDbi::createAssemblyObject(U2Assembly &assembly,
202                                             const QString &folder,
203                                             U2DbiIterator<U2AssemblyRead> *it,
204                                             U2AssemblyReadsImportInfo &importInfo,
205                                             U2OpStatus &os) {
206     MysqlTransaction t(db, os);
207 
208     U2Object fakeObject;
209     fakeObject.visualName = assembly.visualName;
210     fakeObject.trackModType = assembly.trackModType;
211 
212     dbi->getMysqlObjectDbi()->createObject(fakeObject, folder, U2DbiObjectRank_TopLevel, os);
213     SAFE_POINT_OP(os, );
214 
215     assembly.id = U2DbiUtils::toU2DataId(U2DbiUtils::toDbiId(fakeObject.id), U2Type::Assembly, U2DbiUtils::toDbExtra(fakeObject.id));
216     assembly.dbiId = fakeObject.dbiId;
217     assembly.version = fakeObject.version;
218 
219     QString elenMethod = "multi-table-v1";
220     // QString elenMethod = dbi->getProperty(Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_KEY, Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_RTREE, os);
221     //     QString elenMethod = dbi->getProperty(Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_KEY, Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_MULTITABLE_V1, os);
222     // QString elenMethod = dbi->getProperty(Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_KEY, Mysql_DBI_ASSEMBLY_READ_ELEN_METHOD_SINGLE_TABLE, os);
223 
224     U2SqlQuery q("INSERT INTO Assembly(object, reference, imethod, cmethod) VALUES(:object, :reference, :imethod, :cmethod)", db, os);
225     q.bindDataId(":object", assembly.id);
226     q.bindDataId(":reference", assembly.referenceId);
227     q.bindString(":imethod", elenMethod);
228     q.bindString(":cmethod", "no-compression");
229     q.insert();
230     SAFE_POINT_OP(os, );
231 
232     MysqlAssemblyAdapter *a = getAdapter(assembly.id, os);
233     SAFE_POINT_OP(os, );
234 
235     a->createReadsTables(os);
236     SAFE_POINT_OP(os, );
237 
238     if (it != nullptr) {
239         addReads(a, it, importInfo, os);
240         SAFE_POINT_OP(os, );
241     }
242 
243     a->createReadsIndexes(os);
244     SAFE_POINT_OP(os, );
245 }
246 
finalizeAssemblyObject(U2Assembly & assembly,U2OpStatus & os)247 void MysqlAssemblyDbi::finalizeAssemblyObject(U2Assembly &assembly, U2OpStatus &os) {
248     U2OpStatusImpl correctTypeOs;
249     U2OpStatusImpl removeObjectOs;
250 
251     correctAssemblyType(assembly, correctTypeOs);
252 
253     if (os.isCoR() || correctTypeOs.isCoR()) {
254         dbi->getMysqlObjectDbi()->removeObject(assembly.id, true, removeObjectOs);
255     }
256 
257     if (!os.isCoR() && correctTypeOs.isCoR()) {
258         os.setError(correctTypeOs.getError());
259     } else if (!os.isCoR() && removeObjectOs.isCoR()) {
260         os.setError(removeObjectOs.getError());
261     }
262 }
263 
removeAssemblyData(const U2DataId & assemblyId,U2OpStatus & os)264 void MysqlAssemblyDbi::removeAssemblyData(const U2DataId &assemblyId, U2OpStatus &os) {
265     MysqlTransaction t(db, os);
266     CHECK_OP(os, );
267 
268     removeTables(assemblyId, os);
269     CHECK_OP(os, );
270     removeAssemblyEntry(assemblyId, os);
271 }
272 
updateAssemblyObject(U2Assembly & assembly,U2OpStatus & os)273 void MysqlAssemblyDbi::updateAssemblyObject(U2Assembly &assembly, U2OpStatus &os) {
274     MysqlTransaction t(db, os);
275 
276     U2SqlQuery q("UPDATE Assembly SET reference = :reference WHERE object = :object", db, os);
277     q.bindDataId(":reference", assembly.referenceId);
278     q.bindDataId(":object", assembly.id);
279     q.execute();
280 
281     SAFE_POINT_OP(os, );
282 
283     dbi->getMysqlObjectDbi()->updateObject(assembly, os);
284     SAFE_POINT_OP(os, );
285 
286     MysqlObjectDbi::incrementVersion(assembly.id, db, os);
287     SAFE_POINT_OP(os, );
288 }
289 
removeReads(const U2DataId & assemblyId,const QList<U2DataId> & rowIds,U2OpStatus & os)290 void MysqlAssemblyDbi::removeReads(const U2DataId &assemblyId, const QList<U2DataId> &rowIds, U2OpStatus &os) {
291     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
292     if (a != nullptr) {
293         a->removeReads(rowIds, os);
294     }
295 }
296 
addReads(MysqlAssemblyAdapter * a,U2DbiIterator<U2AssemblyRead> * it,U2AssemblyReadsImportInfo & ii,U2OpStatus & os)297 void MysqlAssemblyDbi::addReads(MysqlAssemblyAdapter *a, U2DbiIterator<U2AssemblyRead> *it, U2AssemblyReadsImportInfo &ii, U2OpStatus &os) {
298     GTIMER(c2, t2, "MysqlAssemblyDbi::addReads");
299 
300     quint64 t0 = GTimer::currentTimeMicros();
301 
302     a->addReads(it, ii, os);
303 
304     t2.stop();
305     perfLog.trace(QString("Assembly: %1 reads added in %2 seconds. Auto-packing: %3")
306                       .arg(ii.nReads)
307                       .arg((GTimer::currentTimeMicros() - t0) / float(1000 * 1000))
308                       .arg(ii.packStat.readsCount > 0 ? "yes" : "no"));
309 }
310 
removeTables(const U2DataId & assemblyId,U2OpStatus & os)311 void MysqlAssemblyDbi::removeTables(const U2DataId &assemblyId, U2OpStatus &os) {
312     MysqlTransaction t(db, os);
313     CHECK_OP(os, );
314 
315     AssemblyAdapter *adapter = getAdapter(assemblyId, os);
316     CHECK(nullptr != adapter, );
317     adapter->dropReadsTables(os);
318 }
319 
removeAssemblyEntry(const U2DataId & assemblyId,U2OpStatus & os)320 void MysqlAssemblyDbi::removeAssemblyEntry(const U2DataId &assemblyId, U2OpStatus &os) {
321     MysqlTransaction t(db, os);
322     CHECK_OP(os, );
323 
324     static const QString queryString("DELETE FROM Assembly WHERE object = :object");
325     U2SqlQuery q(queryString, db, os);
326     q.bindDataId(":object", assemblyId);
327     q.execute();
328 }
329 
correctAssemblyType(U2Assembly & assembly,U2OpStatus & os)330 void MysqlAssemblyDbi::correctAssemblyType(U2Assembly &assembly, U2OpStatus &os) {
331     dbi->getMysqlObjectDbi()->updateObjectType(assembly, os);
332     SAFE_POINT_OP(os, );
333 }
334 
addReads(const U2DataId & assemblyId,U2DbiIterator<U2AssemblyRead> * it,U2OpStatus & os)335 void MysqlAssemblyDbi::addReads(const U2DataId &assemblyId, U2DbiIterator<U2AssemblyRead> *it, U2OpStatus &os) {
336     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
337     if (a != nullptr) {
338         U2AssemblyReadsImportInfo ii;
339         addReads(a, it, ii, os);
340     }
341 }
342 
343 /**  Packs assembly rows: assigns packedViewRow value for every read in assembly */
pack(const U2DataId & assemblyId,U2AssemblyPackStat & stat,U2OpStatus & os)344 void MysqlAssemblyDbi::pack(const U2DataId &assemblyId, U2AssemblyPackStat &stat, U2OpStatus &os) {
345     GTIMER(c2, t2, "MysqlAssemblyDbi::pack");
346 
347     quint64 t0 = GTimer::currentTimeMicros();
348 
349     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
350     if (a == nullptr) {
351         return;
352     }
353     stat.readsCount = a->countReads(U2_REGION_MAX, os);
354     a->pack(stat, os);
355     perfLog.trace(QString("Assembly: full pack time: %1 seconds").arg((GTimer::currentTimeMicros() - t0) / float(1000 * 1000)));
356 }
357 
calculateCoverage(const U2DataId & assemblyId,const U2Region & region,U2AssemblyCoverageStat & coverage,U2OpStatus & os)358 void MysqlAssemblyDbi::calculateCoverage(const U2DataId &assemblyId, const U2Region &region, U2AssemblyCoverageStat &coverage, U2OpStatus &os) {
359     GTIMER(c2, t2, "MysqlAssemblyDbi::calculateCoverage");
360 
361     quint64 t0 = GTimer::currentTimeMicros();
362 
363     MysqlAssemblyAdapter *a = getAdapter(assemblyId, os);
364     if (a == nullptr) {
365         return;
366     }
367     a->calculateCoverage(region, coverage, os);
368     perfLog.trace(QString("Assembly: full coverage calculation time for %2..%3: %1 seconds").arg((GTimer::currentTimeMicros() - t0) / float(1000 * 1000)).arg(region.startPos).arg(region.endPos()));
369 }
370 
371 }  // namespace U2
372