Basic support for passing postgresql uri on the commandline

This commit is contained in:
eelke 2025-02-17 18:09:19 +01:00
parent 4b4c95e57e
commit 4caccf1000
11 changed files with 453 additions and 192 deletions

View file

@ -40,7 +40,6 @@ void ConnectionController::init()
m_connectionTreeModel->load();
m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr);
m_connectionManagerWindow->show();
}
void ConnectionController::showConnectionManager()
@ -55,16 +54,21 @@ void ConnectionController::openSqlWindowForConnection(QModelIndex index)
if (retrieveConnectionPassword(*config)) {
m_connectionTreeModel->save(*config);
// TODO instead of directly openening the mainwindow
// do async connect and only open window when we have
// working connection
auto w = new DatabaseWindow(m_masterController, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(*config);
w->showMaximized();
openSqlWindowForConnection(*config);
}
}
}
void ConnectionController::openSqlWindowForConnection(const ConnectionConfig &config)
{
// TODO instead of directly openening the mainwindow
// do async connect and only open window when we have
// working connection
auto w = new DatabaseWindow(m_masterController, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(config);
w->showMaximized();
}
void ConnectionController::openBackupDlgForConnection(QModelIndex index)

View file

@ -38,7 +38,8 @@ public:
void showConnectionManager();
void openSqlWindowForConnection(QModelIndex index);
void openBackupDlgForConnection(QModelIndex index);
void openSqlWindowForConnection(const ConnectionConfig &cfg);
void openBackupDlgForConnection(QModelIndex index);
/// Starts the form for creating a new conncetion.
/// This function returns immidiatly!

View file

@ -1,16 +1,19 @@
#include "ConnectionListModel.h"
#include "ScopeGuard.h"
#include "util.h"
#include <botan/cryptobox.h>
#include <QDir>
#include <QException>
#include <QMimeData>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QString>
#include <QStringBuilder>
#include <QStandardPaths>
#include <QStringBuilder>
#include <unordered_set>
namespace {
@ -41,6 +44,16 @@ CREATE TABLE IF NOT EXISTS connection (
password TEXT
);)__";
const char * const q_create_table_migrations =
R"__(
CREATE TABLE IF NOT EXISTS _migration (
migration_id TEXT PRIMARY KEY
);)__";
const char * const q_load_migrations_present =
R"__(
SELECT migration_id
FROM _migration;)__";
const char * const q_insert_or_replace_into_connection =
@ -49,21 +62,136 @@ R"__(INSERT OR REPLACE INTO connection
:sslmode, :sslcert, :sslkey, :sslrootcert, :sslcrl, :password);
)__" ;
std::tuple<bool, QSqlError> InitConnectionTables(QSqlDatabase &db)
{
QSqlQuery q_create_table(db);
q_create_table.prepare(q_create_table_conngroup);
if (!q_create_table.exec()) {
auto err = q_create_table.lastError();
return { false, err };
}
q_create_table.prepare(q_create_table_connection);
if (!q_create_table.exec()) {
auto err = q_create_table.lastError();
return { false, err };
}
return {true, {}};
}
// Keeping migration function name and id DRY
#define APPLY_MIGRATION(id) ApplyMigration(#id, &MigrationDirector::id)
class MigrationDirector {
public:
explicit MigrationDirector(QSqlDatabase &db)
: db(db)
{
}
void Execute()
{
InitConnectionTables();
present = LoadMigrations();
APPLY_MIGRATION(M20250215_0933_Parameters);
}
private:
QSqlDatabase &db;
std::unordered_set<QString> present;
void M20250215_0933_Parameters()
{
Exec(R"__(
CREATE TABLE connection_parameter (
connection_uuid TEXT,
pname TEXT,
pvalue TEXT NOT NULL,
PRIMARY KEY(connection_uuid, pname)
);)__");
for (QString key : { "host", "hostaddr", "user", "dbname", "sslmode", "sslcert", "sslkey", "sslrootcert", "sslcrl" })
{
Exec(
"INSERT INTO connection_parameter (connection_uuid, pname, pvalue)"
" SELECT uuid, '" % key % "', " % key % "\n"
" FROM connection\n"
" WHERE " % key % " IS NOT NULL and " % key % " <> '';");
}
Exec(R"__(
INSERT INTO connection_parameter (connection_uuid, pname, pvalue)
SELECT uuid, 'port', port
FROM connection
WHERE port IS NOT NULL;
)__");
for (QString column : { "host", "hostaddr", "user", "dbname", "sslmode", "sslcert", "sslkey", "sslrootcert", "sslcrl", "port" })
{
// sqlite does not seem to support dropping more then one column per alter table
Exec("ALTER TABLE connection DROP COLUMN " % column % ";");
}
}
void Exec(QString query)
{
QSqlQuery q(query, db);
Verify(q);
}
void ApplyMigration(QString migration_id, void (MigrationDirector::*func)())
{
if (!present.contains(migration_id))
{
if (!db.transaction())
{
throw std::runtime_error("Failed to start transaction on user configuration database");
}
(this->*func)();
RegisterMigration(migration_id);
if (!db.commit())
{
db.rollback();
throw std::runtime_error("Failed to commit transaction on user configuration database");
}
}
}
std::unordered_set<QString> LoadMigrations()
{
std::unordered_set<QString> result;
QSqlQuery q(q_load_migrations_present, db);
Verify(q);
while (q.next())
{
result.insert(q.value(0).toString());
}
return result;
}
void RegisterMigration(QString migrationId)
{
const char * const q_register_migration =
R"__(INSERT INTO _migration VALUES (:id);)__" ;
QSqlQuery q(db);
q.prepare(q_register_migration);
q.bindValue(":id", migrationId);
if (!q.exec()) {
Verify(q);
}
}
void Verify(QSqlQuery &q)
{
auto err = q.lastError();
if (err.type() == QSqlError::NoError)
return;
db.rollback();
QString errString = err.text();
throw std::runtime_error(errString.toStdString());
}
void InitConnectionTables()
{
// Original schema
QSqlQuery q_create_table(db);
q_create_table.exec(q_create_table_conngroup);
Verify(q_create_table);
q_create_table.exec(q_create_table_connection);
Verify(q_create_table);
// Start using migrations
q_create_table.exec(q_create_table_migrations);
Verify(q_create_table);
}
};
std::optional<QSqlError> SaveConnectionConfig(QSqlDatabase &db, const ConnectionConfig &cc, int conngroup_id)
{
@ -105,7 +233,9 @@ ConnectionTreeModel::ConnectionTreeModel(QObject *parent, QSqlDatabase &db)
void ConnectionTreeModel::load()
{
InitConnectionTables(m_db);
//InitConnectionTables(m_db);
MigrationDirector md(m_db);
md.Execute();
QSqlQuery q(m_db);
q.prepare("SELECT conngroup_id, gname FROM conngroup;");
@ -124,9 +254,7 @@ void ConnectionTreeModel::load()
m_groups.push_back(g);
}
q.prepare("SELECT uuid, cname, conngroup_id, host, hostaddr, port, "
" user, dbname, sslmode, sslcert, sslkey, sslrootcert, sslcrl, "
" password "
q.prepare("SELECT uuid, cname, conngroup_id, password "
"FROM connection ORDER BY conngroup_id, cname;");
if (!q.exec()) {
// auto err = q_create_table.lastError();

View file

@ -1,5 +1,8 @@
#include "MasterController.h"
#include "ConnectionController.h"
#include "utils/PostgresqlUrlParser.h"
#include <ConnectionConfig.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
@ -38,7 +41,20 @@ void MasterController::init()
}
m_connectionController = new ConnectionController(this);
m_connectionController->init();
m_connectionController->init();
QStringList arguments = QCoreApplication::arguments();
for (auto && arg : arguments)
{
ConnectionConfig cfg;
if (TryParsePostgresqlUrl(arg, cfg)) {
m_connectionController->openSqlWindowForConnection(cfg);
return;
}
}
m_connectionController->showConnectionManager();
}
ConnectionController *MasterController::connectionController()