Skip to content

Commit

Permalink
Add PostgreSQL backend
Browse files Browse the repository at this point in the history
Co-authored-by: Kim Lidström <dxtr@users.noreply.github.com>
  • Loading branch information
leso-kn and dxtr committed Jul 17, 2023
1 parent aa7e340 commit bed2fc8
Show file tree
Hide file tree
Showing 9 changed files with 431 additions and 11 deletions.
4 changes: 2 additions & 2 deletions Core/Frameworks/Baikal/Core/Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ static function assertEnvironmentIsOk() {

# Asserting PDO::SQLite or PDO::MySQL
$aPDODrivers = \PDO::getAvailableDrivers();
if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true)) {
exit('<strong>Baikal Fatal Error</strong>: Both <strong>PDO::sqlite</strong> and <strong>PDO::mysql</strong> are unavailable. One of them at least is required by Baikal.');
if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true) && !in_array('pgsql', $aPDODrivers, true)) {
exit('<strong>Baikal Fatal Error</strong>: None of <strong>PDO::sqlite</strong>, <strong>PDO::mysql</strong> or <strong>PDO::pgsql</strong> are available. One of them at least is required by Baikal.');
}

# Assert that the temp folder is writable
Expand Down
4 changes: 2 additions & 2 deletions Core/Frameworks/Baikal/Model/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ function isDefault() {

function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
"count(*)",
"count(*) as count",
"calendarinstances",
"calendarid" . "='" . $this->aData["calendarid"] . "'"
);
Expand All @@ -252,7 +252,7 @@ function hasInstances() {
} else {
reset($aRs);

return $aRs["count(*)"] > 1;
return $aRs["count"] > 1;
}
}

Expand Down
4 changes: 2 additions & 2 deletions Core/Frameworks/Baikal/Model/Calendar/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Calendar extends \Flake\Core\Model\Db {

function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
"count(*)",
"count(*) as count",
"calendarinstances",
"calendarid" . "='" . $this->aData["id"] . "'"
);
Expand All @@ -48,7 +48,7 @@ function hasInstances() {
} else {
reset($aRs);

return $aRs["count(*)"] > 1;
return $aRs["count"] > 1;
}
}

Expand Down
33 changes: 33 additions & 0 deletions Core/Frameworks/Baikal/Model/Config/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class Database extends \Baikal\Model\Config {
"mysql_username" => "",
"mysql_password" => "",
"encryption_key" => "",
"pgsql" => false,
"pgsql_host" => "",
"pgsql_dbname" => "",
"pgsql_username" => "",
"pgsql_password" => "",
];

function __construct() {
Expand Down Expand Up @@ -82,6 +87,34 @@ function formMorphologyForThisModelInstance() {
"label" => "MySQL password",
]));

$oMorpho->add(new \Formal\Element\Checkbox([
"prop" => "pgsql",
"label" => "Use PostgreSQL",
"help" => "If checked, Baïkal will use PostgreSQL",
"refreshonchange" => true,
]));

$oMorpho->add(new \Formal\Element\Text([
"prop" => "pgsql_host",
"label" => "PostgreSQL host",
"help" => "Host ip or name, including <strong>':portnumber'</strong> if port is not the default one (?)",
]));

$oMorpho->add(new \Formal\Element\Text([
"prop" => "pgsql_dbname",
"label" => "PostgreSQL database name",
]));

$oMorpho->add(new \Formal\Element\Text([
"prop" => "pgsql_username",
"label" => "PostgreSQL username",
]));

$oMorpho->add(new \Formal\Element\Password([
"prop" => "pgsql_password",
"label" => "PostgreSQL password",
]));

return $oMorpho;
}

