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 <cmath>
23 
24 #include <QBitArray>
25 #include <QFile>
26 
27 #include <U2Core/AppContext.h>
28 #include <U2Core/U2DbiRegistry.h>
29 #include <U2Core/U2ObjectDbi.h>
30 #include <U2Core/U2OpStatus.h>
31 #include <U2Core/U2OpStatusUtils.h>
32 #include <U2Core/U2SafePoints.h>
33 #include <U2Core/Version.h>
34 
35 #include "U2DbiUtils.h"
36 
37 namespace U2 {
38 
39 #define DB_ID_OFFSET 0
40 #define TYPE_OFFSET 8
41 #define DB_EXTRA_OFFSET 10
42 #define DATAID_MIN_LEN 10
43 
44 static U2DataId emptyId;
45 
46 const QString U2DbiUtils::PUBLIC_DATABASE_NAME = "UGENE public database";
47 const QString U2DbiUtils::PUBLIC_DATABASE_LOGIN = "public";
48 const QString U2DbiUtils::PUBLIC_DATABASE_PASSWORD = "public";
49 const QString U2DbiUtils::PUBLIC_DATABASE_URL = U2DbiUtils::createFullDbiUrl(PUBLIC_DATABASE_LOGIN, "db.ugene.net", 3306, "public_ugene_1_25");
50 
logNotSupported(U2DbiFeature f,U2Dbi * dbi,U2OpStatus & os)51 void U2DbiUtils::logNotSupported(U2DbiFeature f, U2Dbi *dbi, U2OpStatus &os) {
52     QString msg = tr("Feature is not supported: %1, dbi: %2").arg(int(f)).arg(dbi == nullptr ? QString("<unknown>") : dbi->getDbiId());
53     coreLog.error(msg);
54     if (!os.hasError()) {
55         os.setError(msg);
56     }
57 
58 #ifdef _DEBUG
59     if (dbi->getFeatures().contains(f)) {
60         coreLog.error("Calling not-supported method while features is listed in supported features list!");
61     }
62     assert(0);
63 #endif
64 }
65 
toRef(U2Dbi * dbi)66 U2DbiRef U2DbiUtils::toRef(U2Dbi *dbi) {
67     if (dbi == nullptr) {
68         return U2DbiRef();
69     }
70     return U2DbiRef(dbi->getFactoryId(), dbi->getDbiId());
71 }
72 
addLimit(QString & sql,qint64 offset,qint64 count)73 void U2DbiUtils::addLimit(QString &sql, qint64 offset, qint64 count) {
74     if (count == -1) {
75         return;
76     }
77     sql = sql + QString(" LIMIT %1, %2").arg(offset).arg(count).toLatin1();
78 }
79 
toU2DataId(qint64 id,U2DataType type,const QByteArray & dbExtra)80 U2DataId U2DbiUtils::toU2DataId(qint64 id, U2DataType type, const QByteArray &dbExtra) {
81     assert(sizeof(U2DataType) == 2);
82 
83     if (0 == id) {
84         return emptyId;
85     }
86 
87     int extraLen = dbExtra.size();
88     int len = DATAID_MIN_LEN + extraLen;
89     QByteArray res(len, Qt::Uninitialized);
90     char *data = res.data();
91     ((qint64 *)(data + DB_ID_OFFSET))[0] = id;
92     ((U2DataType *)(data + TYPE_OFFSET))[0] = type;
93     if (extraLen > 0) {
94         memcpy(data + DB_EXTRA_OFFSET, dbExtra.constData(), dbExtra.size());
95     }
96 
97     return res;
98 }
99 
toDbiId(const U2DataId & id)100 quint64 U2DbiUtils::toDbiId(const U2DataId &id) {
101     if (id.size() < DATAID_MIN_LEN) {
102         return 0;
103     }
104     return *(qint64 *)(id.constData() + DB_ID_OFFSET);
105 }
106 
toType(const U2DataId & id)107 U2DataType U2DbiUtils::toType(const U2DataId &id) {
108     if (id.size() < DATAID_MIN_LEN) {
109         return 0;
110     }
111     return *(U2DataType *)(id.constData() + TYPE_OFFSET);
112 }
113 
toDbExtra(const U2DataId & id)114 QByteArray U2DbiUtils::toDbExtra(const U2DataId &id) {
115     if (id.size() < DATAID_MIN_LEN) {
116         return emptyId;
117     }
118     return QByteArray(id.constData() + DB_EXTRA_OFFSET, id.length() - DB_EXTRA_OFFSET);
119 }
120 
text(const U2DataId & id)121 QString U2DbiUtils::text(const U2DataId &id) {
122     QString res = QString("[Id: %1, Type: %2, Extra: %3]").arg(toDbiId(id)).arg(int(toType(id))).arg(toDbExtra(id).constData());
123     return res;
124 }
125 
ref2Url(const U2DbiRef & dbiRef)126 QString U2DbiUtils::ref2Url(const U2DbiRef &dbiRef) {
127     U2DbiFactory *dbiFactory = AppContext::getDbiRegistry()->getDbiFactoryById(dbiRef.dbiFactoryId);
128     SAFE_POINT(nullptr != dbiFactory, QString("Invalid database type: %1").arg(dbiRef.dbiFactoryId), "");
129     return dbiFactory->id2Url(dbiRef.dbiId).getURLString();
130 }
131 
createDbiUrl(const QString & host,int port,const QString & dbName)132 QString U2DbiUtils::createDbiUrl(const QString &host, int port, const QString &dbName) {
133     QString portString = (port >= 0 ? QString::number(port) : "");
134     return host + ":" + portString + "/" + dbName;
135 }
136 
createFullDbiUrl(const QString & userName,const QString & host,int port,const QString & dbName)137 QString U2DbiUtils::createFullDbiUrl(const QString &userName, const QString &host, int port, const QString &dbName) {
138     return createFullDbiUrl(userName, createDbiUrl(host, port, dbName));
139 }
140 
createFullDbiUrl(const QString & userName,const QString & dbiUrl)141 QString U2DbiUtils::createFullDbiUrl(const QString &userName, const QString &dbiUrl) {
142     return userName + "@" + dbiUrl;
143 }
144 
parseDbiUrl(const QString & dbiUrl,QString & host,int & port,QString & dbName)145 bool U2DbiUtils::parseDbiUrl(const QString &dbiUrl, QString &host, int &port, QString &dbName) {
146     int sepIndex = dbiUrl.indexOf(":");
147     if (sepIndex < 0) {
148         return false;
149     }
150 
151     host = dbiUrl.left(sepIndex);
152 
153     sepIndex = dbiUrl.indexOf("/", sepIndex);
154     if (sepIndex < 0) {
155         return false;
156     }
157 
158     QString portString = dbiUrl.mid(host.length() + 1, sepIndex - host.length() - 1);
159     if (portString.isEmpty()) {
160         port = -1;
161     } else {
162         bool ok = false;
163         port = portString.toInt(&ok);
164         if (!ok) {
165             return false;
166         }
167     }
168 
169     dbName = dbiUrl.right(dbiUrl.length() - sepIndex - 1);
170 
171     return true;
172 }
173 
parseFullDbiUrl(const QString & dbiUrl,QString & userName,QString & host,int & port,QString & dbName)174 bool U2DbiUtils::parseFullDbiUrl(const QString &dbiUrl, QString &userName, QString &host, int &port, QString &dbName) {
175     return parseDbiUrl(full2shortDbiUrl(dbiUrl, userName), host, port, dbName);
176 }
177 
full2shortDbiUrl(const QString & fullDbiUrl)178 QString U2DbiUtils::full2shortDbiUrl(const QString &fullDbiUrl) {
179     QString unusedUserName;
180     return full2shortDbiUrl(fullDbiUrl, unusedUserName);
181 }
182 
full2shortDbiUrl(const QString & fullDbiUrl,QString & userName)183 QString U2DbiUtils::full2shortDbiUrl(const QString &fullDbiUrl, QString &userName) {
184     int sepIndex = fullDbiUrl.indexOf("@");
185     if (-1 == sepIndex) {
186         return fullDbiUrl;
187     }
188 
189     userName = fullDbiUrl.left(sepIndex);
190     return fullDbiUrl.right(fullDbiUrl.length() - sepIndex - 1);
191 }
192 
isFullDbiUrl(const QString & dbiUrl)193 bool U2DbiUtils::isFullDbiUrl(const QString &dbiUrl) {
194     return dbiUrl.contains('@');
195 }
196 
makeFolderCanonical(const QString & folder)197 QString U2DbiUtils::makeFolderCanonical(const QString &folder) {
198     if (U2ObjectDbi::ROOT_FOLDER == folder) {
199         return folder;
200     }
201 
202     QString result = folder.startsWith(U2ObjectDbi::ROOT_FOLDER + U2ObjectDbi::PATH_SEP) ? folder : U2ObjectDbi::ROOT_FOLDER + U2ObjectDbi::PATH_SEP + folder;
203     result.replace(QRegExp(U2ObjectDbi::PATH_SEP + "+"), U2ObjectDbi::PATH_SEP);
204 
205     if (U2ObjectDbi::ROOT_FOLDER != result &&
206         result.endsWith(U2ObjectDbi::ROOT_FOLDER)) {
207         result.chop(U2ObjectDbi::ROOT_FOLDER.size());
208     }
209 
210     return result;
211 }
212 
isDbiReadOnly(const U2DbiRef & dbiRef)213 bool U2DbiUtils::isDbiReadOnly(const U2DbiRef &dbiRef) {
214     U2OpStatusImpl os;
215     DbiConnection con(dbiRef, os);
216     CHECK_OP(os, true);
217 
218     return con.dbi->isReadOnly();
219 }
220 
getDbMinRequiredVersion(const U2DbiRef & dbiRef,U2OpStatus & os)221 Version U2DbiUtils::getDbMinRequiredVersion(const U2DbiRef &dbiRef, U2OpStatus &os) {
222     DbiConnection con(dbiRef, os);
223     CHECK_OP(os, Version());
224     return Version::parseVersion(con.dbi->getProperty(U2DbiOptions::APP_MIN_COMPATIBLE_VERSION, "", os));
225 }
226 
isDatabaseTooNew(const U2DbiRef & dbiRef,const Version & ugeneVersion,QString & minRequiredVersionString,U2OpStatus & os)227 bool U2DbiUtils::isDatabaseTooNew(const U2DbiRef &dbiRef, const Version &ugeneVersion, QString &minRequiredVersionString, U2OpStatus &os) {
228     const Version minRequiredVersion = getDbMinRequiredVersion(dbiRef, os);
229     CHECK_OP(os, false);
230     minRequiredVersionString = minRequiredVersion.text;
231     return minRequiredVersion > ugeneVersion;
232 }
233 
isDatabaseTooOld(const U2DbiRef & dbiRef,const Version & ugeneVersion,U2OpStatus & os)234 bool U2DbiUtils::isDatabaseTooOld(const U2DbiRef &dbiRef, const Version &ugeneVersion, U2OpStatus &os) {
235     const Version minRequiredVersion = getDbMinRequiredVersion(dbiRef, os);
236     CHECK_OP(os, false);
237     return minRequiredVersion < ugeneVersion;
238 }
239 
240 //////////////////////////////////////////////////////////////////////////
241 // TmpDbiHandle
242 
TmpDbiHandle()243 TmpDbiHandle::TmpDbiHandle() {
244 }
245 
TmpDbiHandle(const QString & _alias,U2OpStatus & os,const U2DbiFactoryId & factoryId)246 TmpDbiHandle::TmpDbiHandle(const QString &_alias, U2OpStatus &os, const U2DbiFactoryId &factoryId)
247     : alias(_alias) {
248     dbiRef = AppContext::getDbiRegistry()->attachTmpDbi(alias, os, factoryId);
249 }
250 
TmpDbiHandle(const TmpDbiHandle & dbiHandle)251 TmpDbiHandle::TmpDbiHandle(const TmpDbiHandle &dbiHandle) {
252     if (dbiHandle.isValid()) {
253         alias = dbiHandle.getAlias();
254         dbiRef = dbiHandle.getDbiRef();
255 
256         U2OpStatus2Log os;
257         AppContext::getDbiRegistry()->attachTmpDbi(dbiHandle.getAlias(), os, dbiRef.dbiFactoryId);
258     }
259 }
260 
operator =(const TmpDbiHandle & dbiHandle)261 TmpDbiHandle &TmpDbiHandle::operator=(const TmpDbiHandle &dbiHandle) {
262     if (this != &dbiHandle) {
263         if (dbiHandle.isValid()) {
264             alias = dbiHandle.getAlias();
265             dbiRef = dbiHandle.getDbiRef();
266 
267             U2OpStatus2Log os;
268             AppContext::getDbiRegistry()->attachTmpDbi(dbiHandle.getAlias(), os, dbiRef.dbiFactoryId);
269         }
270     }
271 
272     return *this;
273 }
274 
~TmpDbiHandle()275 TmpDbiHandle::~TmpDbiHandle() {
276     if (isValid()) {
277         U2OpStatus2Log os;
278         AppContext::getDbiRegistry()->detachTmpDbi(alias, os);
279     }
280 }
281 
282 //////////////////////////////////////////////////////////////////////////
283 // TmpDbiObjects
~TmpDbiObjects()284 TmpDbiObjects::~TmpDbiObjects() {
285     if (os.isCoR()) {
286         foreach (const U2DataId &id, objects) {
287             if (!id.isEmpty()) {
288                 U2OpStatus2Log os2log;
289                 DbiConnection con(dbiRef, os2log);
290                 con.dbi->getObjectDbi()->removeObject(id, os2log);
291             }
292         }
293     }
294 }
295 
DbiOperationsBlock(const U2DbiRef & _dbiRef,U2OpStatus & os)296 DbiOperationsBlock::DbiOperationsBlock(const U2DbiRef &_dbiRef, U2OpStatus &os)
297     : dbiRef(_dbiRef),
298       os(os) {
299     connection = new DbiConnection(dbiRef, os);
300     CHECK_OP(os, );
301     connection->dbi->startOperationsBlock(os);
302 }
303 
~DbiOperationsBlock()304 DbiOperationsBlock::~DbiOperationsBlock() {
305     if (nullptr != connection->dbi) {
306         connection->dbi->stopOperationBlock(os);
307     }
308     delete connection;
309 }
310 
311 }  // namespace U2
312