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 "SQLiteAttributeDbi.h"
23 
24 #include <U2Core/Timer.h>
25 #include <U2Core/U2SafePoints.h>
26 #include <U2Core/U2SqlHelpers.h>
27 
28 namespace U2 {
29 
SQLiteAttributeDbi(SQLiteDbi * dbi)30 SQLiteAttributeDbi::SQLiteAttributeDbi(SQLiteDbi *dbi)
31     : U2AttributeDbi(dbi), SQLiteChildDBICommon(dbi) {
32 }
33 
initSqlSchema(U2OpStatus & os)34 void SQLiteAttributeDbi::initSqlSchema(U2OpStatus &os) {
35     if (os.hasError()) {
36         return;
37     }
38 
39     // object attribute main table
40     // object -> object id this attribute is for
41     // child -> optional object id in case if this attribute shows relation between 2 objects
42     // otype, ctype -> object and child types
43     // oextra, cextra -> object and child db extra
44     // version -> object version is attribute is valid for
45     // name -> name of the attribute
46     SQLiteWriteQuery("CREATE TABLE Attribute (id INTEGER PRIMARY KEY AUTOINCREMENT, type INTEGER NOT NULL, "
47                      "object INTEGER, child INTEGER, otype INTEGER NOT NULL, ctype INTEGER, oextra BLOB NOT NULL, cextra BLOB, "
48                      "version INTEGER NOT NULL, name TEXT NOT NULL, "
49                      "FOREIGN KEY(object) REFERENCES Object(id) ON DELETE CASCADE)",
50                      db,
51                      os)
52         .execute();
53 
54     // TODO: check if index is efficient for getting attribute for specific object
55     SQLiteWriteQuery("CREATE INDEX Attribute_name on Attribute(name)", db, os).execute();
56     SQLiteWriteQuery("CREATE INDEX Attribute_object on Attribute(object)", db, os).execute();
57 
58     SQLiteWriteQuery("CREATE TABLE IntegerAttribute (attribute INTEGER, value INTEGER NOT NULL, "
59                      "FOREIGN KEY(attribute) REFERENCES Attribute(id) ON DELETE CASCADE)",
60                      db,
61                      os)
62         .execute();
63     SQLiteWriteQuery("CREATE INDEX IntegerAttribute_attribute on IntegerAttribute(attribute)", db, os).execute();
64 
65     SQLiteWriteQuery("CREATE TABLE RealAttribute (attribute INTEGER, value REAL NOT NULL, "
66                      "FOREIGN KEY(attribute) REFERENCES Attribute(id) ON DELETE CASCADE)",
67                      db,
68                      os)
69         .execute();
70     SQLiteWriteQuery("CREATE INDEX RealAttribute_attribute on RealAttribute(attribute)", db, os).execute();
71 
72     SQLiteWriteQuery("CREATE TABLE StringAttribute (attribute INTEGER, value TEXT NOT NULL, "
73                      "FOREIGN KEY(attribute) REFERENCES Attribute(id) ON DELETE CASCADE)",
74                      db,
75                      os)
76         .execute();
77     SQLiteWriteQuery("CREATE INDEX StringAttribute_attribute on StringAttribute(attribute)", db, os).execute();
78 
79     SQLiteWriteQuery("CREATE TABLE ByteArrayAttribute (attribute INTEGER, value BLOB NOT NULL, "
80                      "FOREIGN KEY(attribute) REFERENCES Attribute(id) ON DELETE CASCADE)",
81                      db,
82                      os)
83         .execute();
84     SQLiteWriteQuery("CREATE INDEX ByteArrayAttribute_attribute on ByteArrayAttribute(attribute)", db, os).execute();
85 }
86 
87 /** Returns all attribute names available in the database */
getAvailableAttributeNames(U2OpStatus & os)88 QStringList SQLiteAttributeDbi::getAvailableAttributeNames(U2OpStatus &os) {
89     return SQLiteReadQuery("SELECT DISTINCT name FROM Attribute", db, os).selectStrings();
90 }
91 
92 /** Returns all attribute ids for the given object */
getObjectAttributes(const U2DataId & objectId,const QString & name,U2OpStatus & os)93 QList<U2DataId> SQLiteAttributeDbi::getObjectAttributes(const U2DataId &objectId, const QString &name, U2OpStatus &os) {
94     if (name.isEmpty()) {
95         static const QString queryString("SELECT id, type, '' FROM Attribute WHERE object = ?1 ORDER BY id");
96         SQLiteReadQuery q(queryString, db, os);
97         q.bindDataId(1, objectId);
98         return q.selectDataIdsExt();
99     }
100     static const QString queryString("SELECT id, type, '' FROM Attribute WHERE object = ?1 AND name = ?2 ORDER BY id");
101     SQLiteReadQuery q(queryString, db, os);
102     q.bindDataId(1, objectId);
103     q.bindString(2, name);
104     return q.selectDataIdsExt();
105 }
106 
107 /** Returns all attribute ids for the given object */
getObjectPairAttributes(const U2DataId & objectId,const U2DataId & childId,const QString & name,U2OpStatus & os)108 QList<U2DataId> SQLiteAttributeDbi::getObjectPairAttributes(const U2DataId &objectId, const U2DataId &childId, const QString &name, U2OpStatus &os) {
109     if (name.isEmpty()) {
110         static const QString queryString("SELECT id, type, '' FROM Attribute WHERE object = ?1 AND child = ?2 ORDER BY id");
111         SQLiteReadQuery q(queryString, db, os);
112         q.bindDataId(1, objectId);
113         q.bindDataId(2, childId);
114         return q.selectDataIdsExt();
115     }
116     static const QString queryString("SELECT id, type, '' FROM Attribute WHERE object = ?1 AND child = ?2 AND name = ?3 ORDER BY id");
117     SQLiteReadQuery q(queryString, db, os);
118     q.bindDataId(1, objectId);
119     q.bindDataId(2, childId);
120     q.bindString(3, name);
121     return q.selectDataIdsExt();
122 }
123 
124 /** Loads int64 attribute by id */
getIntegerAttribute(const U2DataId & attributeId,U2OpStatus & os)125 U2IntegerAttribute SQLiteAttributeDbi::getIntegerAttribute(const U2DataId &attributeId, U2OpStatus &os) {
126     static const QString queryString(buildSelectAttributeQuery("IntegerAttribute"));
127     SQLiteReadQuery q(queryString, db, os);
128     q.bindDataId(1, attributeId);
129     U2IntegerAttribute res;
130     readAttribute(q, res);
131     res.value = q.getInt64(0);
132     q.ensureDone();
133     return res;
134 }
135 
136 /** Loads real64 attribute by id */
getRealAttribute(const U2DataId & attributeId,U2OpStatus & os)137 U2RealAttribute SQLiteAttributeDbi::getRealAttribute(const U2DataId &attributeId, U2OpStatus &os) {
138     static const QString queryString(buildSelectAttributeQuery("RealAttribute"));
139     SQLiteReadQuery q(queryString, db, os);
140     q.bindDataId(1, attributeId);
141     U2RealAttribute res;
142     readAttribute(q, res);
143     res.value = q.getDouble(0);
144     q.ensureDone();
145     return res;
146 }
147 
148 /** Loads String attribute by id */
getStringAttribute(const U2DataId & attributeId,U2OpStatus & os)149 U2StringAttribute SQLiteAttributeDbi::getStringAttribute(const U2DataId &attributeId, U2OpStatus &os) {
150     static const QString queryString(buildSelectAttributeQuery("StringAttribute"));
151     SQLiteReadQuery q(queryString, db, os);
152     q.bindDataId(1, attributeId);
153     U2StringAttribute res;
154     readAttribute(q, res);
155     res.value = q.getString(0);
156     q.ensureDone();
157     return res;
158 }
159 
160 /** Loads byte attribute by id */
getByteArrayAttribute(const U2DataId & attributeId,U2OpStatus & os)161 U2ByteArrayAttribute SQLiteAttributeDbi::getByteArrayAttribute(const U2DataId &attributeId, U2OpStatus &os) {
162     static const QString queryString(buildSelectAttributeQuery("ByteArrayAttribute"));
163     SQLiteReadQuery q(queryString, db, os);
164     q.bindDataId(1, attributeId);
165     U2ByteArrayAttribute res;
166     readAttribute(q, res);
167     res.value = q.getBlob(0);
168     q.ensureDone();
169     return res;
170 }
171 
buildSelectAttributeQuery(const QString & attributeTable)172 QString SQLiteAttributeDbi::buildSelectAttributeQuery(const QString &attributeTable) {
173     return "SELECT t.value, a.id, a.type, '', a.object, a.otype, a.oextra, a.child, a.ctype, a.cextra, a.version, a.name "
174            " FROM Attribute AS a, " +
175            attributeTable + " AS t WHERE a.id = ?1 AND t.attribute = a.id";
176 }
177 
readAttribute(SQLiteReadQuery & q,U2Attribute & attr)178 void SQLiteAttributeDbi::readAttribute(SQLiteReadQuery &q, U2Attribute &attr) {
179     if (q.hasError()) {
180         return;
181     }
182     if (!q.step()) {
183         coreLog.details(QString("Query %1 can't execute: required attribute not found in sqlite attribute database").arg(q.getQueryText()));
184         return;
185     }
186     attr.id = q.getDataIdExt(1);
187     attr.objectId = q.getDataIdExt(4);
188     attr.childId = q.getDataIdExt(7);
189     attr.version = q.getInt64(10);
190     attr.name = q.getString(11);
191 }
192 
193 /** Sorts all objects in database according to U2DbiSortConfig provided  */
sort(const U2DbiSortConfig &,qint64,qint64,U2OpStatus & os)194 QList<U2DataId> SQLiteAttributeDbi::sort(const U2DbiSortConfig &, qint64, qint64, U2OpStatus &os) {
195     QList<U2DataId> res;
196     os.setError("not implemented");
197     return res;
198 }
199 
removeAttribute(SQLiteQuery * q,const U2DataId & id)200 static void removeAttribute(SQLiteQuery *q, const U2DataId &id) {
201     q->reset();
202     q->bindDataId(1, id);
203     q->execute();
204 }
205 /**
206 Removes attribute from database
207 Requires U2DbiFeature_WriteAttribute feature support
208 */
removeAttributes(const QList<U2DataId> & attributeIds,U2OpStatus & os)209 void SQLiteAttributeDbi::removeAttributes(const QList<U2DataId> &attributeIds, U2OpStatus &os) {
210     SQLiteTransaction t(db, os);
211     static const QString qString("DELETE FROM Attribute WHERE id = ?1");
212     static const QString qiString("DELETE FROM IntegerAttribute WHERE attribute = ?1");
213     static const QString qrString("DELETE FROM RealAttribute WHERE attribute = ?1");
214     static const QString qsString("DELETE FROM StringAttribute WHERE attribute = ?1");
215     static const QString qbString("DELETE FROM ByteArrayAttribute WHERE attribute = ?1");
216     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(qString, db, os);
217     QSharedPointer<SQLiteQuery> qi = t.getPreparedQuery(qiString, db, os);
218     QSharedPointer<SQLiteQuery> qr = t.getPreparedQuery(qrString, db, os);
219     QSharedPointer<SQLiteQuery> qs = t.getPreparedQuery(qsString, db, os);
220     QSharedPointer<SQLiteQuery> qb = t.getPreparedQuery(qbString, db, os);
221     foreach (const U2DataId &id, attributeIds) {
222         U2DataType type = U2DbiUtils::toType(id);
223         switch (type) {
224             case U2Type::AttributeInteger:
225                 removeAttribute(qi.data(), id);
226                 break;
227             case U2Type::AttributeReal:
228                 removeAttribute(qr.data(), id);
229                 break;
230             case U2Type::AttributeString:
231                 removeAttribute(qs.data(), id);
232                 break;
233             case U2Type::AttributeByteArray:
234                 removeAttribute(qb.data(), id);
235                 break;
236             default:
237                 os.setError(U2DbiL10n::tr("Unsupported attribute type: %1").arg(type));
238                 break;
239         }
240         CHECK_OP(os, );
241 
242         q->bindDataId(1, id);
243         q->execute();
244         q->reset();
245         CHECK_OP(os, );
246     }
247 }
248 
removeObjectAttributes(const U2DataId & objectId,U2OpStatus & os)249 void SQLiteAttributeDbi::removeObjectAttributes(const U2DataId &objectId, U2OpStatus &os) {
250     SQLiteTransaction t(db, os);
251     QList<U2DataId> attributes = getObjectAttributes(objectId, "", os);
252     if (!attributes.isEmpty()) {
253         removeAttributes(attributes, os);
254     }
255 }
256 
createAttribute(U2Attribute & attr,U2DataType type,SQLiteTransaction & t,U2OpStatus & os)257 qint64 SQLiteAttributeDbi::createAttribute(U2Attribute &attr, U2DataType type, SQLiteTransaction &t, U2OpStatus &os) {
258     static const QString queryString("INSERT INTO Attribute(type, object, child, otype, ctype, oextra, cextra, version, name) "
259                                      " VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)");
260     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
261     CHECK_OP(os, -1);
262 
263     q->bindType(1, type);
264     q->bindDataId(2, attr.objectId);
265     q->bindDataId(3, attr.childId);
266     q->bindType(4, U2DbiUtils::toType(attr.objectId));
267     q->bindType(5, U2DbiUtils::toType(attr.childId));
268     q->bindBlob(6, U2DbiUtils::toDbExtra(attr.objectId));
269     q->bindBlob(7, U2DbiUtils::toDbExtra(attr.childId));
270     q->bindInt64(8, attr.version);
271     q->bindString(9, attr.name);
272 
273     return q->insert();
274 }
275 
276 /**
277 Creates int64 attribute in database. ObjectId must be already set in attribute and present in the same database
278 Requires U2DbiFeature_WriteAttribute feature support
279 */
createIntegerAttribute(U2IntegerAttribute & a,U2OpStatus & os)280 void SQLiteAttributeDbi::createIntegerAttribute(U2IntegerAttribute &a, U2OpStatus &os) {
281     SQLiteTransaction t(db, os);
282     qint64 id = createAttribute(a, U2Type::AttributeInteger, t, os);
283     if (os.hasError()) {
284         return;
285     }
286     a.id = U2DbiUtils::toU2DataId(id, U2Type::AttributeInteger);
287 
288     static const QString queryString("INSERT INTO IntegerAttribute(attribute, value) VALUES(?1, ?2)");
289     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
290     CHECK_OP(os, );
291     q->bindInt64(1, id);
292     q->bindInt64(2, a.value);
293     q->execute();
294 }
295 
296 /**
297 Creates real64 attribute in database. ObjectId must be already set in attribute and present in the same database
298 Requires U2DbiFeature_WriteAttribute feature support
299 */
createRealAttribute(U2RealAttribute & a,U2OpStatus & os)300 void SQLiteAttributeDbi::createRealAttribute(U2RealAttribute &a, U2OpStatus &os) {
301     SQLiteTransaction t(db, os);
302     qint64 id = createAttribute(a, U2Type::AttributeReal, t, os);
303     if (os.hasError()) {
304         return;
305     }
306     a.id = U2DbiUtils::toU2DataId(id, U2Type::AttributeReal);
307 
308     static const QString queryString("INSERT INTO RealAttribute(attribute, value) VALUES(?1, ?2)");
309     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
310     CHECK_OP(os, );
311     q->bindInt64(1, id);
312     q->bindDouble(2, a.value);
313     q->execute();
314 }
315 
316 /**
317 Creates String attribute in database. ObjectId must be already set in attribute and present in the same database
318 Requires U2DbiFeature_WriteAttribute feature support
319 */
createStringAttribute(U2StringAttribute & a,U2OpStatus & os)320 void SQLiteAttributeDbi::createStringAttribute(U2StringAttribute &a, U2OpStatus &os) {
321     SQLiteTransaction t(db, os);
322     qint64 id = createAttribute(a, U2Type::AttributeString, t, os);
323     if (os.hasError()) {
324         return;
325     }
326     a.id = U2DbiUtils::toU2DataId(id, U2Type::AttributeString);
327 
328     static const QString queryString("INSERT INTO StringAttribute(attribute, value) VALUES(?1, ?2)");
329     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
330     CHECK_OP(os, );
331     q->bindInt64(1, id);
332     q->bindString(2, a.value);
333     q->execute();
334 }
335 
336 /**
337 Creates Byte attribute in database. ObjectId must be already set in attribute and present in the same database
338 Requires U2DbiFeature_WriteAttribute feature support
339 */
createByteArrayAttribute(U2ByteArrayAttribute & a,U2OpStatus & os)340 void SQLiteAttributeDbi::createByteArrayAttribute(U2ByteArrayAttribute &a, U2OpStatus &os) {
341     SQLiteTransaction t(db, os);
342     qint64 id = createAttribute(a, U2Type::AttributeByteArray, t, os);
343     if (os.hasError()) {
344         return;
345     }
346     a.id = U2DbiUtils::toU2DataId(id, U2Type::AttributeByteArray);
347 
348     static const QString queryString("INSERT INTO ByteArrayAttribute(attribute, value) VALUES(?1, ?2)");
349     QSharedPointer<SQLiteQuery> q = t.getPreparedQuery(queryString, db, os);
350     CHECK_OP(os, );
351     q->bindInt64(1, id);
352     q->bindBlob(2, a.value, false);
353     q->execute();
354 }
355 
356 }  // namespace U2
357