/* vi: set sw=4 ts=4: * * Copyright (C) 2017 Christian Hohnstaedt. * * All rights reserved. */ #include #include #include #include "base.h" #include "sql.h" #include "settings.h" #include "XcaWarningCore.h" int DbTransaction::mutex; int DbTransaction::error; bool DbTransaction::hasTransaction; QList DbTransaction::items; quint64 DbTransaction::DatabaseStamp; void DbTransaction::debug(const char *func, const char *file, int line) { qDebug() << QString("%1(%2) Transaction: %3 Level %4, E:%5 ") .arg(file + QString(file).lastIndexOf("/") +1) .arg(line).arg(func).arg(mutex).arg(error); } DbTransaction::~DbTransaction() { if (has_begun) rollback("Destructor", 0); } bool DbTransaction::begin(const char *file, int line) { mutex++; has_begun = true; debug("Begin", file, line); if (mutex > 1 || !hasTransaction) return true; QSqlDatabase db = QSqlDatabase::database(); bool ret = db.transaction(); if (!ret) XCA_SQLERROR(db.lastError()); return ret; } bool DbTransaction::finish(const char *oper, const char *file, int line) { if (!has_begun) return true; if (mutex > 0) mutex--; else qCritical() << "Unbalanced DB Transaction in " << oper; debug(oper, file, line); has_begun = false; if (mutex > 0) return true; QSqlDatabase db = QSqlDatabase::database(); if (error) { error = 0; items.clear(); return hasTransaction ? db.rollback() : true; } mutex++; XSqlQuery q; SQL_PREPARE(q, "SELECT MAX(stamp) +1 from items"); q.exec(); if (q.first()) DatabaseStamp = q.value(0).toULongLong(); SQL_PREPARE(q, "UPDATE items SET stamp=? WHERE stamp=0"); q.bindValue(0, DatabaseStamp); q.exec(); SQL_PREPARE(q, "UPDATE items SET stamp=? WHERE id=?"); q.bindValue(0, DatabaseStamp); foreach(quint64 id, DbTransaction::items) { q.bindValue(1, id); q.exec(); } mutex--; items.clear(); bool ret = hasTransaction ? db.commit() : true; if (!ret) XCA_SQLERROR(db.lastError()); return ret; } bool DbTransaction::commit(const char *file, int line) { return finish("Commit", file, line); } bool DbTransaction::rollback(const char *file, int line) { error++; return finish("Rollback", file, line); } bool DbTransaction::done(QSqlError e, const char *file, int line) { return e.isValid() ? rollback(file, line) : commit(file, line); } QString XSqlQuery::table_prefix; int XSqlQuery::schemaVersion() { qDebug() << "table_prefix:" << table_prefix;; return QSqlDatabase::database().tables() .contains(table_prefix + "settings") ? Settings["schema"] : 0; } QString XSqlQuery::rewriteQuery(QString _q) { static const QStringList tables { "items" , "crls" , "private_keys" , "public_keys" , "tokens" , "token_mechanism" , "templates" , "certs" , "authority" , "revocations" , "requests" , "x509super" , "settings" , "view_public_keys" , "view_certs" , "view_requests" , "view_crls" , "view_templates" , "view_private", }; lastq = query = _q; if (table_prefix.isEmpty()) return query; QString m = tables.join("|") + "|i_" + tables.join("|i_"); m = QString("\\b(%1)").arg(m); query = query.replace(QRegularExpression(m), table_prefix + "\\1"); return query; } QString XSqlQuery::query_details() { QString lq = lastq; #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) QVariantList list = boundValues(); #else QVariantList list = boundValues().values(); #endif QStringList sl; if (query != lastq) { lq = QString("%1 (PREFIX[%2]: %3)").arg(lastq) .arg(table_prefix).arg(query); } for (int i = 0; i < list.size(); ++i) sl << list.at(i).toString(); if (sl.size()) lq += QString("[%1]").arg(sl.join(", ")); return QString("%1:%2 (%3)").arg(file).arg(line).arg(lq); } QSqlError XSqlQuery::lastError() { QSqlError e = QSqlQuery::lastError(); if (!e.isValid()) return e; return QSqlError(QString("%1 - %2").arg(e.driverText()) .arg(query_details()), e.databaseText(), e.type(), e.nativeErrorCode() ); } XSqlQuery::XSqlQuery() : QSqlQuery() { } XSqlQuery::XSqlQuery(QString q) : QSqlQuery() { exec(q); } bool XSqlQuery::exec(QString q) { q = rewriteQuery(q); file = ""; line = 0; return QSqlQuery::exec(q); } bool XSqlQuery::exec() { QString res; setForwardOnly(true); bool r = QSqlQuery::exec(); if (isSelect()) { res = QString("Rows selected: %1").arg(size()); } else { res = QString("Rows affected: %1").arg(numRowsAffected()); if (!DbTransaction::active()) { qCritical("########## MISSING Transaction in %s(%d)", file, line); } } qDebug() << QString("QUERY: %1 - %2").arg(query_details()).arg(res); return r; } bool XSqlQuery::prepare(QString q) { q = rewriteQuery(q); setForwardOnly(true); return QSqlQuery::prepare(q); } void XSqlQuery::location(const char *f, int l) { file = f + QString(f).lastIndexOf("/") +1; line = l; }