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 "MysqlUpgraderFrom_1_16_To_1_24.h"
23 
24 #include <U2Core/U2AttributeUtils.h>
25 
26 #include "mysql_dbi/MysqlDbi.h"
27 #include "mysql_dbi/MysqlObjectDbi.h"
28 #include "mysql_dbi/util/MysqlHelpers.h"
29 
30 namespace U2 {
31 
32 const QString MysqlUpgraderFrom_1_16_To_1_24::META_INFO_MARKER = "##";
33 const QString MysqlUpgraderFrom_1_16_To_1_24::HEADER_MARKER = "#";
34 const QString MysqlUpgraderFrom_1_16_To_1_24::COLUMN_SEPARATOR = "\t";
35 
MysqlUpgraderFrom_1_16_To_1_24(MysqlDbi * dbi)36 MysqlUpgraderFrom_1_16_To_1_24::MysqlUpgraderFrom_1_16_To_1_24(MysqlDbi *dbi)
37     : MysqlUpgrader(Version::parseVersion("1.16.0"), Version::parseVersion("1.24.0"), dbi) {
38 }
39 
upgrade(U2OpStatus & os) const40 void MysqlUpgraderFrom_1_16_To_1_24::upgrade(U2OpStatus &os) const {
41     MysqlTransaction t(dbi->getDbRef(), os);
42 
43     upgradeVariantDbi(os);
44     CHECK_OP(os, );
45 
46     dbi->setProperty(U2DbiOptions::APP_MIN_COMPATIBLE_VERSION, versionTo.text, os);
47 }
48 
upgradeVariantDbi(U2OpStatus & os) const49 void MysqlUpgraderFrom_1_16_To_1_24::upgradeVariantDbi(U2OpStatus &os) const {
50     coreLog.trace("Variant DBI upgrading");
51 
52     MysqlTransaction t(dbi->getDbRef(), os);
53 
54     QMap<U2DataId, QStringList> trackId2header;
55 
56     extractAttributes(os, trackId2header);
57     CHECK_OP(os, );
58 
59     repackInfo(os, trackId2header);
60     CHECK_OP(os, );
61 
62     updateScheme(os);
63 }
64 
65 namespace {
66 
convertInfo(const QString & additionalInfo,const QStringList & header)67 QString convertInfo(const QString &additionalInfo, const QStringList &header) {
68     StrStrMap convertedInfoMap;
69     CHECK(!additionalInfo.isEmpty(), QString());
70     QStringList splittedInfo = additionalInfo.split("\t", QString::SkipEmptyParts);
71     CHECK(!splittedInfo.isEmpty(), QString());
72 
73     convertedInfoMap.insert(U2Variant::VCF4_QUAL, splittedInfo.takeFirst());
74 
75     if (!splittedInfo.isEmpty()) {
76         convertedInfoMap.insert(U2Variant::VCF4_FILTER, splittedInfo.takeFirst());
77     }
78 
79     if (!splittedInfo.isEmpty()) {
80         convertedInfoMap.insert(U2Variant::VCF4_INFO, splittedInfo.takeFirst());
81     }
82 
83     static const int maxVcf4MandatoryColumnNumber = 7;  // VCF4 format supposes 8 mandatory columns
84     for (int i = maxVcf4MandatoryColumnNumber + 1; i < header.size(); i++) {
85         convertedInfoMap.insert(header[i], splittedInfo.isEmpty() ? "." : splittedInfo.takeFirst());
86     }
87 
88     if (!splittedInfo.isEmpty()) {
89         // There is no possibility to split the data correctly, because it was splitted by spaces not by tabulations
90         convertedInfoMap.insert(QString::number(qMax(maxVcf4MandatoryColumnNumber, header.size()) + 1), splittedInfo.join("\t"));
91     }
92 
93     return StrPackUtils::packMap(convertedInfoMap);
94 }
95 
96 }  // namespace
97 
repackInfo(U2OpStatus & os,const QMap<U2DataId,QStringList> & trackId2header) const98 void MysqlUpgraderFrom_1_16_To_1_24::repackInfo(U2OpStatus &os, const QMap<U2DataId, QStringList> &trackId2header) const {
99     coreLog.trace("Additional info repacking");
100 
101     MysqlTransaction t(dbi->getDbRef(), os);
102 
103     const qint64 variantsCount = U2SqlQuery("SELECT count(*) from Variant", dbi->getDbRef(), os).selectInt64();
104 
105     static QString getQueryString("SELECT id, track, additionalInfo FROM Variant");
106     static QString setQueryString("UPDATE Variant SET additionalInfo = :additionalInfo WHERE id = :id");
107     U2SqlQuery getQuery(getQueryString, dbi->getDbRef(), os);
108     U2SqlQuery setQuery(setQueryString, dbi->getDbRef(), os);
109 
110     QSet<U2DataId> trackIds;
111 
112     qint64 number = 0;
113     while (getQuery.step()) {
114         CHECK_OP(os, );
115         const qint64 dbiId = getQuery.getInt64(0);
116         const QString additionalInfo = getQuery.getString(2);
117         const U2DataId trackId = getQuery.getDataId(1, U2Type::VariantTrack);
118         trackIds << trackId;
119 
120         const QString convertedInfo = convertInfo(additionalInfo, trackId2header[trackId]);
121 
122         setQuery.bindString(":additionalInfo", convertedInfo);
123         setQuery.bindInt64(":id", dbiId);
124         setQuery.execute();
125         CHECK_OP(os, );
126 
127         number++;
128         if (number % 100 == 0) {
129             coreLog.trace(QString("Variants processed: %1/%2").arg(number).arg(variantsCount));
130         }
131     }
132 
133     if (number % 100 != 0) {
134         coreLog.trace(QString("Variants processed: %1/%2").arg(number).arg(variantsCount));
135     }
136 
137     number = 0;
138     foreach (const U2DataId &trackId, trackIds) {
139         MysqlObjectDbi::incrementVersion(trackId, dbi->getDbRef(), os);
140         CHECK_OP(os, );
141 
142         number++;
143         if (number % 10 == 0) {
144             coreLog.trace(QString("Object versions processed: %1/%2").arg(number).arg(trackIds.size()));
145         }
146     }
147 
148     if (number % 10 != 0) {
149         coreLog.trace(QString("Object versions processed: %1/%2").arg(number).arg(trackIds.size()));
150     }
151 }
152 
extractAttributes(U2OpStatus & os,QMap<U2DataId,QStringList> & trackId2header) const153 void MysqlUpgraderFrom_1_16_To_1_24::extractAttributes(U2OpStatus &os, QMap<U2DataId, QStringList> &trackId2header) const {
154     coreLog.trace("Attributes extracting");
155 
156     const qint64 tracksCount = U2SqlQuery("SELECT count(*) from VariantTrack", dbi->getDbRef(), os).selectInt64();
157     CHECK_OP(os, );
158 
159     QScopedPointer<U2DbiIterator<U2VariantTrack>> variantTracksIterator(dbi->getVariantDbi()->getVariantTracks(TrackType_All, os));
160     CHECK_OP(os, );
161 
162     qint64 number = 0;
163     while (variantTracksIterator->hasNext()) {
164         U2VariantTrack variantTrack = variantTracksIterator->next();
165         CHECK_OP(os, );
166 
167         QString metaInfo;
168         QStringList header;
169         splitFileHeader(variantTrack.fileHeader, metaInfo, header);
170 
171         trackId2header.insert(variantTrack.id, header);
172 
173         addStringAttribute(os, variantTrack, U2VariantTrack::META_INFO_ATTRIBUTE, metaInfo);
174         CHECK_OP(os, );
175         addStringAttribute(os, variantTrack, U2VariantTrack::HEADER_ATTRIBUTE, StrPackUtils::packStringList(header));
176         CHECK_OP(os, );
177 
178         number++;
179         if (number % 10 == 0) {
180             coreLog.trace(QString("Variant tracks processed: %1/%2").arg(number).arg(tracksCount));
181         }
182     }
183 
184     if (number % 10 != 0) {
185         coreLog.trace(QString("Variant tracks processed: %1/%2").arg(number).arg(tracksCount));
186     }
187 }
188 
updateScheme(U2OpStatus & os) const189 void MysqlUpgraderFrom_1_16_To_1_24::updateScheme(U2OpStatus &os) const {
190     coreLog.trace("Scheme updating");
191     U2SqlQuery("ALTER TABLE VariantTrack DROP COLUMN fileHeader;", dbi->getDbRef(), os).execute();
192 }
193 
addStringAttribute(U2OpStatus & os,const U2VariantTrack & variantTrack,const QString & attributeName,const QString & attributeValue) const194 void MysqlUpgraderFrom_1_16_To_1_24::addStringAttribute(U2OpStatus &os, const U2VariantTrack &variantTrack, const QString &attributeName, const QString &attributeValue) const {
195     CHECK(!attributeValue.isEmpty(), );
196     U2StringAttribute attribute;
197     U2AttributeUtils::init(attribute, variantTrack, attributeName);
198     attribute.value = attributeValue;
199     dbi->getAttributeDbi()->createStringAttribute(attribute, os);
200 }
201 
splitFileHeader(const QString & fileHeader,QString & metaInfo,QStringList & header)202 void MysqlUpgraderFrom_1_16_To_1_24::splitFileHeader(const QString &fileHeader, QString &metaInfo, QStringList &header) {
203     const QStringList lines = fileHeader.split(QRegExp("\\n\\r?"), QString::SkipEmptyParts);
204     foreach (const QString &line, lines) {
205         if (line.startsWith(META_INFO_MARKER)) {
206             metaInfo += line + "\n";
207         } else if (line.startsWith(HEADER_MARKER)) {
208             header = line.split(COLUMN_SEPARATOR);
209         }
210     }
211 }
212 
213 }  // namespace U2
214