Skip to content

Commit c5227aa

Browse files
leso-kndxtr
andauthored
Add PostgreSQL backend (#1194)
Co-authored-by: Kim Lidström <[email protected]>
1 parent cc3eca1 commit c5227aa

File tree

12 files changed

+466
-46
lines changed

12 files changed

+466
-46
lines changed

Core/Frameworks/Baikal/Core/Tools.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ static function assertEnvironmentIsOk() {
4545

4646
# Asserting PDO::SQLite or PDO::MySQL
4747
$aPDODrivers = \PDO::getAvailableDrivers();
48-
if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true)) {
49-
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.');
48+
if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true) && !in_array('pgsql', $aPDODrivers, true)) {
49+
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.');
5050
}
5151

5252
# Assert that the temp folder is writable

Core/Frameworks/Baikal/Model/Calendar.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ function isDefault() {
244244

245245
function hasInstances() {
246246
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
247-
"count(*)",
247+
"count(*) as count",
248248
"calendarinstances",
249249
"calendarid='" . $this->aData["calendarid"] . "'"
250250
);
@@ -254,7 +254,7 @@ function hasInstances() {
254254
} else {
255255
reset($aRs);
256256

257-
return $aRs["count(*)"] > 1;
257+
return $aRs["count"] > 1;
258258
}
259259
}
260260

Core/Frameworks/Baikal/Model/Calendar/Calendar.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Calendar extends \Flake\Core\Model\Db {
3838

3939
function hasInstances() {
4040
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
41-
"count(*)",
41+
"count(*) as count",
4242
"calendarinstances",
4343
"calendarid='" . $this->aData["id"] . "'"
4444
);
@@ -48,7 +48,7 @@ function hasInstances() {
4848
} else {
4949
reset($aRs);
5050

51-
return $aRs["count(*)"] > 1;
51+
return $aRs["count"] > 1;
5252
}
5353
}
5454

Core/Frameworks/Baikal/Model/Config/Database.php

