Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostgreSQL backend #1194

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -244,7 +244,7 @@ function isDefault() {

function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
"count(*)",
"count(*) as count",
"calendarinstances",
"calendarid='" . $this->aData["calendarid"] . "'"
);
Expand All @@ -254,7 +254,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
42 changes: 34 additions & 8 deletions Core/Frameworks/Baikal/Model/Config/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ class Database extends \Baikal\Model\Config {
# Default values
protected $aData = [
"sqlite_file" => PROJECT_PATH_SPECIFIC . "db/db.sqlite",
"mysql" => false,
"backend" => "",
"mysql_host" => "",
"mysql_dbname" => "",
"mysql_username" => "",
"mysql_password" => "",
"encryption_key" => "",
"pgsql_host" => "",
"pgsql_dbname" => "",
"pgsql_username" => "",
"pgsql_password" => "",
];

function __construct() {
Expand All @@ -46,6 +50,14 @@ function __construct() {
function formMorphologyForThisModelInstance() {
$oMorpho = new \Formal\Form\Morphology();

$oMorpho->add(new \Formal\Element\Listbox([
"prop" => "backend",
"label" => "Database Backend",
"validation" => "required",
"options" => ['sqlite', 'mysql', 'pgsql'],
"refreshonchange" => true,
]));

$oMorpho->add(new \Formal\Element\Text([
"prop" => "sqlite_file",
"label" => "SQLite file path",
Expand All @@ -54,13 +66,6 @@ function formMorphologyForThisModelInstance() {
"help" => "The absolute server path to the SQLite file",
]));

$oMorpho->add(new \Formal\Element\Checkbox([
"prop" => "mysql",
"label" => "Use MySQL",
"help" => "If checked, Baïkal will use MySQL instead of SQLite.",
"refreshonchange" => true,
]));

$oMorpho->add(new \Formal\Element\Text([
"prop" => "mysql_host",
"label" => "MySQL host",
Expand All @@ -82,6 +87,27 @@ function formMorphologyForThisModelInstance() {
"label" => "MySQL password",
]));

$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
121 changes: 112 additions & 9 deletions Core/Frameworks/BaikalAdmin/Controller/Install/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,22 @@ function execute() {
if (file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) {
require_once PROJECT_PATH_SPECIFIC . "config.system.php";
$this->oModel->set('sqlite_file', PROJECT_SQLITE_FILE);
$this->oModel->set('mysql', PROJECT_DB_MYSQL);
$this->oModel->set('backend', PROJECT_DB_BACKEND);
$this->oModel->set('mysql_host', PROJECT_DB_MYSQL_HOST);
$this->oModel->set('mysql_dbname', PROJECT_DB_MYSQL_DBNAME);
$this->oModel->set('mysql_username', PROJECT_DB_MYSQL_USERNAME);
$this->oModel->set('mysql_password', PROJECT_DB_MYSQL_PASSWORD);
$this->oModel->set('pgsql_host', PROJECT_DB_PGSQL_HOST);
$this->oModel->set('pgsql_dbname', PROJECT_DB_PGSQL_DBNAME);
$this->oModel->set('pgsql_username', PROJECT_DB_PGSQL_USERNAME);
$this->oModel->set('pgsql_password', PROJECT_DB_PGSQL_PASSWORD);
$this->oModel->set('encryption_key', BAIKAL_ENCRYPTION_KEY);
}

$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,11 +103,11 @@ function render() {
return $oView->render();
}

function validateConnection($oForm, $oMorpho) {
function validateMySQLConnection($oForm, $oMorpho) {
if ($oForm->refreshed()) {
return true;
}
$bMySQLEnabled = $oMorpho->element("mysql")->value();
$bMySQLEnabled = $oMorpho->element("backend")->value() == 'mysql';

if ($bMySQLEnabled) {
$sHost = $oMorpho->element("mysql_host")->value();
Expand All @@ -129,7 +133,7 @@ function validateConnection($oForm, $oMorpho) {
$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("mysql"),
$oMorpho->element("backend"),
$sMessage
);
} else {
Expand All @@ -142,7 +146,7 @@ function validateConnection($oForm, $oMorpho) {

return true;
} catch (\Exception $e) {
$oForm->declareError($oMorpho->element("mysql"),
$oForm->declareError($oMorpho->element("backend"),
"Baïkal was not able to establish a connexion to the MySQL database as configured.<br />MySQL says: " . $e->getMessage());
$oForm->declareError($oMorpho->element("mysql_host"));
$oForm->declareError($oMorpho->element("mysql_dbname"));
Expand Down Expand Up @@ -211,10 +215,10 @@ function validateConnection($oForm, $oMorpho) {

function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->submitted()) {
$bMySQL = (intval($oForm->postValue("mysql")) === 1);
$bMySQL = ($oForm->postValue("backend") == 'mysql');
} else {
// oMorpho won't have the values from the model set on it yet
$bMySQL = $this->oModel->get("mysql");
$bMySQL = $this->oModel->get("backend") == 'mysql';
}

if ($bMySQL === true) {
Expand All @@ -226,4 +230,103 @@ function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $
$oMorpho->remove("mysql_password");
}
}

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

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("backend"),
$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("backend"),
"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("backend")->value() == 'mysql') {
$this->validateMySQLConnection($oForm, $oMorpho);
} elseif ($oMorpho->element("backend")->value() == 'pgsql') {
$this->validatePgSQLConnection($oForm, $oMorpho);
}
}

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

public function hidePgSQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
leso-kn marked this conversation as resolved.
Show resolved Hide resolved
if ($oForm->submitted()) {
$bPgSQL = ($oForm->postValue("backend")) == 'pgsql';
} else {
// oMorpho won't have the values from the model set on it yet
$bPgSQL = $this->oModel->get("backend") == '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");
}
}
}
8 changes: 6 additions & 2 deletions Core/Frameworks/BaikalAdmin/Controller/Install/Initialize.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ function execute() {

# Default: PDO::SQLite or PDO::MySQL ?
$aPDODrivers = \PDO::getAvailableDrivers();
if (!in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk()
$oDatabaseConfig->set("mysql", true);
if (in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk()
$oDatabaseConfig->set("backend", 'sqlite');
} elseif (in_array('mysql', $aPDODrivers)) {
$oDatabaseConfig->set("backend", 'mysql');
} else {
$oDatabaseConfig->set("backend", 'pgsql');
}

$oDatabaseConfig->persist();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,14 @@ protected function upgrade($databaseConfig, $sVersionFrom, $sVersionTo) {
$this->aSuccess[] = 'Updated default values in calendarinstances table';
}

if (version_compare($sVersionFrom, '0.10.0', '<')) {
$config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml");

$oConfig = new \Baikal\Model\Config\Database();
$oConfig->set("backend", intval($config['database']['mysql']) === 1 ? 'mysql' : 'sqlite');
$oConfig->persist();
}

$this->updateConfiguredVersion($sVersionTo);

return true;
Expand Down
Loading