File indexing completed on 2024-04-28 04:50:50

0001 /*
0002  * sqlinterface.cpp
0003  *
0004  * Copyright (C) 2009-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include "log.h"
0022 
0023 #include <QAbstractItemModel>
0024 #include <QStringList>
0025 
0026 #include "sqlhelper.h"
0027 #include "sqlinterface.h"
0028 
0029 SqlInterface::SqlInterface() : createTable(false), hasPendingStatements(false),
0030     sqlColumnCount(0)
0031 {
0032     sqlHelper = SqlHelper::getInstance();
0033 }
0034 
0035 SqlInterface::~SqlInterface()
0036 {
0037     if (hasPendingStatements) {
0038         qCWarning(logSql, "Pending statements at destruction");
0039         /* data isn't valid anymore */
0040         pendingStatements.clear();
0041         createTable = false;
0042         /* make sure we don't get called after destruction */
0043         sqlHelper->collectSubmissions();
0044     }
0045 }
0046 
0047 void SqlInterface::sqlFlush()
0048 {
0049     if (hasPendingStatements) {
0050         sqlHelper->collectSubmissions();
0051     }
0052 }
0053 
0054 void SqlInterface::sqlInit(const QString &tableName, const QStringList &columnNames)
0055 {
0056     QString existsStatement = QLatin1String("SELECT name FROM sqlite_master WHERE name='") + tableName +
0057         QLatin1String("' AND type = 'table'");
0058     createStatement = QLatin1String("CREATE TABLE ") + tableName + QLatin1String(" (Id INTEGER PRIMARY KEY, ");
0059     QString selectStatement = QLatin1String("SELECT Id, ");
0060     insertStatement = QLatin1String("INSERT INTO ") + tableName + QLatin1String(" (Id, ");
0061     updateStatement = QLatin1String("UPDATE ") + tableName + QLatin1String(" SET ");
0062     deleteStatement = QLatin1String("DELETE FROM ") + tableName + QLatin1String(" WHERE Id = ?");
0063 
0064     sqlColumnCount = columnNames.size();
0065 
0066     for (int i = 0; i < sqlColumnCount; ++i) {
0067         if (i > 0) {
0068             createStatement.append(QLatin1String(", "));
0069             selectStatement.append(QLatin1String(", "));
0070             insertStatement.append(QLatin1String(", "));
0071             updateStatement.append(QLatin1String(" = ?, "));
0072         }
0073 
0074         const QString &columnName = columnNames.at(i);
0075         createStatement.append(columnName);
0076         selectStatement.append(columnName);
0077         insertStatement.append(columnName);
0078         updateStatement.append(columnName);
0079     }
0080 
0081     createStatement.append(QLatin1Char(')'));
0082     selectStatement.append(QLatin1String(" FROM "));
0083     selectStatement.append(tableName);
0084     insertStatement.append(QLatin1String(") VALUES (?"));
0085     updateStatement.append(QLatin1String(" = ? WHERE Id = ?"));
0086 
0087     for (int i = 0; i < sqlColumnCount; ++i) {
0088         insertStatement.append(QLatin1String(", ?"));
0089     }
0090 
0091     insertStatement.append(QLatin1Char(')'));
0092 
0093     if (!sqlHelper->exec(existsStatement).next()) {
0094         createTable = true;
0095         requestSubmission();
0096     } else {
0097         // queries can only be prepared if the table exists
0098         insertQuery = sqlHelper->prepare(insertStatement);
0099         updateQuery = sqlHelper->prepare(updateStatement);
0100         deleteQuery = sqlHelper->prepare(deleteStatement);
0101 
0102         for (QSqlQuery query = sqlHelper->exec(selectStatement); query.next();) {
0103             qint64 fullKey = query.value(0).toLongLong();
0104             SqlKey sqlKey(static_cast<int>(fullKey));
0105 
0106             if (!sqlKey.isSqlKeyValid() || (sqlKey.sqlKey != fullKey)) {
0107                 qCWarning(logSql, "Invalid key %Ld", fullKey);
0108                 continue;
0109             }
0110 
0111             if (!insertFromSqlQuery(sqlKey, query, 1)) {
0112                 pendingStatements.insert(sqlKey, Remove);
0113                 requestSubmission();
0114             }
0115         }
0116     }
0117 }
0118 
0119 void SqlInterface::sqlInsert(SqlKey key)
0120 {
0121     PendingStatement pendingStatement = pendingStatements.value(key, Nothing);
0122 
0123     switch (pendingStatement) {
0124     case Nothing:
0125         pendingStatements.insert(key, Insert);
0126         requestSubmission();
0127         return;
0128     case Remove:
0129         pendingStatements.insert(key, RemoveAndInsert);
0130         requestSubmission();
0131         return;
0132     case RemoveAndInsert:
0133     case Insert:
0134     case Update:
0135         break;
0136     }
0137 
0138     qCWarning(logSql, "Invalid pending statement '%d'", pendingStatement);
0139 }
0140 
0141 void SqlInterface::sqlUpdate(SqlKey key)
0142 {
0143     PendingStatement pendingStatement = pendingStatements.value(key, Nothing);
0144 
0145     switch (pendingStatement) {
0146     case Nothing:
0147         pendingStatements.insert(key, Update);
0148         requestSubmission();
0149         return;
0150     case RemoveAndInsert:
0151     case Insert:
0152     case Update:
0153         return;
0154     case Remove:
0155         break;
0156     }
0157 
0158     qCWarning(logSql, "Invalid pending statement '%d'", pendingStatement);
0159 }
0160 
0161 void SqlInterface::sqlRemove(SqlKey key)
0162 {
0163     PendingStatement pendingStatement = pendingStatements.value(key, Nothing);
0164 
0165     switch (pendingStatement) {
0166     case Nothing:
0167     case RemoveAndInsert:
0168     case Update:
0169         pendingStatements.insert(key, Remove);
0170         requestSubmission();
0171         return;
0172     case Insert:
0173         pendingStatements.remove(key);
0174         return;
0175     case Remove:
0176         break;
0177     }
0178 
0179     qCWarning(logSql, "Invalid pending statement %d", pendingStatement);
0180 }
0181 
0182 void SqlInterface::requestSubmission()
0183 {
0184     if (!hasPendingStatements) {
0185         hasPendingStatements = true;
0186         sqlHelper->requestSubmission(this);
0187     }
0188 }
0189 
0190 void SqlInterface::sqlSubmit()
0191 {
0192     if (createTable) {
0193         createTable = false;
0194         sqlHelper->exec(createStatement);
0195 
0196         // queries can only be prepared if the table exists
0197         insertQuery = sqlHelper->prepare(insertStatement);
0198         updateQuery = sqlHelper->prepare(updateStatement);
0199         deleteQuery = sqlHelper->prepare(deleteStatement);
0200     }
0201 
0202     for (QMap<SqlKey, PendingStatement>::ConstIterator it = pendingStatements.constBegin();
0203          it != pendingStatements.constEnd(); ++it) {
0204         PendingStatement pendingStatement = it.value();
0205 
0206         switch (pendingStatement) {
0207         case Nothing:
0208             break;
0209         case RemoveAndInsert:
0210             deleteQuery.bindValue(0, it.key().sqlKey);
0211             sqlHelper->exec(deleteQuery);
0212             // fall through
0213         case Insert:
0214             bindToSqlQuery(it.key(), insertQuery, 1);
0215             insertQuery.bindValue(0, it.key().sqlKey);
0216             sqlHelper->exec(insertQuery);
0217             continue;
0218         case Update:
0219             bindToSqlQuery(it.key(), updateQuery, 0);
0220             updateQuery.bindValue(sqlColumnCount, it.key().sqlKey);
0221             sqlHelper->exec(updateQuery);
0222             continue;
0223         case Remove:
0224             deleteQuery.bindValue(0, it.key().sqlKey);
0225             sqlHelper->exec(deleteQuery);
0226             continue;
0227         }
0228 
0229         qCWarning(logSql, "Invalid pending statement %d", pendingStatement);
0230     }
0231 
0232     pendingStatements.clear();
0233     hasPendingStatements = false;
0234 }