+34-8
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ class Database extends \Baikal\Model\Config {
3131
# Default values
3232
protected $aData = [
3333
"sqlite_file" => PROJECT_PATH_SPECIFIC . "db/db.sqlite",
34-
"mysql" => false,
34+
"backend" => "",
3535
"mysql_host" => "",
3636
"mysql_dbname" => "",
3737
"mysql_username" => "",
3838
"mysql_password" => "",
3939
"encryption_key" => "",
40+
"pgsql_host" => "",
41+
"pgsql_dbname" => "",
42+
"pgsql_username" => "",
43+
"pgsql_password" => "",
4044
];
4145

4246
function __construct() {
@@ -46,6 +50,14 @@ function __construct() {
4650
function formMorphologyForThisModelInstance() {
4751
$oMorpho = new \Formal\Form\Morphology();
4852

53+
$oMorpho->add(new \Formal\Element\Listbox([
54+
"prop" => "backend",
55+
"label" => "Database Backend",
56+
"validation" => "required",
57+
"options" => ['sqlite', 'mysql', 'pgsql'],
58+
"refreshonchange" => true,
59+
]));
60+
4961
$oMorpho->add(new \Formal\Element\Text([
5062
"prop" => "sqlite_file",
5163
"label" => "SQLite file path",
@@ -54,13 +66,6 @@ function formMorphologyForThisModelInstance() {
5466
"help" => "The absolute server path to the SQLite file",
5567
]));
5668

57-
$oMorpho->add(new \Formal\Element\Checkbox([
58-
"prop" => "mysql",
59-
"label" => "Use MySQL",
60-
"help" => "If checked, Baïkal will use MySQL instead of SQLite.",
61-
"refreshonchange" => true,
62-
]));
63-
6469
$oMorpho->add(new \Formal\Element\Text([
6570
"prop" => "mysql_host",
6671
"label" => "MySQL host",
@@ -82,6 +87,27 @@ function formMorphologyForThisModelInstance() {
8287
"label" => "MySQL password",
8388
]));
8489

90+
$oMorpho->add(new \Formal\Element\Text([
91+
"prop" => "pgsql_host",
92+
"label" => "PostgreSQL host",
93+
"help" => "Host ip or name, including <strong>':portnumber'</strong> if port is not the default one (?)",
94+
]));
95+
96+
$oMorpho->add(new \Formal\Element\Text([
97+
"prop" => "pgsql_dbname",
98+
"label" => "PostgreSQL database name",
99+
]));
100+
101+
$oMorpho->add(new \Formal\Element\Text([
102+
"prop" => "pgsql_username",
103+
"label" => "PostgreSQL username",
104+
]));
105+
106+
$oMorpho->add(new \Formal\Element\Password([
107+
"prop" => "pgsql_password",
108+
"label" => "PostgreSQL password",
109+
]));
110+
85111
return $oMorpho;
86112
}
87113

Core/Frameworks/BaikalAdmin/Controller/Install/Database.php

+112-9
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,22 @@ function execute() {
3838
if (file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) {
3939
require_once PROJECT_PATH_SPECIFIC . "config.system.php";
4040
$this->oModel->set('sqlite_file', PROJECT_SQLITE_FILE);
41-
$this->oModel->set('mysql', PROJECT_DB_MYSQL);
41+
$this->oModel->set('backend', PROJECT_DB_BACKEND);
4242
$this->oModel->set('mysql_host', PROJECT_DB_MYSQL_HOST);
4343
$this->oModel->set('mysql_dbname', PROJECT_DB_MYSQL_DBNAME);
4444
$this->oModel->set('mysql_username', PROJECT_DB_MYSQL_USERNAME);
4545
$this->oModel->set('mysql_password', PROJECT_DB_MYSQL_PASSWORD);
46+
$this->oModel->set('pgsql_host', PROJECT_DB_PGSQL_HOST);
47+
$this->oModel->set('pgsql_dbname', PROJECT_DB_PGSQL_DBNAME);
48+
$this->oModel->set('pgsql_username', PROJECT_DB_PGSQL_USERNAME);
49+
$this->oModel->set('pgsql_password', PROJECT_DB_PGSQL_PASSWORD);
4650
$this->oModel->set('encryption_key', BAIKAL_ENCRYPTION_KEY);
4751
}
4852

4953
$this->oForm = $this->oModel->formForThisModelInstance([
5054
"close" => false,
51-
"hook.validation" => [$this, "validateConnection"],
52-
"hook.morphology" => [$this, "hideMySQLFieldWhenNeeded"],
55+
"hook.validation" => [$this, "validateSQLConnection"],
56+
"hook.morphology" => [$this, "hideSQLFieldWhenNeeded"],
5357
]);
5458

5559
if ($this->oForm->submitted()) {
@@ -99,11 +103,11 @@ function render() {
99103
return $oView->render();
100104
}
101105

102-
function validateConnection($oForm, $oMorpho) {
106+
function validateMySQLConnection($oForm, $oMorpho) {
103107
if ($oForm->refreshed()) {
104108
return true;
105109
}
106-
$bMySQLEnabled = $oMorpho->element("mysql")->value();
110+
$bMySQLEnabled = $oMorpho->element("backend")->value() == 'mysql';
107111

108112
if ($bMySQLEnabled) {
109113
$sHost = $oMorpho->element("mysql_host")->value();
@@ -129,7 +133,7 @@ function validateConnection($oForm, $oMorpho) {
129133
$sMessage .= "<br /><p>Nothing has been saved. <strong>Please, add these tables to the database before pursuing Baïkal initialization.</strong></p>";
130134

131135
$oForm->declareError(
132-
$oMorpho->element("mysql"),
136+
$oMorpho->element("backend"),
133137
$sMessage
134138
);
135139
} else {
@@ -142,7 +146,7 @@ function validateConnection($oForm, $oMorpho) {
142146

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

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

220224
if ($bMySQL === true) {
@@ -226,4 +230,103 @@ function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $
226230
$oMorpho->remove("mysql_password");
227231
}
228232
}
233+
234+
function validatePgSQLConnection($oForm, $oMorpho) {
235+
$bPgSqlEnabled = $oMorpho->element("backend")->value() == 'pgsql';
236+
237+
if ($bPgSqlEnabled) {
238+
$sHost = $oMorpho->element("pgsql_host")->value();
239+
$sDbname = $oMorpho->element("pgsql_dbname")->value();
240+
$sUsername = $oMorpho->element("pgsql_username")->value();
241+
$sPassword = $oMorpho->element("pgsql_password")->value();
242+
243+
try {
244+
$oDb = new \Flake\Core\Database\Pgsql(
245+
$sHost,
246+
$sDbname,
247+
$sUsername,
248+
$sPassword
249+
);
250+
251+
if (($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDb)) !== true) {
252+
# Checking if all tables are missing
253+
$aRequiredTables = \Baikal\Core\Tools::getRequiredTablesList();
254+
if (count($aRequiredTables) !== count($aMissingTables)) {
255+
$sMessage = "<br /><p><strong>Database is not structurally complete.</strong></p>";
256+
$sMessage .= "<p>Missing tables are: <strong>" . implode("</strong>, <strong>", $aMissingTables) . "</strong></p>";
257+
$sMessage .= "<p>You will find the SQL definition of Baïkal tables in this file: <strong>Core/Resources/Db/PgSQL/db.sql</strong></p>";
258+
$sMessage .= "<br /><p>Nothing has been saved. <strong>Please, add these tables to the database before pursuing Baïkal initialization.</strong></p>";
259+
260+
$oForm->declareError(
261+
$oMorpho->element("backend"),
262+
$sMessage
263+
);
264+
} else {
265+
# All tables are missing
266+
# We add these tables ourselves to the database, to initialize Baïkal
267+
$sSqlDefinition = file_get_contents(PROJECT_PATH_CORERESOURCES . "Db/PgSQL/db.sql");
268+
$oDb->getPDO()->exec($sSqlDefinition);
269+
}
270+
}
271+
272+
return true;
273+
} catch (\Exception $e) {
274+
$oForm->declareError(
275+
$oMorpho->element("backend"),
276+
"Baïkal was not able to establish a connexion to the PostgreSQL database as configured.<br />PostgreSQL says: " . $e->getMessage()
277+
);
278+
279+
$oForm->declareError(
280+
$oMorpho->element("pgsql_host")
281+
);
282+
283+
$oForm->declareError(
284+
$oMorpho->element("pgsql_dbname")
285+
);
286+
287+
$oForm->declareError(
288+
$oMorpho->element("pgsql_username")
289+
);
290+
291+
$oForm->declareError(
292+
$oMorpho->element("pgsql_password")
293+
);
294+
}
295+
}
296+
}
297+
298+
public function validateSQLConnection($oForm, $oMorpho) {
299+
if ($oMorpho->element("backend")->value() == 'mysql') {
300+
$this->validateMySQLConnection($oForm, $oMorpho);
301+
} elseif ($oMorpho->element("backend")->value() == 'pgsql') {
302+
$this->validatePgSQLConnection($oForm, $oMorpho);
303+
}
304+
}
305+
306+
public function hideSqlFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
307+
if ($oMorpho->element("backend")->value() == 'mysql') {
308+
$this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
309+
} elseif ($oMorpho->element("backend")->value() == 'pgsql') {
310+
$this->hidePgSQLFieldWhenNeeded($oForm, $oMorpho);
311+
}
312+
}
313+
314+
public function hidePgSQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
315+
if ($oForm->submitted()) {
316+
$bPgSQL = ($oForm->postValue("backend")) == 'pgsql';
317+
} else {
318+
// oMorpho won't have the values from the model set on it yet
319+
$bPgSQL = $this->oModel->get("backend") == 'pgsql';
320+
}
321+
322+
if ($bPgSQL === true) {
323+
$oMorpho->remove("sqlite_file");
324+
$this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
325+
} else {
326+
$oMorpho->remove("pgsql_host");
327+
$oMorpho->remove("pgsql_dbname");
328+
$oMorpho->remove("pgsql_username");
329+
$oMorpho->remove("pgsql_password");
330+
}
331+
}
229332
}

Core/Frameworks/BaikalAdmin/Controller/Install/Initialize.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ function execute() {
8282

8383
# Default: PDO::SQLite or PDO::MySQL ?
8484
$aPDODrivers = \PDO::getAvailableDrivers();
85-
if (!in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk()
86-
$oDatabaseConfig->set("mysql", true);
85+
if (in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk()
86+
$oDatabaseConfig->set("backend", 'sqlite');
87+
} elseif (in_array('mysql', $aPDODrivers)) {
88+
$oDatabaseConfig->set("backend", 'mysql');
89+
} else {
90+
$oDatabaseConfig->set("backend", 'pgsql');
8791
}
8892

8993
$oDatabaseConfig->persist();

Core/Frameworks/BaikalAdmin/Controller/Install/VersionUpgrade.php

+8
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,14 @@ protected function upgrade($databaseConfig, $sVersionFrom, $sVersionTo) {
505505
$this->aSuccess[] = 'Updated default values in calendarinstances table';
506506
}
507507

508+
if (version_compare($sVersionFrom, '0.10.0', '<')) {
509+
$config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml");
510+
511+
$oConfig = new \Baikal\Model\Config\Database();
512+
$oConfig->set("backend", intval($config['database']['mysql']) === 1 ? 'mysql' : 'sqlite');
513+
$oConfig->persist();
514+
}
515+
508516
$this->updateConfiguredVersion($sVersionTo);
509517

510518
return true;

0 commit comments

Comments
 (0)