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 "SQLiteFeatureDbi.h"
23 
24 #include <U2Core/U2SafePoints.h>
25 #include <U2Core/U2SqlHelpers.h>
26 
27 #include "SQLiteObjectDbi.h"
28 
29 static const QString FDBI_FIELDS("f.id, f.class, f.type, f.parent, f.root, f.name, f.sequence, f.strand, f.start, f.len");
30 
31 namespace U2 {
32 
SQLiteFeatureDbi(SQLiteDbi * dbi)33 SQLiteFeatureDbi::SQLiteFeatureDbi(SQLiteDbi *dbi)
34     : U2FeatureDbi(dbi), SQLiteChildDBICommon(dbi) {
35 }
36 
getQueryForFeatureDeletionTrigger()37 static QString getQueryForFeatureDeletionTrigger() {
38     return "CREATE TRIGGER FeatureDeletion BEFORE DELETE ON Feature "
39            "FOR EACH ROW "
40            "BEGIN "
41            "DELETE FROM Feature WHERE parent = OLD.id;"
42            "END";
43 }
44 
initSqlSchema(U2OpStatus & os)45 void SQLiteFeatureDbi::initSqlSchema(U2OpStatus &os) {
46     // nameHash is used for better indexing
47     SQLiteWriteQuery("CREATE TABLE Feature (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
48                      "class INTEGER NOT NULL, type INTEGER NOT NULL, parent INTEGER, root INTEGER, nameHash INTEGER, name TEXT, "
49                      "sequence INTEGER, strand INTEGER NOT NULL, "
50                      "start INTEGER NOT NULL DEFAULT 0, len INTEGER NOT NULL DEFAULT 0)",
51                      db,
52                      os)
53         .execute();
54 
55     SQLiteWriteQuery("CREATE TABLE FeatureKey (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
56                      "feature INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL, "
57                      "FOREIGN KEY(feature) REFERENCES Feature(id) ON DELETE CASCADE)",
58                      db,
59                      os)
60         .execute();
61 
62     // annotation table object
63     SQLiteWriteQuery("CREATE TABLE AnnotationTable (object INTEGER PRIMARY KEY, rootId INTEGER NOT NULL, "
64                      "FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE, "
65                      "FOREIGN KEY(rootId) REFERENCES Feature(id) ON DELETE CASCADE)",
66                      db,
67                      os)
68         .execute();
69 
70     SQLiteWriteQuery("CREATE INDEX IF NOT EXISTS FeatureRootIndex ON Feature(root, class)", db, os).execute();
71     SQLiteWriteQuery("CREATE INDEX IF NOT EXISTS FeatureParentIndex ON Feature(parent)", db, os).execute();
72     SQLiteWriteQuery("CREATE INDEX IF NOT EXISTS FeatureNameIndex ON Feature(root, nameHash)", db, os).execute();
73 
74     // FeatureKey index
75     SQLiteWriteQuery("CREATE INDEX IF NOT EXISTS FeatureKeyIndex ON FeatureKey(feature)", db, os).execute();
76 
77     // Deletion triggers
78     SQLiteWriteQuery(getQueryForFeatureDeletionTrigger(), db, os).execute();
79 }
80 
81 class SqlFeatureRSLoader : public SQLiteResultSetLoader<U2Feature> {
82 public:
load(SQLiteQuery * q)83     U2Feature load(SQLiteQuery *q) {
84         return loadStatic(q);
85     }
86 
loadStatic(SQLiteQuery * q)87     static U2Feature loadStatic(SQLiteQuery *q) {
88         U2Feature res;
89         // class, type, parent, root, name, sequence, strand, start, len
90         res.id = q->getDataId(0, U2Type::Feature);
91         res.featureClass = static_cast<U2Feature::FeatureClass>(q->getInt32(1));
92         res.featureType = static_cast<U2FeatureType>(q->getInt32(2));
93         res.parentFeatureId = q->getDataId(3, U2Type::Feature);
94         res.rootFeatureId = q->getDataId(4, U2Type::Feature);
95         res.name = q->getString(5);
96         res.sequenceId = q->getDataId(6, U2Type::Sequence);
97         res.location.strand = U2Strand(U2Strand::Direction(q->getInt32(7)));
98         res.location.region.startPos = q->getInt64(8);
99         res.location.region.length = q->getInt64(9);
100         return res;
101     }
102 };
103 
104 class SqlFeatureFilter : public SQLiteResultSetFilter<U2Feature> {
105 public:
SqlFeatureFilter(const QString & name,const U2DataId & _seqId)106     SqlFeatureFilter(const QString &name, const U2DataId &_seqId) {
107         nameToFilter = name;
108         seqId = _seqId;
109     }
filter(const U2Feature & f)110     bool filter(const U2Feature &f) {
111         if ((nameToFilter.isEmpty() || f.name == nameToFilter) && (seqId.isEmpty() || seqId == f.sequenceId)) {
112             return true;
113         }
114         return false;
115     }
116 
117 private:
118     QString nameToFilter;
119     U2DataId seqId;
120 };
121 
createAnnotationTableObject(U2AnnotationTable & table,const QString & folder,U2OpStatus & os)122 void SQLiteFeatureDbi::createAnnotationTableObject(U2AnnotationTable &table, const QString &folder, U2OpStatus &os) {
123     dbi->getSQLiteObjectDbi()->createObject(table, folder, U2DbiObjectRank_TopLevel, os);
124     CHECK_OP(os, );
125 
126     static const QString queryString("INSERT INTO AnnotationTable (object, rootId) VALUES(?1, ?2)");
127     SQLiteWriteQuery q(queryString, db, os);
128     CHECK_OP(os, );
129     q.bindDataId(1, table.id);
130     q.bindDataId(2, table.rootFeature);
131     q.insert();
132 }
133 
getAnnotationTableObject(const U2DataId & tableId,U2OpStatus & os)134 U2AnnotationTable SQLiteFeatureDbi::getAnnotationTableObject(const U2DataId &tableId, U2OpStatus &os) {
135     U2AnnotationTable result;
136 
137     DBI_TYPE_CHECK(tableId, U2Type::AnnotationTable, os, result);
138 
139     SQLiteReadQuery q("SELECT rootId, name FROM AnnotationTable, Object WHERE object = ?1 AND id = ?1", db, os);
140     q.bindDataId(1, tableId);
141     if (q.step()) {
142         result.rootFeature = q.getDataId(0, U2Type::Feature);
143         result.visualName = q.getString(1);
144         q.ensureDone();
145     } else if (!os.hasError()) {
146         os.setError(U2DbiL10n::tr("Annotation table object not found."));
147     }
148     result.id = tableId;
149 
150     return result;
151 }
152 
removeAnnotationTableData(const U2DataId & tableId,U2OpStatus & os)153 void SQLiteFeatureDbi::removeAnnotationTableData(const U2DataId &tableId, U2OpStatus &os) {
154     DBI_TYPE_CHECK(tableId, U2Type::AnnotationTable, os, );
155     static const QString rootQueryStr = "(SELECT rootId FROM AnnotationTable WHERE object = ?1)";
156 
157     SQLiteWriteQuery removeAnnTableQuery(QString("DELETE FROM Feature WHERE root IN %1 OR id IN %1").arg(rootQueryStr), db, os);
158     removeAnnTableQuery.bindDataId(1, tableId);
159     removeAnnTableQuery.execute();
160 }
161 
getFeature(const U2DataId & featureId,U2OpStatus & os)162 U2Feature SQLiteFeatureDbi::getFeature(const U2DataId &featureId, U2OpStatus &os) {
163     U2Feature res;
164     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, res);
165 
166     const QString queryString("SELECT " + FDBI_FIELDS + " FROM Feature AS f WHERE id = ?1");
167     SQLiteReadQuery q(queryString, db, os);
168     q.bindDataId(1, featureId);
169     q.execute();
170     CHECK_OP(os, res);
171 
172     res = SqlFeatureRSLoader::loadStatic(&q);
173     return res;
174 }
175 
add(QString & buf,const QString & str,const QString & op,int & n)176 static void add(QString &buf, const QString &str, const QString &op, int &n) {
177     if (!buf.isEmpty()) {
178         buf += " AND ";
179     }
180     buf += str;
181     if (!op.isEmpty()) {
182         n++;
183         buf += op + "?" + QString::number(n);
184     }
185 }
186 
toSqlCompareOp(ComparisonOp op)187 static QString toSqlCompareOp(ComparisonOp op) {
188     QString res;
189     switch (op) {
190         case ComparisonOp_EQ:
191             res = "=";
192             break;
193         case ComparisonOp_NEQ:
194             res = "!=";
195             break;
196         case ComparisonOp_GT:
197             res = ">";
198             break;
199         case ComparisonOp_GET:
200             res = ">=";
201             break;
202         case ComparisonOp_LT:
203             res = "<";
204             break;
205         case ComparisonOp_LET:
206             res = "<=";
207             break;
208         default:
209             res = "?";
210             break;
211     }
212     return res;
213 }
214 
toSqlOrderOp(OrderOp op)215 static QString toSqlOrderOp(OrderOp op) {
216     QString res;
217     switch (op) {
218         case OrderOp_Asc:
219             res = "ASC";
220             break;
221         case OrderOp_Desc:
222             res = "DESC";
223             break;
224         default:
225             break;
226     }
227     return res;
228 }
229 
toSqlOrderOpFromCompareOp(ComparisonOp op)230 static QString toSqlOrderOpFromCompareOp(ComparisonOp op) {
231     QString res;
232     switch (op) {
233         case ComparisonOp_EQ:
234             break;
235         case ComparisonOp_NEQ:
236             break;
237         case ComparisonOp_GT:
238             res = "ASC";
239             break;
240         case ComparisonOp_GET:
241             res = "ASC";
242             break;
243         case ComparisonOp_LT:
244             res = "DESC";
245             break;
246         case ComparisonOp_LET:
247             res = "DESC";
248             break;
249         default:
250             break;
251     }
252     return res;
253 }
254 
getWhereQueryPartFromType(const QString & featurePlaceholder,const FeatureFlags & types)255 static QString getWhereQueryPartFromType(const QString &featurePlaceholder, const FeatureFlags &types) {
256     QString result;
257     if (types.testFlag(U2Feature::Annotation)) {
258         result += featurePlaceholder + ".class = " + QString::number(U2Feature::Annotation);
259     }
260     if (types.testFlag(U2Feature::Group)) {
261         if (!result.isEmpty()) {
262             result += " OR ";
263         }
264         result += featurePlaceholder + ".class = " + QString::number(U2Feature::Group);
265     }
266 
267     if (!result.isEmpty()) {
268         result = " AND (" + result + ") ";
269     }
270     return result;
271 }
272 
createFeatureQuery(const QString & selectPart,const FeatureQuery & fq,bool useOrder,U2OpStatus & os,SQLiteTransaction * trans)273 QSharedPointer<SQLiteQuery> SQLiteFeatureDbi::createFeatureQuery(const QString &selectPart, const FeatureQuery &fq, bool useOrder, U2OpStatus &os, SQLiteTransaction *trans) {
274     QString wherePart;
275     int n = 0;
276 
277     bool useParent = !fq.parentFeatureId.isEmpty();
278     if (useParent) {
279         DBI_TYPE_CHECK(fq.parentFeatureId, U2Type::Feature, os, QSharedPointer<SQLiteReadQuery>());
280         add(wherePart, "f.parent", "=", n);
281     } else if (fq.topLevelOnly) {
282         add(wherePart, "f.parent", "=", n);
283     }
284 
285     bool useRoot = !fq.rootFeatureId.isEmpty();
286     if (useRoot) {
287         DBI_TYPE_CHECK(fq.rootFeatureId, U2Type::Feature, os, QSharedPointer<SQLiteReadQuery>());
288         add(wherePart, "f.root", "=", n);
289     }
290 
291     bool useClass = (U2Feature::Invalid != fq.featureClass);
292     if (useClass) {
293         add(wherePart, "f.class", "=", n);
294     }
295 
296     bool useType = (U2FeatureTypes::Invalid != fq.featureType);
297     if (useType) {
298         add(wherePart, "f.type", "=", n);
299     }
300 
301     bool useName = !fq.featureName.isEmpty();
302     if (useName) {
303         add(wherePart, "f.nameHash", "=", n);
304     }
305 
306     bool useSequence = !fq.sequenceId.isEmpty();
307     if (useSequence) {
308         DBI_TYPE_CHECK(fq.sequenceId, U2Type::Sequence, os, QSharedPointer<SQLiteReadQuery>());
309         add(wherePart, "f.sequence", "=", n);
310     }
311 
312     bool useRegion = (0 < fq.intersectRegion.length);
313     bool oneClosestFeature = (ComparisonOp_Invalid != fq.closestFeature);
314     if (useRegion) {
315         if (!oneClosestFeature) {  // check if intersects
316             add(wherePart, QString("f.start < ?%2 AND f.start + f.len > ?%1").arg(n + 1).arg(n + 2), "", n);
317             n += 2;
318         } else {  // check if close
319             add(wherePart, QString("f.start %1 ?%2").arg(toSqlCompareOp(fq.closestFeature)).arg(n + 1), "", n);
320             n++;
321         }
322     }
323 
324     bool useStrand = (Strand_Both != fq.strandQuery);
325     if (useStrand) {
326         add(wherePart, "f.strand", "=", n);
327     }
328 
329     bool useKeyName = !fq.keyName.isEmpty();
330     if (useKeyName) {
331         add(wherePart, "fk.name", "=", n);
332     }
333 
334     bool useKeyValue = !fq.keyValue.isEmpty();
335     if (useKeyValue) {
336         add(wherePart, "fk.value", toSqlCompareOp(fq.keyValueCompareOp), n);
337     }
338 
339     bool useKeyTable = (useKeyName || useKeyValue);
340     if (useKeyTable) {
341         wherePart += " AND fk.feature = f.id ";
342     }
343 
344     if (useOrder) {
345         if (OrderOp_None != fq.keyNameOrderOp) {
346             wherePart += " ORDER BY fk.name " + toSqlOrderOp(fq.keyNameOrderOp);
347         } else if (OrderOp_None != fq.keyValueOrderOp) {
348             wherePart += " ORDER BY fk.value";
349         } else if (OrderOp_None != fq.startPosOrderOp && useRegion) {
350             if (!oneClosestFeature) {
351                 wherePart += " ORDER BY f.start ";
352             } else {
353                 wherePart += QString(" ORDER BY f.start %1 ")
354                                  .arg(toSqlOrderOpFromCompareOp(fq.closestFeature));
355             }
356         }
357     }
358 
359     QString tablesPart = "Feature AS f";
360     if (useKeyTable) {
361         tablesPart += ", FeatureKey AS fk";
362     }
363 
364     QString fullQuery = selectPart + " FROM " + tablesPart;
365     if (!wherePart.isEmpty()) {
366         fullQuery += " WHERE " + wherePart;
367     }
368     if (oneClosestFeature) {
369         fullQuery += " LIMIT 1 ";
370     }
371 
372     QSharedPointer<SQLiteQuery> q;
373     if (nullptr == trans) {
374         q = QSharedPointer<SQLiteReadQuery>(new SQLiteReadQuery(fullQuery, db, os));
375     } else {
376         q = trans->getPreparedQuery(fullQuery, db, os);
377         CHECK_OP(os, QSharedPointer<SQLiteReadQuery>());
378     }
379     int m = 0;
380 
381     if (useParent) {
382         q->bindDataId(++m, fq.parentFeatureId);
383     } else if (fq.topLevelOnly) {
384         q->bindDataId(++m, U2DataId());
385     }
386     if (useRoot) {
387         q->bindDataId(++m, fq.rootFeatureId);
388     }
389     if (useClass) {
390         q->bindInt32(++m, fq.featureClass);
391     }
392     if (useType) {
393         q->bindInt32(++m, fq.featureType);
394     }
395     if (useName) {
396         q->bindInt32(++m, qHash(fq.featureName));
397     }
398     if (useSequence) {
399         q->bindDataId(++m, fq.sequenceId);
400     }
401     if (useRegion) {
402         if (!oneClosestFeature) {
403             q->bindInt64(++m, fq.intersectRegion.startPos);
404             q->bindInt64(++m, fq.intersectRegion.endPos());
405         } else {
406             q->bindInt64(++m, fq.intersectRegion.startPos);
407         }
408     }
409     if (useStrand) {
410         U2Strand::Direction direction = U2Strand::Direct;
411         if (Strand_Direct == fq.strandQuery) {
412             direction = U2Strand::Direct;
413         } else if (Strand_Compl == fq.strandQuery) {
414             direction = U2Strand::Complementary;
415         }
416         q->bindInt32(++m, direction);
417     }
418 
419     if (useKeyName) {
420         q->bindString(++m, fq.keyName);
421     }
422     if (useKeyValue) {
423         q->bindString(++m, fq.keyValue);
424     }
425 
426     return q;
427 }
428 
countFeatures(const FeatureQuery & fq,U2OpStatus & os)429 qint64 SQLiteFeatureDbi::countFeatures(const FeatureQuery &fq, U2OpStatus &os) {
430     QSharedPointer<SQLiteQuery> q(createFeatureQuery("SELECT COUNT(*)", fq, false, os));
431     CHECK_OP(os, -1);
432     return q->selectInt64();
433 }
434 
getFeatures(const FeatureQuery & fq,U2OpStatus & os)435 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeatures(const FeatureQuery &fq, U2OpStatus &os) {
436     QSharedPointer<SQLiteQuery> q = createFeatureQuery("SELECT " + FDBI_FIELDS, fq, true, os);
437     CHECK_OP(os, nullptr);
438     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), nullptr, U2Feature(), os);
439 }
440 
getFeatureKeys(const U2DataId & featureId,U2OpStatus & os)441 QList<U2FeatureKey> SQLiteFeatureDbi::getFeatureKeys(const U2DataId &featureId, U2OpStatus &os) {
442     SQLiteTransaction t(db, os);
443     static const QString queryString("SELECT name, value FROM FeatureKey WHERE feature = ?1");
444     SQLiteReadQuery q(queryString, db, os);
445 
446     q.bindDataId(1, featureId);
447     CHECK_OP(os, QList<U2FeatureKey>());
448     QList<U2FeatureKey> result;
449     while (q.step()) {
450         U2FeatureKey key;
451         key.name = q.getCString(0);
452         key.value = q.getCString(1);
453         result.append(key);
454     }
455     return result;
456 }
457 
458 namespace {
459 
addKeyCommon(SQLiteWriteQuery & qk,const U2DataId & featureId,const U2FeatureKey & key)460 void addKeyCommon(SQLiteWriteQuery &qk, const U2DataId &featureId, const U2FeatureKey &key) {
461     qk.reset();
462     qk.bindDataId(1, featureId);
463     qk.bindString(2, key.name);
464     qk.bindString(3, key.value);
465     qk.insert();
466 }
467 
getFeatureKeyInsertQuery(int keyCount)468 QString getFeatureKeyInsertQuery(int keyCount) {
469     SAFE_POINT(keyCount > 0, "Unexpected feature keys number", QString());
470 
471     QString queryStringk("INSERT INTO FeatureKey(feature, name, value) VALUES");
472     for (int i = 1, n = 3 * keyCount; i <= n; i += 3) {
473         queryStringk += QString("(?%1, ?%2, ?%3),").arg(i).arg(i + 1).arg(i + 2);
474     }
475     queryStringk.chop(1);  // remove last comma
476     return queryStringk;
477 }
478 
addFeatureKeys(const QList<U2FeatureKey> & keys,const U2DataId & featureId,DbRef * db,U2OpStatus & os)479 void addFeatureKeys(const QList<U2FeatureKey> &keys, const U2DataId &featureId, DbRef *db, U2OpStatus &os) {
480     SQLiteTransaction t(db, os);
481 
482     const int keyCount = keys.count();
483     CHECK(keyCount > 0, );
484 
485     const int maximumBoundKeysNumber = SQLiteDbi::BIND_PARAMETERS_LIMIT / 3;
486     const int residualBindQueryCount = keyCount % maximumBoundKeysNumber;  // 3 is the number of FeatureKey table attributes
487     const int fullBindQueryCount = keyCount / maximumBoundKeysNumber;
488     const bool fullQueryPresents = fullBindQueryCount > 0;
489 
490     const QString fullQueryStr = fullQueryPresents ? getFeatureKeyInsertQuery(maximumBoundKeysNumber) : QString();
491     const QString residualQueryStr = getFeatureKeyInsertQuery(residualBindQueryCount);
492 
493     QSharedPointer<SQLiteQuery> residualQuery(t.getPreparedQuery(residualQueryStr, db, os));
494     QSharedPointer<SQLiteQuery> fullQuery;
495     if (fullQueryPresents) {
496         fullQuery = t.getPreparedQuery(fullQueryStr, db, os);
497     }
498 
499     for (int i = 1; i <= 3 * residualBindQueryCount; i += 3) {
500         const U2FeatureKey &key = keys[(i - 1) / 3];
501         residualQuery->bindDataId(i, featureId);
502         residualQuery->bindString(i + 1, key.name);
503         residualQuery->bindString(i + 2, key.value);
504     }
505     residualQuery->insert();
506     CHECK_OP(os, );
507 
508     if (fullQueryPresents) {
509         SAFE_POINT(nullptr != fullQuery.data(), "Invalid database query detected", );
510         for (int currentFullQuery = 0; currentFullQuery < fullBindQueryCount && !os.isCoR(); ++currentFullQuery) {
511             const int firstBindingPos = residualBindQueryCount + currentFullQuery * maximumBoundKeysNumber;
512             const int lastBindingPos = residualBindQueryCount + (currentFullQuery + 1) * maximumBoundKeysNumber;
513             for (int keyNum = firstBindingPos, paramNum = 1; keyNum < lastBindingPos; ++keyNum, paramNum += 3) {
514                 const U2FeatureKey &key = keys[keyNum];
515                 fullQuery->bindDataId(paramNum, featureId);
516                 fullQuery->bindString(paramNum + 1, key.name);
517                 fullQuery->bindString(paramNum + 2, key.value);
518             }
519             fullQuery->insert();
520             CHECK_OP(os, );
521             fullQuery->reset();
522         }
523     }
524 }
525 
526 }  // namespace
527 
createFeature(U2Feature & feature,const QList<U2FeatureKey> & keys,U2OpStatus & os)528 void SQLiteFeatureDbi::createFeature(U2Feature &feature, const QList<U2FeatureKey> &keys, U2OpStatus &os) {
529     SQLiteTransaction t(db, os);
530 
531     static const QString queryStringf("INSERT INTO Feature(class, type, parent, root, name, sequence, strand, start, len, nameHash) "
532                                       "VALUES(?1,    ?2,   ?3,     ?4,   ?5,   ?6,       ?7,     ?8,    ?9,   ?10)");
533     QSharedPointer<SQLiteQuery> qf = t.getPreparedQuery(queryStringf, db, os);
534 
535     CHECK_OP(os, );
536     qf->bindInt32(1, feature.featureClass);
537     qf->bindInt32(2, feature.featureType);
538     qf->bindDataId(3, feature.parentFeatureId);
539     qf->bindDataId(4, feature.rootFeatureId);
540     qf->bindString(5, feature.name);
541     qf->bindDataId(6, feature.sequenceId);
542     qf->bindInt32(7, feature.location.strand.getDirectionValue());
543     qf->bindInt64(8, feature.location.region.startPos);
544     qf->bindInt64(9, feature.location.region.length);
545     qf->bindInt32(10, qHash(feature.name));
546     feature.id = qf->insert(U2Type::Feature);
547     CHECK_OP(os, );
548 
549     addFeatureKeys(keys, feature.id, db, os);
550 }
551 
addKey(const U2DataId & featureId,const U2FeatureKey & key,U2OpStatus & os)552 void SQLiteFeatureDbi::addKey(const U2DataId &featureId, const U2FeatureKey &key, U2OpStatus &os) {
553     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
554 
555     SQLiteWriteQuery qk("INSERT INTO FeatureKey(feature, name, value) VALUES(?1, ?2, ?3)", db, os);
556     addKeyCommon(qk, featureId, key);
557 }
558 
removeAllKeys(const U2DataId & featureId,const QString & keyName,U2OpStatus & os)559 void SQLiteFeatureDbi::removeAllKeys(const U2DataId &featureId, const QString &keyName, U2OpStatus &os) {
560     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
561 
562     SQLiteWriteQuery q("DELETE FROM FeatureKey WHERE feature = ?1 AND name = ?2", db, os);
563     q.bindDataId(1, featureId);
564     q.bindString(2, keyName);
565     q.execute();
566 }
567 
removeKey(const U2DataId & featureId,const U2FeatureKey & key,U2OpStatus & os)568 void SQLiteFeatureDbi::removeKey(const U2DataId &featureId, const U2FeatureKey &key, U2OpStatus &os) {
569     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
570 
571     SQLiteWriteQuery q("DELETE FROM FeatureKey WHERE feature = ?1 AND name = ?2 AND value = ?3", db, os);
572     q.bindDataId(1, featureId);
573     q.bindString(2, key.name);
574     q.bindString(3, key.value);
575     q.execute();
576 }
577 
updateName(const U2DataId & featureId,const QString & newName,U2OpStatus & os)578 void SQLiteFeatureDbi::updateName(const U2DataId &featureId, const QString &newName, U2OpStatus &os) {
579     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
580 
581     SQLiteWriteQuery qf("UPDATE Feature SET name = ?3, nameHash = ?2 WHERE id = ?1", db, os);
582     qf.bindDataId(1, featureId);
583     qf.bindInt32(2, qHash(newName));
584     qf.bindString(3, newName);
585     qf.execute();
586 }
587 
updateParentId(const U2DataId & featureId,const U2DataId & parentId,U2OpStatus & os)588 void SQLiteFeatureDbi::updateParentId(const U2DataId &featureId, const U2DataId &parentId, U2OpStatus &os) {
589     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
590     DBI_TYPE_CHECK(parentId, U2Type::Feature, os, );
591 
592     SQLiteWriteQuery qf("UPDATE Feature SET parent = ?1 WHERE id = ?2", db, os);
593     qf.bindDataId(1, parentId);
594     qf.bindDataId(2, featureId);
595     qf.execute();
596 }
597 
updateSequenceId(const U2DataId & featureId,const U2DataId & seqId,U2OpStatus & os)598 void SQLiteFeatureDbi::updateSequenceId(const U2DataId &featureId, const U2DataId &seqId, U2OpStatus &os) {
599     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
600     DBI_TYPE_CHECK(seqId, U2Type::Sequence, os, );
601 
602     SQLiteWriteQuery qf("UPDATE Feature SET sequence = ?1 WHERE id = ?2", db, os);
603     qf.bindDataId(1, seqId);
604     qf.bindDataId(2, featureId);
605     qf.execute();
606 }
607 
updateKeyValue(const U2DataId & featureId,const U2FeatureKey & key,U2OpStatus & os)608 void SQLiteFeatureDbi::updateKeyValue(const U2DataId &featureId, const U2FeatureKey &key, U2OpStatus &os) {
609     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
610 
611     SQLiteWriteQuery q("UPDATE FeatureKey SET value = ?3 WHERE feature = ?1 AND name = ?2", db, os);
612     q.bindDataId(1, featureId);
613     q.bindString(2, key.name);
614     q.bindString(3, key.value);
615     q.execute();
616 }
617 
getKeyValue(const U2DataId & featureId,U2FeatureKey & key,U2OpStatus & os)618 bool SQLiteFeatureDbi::getKeyValue(const U2DataId &featureId, U2FeatureKey &key, U2OpStatus &os) {
619     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, false);
620 
621     static const QString queryString("SELECT value FROM FeatureKey WHERE feature = ?1 AND name = ?2");
622     SQLiteReadQuery q(queryString, db, os);
623     CHECK_OP(os, false);
624 
625     q.bindDataId(1, featureId);
626     q.bindString(2, key.name);
627 
628     if (q.step()) {
629         key.value = q.getCString(0);
630         return true;
631     } else {
632         return false;
633     }
634 }
635 
updateLocation(const U2DataId & featureId,const U2FeatureLocation & location,U2OpStatus & os)636 void SQLiteFeatureDbi::updateLocation(const U2DataId &featureId, const U2FeatureLocation &location, U2OpStatus &os) {
637     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
638 
639     SQLiteTransaction t(db, os);
640 
641     SQLiteWriteQuery qf("UPDATE Feature SET strand = ?1, start = ?2, len = ?3 WHERE id = ?4", db, os);
642     qf.bindInt32(1, location.strand.getDirectionValue());
643     qf.bindInt64(2, location.region.startPos);
644     qf.bindInt64(3, location.region.length);
645     qf.bindDataId(4, featureId);
646     qf.execute();
647     CHECK_OP(os, );
648 }
649 
updateType(const U2DataId & featureId,U2FeatureType newType,U2OpStatus & os)650 void SQLiteFeatureDbi::updateType(const U2DataId &featureId, U2FeatureType newType, U2OpStatus &os) {
651     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
652 
653     SQLiteWriteQuery qf("UPDATE Feature SET type = ?2 WHERE id = ?1", db, os);
654     qf.bindDataId(1, featureId);
655     qf.bindInt32(2, newType);
656     qf.execute();
657 }
658 
removeFeature(const U2DataId & featureId,U2OpStatus & os)659 void SQLiteFeatureDbi::removeFeature(const U2DataId &featureId, U2OpStatus &os) {
660     DBI_TYPE_CHECK(featureId, U2Type::Feature, os, );
661 
662     SQLiteTransaction t(db, os);
663 
664     SQLiteWriteQuery qk("DELETE FROM Feature WHERE id = ?1", db, os);
665     qk.bindDataId(1, featureId);
666     qk.execute();
667 }
668 
removeFeaturesByParent(const U2DataId & parentId,U2OpStatus & os,SubfeatureSelectionMode mode)669 void SQLiteFeatureDbi::removeFeaturesByParent(const U2DataId &parentId, U2OpStatus &os, SubfeatureSelectionMode mode) {
670     DBI_TYPE_CHECK(parentId, U2Type::Feature, os, );
671 
672     const bool includeParent = SelectParentFeature == mode;
673 
674     SQLiteTransaction t(db, os);
675 
676     SQLiteWriteQuery qf("DELETE FROM Feature WHERE parent = ?1" + (includeParent ? QString(" OR id = ?2") : ""), db, os);
677     qf.bindDataId(1, parentId);
678     if (includeParent) {
679         qf.bindDataId(2, parentId);
680     }
681     qf.execute();
682 }
683 
684 namespace {
685 
executeDeleteFeaturesByParentsQuery(const QList<U2DataId> & parentIds,DbRef * db,U2OpStatus & os)686 void executeDeleteFeaturesByParentsQuery(const QList<U2DataId> &parentIds, DbRef *db, U2OpStatus &os) {
687     SAFE_POINT(nullptr != db, "Invalid database handler", );
688 
689     QString idsList = "(";
690     for (int i = 1, n = parentIds.count(); i <= n; i++) {
691         idsList += QString("?%1,").arg(i);
692     }
693     idsList.chop(1);  // remove last comma
694     idsList += ")";
695 
696     SQLiteWriteQuery qf(QString("DELETE FROM Feature WHERE parent IN %1 OR id IN %1").arg(idsList), db, os);
697     for (int i = 1, n = parentIds.count(); i <= n; i++) {
698         qf.bindDataId(i, parentIds.at(i - 1));
699     }
700     qf.execute();
701 }
702 
703 }  // namespace
704 
removeFeaturesByParents(const QList<U2DataId> & parentIds,U2OpStatus & os)705 void SQLiteFeatureDbi::removeFeaturesByParents(const QList<U2DataId> &parentIds, U2OpStatus &os) {
706     SQLiteTransaction t(db, os);
707 
708     int parentsNumber = parentIds.count();
709     if (parentsNumber <= SQLiteDbi::BIND_PARAMETERS_LIMIT) {
710         executeDeleteFeaturesByParentsQuery(parentIds, db, os);
711     } else {
712         int deletedFeaturesNumber = 0;
713         while (parentsNumber - deletedFeaturesNumber > 0) {
714             int numDeletions = parentsNumber - deletedFeaturesNumber >= SQLiteDbi::BIND_PARAMETERS_LIMIT ? SQLiteDbi::BIND_PARAMETERS_LIMIT : -1;
715             const QList<U2DataId> copiedAnnotations = parentIds.mid(deletedFeaturesNumber, numDeletions);
716             executeDeleteFeaturesByParentsQuery(copiedAnnotations, db, os);
717             deletedFeaturesNumber += SQLiteDbi::BIND_PARAMETERS_LIMIT;
718         }
719     }
720 }
721 
removeFeaturesByRoot(const U2DataId & rootId,U2OpStatus & os,SubfeatureSelectionMode mode)722 void SQLiteFeatureDbi::removeFeaturesByRoot(const U2DataId &rootId, U2OpStatus &os, SubfeatureSelectionMode mode) {
723     DBI_TYPE_CHECK(rootId, U2Type::Feature, os, );
724 
725     const bool includeParent = SelectParentFeature == mode;
726 
727     SQLiteTransaction t(db, os);
728 
729     SQLiteWriteQuery("DROP TRIGGER FeatureDeletion", db, os).execute();
730     CHECK_OP(os, );
731 
732     SQLiteWriteQuery qf("DELETE FROM Feature WHERE root = ?1" + (includeParent ? QString(" OR id = ?2") : ""), db, os);
733     qf.bindDataId(1, rootId);
734     if (includeParent) {
735         qf.bindDataId(2, rootId);
736     }
737     qf.execute();
738     CHECK_OP(os, );
739 
740     SQLiteWriteQuery(getQueryForFeatureDeletionTrigger(), db, os).execute();
741 }
742 
getFeaturesByRegion(const U2Region & reg,const U2DataId & rootId,const QString & featureName,const U2DataId & seqId,U2OpStatus & os,bool contains)743 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeaturesByRegion(const U2Region &reg, const U2DataId &rootId, const QString &featureName, const U2DataId &seqId, U2OpStatus &os, bool contains) {
744     SQLiteTransaction t(db, os);
745 
746     const bool selectByRoot = !rootId.isEmpty();
747     const QString queryByRegion = "SELECT " + FDBI_FIELDS + " FROM Feature AS f WHERE " +
748                                   (selectByRoot ? QString("f.root = ?3 AND ") : QString()) + (contains ? "f.start >= ?1 AND f.start + f.len <= ?2" : "f.start <= ?2 AND f.start + f.len >= ?1");
749 
750     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryByRegion, db, os);
751 
752     q->bindInt64(1, reg.startPos);
753     q->bindInt64(2, reg.endPos() - 1);
754     if (selectByRoot) {
755         q->bindDataId(3, rootId);
756     }
757 
758     CHECK_OP(os, nullptr);
759     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), new SqlFeatureFilter(featureName, seqId), U2Feature(), os);
760 }
761 
getFeaturesBySequence(const QString & featureName,const U2DataId & seqId,U2OpStatus & os)762 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeaturesBySequence(const QString &featureName, const U2DataId &seqId, U2OpStatus &os) {
763     SQLiteTransaction t(db, os);
764     static const QString queryStringk("SELECT " + FDBI_FIELDS + " FROM Feature AS f "
765                                                                 "WHERE f.sequence = ?1 and f.name = ?2 ORDER BY f.start");
766     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
767 
768     q->bindDataId(1, seqId);
769     q->bindString(2, featureName);
770     CHECK_OP(os, nullptr);
771     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), nullptr, U2Feature(), os);
772 }
773 
getFeaturesByParent(const U2DataId & parentId,const QString & featureName,const U2DataId & seqId,U2OpStatus & os,SubfeatureSelectionMode mode)774 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeaturesByParent(const U2DataId &parentId, const QString &featureName, const U2DataId &seqId, U2OpStatus &os, SubfeatureSelectionMode mode) {
775     SQLiteTransaction t(db, os);
776     const bool includeParent = SelectParentFeature == mode;
777     const QString queryStringk("SELECT " + FDBI_FIELDS + " FROM Feature AS f "
778                                                          "WHERE f.parent = ?1" +
779                                (includeParent ? " OR f.id = ?2" : "") + " ORDER BY f.start");
780     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
781 
782     q->bindDataId(1, parentId);
783     if (includeParent) {
784         q->bindDataId(2, parentId);
785     }
786     CHECK_OP(os, nullptr);
787     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), new SqlFeatureFilter(featureName, seqId), U2Feature(), os);
788 }
789 
getFeaturesByRoot(const U2DataId & rootId,const FeatureFlags & types,U2OpStatus & os)790 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeaturesByRoot(const U2DataId &rootId, const FeatureFlags &types, U2OpStatus &os) {
791     SQLiteTransaction t(db, os);
792     const QString queryStringk("SELECT " + FDBI_FIELDS + " FROM Feature AS f "
793                                                          "WHERE f.root = ?1" +
794                                getWhereQueryPartFromType("f", types) + "ORDER BY f.start");
795     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
796 
797     q->bindDataId(1, rootId);
798     CHECK_OP(os, nullptr);
799     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), new SqlFeatureFilter(QString(), U2DataId()), U2Feature(), os);
800 }
801 
getFeaturesByName(const U2DataId & rootId,const QString & name,const FeatureFlags & types,U2OpStatus & os)802 U2DbiIterator<U2Feature> *SQLiteFeatureDbi::getFeaturesByName(const U2DataId &rootId, const QString &name, const FeatureFlags &types, U2OpStatus &os) {
803     SQLiteTransaction t(db, os);
804     const QString queryStringk("SELECT " + FDBI_FIELDS + " FROM Feature AS f "
805                                                          "WHERE f.root = ?1" +
806                                getWhereQueryPartFromType("f", types) + " AND nameHash = ?2 ORDER BY f.start");
807     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
808 
809     q->bindDataId(1, rootId);
810     q->bindInt32(2, qHash(name));
811     CHECK_OP(os, nullptr);
812     return new SQLiteResultSetIterator<U2Feature>(q, new SqlFeatureRSLoader(), new SqlFeatureFilter(QString(), U2DataId()), U2Feature(), os);
813 }
814 
getFeatureTable(const U2DataId & rootFeatureId,U2OpStatus & os)815 QList<FeatureAndKey> SQLiteFeatureDbi::getFeatureTable(const U2DataId &rootFeatureId, U2OpStatus &os) {
816     SQLiteTransaction t(db, os);
817     static const QString queryStringk("SELECT " + FDBI_FIELDS + ", fk.name, fk.value FROM Feature AS f "
818                                                                 "LEFT OUTER JOIN FeatureKey AS fk ON f.id = fk.feature WHERE f.root = ?1 ORDER BY f.class DESC, f.start, f.len");
819     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
820 
821     q->bindDataId(1, rootFeatureId);
822     QList<FeatureAndKey> result;
823     CHECK_OP(os, result);
824 
825     while (q->step()) {
826         FeatureAndKey fnk;
827         fnk.feature = SqlFeatureRSLoader::loadStatic(q.data());
828         fnk.key.name = q->getCString(10);
829         fnk.key.value = q->getCString(11);
830         result.append(fnk);
831     }
832     return result;
833 }
834 
getAnnotationTablesByFeatureKey(const QStringList & values,U2OpStatus & os)835 QMap<U2DataId, QStringList> SQLiteFeatureDbi::getAnnotationTablesByFeatureKey(const QStringList &values, U2OpStatus &os) {
836     SQLiteTransaction t(db, os);
837     QMap<U2DataId, QStringList> result;
838     CHECK(!values.isEmpty(), result);
839     // Pay attention here if there is the need of processing more search terms
840     CHECK_EXT(values.size() < SQLiteDbi::BIND_PARAMETERS_LIMIT, os.setError("Too many search terms provided"), result);
841 
842     QString queryStringk("SELECT DISTINCT A.object, F.name FROM AnnotationTable AS A, Feature AS F, FeatureKey AS FK "
843                          "WHERE A.rootId = F.root AND F.id = FK.feature ");
844 
845     for (int i = 1, n = values.size(); i <= n; ++i) {
846         queryStringk.append(QString("AND FK.value LIKE ?%1 ").arg(i));
847     }
848 
849     queryStringk.append("COLLATE NOCASE");
850 
851     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryStringk, db, os);
852 
853     for (int i = 1, n = values.size(); i <= n; ++i) {
854         q->bindString(i, QString("%%1%").arg(values[i - 1]));
855         CHECK_OP(os, result);
856     }
857 
858     while (q->step()) {
859         result[q->getDataId(0, U2Type::AnnotationTable)].append(q->getString(1));
860     }
861 
862     return result;
863 }
864 
865 }  // namespace U2
866