Commit 450701d8 authored by Jan Koester's avatar Jan Koester
Browse files

recoonect db

parent 72690fcf
Loading
Loading
Loading
Loading
+74 −60
Original line number Diff line number Diff line
@@ -71,6 +71,20 @@ webedit::Database::Database(const std::string &driver, const std::string &connec
webedit::Database::~Database() {
}

int webedit::Database::execWithRetry(const dbpp::SQL &sql, dbpp::DBResult &res) {
    try {
        return _db->exec(sql, res);
    } catch (const std::exception &e) {
        if (_db->isConnected()) {
            throw;
        }
        std::cerr << "webedit::Database: DB connection lost (" << e.what()
                  << "), reconnecting..." << std::endl;
        _db->reset();
        return _db->exec(sql, res);
    }
}

void webedit::Database::initTables() {
    dbpp::SQL sql;
    dbpp::DBResult res;
@@ -91,7 +105,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -103,7 +117,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -115,7 +129,7 @@ void webedit::Database::initTables() {
        <<   "FOREIGN KEY (sessionid) REFERENCES session (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -132,7 +146,7 @@ void webedit::Database::initTables() {
        <<   "FOREIGN KEY (document_id) REFERENCES documents (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -144,7 +158,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -161,7 +175,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -173,7 +187,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (owner_uid)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -190,7 +204,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -207,7 +221,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -224,7 +238,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -241,7 +255,7 @@ void webedit::Database::initTables() {
        <<   "PRIMARY KEY (id)"
        << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    // Schema migrations — add columns introduced after initial release.
    // ALTER TABLE ADD COLUMN fails if the column already exists; swallow those errors.
@@ -249,7 +263,7 @@ void webedit::Database::initTables() {
        try {
            sql.clear(); res.clear();
            sql << stmt;
            _db->exec(sql, res);
            execWithRetry(sql, res);
        } catch (...) {}
    };

@@ -269,7 +283,7 @@ void webedit::Database::initTables() {
        try {
            sql.clear(); res.clear();
            sql << stmt;
            _db->exec(sql, res);
            execWithRetry(sql, res);
        } catch (...) {}
    };

@@ -301,7 +315,7 @@ std::string webedit::Database::getOption(const std::string &key,
    sql << "SELECT value FROM options WHERE " << dbpp::quoteIdentifier(*_db, "key") << "='"
        << dbpp::SQL::escaped(escapedKey, key.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1)
        return defaultVal;
    return res[0][0];
@@ -316,7 +330,7 @@ void webedit::Database::setOption(const std::string &key, const std::string &val
    sql << "SELECT value FROM options WHERE " << keyColumn << "='"
        << dbpp::SQL::escaped(escapedKey, key.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    sql.clear();
    res.clear();
    escapedKey.clear();
@@ -334,7 +348,7 @@ void webedit::Database::setOption(const std::string &key, const std::string &val
            << "' WHERE " << keyColumn << "='"
            << dbpp::SQL::escaped(escapedKey, key.c_str()) << "';";
    }
    _db->exec(sql, res);
    execWithRetry(sql, res);
}

std::string webedit::Database::saveDocument(const std::string &ownerUid,
@@ -357,7 +371,7 @@ std::string webedit::Database::saveDocument(const std::string &ownerUid,
        << dbpp::SQL::escaped(escapedName, name.c_str()) << "','"
        << dbpp::SQL::escaped(escapedXml, xml.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    // Create initial revision (revision 1)
    uuid::uuid revId;
@@ -375,7 +389,7 @@ std::string webedit::Database::saveDocument(const std::string &ownerUid,
        << dbpp::SQL::escaped(escapedXml, xml.c_str()) << "','"
        << dbpp::SQL::escaped(escapedAuthor, author.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    return docId.c_str();
}
@@ -399,7 +413,7 @@ std::string webedit::Database::updateDocument(const std::string &domain,
        << "' AND domain='"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    // Get next revision number
    sql.clear();
@@ -409,7 +423,7 @@ std::string webedit::Database::updateDocument(const std::string &domain,
    sql << "SELECT COALESCE(MAX(revision),0) FROM document_revisions WHERE document_id='"
        << dbpp::SQL::escaped(escapedId, id.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    int nextRev = 1;
    if (rows > 0) {
        try { nextRev = std::stoi(res[0][0]) + 1; } catch (...) {}
@@ -433,7 +447,7 @@ std::string webedit::Database::updateDocument(const std::string &domain,
        << dbpp::SQL::escaped(escapedXml, xml.c_str()) << "','"
        << dbpp::SQL::escaped(escapedAuthor, author.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    return id;
}
@@ -451,7 +465,7 @@ bool webedit::Database::loadDocument(const std::string &domain,
        << "' AND domain='"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1)
        return false;

@@ -473,7 +487,7 @@ bool webedit::Database::deleteDocument(const std::string &domain,
        << "' AND domain='"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1)
        return false;

@@ -484,7 +498,7 @@ bool webedit::Database::deleteDocument(const std::string &domain,
    // Delete revisions first
    sql << "DELETE FROM document_revisions WHERE document_id='"
        << dbpp::SQL::escaped(escapedId, id.c_str()) << "';";
    _db->exec(sql, res);
    execWithRetry(sql, res);

    sql.clear();
    res.clear();
@@ -496,7 +510,7 @@ bool webedit::Database::deleteDocument(const std::string &domain,
        << "' AND domain='"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -509,7 +523,7 @@ std::string webedit::Database::listDocuments(const std::string &domain) {
        << dbpp::SQL::escaped(escapedDomain, domain.c_str())
        << "' ORDER BY group_name, name, modified DESC;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);

    json_object *arr = json_object_new_array();

@@ -542,7 +556,7 @@ bool webedit::Database::findDocumentIdByNameGroup(const std::string &domain,
        << "' AND group_name='" << dbpp::SQL::escaped(escapedGroup, group.c_str())
        << "' ORDER BY modified DESC LIMIT 1;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) {
        docId.clear();
        return false;
@@ -561,7 +575,7 @@ std::string webedit::Database::listRevisions(const std::string &docId) {
        << dbpp::SQL::escaped(escapedId, docId.c_str())
        << "' ORDER BY revision DESC;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);

    json_object *arr = json_object_new_array();

@@ -589,7 +603,7 @@ bool webedit::Database::loadRevision(const std::string &revisionId,
    sql << "SELECT name, xml FROM document_revisions WHERE id='"
        << dbpp::SQL::escaped(escapedId, revisionId.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1)
        return false;

@@ -633,7 +647,7 @@ std::string webedit::Database::saveConnection(const std::string &ownerUid,
        << dbpp::SQL::escaped(eUrl, url.c_str()) << "',"
        << (ignoreSsl ? 1 : 0) << ");";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return connId.c_str();
}

@@ -655,7 +669,7 @@ std::string webedit::Database::updateConnection(const std::string &domain,
        << dbpp::SQL::escaped(eId, id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return id;
}

@@ -669,7 +683,7 @@ bool webedit::Database::deleteConnection(const std::string &domain,
        << dbpp::SQL::escaped(eId, id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -683,7 +697,7 @@ std::string webedit::Database::listConnections(const std::string &domain) {
        << dbpp::SQL::escaped(eDomain, domain.c_str())
        << "' ORDER BY group_name, name;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);

    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
@@ -715,7 +729,7 @@ bool webedit::Database::getConnection(const std::string &domain,
        << dbpp::SQL::escaped(eId, id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) return false;

    name = res[0][0];
@@ -747,7 +761,7 @@ std::string webedit::Database::createApiToken(const std::string &ownerUid,
        << dbpp::SQL::escaped(eLabel, label.c_str()) << "','"
        << dbpp::SQL::escaped(eHash, tokenHash.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return id.c_str();
}

@@ -760,7 +774,7 @@ bool webedit::Database::deleteApiToken(const std::string &ownerUid, const std::s
        << dbpp::SQL::escaped(eId, id.c_str()) << "' AND owner_uid='"
        << dbpp::SQL::escaped(eOwner, ownerUid.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -773,7 +787,7 @@ std::string webedit::Database::listApiTokens(const std::string &ownerUid) {
        << dbpp::SQL::escaped(eOwner, ownerUid.c_str())
        << "' ORDER BY created DESC;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);

    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
@@ -802,7 +816,7 @@ bool webedit::Database::findTokenOwner(const std::string &tokenHash, std::string
    sql << "SELECT id, owner_uid, domain, authid FROM api_tokens WHERE token_hash='"
        << dbpp::SQL::escaped(eHash, tokenHash.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1)
        return false;

@@ -821,7 +835,7 @@ void webedit::Database::touchApiTokenLastUsed(const std::string &tokenId) {
    sql << "UPDATE api_tokens SET last_used=CURRENT_TIMESTAMP WHERE id='"
        << dbpp::SQL::escaped(eId, tokenId.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
}

// --- AI Chat Messages ---
@@ -838,14 +852,14 @@ void webedit::Database::saveChatMessages(const std::string &ownerUid,
        << "', modified=CURRENT_TIMESTAMP WHERE owner_uid='"
        << dbpp::SQL::escaped(eUid, ownerUid.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);

    // Check if row exists
    sql.clear(); res.clear();
    eUid.clear();
    sql << "SELECT owner_uid FROM ai_chat_messages WHERE owner_uid='"
        << dbpp::SQL::escaped(eUid, ownerUid.c_str()) << "';";
    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);

    if (rows < 1) {
        sql.clear(); res.clear();
@@ -853,7 +867,7 @@ void webedit::Database::saveChatMessages(const std::string &ownerUid,
        sql << "INSERT INTO ai_chat_messages (owner_uid, messages) VALUES ('"
            << dbpp::SQL::escaped(eUid, ownerUid.c_str()) << "','"
            << dbpp::SQL::escaped(eMsg, messagesJson.c_str()) << "');";
        _db->exec(sql, res);
        execWithRetry(sql, res);
    }
}

@@ -865,7 +879,7 @@ std::string webedit::Database::loadChatMessages(const std::string &ownerUid) {
    sql << "SELECT messages FROM ai_chat_messages WHERE owner_uid='"
        << dbpp::SQL::escaped(eUid, ownerUid.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) return "[]";
    return res[0][0];
}
@@ -877,7 +891,7 @@ void webedit::Database::clearChatMessages(const std::string &ownerUid) {

    sql << "DELETE FROM ai_chat_messages WHERE owner_uid='"
        << dbpp::SQL::escaped(eUid, ownerUid.c_str()) << "';";
    _db->exec(sql, res);
    execWithRetry(sql, res);
}

// ---------------------------------------------------------------------------
@@ -904,7 +918,7 @@ std::string webedit::Database::saveSurvey(const std::string &ownerUid,
        << dbpp::SQL::escaped(eTitle, title.c_str()) << "','"
        << dbpp::SQL::escaped(eXml,   xml.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return std::string(id.c_str());
}

@@ -923,7 +937,7 @@ bool webedit::Database::updateSurvey(const std::string &domain, const std::strin
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -935,7 +949,7 @@ bool webedit::Database::deleteSurvey(const std::string &domain, const std::strin
    sql << "DELETE FROM survey_templates WHERE id='"
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";
    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -949,7 +963,7 @@ std::string webedit::Database::listSurveys(const std::string &domain) {
        << dbpp::SQL::escaped(eDomain, domain.c_str())
        << "' ORDER BY name;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
        json_object *obj = json_object_new_object();
@@ -974,7 +988,7 @@ std::string webedit::Database::listSurveyNames(const std::string &domain) {
    sql << "SELECT id, name FROM survey_templates WHERE domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "' ORDER BY name;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
        json_object *obj = json_object_new_object();
@@ -998,7 +1012,7 @@ std::string webedit::Database::getSurvey(const std::string &domain, const std::s
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) return "{}";

    json_object *survey = json_object_new_object();
@@ -1038,7 +1052,7 @@ std::string webedit::Database::saveElementTemplate(const std::string &ownerUid,
        << dbpp::SQL::escaped(eRootType, rootType.c_str()) << "','"
        << dbpp::SQL::escaped(eXml,   xml.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return std::string(id.c_str());
}

@@ -1054,7 +1068,7 @@ bool webedit::Database::renameElementTemplate(const std::string &domain, const s
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -1066,7 +1080,7 @@ bool webedit::Database::deleteElementTemplate(const std::string &domain, const s
    sql << "DELETE FROM element_templates WHERE id='"
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";
    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -1080,7 +1094,7 @@ std::string webedit::Database::listElementTemplates(const std::string &domain) {
        << dbpp::SQL::escaped(eDomain, domain.c_str())
        << "' ORDER BY name;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
        json_object *obj = json_object_new_object();
@@ -1107,7 +1121,7 @@ std::string webedit::Database::getElementTemplate(const std::string &domain, con
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) return "{}";

    json_object *tpl = json_object_new_object();
@@ -1147,7 +1161,7 @@ std::string webedit::Database::saveIcon(const std::string &ownerUid,
        << dbpp::SQL::escaped(eTags,  tags.c_str()) << "','"
        << dbpp::SQL::escaped(eSvg,   svg.c_str()) << "');";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return std::string(id.c_str());
}

@@ -1166,7 +1180,7 @@ bool webedit::Database::updateIcon(const std::string &domain, const std::string
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -1178,7 +1192,7 @@ bool webedit::Database::deleteIcon(const std::string &domain, const std::string
    sql << "DELETE FROM icons WHERE id='"
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";
    _db->exec(sql, res);
    execWithRetry(sql, res);
    return true;
}

@@ -1192,7 +1206,7 @@ std::string webedit::Database::listIcons(const std::string &domain) {
        << dbpp::SQL::escaped(eDomain, domain.c_str())
        << "' ORDER BY name;";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    json_object *arr = json_object_new_array();
    for (int i = 0; i < rows; ++i) {
        json_object *obj = json_object_new_object();
@@ -1220,7 +1234,7 @@ std::string webedit::Database::getIcon(const std::string &domain, const std::str
        << dbpp::SQL::escaped(eId,    id.c_str()) << "' AND domain='"
        << dbpp::SQL::escaped(eDomain, domain.c_str()) << "';";

    int rows = _db->exec(sql, res);
    int rows = execWithRetry(sql, res);
    if (rows < 1) return "{}";

    json_object *icon = json_object_new_object();
+5 −0
Original line number Diff line number Diff line
@@ -138,6 +138,11 @@ namespace webedit {
    private:
        std::string _driver;
        std::unique_ptr<dbpp::ReplicatedDatabase> _db;

        // Wraps _db->exec(): on failure, checks whether the connection
        // dropped and if so reconnects and retries the query once before
        // giving up (mirrors the retry the main blogi app does per-request).
        int execWithRetry(const dbpp::SQL &sql, dbpp::DBResult &res);
    };

}