Expand Down
104 changes: 101 additions & 3 deletions Core/Frameworks/BaikalAdmin/Controller/Install/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ function execute() {

$this->oForm = $this->oModel->formForThisModelInstance([
"close" => false,
"hook.validation" => [$this, "validateConnection"],
"hook.morphology" => [$this, "hideMySQLFieldWhenNeeded"],
"hook.validation" => [$this, "validateSQLConnection"],
"hook.morphology" => [$this, "hideSQLFieldWhenNeeded"],
]);

if ($this->oForm->submitted()) {
Expand Down Expand Up @@ -99,7 +99,7 @@ function render() {
return $oView->render();
}

function validateConnection($oForm, $oMorpho) {
function validateMySQLConnection($oForm, $oMorpho) {
if ($oForm->refreshed()) {
return true;
}
Expand Down Expand Up @@ -226,4 +226,102 @@ function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $
$oMorpho->remove("mysql_password");
}
}

function validatePgSQLConnection($oForm, $oMorpho) {
$bPgSqlEnabled = $oMorpho->element("pgsql")->value();

if ($bPgSqlEnabled) {
$sHost = $oMorpho->element("pgsql_host")->value();
$sDbname = $oMorpho->element("pgsql_dbname")->value();
$sUsername = $oMorpho->element("pgsql_username")->value();
$sPassword = $oMorpho->element("pgsql_password")->value();

try {
$oDb = new \Flake\Core\Database\Pgsql(
$sHost,
$sDbname,
$sUsername,
$sPassword
);

if (($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDb)) !== true) {
# Checking if all tables are missing
$aRequiredTables = \Baikal\Core\Tools::getRequiredTablesList();
if (count($aRequiredTables) !== count($aMissingTables)) {
$sMessage = "<br /><p><strong>Database is not structurally complete.</strong></p>";
$sMessage .= "<p>Missing tables are: <strong>" . implode("</strong>, <strong>", $aMissingTables) . "</strong></p>";
$sMessage .= "<p>You will find the SQL definition of Baïkal tables in this file: <strong>Core/Resources/Db/PgSQL/db.sql</strong></p>";
$sMessage .= "<br /><p>Nothing has been saved. <strong>Please, add these tables to the database before pursuing Baïkal initialization.</strong></p>";

$oForm->declareError(
$oMorpho->element("pgsql"),
$sMessage
);
} else {
# All tables are missing
# We add these tables ourselves to the database, to initialize Baïkal
$sSqlDefinition = file_get_contents(PROJECT_PATH_CORERESOURCES . "Db/PgSQL/db.sql");
$oDb->getPDO()->exec($sSqlDefinition);
}
}

return true;
} catch (\Exception $e) {
$oForm->declareError(
$oMorpho->element("pgsql"),
"Baïkal was not able to establish a connexion to the PostgreSQL database as configured.<br />PostgreSQL says: " . $e->getMessage()
);

$oForm->declareError(
$oMorpho->element("pgsql_host")
);

$oForm->declareError(
$oMorpho->element("pgsql_dbname")
);

$oForm->declareError(
$oMorpho->element("pgsql_username")
);

$oForm->declareError(
$oMorpho->element("pgsql_password")
);
}
}
}

public function validateSQLConnection($oForm, $oMorpho) {
if ($oMorpho->element("mysql")->value()) {
$this->validateMySQLConnection($oForm, $oMorpho);
} elseif ($oMorpho->element("pgsql")->value()) {
$this->validatePgSQLConnection($oForm, $oMorpho);
}
}

public function hideSqlFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oMorpho->element("mysql")->value()) {
$this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
} elseif ($oMorpho->element("pgsql")->value()) {
$this->hidePgSQLFieldWhenNeeded($oForm, $oMorpho);
}
}

public function hidePgSQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->submitted()) {
$bPgSQL = (intval($oForm->postValue("pgsql")) === 1);
} else {
$bPgSQL = pgsql;
}

if ($bPgSQL === true) {
$oMorpho->remove("sqlite_file");
$this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
} else {
$oMorpho->remove("pgsql_host");
$oMorpho->remove("pgsql_dbname");
$oMorpho->remove("pgsql_username");
$oMorpho->remove("pgsql_password");
}
}
}
49 changes: 47 additions & 2 deletions Core/Frameworks/BaikalAdmin/Controller/Settings/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,34 @@ function render() {
function morphologyHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->submitted()) {
$bMySQL = (intval($oForm->postValue("mysql")) === 1);
$bPgSQL = (intval($oForm->postValue("pgsql")) === 1);
} else {
try {
$config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml");
} catch (\Exception $e) {
error_log('Error reading baikal.yaml file : ' . $e->getMessage());
}
$bMySQL = $config['database']['mysql'] ?? true;
$bPgSQL = $config['database']['pgsql'] ?? true;
}

