root/sqlconvert/main.cpp

Revision 373:4e43bfec5741, 10.1 KB (checked in by Dmitry Nezhevenko <dion@…>, 3 years ago)

Fix some issues with jids that are in CAPS

Line 
1/***************************************************************************
2 *   Copyright (C) 2009 by Dmitry Nezhevenko                               *
3 *   dion@inhex.net                                                        *
4 *                                                                         *
5 *   This program is free software; you can redistribute it and/or modify  *
6 *   it under the terms of the GNU General Public License as published by  *
7 *   the Free Software Foundation; either version 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 *                                                                         *
10 *   This program is distributed in the hope that it will be useful,       *
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13 *   GNU General Public License for more details.                          *
14 *                                                                         *
15 *   You should have received a copy of the GNU General Public License     *
16 *   along with this program; if not, write to the                         *
17 *   Free Software Foundation, Inc.,                                       *
18 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19 ***************************************************************************/
20#include <QCoreApplication>
21#include <QTextStream>
22#include <QStringList>
23#include <QSet>
24#include <QtSql>
25
26#error This is very ugly. Please read code first
27
28QTextStream stream(stdout);
29
30#define TARGET_PGSQL
31
32bool exec(QSqlDatabase& db, const QString& query)
33{
34        QSqlQuery q(db);
35        if (!q.exec(query)) {
36                stream << "Unable to execute: " << q.lastError().text() << endl;
37                return false;
38        }
39        return true;
40}
41
42
43int main(int argc, char *argv[])
44{
45        QStringList tables;
46        QStringList safeErrors;
47
48        safeErrors << "conference_alists_conference_id_key";
49        safeErrors << "insert or update on table \"conference_jids\" violates foreign key constraint \"conference_jids_conference_id_fkey\"";
50        safeErrors << "conference_nicks_jid_fkey";
51        safeErrors << "conference_nicks_conference_id_fkey";
52
53
54        QTextCodec::setCodecForLocale(QTextCodec::codecForName("utf8"));
55        QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
56
57        tables << "acl" << "aliases";
58        tables << "conferences";
59        tables << "conference_alists";
60        tables << "conference_jids";
61        tables << "conference_nicks";
62        tables << "webstatus";
63        tables << "words";
64        tables << "configuration";
65        tables << "configuration_fields";
66        tables << "roster";
67
68        QSet<QString> forcedBool;
69        forcedBool << "aliases.global"
70                << "conferences.autojoin" << "conferences.online" << "conferences.autoleave"
71                << "conference_alists.inv" << "conference_jids.temporary" << "conference_nicks.online";
72
73        QCoreApplication app(argc, argv);
74#ifdef CONVERT_DB
75        QSqlDatabase dbSrc = QSqlDatabase::addDatabase("QMYSQL", "src");
76        dbSrc.setHostName("localhost");
77        dbSrc.setDatabaseName("gluxi");
78        dbSrc.setUserName("root");
79        dbSrc.setPassword("root");
80
81        if (dbSrc.open()) {
82                stream << "Connected to source database" << endl;
83        } else {
84                stream << "Unable to open source database: " <<
85                                dbSrc.lastError().text() << endl;
86                return 1;
87        }
88#endif
89
90        QSqlDatabase dbDst = QSqlDatabase::addDatabase("QPSQL7", "dst");
91        dbDst.setHostName("localhost");
92        dbDst.setDatabaseName("gluxi");
93        dbDst.setUserName("gluxi");
94        dbDst.setPassword("gluxi");
95
96        if (dbDst.open()) {
97                stream << "Connected to destination database" << endl;
98        } else {
99                stream << "Unable to open destination database: " <<
100                dbDst.lastError().text() << endl;
101                return 1;
102        }
103
104#ifdef CONVERT_DB
105        for(QStringList::iterator it = tables.begin(); it != tables.end(); ++it) {
106                QString tbl = *it;
107                stream << "==> Converting: " << tbl << endl;
108
109
110                if (exec(dbDst, QString("TRUNCATE %1 CASCADE").arg(tbl)))
111                        stream << "Destination table truncated" << endl;
112                else
113                        return 1;
114
115
116                QSqlQuery countQuery(dbSrc);
117                if (!countQuery.exec(QString("SELECT COUNT(*) FROM %1").arg(tbl))
118                                || !countQuery.next()) {
119                        stream << "Unable to query record count: " << countQuery.lastError().text();
120                        return 1;
121                }
122
123                int recCount = countQuery.value(0).toInt();
124
125                stream << "Record count: " << recCount << endl;
126
127                int recNum = 0;
128                int numErr = 0;
129#if 0
130                while (recNum < recCount)
131                {
132#endif
133                        QSqlQuery srcQuery(dbSrc);
134                        srcQuery.setForwardOnly(true);
135                        if (!srcQuery.exec(QString("SELECT * FROM %1").arg(tbl))) {
136                                stream << "Unable to query records: " << srcQuery.lastError().text();
137                                return 1;
138                        }
139
140#ifdef TARGET_PGSQL
141                        bool hasSequence = false;
142#endif
143
144                        QSqlRecord rec = srcQuery.record();
145                        QString insertStr = QString ("INSERT INTO %1 (").arg(tbl);
146                        QString valueStr = "";
147                        for (int i = 0; i < rec.count(); ++i) {
148                                if (i != 0) {
149                                        insertStr += ", ";
150                                        valueStr += ", ";
151                                }
152                                insertStr += rec.fieldName(i);
153#ifdef TARGET_PGSQL
154                                if (rec.fieldName(i) == "id")
155                                        hasSequence = true;
156#endif
157                                valueStr += "?";
158                        }
159                        insertStr += ") VALUES (" + valueStr+ ")";
160
161                        stream << "" << insertStr << endl;
162
163                        QSqlQuery dstQuery(dbDst);
164#if 1
165                        if (!dstQuery.prepare(insertStr)) {
166                                stream << "Unable to prepare dst query" << endl;
167                                return 1;
168                        }
169#endif
170                        while (srcQuery.next()) {
171                                int cnt = srcQuery.record().count();
172                                for (int i = 0; i < cnt; ++i) {
173                                        QVariant v = srcQuery.value(i);
174                                        if (forcedBool.contains(QString("%1.%2").arg(tbl, srcQuery.record().fieldName(i)))) {
175                                                v = v.toInt() ? QVariant(true) : QVariant(false);
176                                        }
177                                        if (v.type()==QVariant::ByteArray)
178                                                v = QString(v.toByteArray());
179                                        dstQuery.bindValue(i, v);
180                                }
181#if 1
182                                if (!dstQuery.exec()) {
183                                        stream << "   record: " << recNum << endl;
184                                        QString errText = dstQuery.lastError().text();
185                                        bool safe = false;
186                                        foreach(QString safeErr, safeErrors) {
187                                                if (errText.contains(safeErr)) {
188                                                        safe = true;
189                                                        break;
190                                                }
191                                        }
192
193                                        stream << "==> Unable to exec insert: " << dstQuery.lastError().text() << endl;
194                                        int cnt = srcQuery.record().count();
195                                        for (int i = 0; i < cnt; ++i) {
196                                                QVariant v = srcQuery.value(i);
197                                                stream << "==> " << srcQuery.record().fieldName(i) << ": " << v.toString() << endl;
198                                        }
199                                        if (!safe) {
200                                                return 1;
201                                        } else {
202                                                ++numErr;
203                                        }
204                                }
205#endif
206                                stream << "  processed record " << recNum << endl;
207                                ++recNum;
208                        }
209#if 0
210                }
211#endif
212                stream << "Done, " << recNum << " records" << ", Errors: " << numErr << endl;
213
214#ifdef TARGET_PGSQL
215                if (hasSequence)
216                {
217                        stream << "Setting new value for sequence, tbl=" << tbl << endl;
218
219                        QSqlQuery maxValueQuery(dbDst);
220                        if (!maxValueQuery.exec(QString("SELECT max(id) FROM %1").arg(tbl))
221                                        || !maxValueQuery.next()) {
222                                stream << "Unable to get max value for SERIAL" << endl;
223                                return 1;
224                        }
225                        int curValue = maxValueQuery.value(0).toInt();
226                        stream << "... curValue=" << curValue << endl;
227                        ++curValue;
228                        if (!maxValueQuery.exec(QString(
229                                        "alter sequence %1_id_seq restart with %2").arg(tbl).arg(curValue)))
230                        {
231                                stream << "Unable to alter sequence value: " << maxValueQuery.lastError().text() << endl;
232                                return 1;
233                        }
234                }
235#endif
236        }
237#else
238
239        stream << "Removing duplicate JID records" << endl;
240        QSqlQuery badCaseQuery(dbDst);
241        if (!badCaseQuery.exec("select conference_id, jid from (select conference_id, jid, count(jid) as cnt from conference_jids group by conference_id, jid) as tbl where cnt > 1"))
242        {
243                stream << "Unable to query for bad case JIDs" << endl;
244                return 1;
245        }
246        while (badCaseQuery.next()) {
247                int conference_id = badCaseQuery.value(0).toInt();
248                QString jid = badCaseQuery.value(1).toString();
249                stream << "conference_id: " << conference_id << ", jid=" << jid << endl;
250
251                QSqlQuery bestJidQuery(dbDst);
252                bestJidQuery.prepare("select id from conference_jids where conference_id=? and jid=? order by created limit 1");
253                bestJidQuery.addBindValue(conference_id);
254                bestJidQuery.addBindValue(jid);
255                if (!bestJidQuery.exec() || !bestJidQuery.next()) {
256                        stream << "Unable to query for best jid" << endl;
257                        return 1;
258                }
259                int bestJidId = bestJidQuery.value(0).toInt();
260                stream << " best jid id: " << bestJidId << endl;
261                QSqlQuery fixQuery(dbDst);
262                fixQuery.prepare(
263                                "UPDATE conference_nicks set jid=? where conference_id=? and jid in ("
264                                "select id from conference_jids where conference_id=? and jid=?)");
265                fixQuery.addBindValue(bestJidId);
266                fixQuery.addBindValue(conference_id);
267                fixQuery.addBindValue(conference_id);
268                fixQuery.addBindValue(jid);
269                if (!fixQuery.exec()) {
270                        stream << "Unable to fix nicks" << endl;
271                        return 1;
272                }
273                stream << " fixed nicks: " << fixQuery.numRowsAffected() << endl;
274
275                fixQuery.prepare("DELETE from conference_jids where conference_id=? and jid=? and id<>?");
276                fixQuery.addBindValue(conference_id);
277                fixQuery.addBindValue(jid);
278                fixQuery.addBindValue(bestJidId);
279                if (!fixQuery.exec()) {
280                        stream << "Unable to remove extra jids" << endl;
281                        return 1;
282                }
283                stream << " removed jids: " << fixQuery.numRowsAffected() << endl;
284        }
285
286        stream << "Removing duplicate nick records" << endl;
287
288        QSqlQuery dublNickQuery(dbDst);
289        if (!dublNickQuery.exec("select * from (select conference_id, nick, jid, count(jid) from conference_nicks group by conference_id, nick, jid) as tbl where count > 1"))
290        {
291                stream << "Unable to query for bad case JIDs" << endl;
292                return 1;
293        }
294        while (dublNickQuery.next()) {
295                int conference_id = dublNickQuery.value(0).toInt();
296                QString nick = dublNickQuery.value(1).toString();
297                int jid_id = dublNickQuery.value(2).toInt();
298
299                stream << "conference_id: " << conference_id << ", nick:" << nick << ", jid_id:" << jid_id << endl;
300                QSqlQuery fixQuery(dbDst);
301                fixQuery.prepare(
302                                "delete from conference_nicks where conference_id=? and jid=? and nick=? and not id in ("
303                                "select id from conference_nicks where conference_id=? and jid=? and nick=? order by created LIMIT 1)");
304                fixQuery.addBindValue(conference_id);
305                fixQuery.addBindValue(jid_id);
306                fixQuery.addBindValue(nick);
307                fixQuery.addBindValue(conference_id);
308                fixQuery.addBindValue(jid_id);
309                fixQuery.addBindValue(nick);
310
311                if (!fixQuery.exec())
312                {
313                        stream << "Unable to remove duplicate nicks";
314                }
315                stream << " removed nicks: " << fixQuery.numRowsAffected() << endl;
316        }
317
318#endif
319}
Note: See TracBrowser for help on using the browser.