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