if ($bMySQL === true) {
if ($bMySQL === true || $bPgSQL === true) {
$oMorpho->remove("sqlite_file");
} else {
}

if (!$bMySQL) {
$oMorpho->remove("mysql_host");
$oMorpho->remove("mysql_dbname");
$oMorpho->remove("mysql_username");
$oMorpho->remove("mysql_password");
}

if (!$bPgSQL) {
$oMorpho->remove("pgsql_host");
$oMorpho->remove("pgsql_dbname");
$oMorpho->remove("pgsql_username");
$oMorpho->remove("pgsql_password");
}
}

function validationHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
Expand Down Expand Up @@ -130,6 +141,40 @@ function validationHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {

$oForm->declareError($oMorpho->element("mysql"), $sMessage);

return;
}
} else if (intval($oForm->modelInstance()->get("pgsql")) === 1) {
# We have to check the PostgreSQL connection
$sHost = $oForm->modelInstance()->get("pgsql_host");
$sDbName = $oForm->modelInstance()->get("pgsql_dbname");
$sUsername = $oForm->modelInstance()->get("pgsql_username");
$sPassword = $oForm->modelInstance()->get("pgsql_password");

try {
$oDB = new \Flake\Core\Database\Pgsql(
$sHost,
$sDbName,
$sUsername,
$sPassword
);
} catch (\Exception $e) {
$sMessage = "<strong>PostgreSQL error:</strong> " . $e->getMessage();
$sMessage .= "<br /><strong>Nothing has been saved</strong>";
$oForm->declareError($oMorpho->element("pgsql_host"), $sMessage);
$oForm->declareError($oMorpho->element("pgsql_dbname"));
$oForm->declareError($oMorpho->element("pgsql_username"));
$oForm->declareError($oMorpho->element("pgsql_password"));

return;
}

if (($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDB)) !== true) {
$sMessage = "<strong>PostgresSQL error:</strong> These tables, required by Baïkal, are missing: <strong>" . implode(", ", $aMissingTables) . "</strong><br />";
$sMessage .= "You may want create these tables using the file <strong>Core/Resources/Db/PgSQL/db.sql</strong>";
$sMessage .= "<br /><br /><strong>Nothing has been saved</strong>";

$oForm->declareError($oMorpho->element("pgsql"), $sMessage);

return;
}
} else {
Expand Down
68 changes: 68 additions & 0 deletions Core/Frameworks/Flake/Core/Database/Pgsql.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

#################################################################
# Copyright notice
#
# (c) 2013 Kim Lidström <kim@dxtr.im>
# All rights reserved
#
# http://flake.codr.fr
#
# This script is part of the Flake project. The Flake
# project is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# This copyright notice MUST APPEAR in all copies of the script!
#################################################################

namespace Flake\Core\Database;

class Pgsql extends \Flake\Core\Database {
protected $oDb = false; // current DB link
protected $debugOutput = false;
protected $store_lastBuiltQuery = true;
protected $debug_lastBuiltQuery = "";
protected $sHost = "";
protected $sDbName = "";
protected $sUsername = "";
protected $sPassword = "";

public function __construct($sHost, $sDbName, $sUsername, $sPassword) {
$this->sHost = $sHost;
$this->sDbName = $sDbName;
$this->sUsername = $sUsername;
$this->sPassword = $sPassword;

$this->oDb = new \PDO(
'pgsql:host=' . $this->sHost . ';dbname=' . $this->sDbName,
$this->sUsername,
$this->sPassword
);
}

public function tables() {
$aTables = [];

$sSql = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'";
$oStmt = $this->query($sSql);

while (($aRs = $oStmt->fetch()) !== false) {
$aTables[] = array_shift($aRs);
}

asort($aTables);
reset($aTables);

return $aTables;
}
}
Loading

0 comments on commit bed2fc8

Please sign in to comment.