Basic support for passing postgresql uri on the commandline
This commit is contained in:
parent
4b4c95e57e
commit
4caccf1000
11 changed files with 453 additions and 192 deletions
|
|
@ -40,7 +40,6 @@ void ConnectionController::init()
|
||||||
m_connectionTreeModel->load();
|
m_connectionTreeModel->load();
|
||||||
|
|
||||||
m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr);
|
m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr);
|
||||||
m_connectionManagerWindow->show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionController::showConnectionManager()
|
void ConnectionController::showConnectionManager()
|
||||||
|
|
@ -55,16 +54,21 @@ void ConnectionController::openSqlWindowForConnection(QModelIndex index)
|
||||||
|
|
||||||
if (retrieveConnectionPassword(*config)) {
|
if (retrieveConnectionPassword(*config)) {
|
||||||
m_connectionTreeModel->save(*config);
|
m_connectionTreeModel->save(*config);
|
||||||
// TODO instead of directly openening the mainwindow
|
|
||||||
// do async connect and only open window when we have
|
openSqlWindowForConnection(*config);
|
||||||
// working connection
|
|
||||||
auto w = new DatabaseWindow(m_masterController, nullptr);
|
|
||||||
w->setAttribute( Qt::WA_DeleteOnClose );
|
|
||||||
w->setConfig(*config);
|
|
||||||
w->showMaximized();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
void ConnectionController::openBackupDlgForConnection(QModelIndex index)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ public:
|
||||||
|
|
||||||
void showConnectionManager();
|
void showConnectionManager();
|
||||||
void openSqlWindowForConnection(QModelIndex index);
|
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.
|
/// Starts the form for creating a new conncetion.
|
||||||
/// This function returns immidiatly!
|
/// This function returns immidiatly!
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
#include "ConnectionListModel.h"
|
#include "ConnectionListModel.h"
|
||||||
#include "ScopeGuard.h"
|
#include "ScopeGuard.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <botan/cryptobox.h>
|
#include <botan/cryptobox.h>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QException>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringBuilder>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -41,6 +44,16 @@ CREATE TABLE IF NOT EXISTS connection (
|
||||||
password TEXT
|
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 =
|
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);
|
:sslmode, :sslcert, :sslkey, :sslrootcert, :sslcrl, :password);
|
||||||
)__" ;
|
)__" ;
|
||||||
|
|
||||||
std::tuple<bool, QSqlError> InitConnectionTables(QSqlDatabase &db)
|
|
||||||
{
|
// Keeping migration function name and id DRY
|
||||||
QSqlQuery q_create_table(db);
|
#define APPLY_MIGRATION(id) ApplyMigration(#id, &MigrationDirector::id)
|
||||||
q_create_table.prepare(q_create_table_conngroup);
|
|
||||||
if (!q_create_table.exec()) {
|
class MigrationDirector {
|
||||||
auto err = q_create_table.lastError();
|
public:
|
||||||
return { false, err };
|
explicit MigrationDirector(QSqlDatabase &db)
|
||||||
}
|
: db(db)
|
||||||
q_create_table.prepare(q_create_table_connection);
|
{
|
||||||
if (!q_create_table.exec()) {
|
}
|
||||||
auto err = q_create_table.lastError();
|
|
||||||
return { false, err };
|
void Execute()
|
||||||
}
|
{
|
||||||
return {true, {}};
|
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)
|
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()
|
void ConnectionTreeModel::load()
|
||||||
{
|
{
|
||||||
InitConnectionTables(m_db);
|
//InitConnectionTables(m_db);
|
||||||
|
MigrationDirector md(m_db);
|
||||||
|
md.Execute();
|
||||||
|
|
||||||
QSqlQuery q(m_db);
|
QSqlQuery q(m_db);
|
||||||
q.prepare("SELECT conngroup_id, gname FROM conngroup;");
|
q.prepare("SELECT conngroup_id, gname FROM conngroup;");
|
||||||
|
|
@ -124,9 +254,7 @@ void ConnectionTreeModel::load()
|
||||||
m_groups.push_back(g);
|
m_groups.push_back(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
q.prepare("SELECT uuid, cname, conngroup_id, host, hostaddr, port, "
|
q.prepare("SELECT uuid, cname, conngroup_id, password "
|
||||||
" user, dbname, sslmode, sslcert, sslkey, sslrootcert, sslcrl, "
|
|
||||||
" password "
|
|
||||||
"FROM connection ORDER BY conngroup_id, cname;");
|
"FROM connection ORDER BY conngroup_id, cname;");
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
// auto err = q_create_table.lastError();
|
// auto err = q_create_table.lastError();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
#include "MasterController.h"
|
#include "MasterController.h"
|
||||||
#include "ConnectionController.h"
|
#include "ConnectionController.h"
|
||||||
|
#include "utils/PostgresqlUrlParser.h"
|
||||||
|
#include <ConnectionConfig.h>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
@ -38,7 +41,20 @@ void MasterController::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connectionController = new ConnectionController(this);
|
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()
|
ConnectionController *MasterController::connectionController()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,20 @@ namespace {
|
||||||
return v.empty() ? nullptr : v.c_str();
|
return v.empty() ? nullptr : v.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char * host = "host";
|
||||||
|
const char * hostaddr = "hostaddr";
|
||||||
|
const char * port = "port";
|
||||||
|
const char * dbname = "dbname";
|
||||||
|
const char * user = "user";
|
||||||
|
const char * password = "password";
|
||||||
|
const char * sslmode = "sslmode";
|
||||||
|
const char * sslcert = "sslcert";
|
||||||
|
const char * sslkey = "sslkey";
|
||||||
|
const char * sslrootcert = "sslrootcert";
|
||||||
|
const char * sslcrl = "sslcrl";
|
||||||
|
} keywords;
|
||||||
|
|
||||||
} // end unnamed namespace
|
} // end unnamed namespace
|
||||||
|
|
||||||
QString SslModeToString(SslMode sm)
|
QString SslModeToString(SslMode sm)
|
||||||
|
|
@ -46,7 +60,7 @@ SslMode StringToSslMode(QString s)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionConfig::ConnectionConfig()
|
ConnectionConfig::ConnectionConfig()
|
||||||
: m_applicationName(QCoreApplication::applicationName().toUtf8().data())
|
: m_parameters{{"application_name", QCoreApplication::applicationName()}}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
const ConnectionGroup *ConnectionConfig::parent() const
|
const ConnectionGroup *ConnectionConfig::parent() const
|
||||||
|
|
@ -92,168 +106,131 @@ const QString& ConnectionConfig::name() const
|
||||||
|
|
||||||
void ConnectionConfig::setHost(const QString& host)
|
void ConnectionConfig::setHost(const QString& host)
|
||||||
{
|
{
|
||||||
if (m_host != host)
|
setParameter(keywords.host, host);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_host = std::move(host);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::host() const
|
QString ConnectionConfig::host() const
|
||||||
{
|
{
|
||||||
return m_host;
|
return getParameter(keywords.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setHostAddr(const QString &v)
|
void ConnectionConfig::setHostAddr(const QString &v)
|
||||||
{
|
{
|
||||||
if (m_hostaddr != v)
|
setParameter(keywords.hostaddr, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_hostaddr = std::move(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::hostAddr() const
|
QString ConnectionConfig::hostAddr() const
|
||||||
{
|
{
|
||||||
return m_hostaddr;
|
return getParameter(keywords.hostaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setPort(unsigned short port)
|
void ConnectionConfig::setPort(unsigned short port)
|
||||||
{
|
{
|
||||||
if (m_port != port)
|
setParameter(keywords.port, QString::number(port));
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_port = port;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short ConnectionConfig::port() const
|
unsigned short ConnectionConfig::port() const
|
||||||
{
|
{
|
||||||
return m_port;
|
QString s = getParameter(keywords.port);
|
||||||
|
if (s.isEmpty())
|
||||||
|
return 5432;
|
||||||
|
|
||||||
|
unsigned short port = static_cast<unsigned short>(s.toInt());
|
||||||
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setUser(const QString& v)
|
void ConnectionConfig::setUser(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_user != v)
|
setParameter(keywords.user, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_user = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::user() const
|
QString ConnectionConfig::user() const
|
||||||
{
|
{
|
||||||
return m_user;
|
return getParameter(keywords.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setPassword(const QString& v)
|
void ConnectionConfig::setPassword(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_password != v)
|
setParameter(keywords.password, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_password = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::password() const
|
QString ConnectionConfig::password() const
|
||||||
{
|
{
|
||||||
return m_password;
|
return getParameter(keywords.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setDbname(const QString& v)
|
void ConnectionConfig::setDbname(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_dbname != v)
|
setParameter(keywords.dbname, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_dbname = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::dbname() const
|
QString ConnectionConfig::dbname() const
|
||||||
{
|
{
|
||||||
return m_dbname;
|
return getParameter(keywords.dbname);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setSslMode(SslMode m)
|
void ConnectionConfig::setSslMode(SslMode m)
|
||||||
{
|
{
|
||||||
if (m_sslMode != m)
|
setParameter(keywords.sslmode, SslModeToString(m));
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_sslMode = m;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SslMode ConnectionConfig::sslMode() const
|
SslMode ConnectionConfig::sslMode() const
|
||||||
{
|
{
|
||||||
return m_sslMode;
|
QString s = getParameter(keywords.sslmode);
|
||||||
|
return StringToSslMode(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setSslCert(const QString& v)
|
void ConnectionConfig::setSslCert(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_sslCert != v)
|
setParameter(keywords.sslcert, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_sslCert = std::move(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::sslCert() const
|
QString ConnectionConfig::sslCert() const
|
||||||
{
|
{
|
||||||
return m_sslCert;
|
return getParameter(keywords.sslcert);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setSslKey(const QString& v)
|
void ConnectionConfig::setSslKey(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_sslKey != v)
|
setParameter(keywords.sslkey, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_sslKey = std::move(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::sslKey() const
|
QString ConnectionConfig::sslKey() const
|
||||||
{
|
{
|
||||||
return m_sslKey;
|
return getParameter(keywords.sslkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setSslRootCert(const QString& v)
|
void ConnectionConfig::setSslRootCert(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_sslRootCert != v)
|
setParameter(keywords.sslrootcert, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_sslRootCert = std::move(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::sslRootCert() const
|
QString ConnectionConfig::sslRootCert() const
|
||||||
{
|
{
|
||||||
return m_sslRootCert;
|
return getParameter(keywords.sslrootcert);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::setSslCrl(const QString& v)
|
void ConnectionConfig::setSslCrl(const QString& v)
|
||||||
{
|
{
|
||||||
if (m_sslCrl != v)
|
setParameter(keywords.sslcrl, v);
|
||||||
{
|
|
||||||
m_dirty = true;
|
|
||||||
m_sslCrl = std::move(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& ConnectionConfig::sslCrl() const
|
QString ConnectionConfig::sslCrl() const
|
||||||
{
|
{
|
||||||
return m_sslCrl;
|
return getParameter(keywords.sslcrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
|
// bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
|
||||||
{
|
// {
|
||||||
return m_host == rhs.m_host
|
// return host() == rhs.host()
|
||||||
&& m_hostaddr == rhs.m_hostaddr
|
// // && m_hostaddr == rhs.m_hostaddr
|
||||||
&& m_port == rhs.m_port
|
// // && m_port == rhs.m_port
|
||||||
&& m_user == rhs.m_user
|
// // && m_user == rhs.m_user
|
||||||
&& m_password == rhs.m_password
|
// // && m_password == rhs.m_password
|
||||||
&& m_dbname == rhs.m_dbname;
|
// // && m_dbname == rhs.m_dbname
|
||||||
}
|
// ;
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
bool ConnectionConfig::dirty() const
|
bool ConnectionConfig::dirty() const
|
||||||
{
|
{
|
||||||
|
|
@ -314,58 +291,111 @@ QString ConnectionConfig::escapeConnectionStringValue(const QString &value)
|
||||||
QString ConnectionConfig::connectionString() const
|
QString ConnectionConfig::connectionString() const
|
||||||
{
|
{
|
||||||
QString s;
|
QString s;
|
||||||
s += "host="
|
|
||||||
% escapeConnectionStringValue(m_host)
|
|
||||||
% " port="
|
|
||||||
% QString::number(m_port)
|
|
||||||
% " user="
|
|
||||||
% escapeConnectionStringValue(m_user);
|
|
||||||
s += " password=";
|
|
||||||
s += escapeConnectionStringValue(m_password);
|
|
||||||
s += " dbname=";
|
|
||||||
s += escapeConnectionStringValue(m_dbname);
|
|
||||||
s += " sslmode=";
|
|
||||||
s += SslModeToString(m_sslMode);
|
|
||||||
if (!m_sslCert.isEmpty())
|
|
||||||
{
|
|
||||||
s += " sslcert=";
|
|
||||||
s += escapeConnectionStringValue(m_sslCert);
|
|
||||||
}
|
|
||||||
if (!m_sslKey.isEmpty())
|
|
||||||
{
|
|
||||||
s += " sslkey=";
|
|
||||||
s += escapeConnectionStringValue(m_sslKey);
|
|
||||||
}
|
|
||||||
if (!m_sslRootCert.isEmpty())
|
|
||||||
{
|
|
||||||
s += " sslrootcrt=";
|
|
||||||
s += escapeConnectionStringValue(m_sslRootCert);
|
|
||||||
}
|
|
||||||
if (!m_sslCrl.isEmpty())
|
|
||||||
{
|
|
||||||
s += " sslCrl=";
|
|
||||||
s += escapeConnectionStringValue(m_sslCrl);
|
|
||||||
}
|
|
||||||
s += " client_encoding=utf8";
|
|
||||||
s += " application_name=";
|
|
||||||
s += escapeConnectionStringValue(m_applicationName);
|
|
||||||
|
|
||||||
return s;
|
for (auto && param : m_parameters)
|
||||||
|
{
|
||||||
|
// maybe we should prevent empty parameters from staying in the map?
|
||||||
|
if (!param.second.isEmpty())
|
||||||
|
{
|
||||||
|
s += param.first % "=" % escapeConnectionStringValue(param.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// s += "host="
|
||||||
|
// % escapeConnectionStringValue(m_host)
|
||||||
|
// % " port="
|
||||||
|
// % QString::number(m_port)
|
||||||
|
// % " user="
|
||||||
|
// % escapeConnectionStringValue(m_user);
|
||||||
|
// s += " password=";
|
||||||
|
// s += escapeConnectionStringValue(m_password);
|
||||||
|
// s += " dbname=";
|
||||||
|
// s += escapeConnectionStringValue(m_dbname);
|
||||||
|
// s += " sslmode=";
|
||||||
|
// s += SslModeToString(m_sslMode);
|
||||||
|
// if (!m_sslCert.isEmpty())
|
||||||
|
// {
|
||||||
|
// s += " sslcert=";
|
||||||
|
// s += escapeConnectionStringValue(m_sslCert);
|
||||||
|
// }
|
||||||
|
// if (!m_sslKey.isEmpty())
|
||||||
|
// {
|
||||||
|
// s += " sslkey=";
|
||||||
|
// s += escapeConnectionStringValue(m_sslKey);
|
||||||
|
// }
|
||||||
|
// if (!m_sslRootCert.isEmpty())
|
||||||
|
// {
|
||||||
|
// s += " sslrootcrt=";
|
||||||
|
// s += escapeConnectionStringValue(m_sslRootCert);
|
||||||
|
// }
|
||||||
|
// if (!m_sslCrl.isEmpty())
|
||||||
|
// {
|
||||||
|
// s += " sslCrl=";
|
||||||
|
// s += escapeConnectionStringValue(m_sslCrl);
|
||||||
|
// }
|
||||||
|
// s += " client_encoding=utf8";
|
||||||
|
// s += " application_name=";
|
||||||
|
// s += escapeConnectionStringValue(m_applicationName);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionConfig::setParameter(const QString &name, const QString &value)
|
||||||
|
{
|
||||||
|
if (value.isEmpty())
|
||||||
|
{
|
||||||
|
if (m_parameters.erase(name) > 0)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto findResult = m_parameters.find(name);
|
||||||
|
if (findResult == m_parameters.end())
|
||||||
|
{
|
||||||
|
m_parameters.insert({name, value});
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
else if (findResult->second != value)
|
||||||
|
{
|
||||||
|
findResult->second = value;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (name == "sslMode")
|
||||||
|
// m_sslMode = StringToSslMode(value);
|
||||||
|
|
||||||
|
// Would it better to store everything in the map or keep the specific
|
||||||
|
// fields for common keywords?
|
||||||
|
// Map over fields
|
||||||
|
// + can use foreach
|
||||||
|
// - not strongly typed, but we should be carefull not to restrict ourselves
|
||||||
|
// the specific fields are more something that helps in the UI
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ConnectionConfig::getParameter(const QString &name) const
|
||||||
|
{
|
||||||
|
auto findResult = m_parameters.find(name);
|
||||||
|
if (findResult == m_parameters.end())
|
||||||
|
return {};
|
||||||
|
return findResult->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionConfig::writeToEnvironment(QProcessEnvironment &env) const
|
void ConnectionConfig::writeToEnvironment(QProcessEnvironment &env) const
|
||||||
{
|
{
|
||||||
strToEnv(env, "PGHOST", m_host);
|
strToEnv(env, "PGHOST", getParameter(keywords.host));
|
||||||
strToEnv(env, "PGHOSTADDR", m_hostaddr);
|
strToEnv(env, "PGHOSTADDR", getParameter(keywords.hostaddr));
|
||||||
strToEnv(env, "PGPORT", QString::number(m_port));
|
strToEnv(env, "PGPORT", getParameter(keywords.port));
|
||||||
strToEnv(env, "PGDATABASE", m_dbname);
|
strToEnv(env, "PGDATABASE", getParameter(keywords.dbname));
|
||||||
strToEnv(env, "PGUSER", m_user);
|
strToEnv(env, "PGUSER", getParameter(keywords.user));
|
||||||
strToEnv(env, "PGPASSWORD", m_password);
|
strToEnv(env, "PGPASSWORD", getParameter(keywords.password));
|
||||||
strToEnv(env, "PGSSLMODE", SslModeToString(m_sslMode));
|
strToEnv(env, "PGSSLMODE", getParameter(keywords.sslmode));
|
||||||
strToEnv(env, "PGSSLCERT", m_sslCert);
|
// strToEnv(env, "PGSSLCERT", m_sslCert);
|
||||||
strToEnv(env, "PGSSLKEY", m_sslKey);
|
// strToEnv(env, "PGSSLKEY", m_sslKey);
|
||||||
strToEnv(env, "PGSSLROOTCERT", m_sslRootCert);
|
// strToEnv(env, "PGSSLROOTCERT", m_sslRootCert);
|
||||||
strToEnv(env, "PGSSLCRL", m_sslCrl);
|
// strToEnv(env, "PGSSLCRL", m_sslCrl);
|
||||||
strToEnv(env, "PGSSLCOMPRESSION", "0");
|
strToEnv(env, "PGSSLCOMPRESSION", "0");
|
||||||
strToEnv(env, "PGCONNECT_TIMEOUT", "10");
|
strToEnv(env, "PGCONNECT_TIMEOUT", "10");
|
||||||
env.insert("PGCLIENTENCODING", "utf8");
|
env.insert("PGCLIENTENCODING", "utf8");
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -74,42 +75,42 @@ public:
|
||||||
const QString& name() const;
|
const QString& name() const;
|
||||||
|
|
||||||
void setHost(const QString& host);
|
void setHost(const QString& host);
|
||||||
const QString& host() const;
|
QString host() const;
|
||||||
|
|
||||||
void setHostAddr(const QString& v);
|
void setHostAddr(const QString& v);
|
||||||
const QString& hostAddr() const;
|
QString hostAddr() const;
|
||||||
|
|
||||||
void setPort(unsigned short port);
|
void setPort(unsigned short port);
|
||||||
unsigned short port() const;
|
unsigned short port() const;
|
||||||
|
|
||||||
void setUser(const QString& v);
|
void setUser(const QString& v);
|
||||||
const QString& user() const;
|
QString user() const;
|
||||||
|
|
||||||
void setPassword(const QString& v);
|
void setPassword(const QString& v);
|
||||||
const QString& password() const;
|
QString password() const;
|
||||||
|
|
||||||
void setDbname(const QString& v);
|
void setDbname(const QString& v);
|
||||||
const QString& dbname() const;
|
QString dbname() const;
|
||||||
|
|
||||||
void setSslMode(SslMode m);
|
void setSslMode(SslMode m);
|
||||||
SslMode sslMode() const;
|
SslMode sslMode() const;
|
||||||
|
|
||||||
void setSslCert(const QString& v);
|
void setSslCert(const QString& v);
|
||||||
const QString& sslCert() const;
|
QString sslCert() const;
|
||||||
|
|
||||||
void setSslKey(const QString& v);
|
void setSslKey(const QString& v);
|
||||||
const QString& sslKey() const;
|
QString sslKey() const;
|
||||||
|
|
||||||
void setSslRootCert(const QString& v);
|
void setSslRootCert(const QString& v);
|
||||||
const QString& sslRootCert() const;
|
QString sslRootCert() const;
|
||||||
|
|
||||||
void setSslCrl(const QString& v);
|
void setSslCrl(const QString& v);
|
||||||
const QString& sslCrl() const;
|
QString sslCrl() const;
|
||||||
|
|
||||||
// const char * const * getKeywords() const;
|
// const char * const * getKeywords() const;
|
||||||
// const char * const * getValues() const;
|
// const char * const * getValues() const;
|
||||||
|
|
||||||
bool isSameDatabase(const ConnectionConfig &rhs) const;
|
// bool isSameDatabase(const ConnectionConfig &rhs) const;
|
||||||
|
|
||||||
void writeToEnvironment(QProcessEnvironment &env) const;
|
void writeToEnvironment(QProcessEnvironment &env) const;
|
||||||
|
|
||||||
|
|
@ -131,26 +132,31 @@ public:
|
||||||
*/
|
*/
|
||||||
static QString escapeConnectionStringValue(const QString &value);
|
static QString escapeConnectionStringValue(const QString &value);
|
||||||
QString connectionString() const;
|
QString connectionString() const;
|
||||||
|
|
||||||
|
void setParameter(const QString &name, const QString &value);
|
||||||
|
QString getParameter(const QString &name) const;
|
||||||
private:
|
private:
|
||||||
QUuid m_uuid;
|
QUuid m_uuid;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_host;
|
// QString m_host;
|
||||||
QString m_hostaddr;
|
// QString m_hostaddr;
|
||||||
uint16_t m_port = 5432;
|
// uint16_t m_port = 5432;
|
||||||
|
|
||||||
QString m_user;
|
// QString m_user;
|
||||||
QString m_password; ///< Note this is not saved in the DB only the m_encodedPassword is safed.
|
// QString m_password; ///< Note this is not saved in the DB only the m_encodedPassword is safed.
|
||||||
QString m_dbname;
|
// QString m_dbname;
|
||||||
|
|
||||||
SslMode m_sslMode = SslMode::prefer;
|
// SslMode m_sslMode = SslMode::prefer;
|
||||||
QString m_sslCert;
|
// QString m_sslCert;
|
||||||
QString m_sslKey;
|
// QString m_sslKey;
|
||||||
QString m_sslRootCert;
|
// QString m_sslRootCert;
|
||||||
QString m_sslCrl;
|
// QString m_sslCrl;
|
||||||
|
|
||||||
QString m_applicationName;
|
// QString m_applicationName;
|
||||||
QByteArray m_encodedPassword;
|
QByteArray m_encodedPassword;
|
||||||
|
|
||||||
|
std::unordered_map<QString, QString> m_parameters;
|
||||||
|
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
ConnectionGroup* m_group = nullptr;
|
ConnectionGroup* m_group = nullptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ SOURCES += \
|
||||||
catalog/PgAcl.cpp \
|
catalog/PgAcl.cpp \
|
||||||
catalog/PgSequence.cpp \
|
catalog/PgSequence.cpp \
|
||||||
catalog/PgSequenceContainer.cpp \
|
catalog/PgSequenceContainer.cpp \
|
||||||
utils/HumanReadableBytes.cpp
|
utils/HumanReadableBytes.cpp \
|
||||||
|
utils/PostgresqlUrlParser.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
Pglablib.h \
|
Pglablib.h \
|
||||||
|
|
@ -164,7 +165,8 @@ HEADERS += \
|
||||||
catalog/PgAcl.h \
|
catalog/PgAcl.h \
|
||||||
catalog/PgSequence.h \
|
catalog/PgSequence.h \
|
||||||
catalog/PgSequenceContainer.h \
|
catalog/PgSequenceContainer.h \
|
||||||
utils/HumanReadableBytes.h
|
utils/HumanReadableBytes.h \
|
||||||
|
utils/PostgresqlUrlParser.h
|
||||||
|
|
||||||
unix {
|
unix {
|
||||||
target.path = /usr/lib
|
target.path = /usr/lib
|
||||||
|
|
|
||||||
38
pglablib/utils/PostgresqlUrlParser.cpp
Normal file
38
pglablib/utils/PostgresqlUrlParser.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "PostgresqlUrlParser.h"
|
||||||
|
|
||||||
|
#include <ConnectionConfig.h>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
//PostgresqlUrlParser::PostgresqlUrlParser() {}
|
||||||
|
|
||||||
|
bool TryParsePostgresqlUrl(const QString &urlString, ConnectionConfig &out)
|
||||||
|
{
|
||||||
|
const char* shortPrefix = "postgres";
|
||||||
|
const char* longPrefix = "postgresql";
|
||||||
|
|
||||||
|
auto url = QUrl(urlString, QUrl::StrictMode);
|
||||||
|
if (url.scheme() != shortPrefix && url.scheme() != longPrefix)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
out.setUser(url.userName());
|
||||||
|
out.setPassword(url.password());
|
||||||
|
out.setHost(url.host());
|
||||||
|
out.setDbname(url.fileName());
|
||||||
|
out.setPort(url.port());
|
||||||
|
|
||||||
|
QUrlQuery query(url.query());
|
||||||
|
for (auto && param : query.queryItems())
|
||||||
|
{
|
||||||
|
if (param.first == "ssl" && param.second == "true") {
|
||||||
|
out.setSslMode(SslMode::require);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.setParameter(param.first, param.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
15
pglablib/utils/PostgresqlUrlParser.h
Normal file
15
pglablib/utils/PostgresqlUrlParser.h
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionConfig;
|
||||||
|
|
||||||
|
bool TryParsePostgresqlUrl(const QString &urlString, ConnectionConfig &out);
|
||||||
|
|
||||||
|
// class PostgresqlUrlParser
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// PostgresqlUrlParser();
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
@ -19,6 +19,7 @@ SOURCES += main.cpp \
|
||||||
tst_ConvertToMultiLineCString.cpp \
|
tst_ConvertToMultiLineCString.cpp \
|
||||||
tst_ExplainJsonParser.cpp \
|
tst_ExplainJsonParser.cpp \
|
||||||
tst_HumanReadableBytes.cpp \
|
tst_HumanReadableBytes.cpp \
|
||||||
|
tst_TryParsePostgresqlUrl.cpp \
|
||||||
tst_escapeConnectionStringValue.cpp \
|
tst_escapeConnectionStringValue.cpp \
|
||||||
tst_expected.cpp \
|
tst_expected.cpp \
|
||||||
tst_SqlLexer.cpp \
|
tst_SqlLexer.cpp \
|
||||||
|
|
|
||||||
20
tests/pglabtests/tst_TryParsePostgresqlUrl.cpp
Normal file
20
tests/pglabtests/tst_TryParsePostgresqlUrl.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock-matchers.h>
|
||||||
|
#include <ConnectionConfig.h>
|
||||||
|
#include "utils/PostgresqlUrlParser.h"
|
||||||
|
#include "PrintTo_Qt.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
TEST(TryParsePostgresqlUrl, emptyInput)
|
||||||
|
{
|
||||||
|
QString urlString = "postgresql://user:secret@localhost:5433/otherdb?connect_timeout=10&application_name=myapp";
|
||||||
|
ConnectionConfig config;
|
||||||
|
bool result = TryParsePostgresqlUrl(urlString, config);
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_THAT(config.user(), Eq("user"));
|
||||||
|
ASSERT_THAT(config.password(), Eq("secret"));
|
||||||
|
ASSERT_THAT(config.host(), Eq("localhost"));
|
||||||
|
ASSERT_THAT(config.dbname(), Eq("otherdb"));
|
||||||
|
ASSERT_THAT(config.port(), Eq(5433));
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue