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 ®, 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