Add PostgreSQL backend

Co-authored-by: Kim Lidström <dxtr@users.noreply.github.com>
This commit is contained in:
leso-kn 2024-07-21 18:30:25 +02:00
parent cc3eca1dc5
commit 35a3d018f1
No known key found for this signature in database
GPG key ID: 46333567BC1BF351
12 changed files with 457 additions and 46 deletions

View file

@ -45,8 +45,8 @@ class Tools {
# Asserting PDO::SQLite or PDO::MySQL # Asserting PDO::SQLite or PDO::MySQL
$aPDODrivers = \PDO::getAvailableDrivers(); $aPDODrivers = \PDO::getAvailableDrivers();
if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true)) { if (!in_array('sqlite', $aPDODrivers, true) && !in_array('mysql', $aPDODrivers, true) && !in_array('pgsql', $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.'); 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 # Assert that the temp folder is writable

View file

@ -244,7 +244,7 @@ class Calendar extends \Flake\Core\Model\Db {
function hasInstances() { function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery( $rSql = $GLOBALS["DB"]->exec_SELECTquery(
"count(*)", "count(*) as count",
"calendarinstances", "calendarinstances",
"calendarid='" . $this->aData["calendarid"] . "'" "calendarid='" . $this->aData["calendarid"] . "'"
); );
@ -254,7 +254,7 @@ class Calendar extends \Flake\Core\Model\Db {
} else { } else {
reset($aRs); reset($aRs);
return $aRs["count(*)"] > 1; return $aRs["count"] > 1;
} }
} }

View file

@ -38,7 +38,7 @@ class Calendar extends \Flake\Core\Model\Db {
function hasInstances() { function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery( $rSql = $GLOBALS["DB"]->exec_SELECTquery(
"count(*)", "count(*) as count",
"calendarinstances", "calendarinstances",
"calendarid='" . $this->aData["id"] . "'" "calendarid='" . $this->aData["id"] . "'"
); );
@ -48,7 +48,7 @@ class Calendar extends \Flake\Core\Model\Db {
} else { } else {
reset($aRs); reset($aRs);
return $aRs["count(*)"] > 1; return $aRs["count"] > 1;
} }
} }

View file

@ -31,12 +31,16 @@ class Database extends \Baikal\Model\Config {
# Default values # Default values
protected $aData = [ protected $aData = [
"sqlite_file" => PROJECT_PATH_SPECIFIC . "db/db.sqlite", "sqlite_file" => PROJECT_PATH_SPECIFIC . "db/db.sqlite",
"mysql" => false, "backend" => "",
"mysql_host" => "", "mysql_host" => "",
"mysql_dbname" => "", "mysql_dbname" => "",
"mysql_username" => "", "mysql_username" => "",
"mysql_password" => "", "mysql_password" => "",
"encryption_key" => "", "encryption_key" => "",
"pgsql_host" => "",
"pgsql_dbname" => "",
"pgsql_username" => "",
"pgsql_password" => "",
]; ];
function __construct() { function __construct() {
@ -46,6 +50,14 @@ class Database extends \Baikal\Model\Config {
function formMorphologyForThisModelInstance() { function formMorphologyForThisModelInstance() {
$oMorpho = new \Formal\Form\Morphology(); $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([ $oMorpho->add(new \Formal\Element\Text([
"prop" => "sqlite_file", "prop" => "sqlite_file",
"label" => "SQLite file path", "label" => "SQLite file path",
@ -54,13 +66,6 @@ class Database extends \Baikal\Model\Config {
"help" => "The absolute server path to the SQLite file", "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([ $oMorpho->add(new \Formal\Element\Text([
"prop" => "mysql_host", "prop" => "mysql_host",
"label" => "MySQL host", "label" => "MySQL host",
@ -82,6 +87,27 @@ class Database extends \Baikal\Model\Config {
"label" => "MySQL password", "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; return $oMorpho;
} }

View file

@ -38,18 +38,22 @@ class Database extends \Flake\Core\Controller {
if (file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) { if (file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) {
require_once 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('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_host', PROJECT_DB_MYSQL_HOST);
$this->oModel->set('mysql_dbname', PROJECT_DB_MYSQL_DBNAME); $this->oModel->set('mysql_dbname', PROJECT_DB_MYSQL_DBNAME);
$this->oModel->set('mysql_username', PROJECT_DB_MYSQL_USERNAME); $this->oModel->set('mysql_username', PROJECT_DB_MYSQL_USERNAME);
$this->oModel->set('mysql_password', PROJECT_DB_MYSQL_PASSWORD); $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->oModel->set('encryption_key', BAIKAL_ENCRYPTION_KEY);
} }
$this->oForm = $this->oModel->formForThisModelInstance([ $this->oForm = $this->oModel->formForThisModelInstance([
"close" => false, "close" => false,
"hook.validation" => [$this, "validateConnection"], "hook.validation" => [$this, "validateSQLConnection"],
"hook.morphology" => [$this, "hideMySQLFieldWhenNeeded"], "hook.morphology" => [$this, "hideSQLFieldWhenNeeded"],
]); ]);
if ($this->oForm->submitted()) { if ($this->oForm->submitted()) {
@ -99,11 +103,11 @@ class Database extends \Flake\Core\Controller {
return $oView->render(); return $oView->render();
} }
function validateConnection($oForm, $oMorpho) { function validateMySQLConnection($oForm, $oMorpho) {
if ($oForm->refreshed()) { if ($oForm->refreshed()) {
return true; return true;
} }
$bMySQLEnabled = $oMorpho->element("mysql")->value(); $bMySQLEnabled = $oMorpho->element("backend")->value() == 'mysql';
if ($bMySQLEnabled) { if ($bMySQLEnabled) {
$sHost = $oMorpho->element("mysql_host")->value(); $sHost = $oMorpho->element("mysql_host")->value();
@ -129,7 +133,7 @@ class Database extends \Flake\Core\Controller {
$sMessage .= "<br /><p>Nothing has been saved. <strong>Please, add these tables to the database before pursuing Baïkal initialization.</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( $oForm->declareError(
$oMorpho->element("mysql"), $oMorpho->element("backend"),
$sMessage $sMessage
); );
} else { } else {
@ -142,7 +146,7 @@ class Database extends \Flake\Core\Controller {
return true; return true;
} catch (\Exception $e) { } 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()); "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_host"));
$oForm->declareError($oMorpho->element("mysql_dbname")); $oForm->declareError($oMorpho->element("mysql_dbname"));
@ -211,10 +215,10 @@ class Database extends \Flake\Core\Controller {
function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) { function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->submitted()) { if ($oForm->submitted()) {
$bMySQL = (intval($oForm->postValue("mysql")) === 1); $bMySQL = ($oForm->postValue("backend") == 'mysql');
} else { } else {
// oMorpho won't have the values from the model set on it yet // 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) { if ($bMySQL === true) {
@ -226,4 +230,103 @@ class Database extends \Flake\Core\Controller {
$oMorpho->remove("mysql_password"); $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) {
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");
}
}
} }

View file

@ -82,8 +82,12 @@ class Initialize extends \Flake\Core\Controller {
# Default: PDO::SQLite or PDO::MySQL ? # Default: PDO::SQLite or PDO::MySQL ?
$aPDODrivers = \PDO::getAvailableDrivers(); $aPDODrivers = \PDO::getAvailableDrivers();
if (!in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk() if (in_array('sqlite', $aPDODrivers)) { # PDO::MySQL is already asserted in \Baikal\Core\Tools::assertEnvironmentIsOk()
$oDatabaseConfig->set("mysql", true); $oDatabaseConfig->set("backend", 'sqlite');
} elseif (in_array('mysql', $aPDODrivers)) {
$oDatabaseConfig->set("backend", 'mysql');
} else {
$oDatabaseConfig->set("backend", 'pgsql');
} }
$oDatabaseConfig->persist(); $oDatabaseConfig->persist();

View file

@ -505,6 +505,14 @@ SQL
$this->aSuccess[] = 'Updated default values in calendarinstances table'; $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); $this->updateConfiguredVersion($sVersionTo);
return true; return true;

View file

@ -74,61 +74,81 @@ class Database extends \Flake\Core\Controller {
function morphologyHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) { function morphologyHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->submitted()) { if ($oForm->submitted()) {
$bMySQL = (intval($oForm->postValue("mysql")) === 1); $bMySQL = ($oForm->postValue("backend") == 'mysql');
$bPgSQL = ($oForm->postValue("backend") == 'pgsql');
} else { } else {
try { try {
$config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml"); $config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml");
} catch (\Exception $e) { } catch (\Exception $e) {
error_log('Error reading baikal.yaml file : ' . $e->getMessage()); error_log('Error reading baikal.yaml file : ' . $e->getMessage());
} }
$bMySQL = $config['database']['mysql'] ?? true; $bMySQL = $config['database']['backend'] == 'mysql';
$bPgSQL = $config['database']['backend'] == 'pgsql';
} }
if ($bMySQL === true) { if ($bMySQL === true || $bPgSQL === true) {
$oMorpho->remove("sqlite_file"); $oMorpho->remove("sqlite_file");
} else { }
if (!$bMySQL) {
$oMorpho->remove("mysql_host"); $oMorpho->remove("mysql_host");
$oMorpho->remove("mysql_dbname"); $oMorpho->remove("mysql_dbname");
$oMorpho->remove("mysql_username"); $oMorpho->remove("mysql_username");
$oMorpho->remove("mysql_password"); $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) { function validationHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
if ($oForm->refreshed()) { if ($oForm->refreshed()) {
return true; return true;
} }
if (intval($oForm->modelInstance()->get("mysql")) === 1) { if ($oForm->modelInstance()->get("backend") == 'mysql' || $oForm->modelInstance()->get("backend") == 'pgsql') {
# We have to check the MySQL connection $dbBackendName = $oForm->modelInstance()->get("backend") == 'pgsql' ? 'PostgreSQL' : 'MySQL';
$sHost = $oForm->modelInstance()->get("mysql_host"); $dbBackendPrefix = $oForm->modelInstance()->get("backend");
$sDbName = $oForm->modelInstance()->get("mysql_dbname");
$sUsername = $oForm->modelInstance()->get("mysql_username"); # We have to check the MySQL or PostgreSQL connection
$sPassword = $oForm->modelInstance()->get("mysql_password"); $sHost = $oForm->modelInstance()->get("{$dbBackendPrefix}_host");
$sDbName = $oForm->modelInstance()->get("{$dbBackendPrefix}_dbname");
$sUsername = $oForm->modelInstance()->get("{$dbBackendPrefix}_username");
$sPassword = $oForm->modelInstance()->get("{$dbBackendPrefix}_password");
try { try {
$oDB = new \Flake\Core\Database\Mysql( $oDB = (($oForm->modelInstance()->get("backend")) == 'pgsql'
) ? new \Flake\Core\Database\Pgsql(
$sHost,
$sDbName,
$sUsername,
$sPassword
) : new \Flake\Core\Database\Mysql(
$sHost, $sHost,
$sDbName, $sDbName,
$sUsername, $sUsername,
$sPassword $sPassword
); );
} catch (\Exception $e) { } catch (\Exception $e) {
$sMessage = "<strong>MySQL error:</strong> " . $e->getMessage(); $sMessage = "<strong>{$dbBackendName} error:</strong> " . $e->getMessage();
$sMessage .= "<br /><strong>Nothing has been saved</strong>"; $sMessage .= "<br /><strong>Nothing has been saved</strong>";
$oForm->declareError($oMorpho->element("mysql_host"), $sMessage); $oForm->declareError($oMorpho->element("{$dbBackendPrefix}_host"), $sMessage);
$oForm->declareError($oMorpho->element("mysql_dbname")); $oForm->declareError($oMorpho->element("{$dbBackendPrefix}_dbname"));
$oForm->declareError($oMorpho->element("mysql_username")); $oForm->declareError($oMorpho->element("{$dbBackendPrefix}_username"));
$oForm->declareError($oMorpho->element("mysql_password")); $oForm->declareError($oMorpho->element("{$dbBackendPrefix}_password"));
return; return;
} }
if (($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDB)) !== true) { if (($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDB)) !== true) {
$sMessage = "<strong>MySQL error:</strong> These tables, required by Baïkal, are missing: <strong>" . implode(", ", $aMissingTables) . "</strong><br />"; $sMessage = "<strong>{$dbBackendName} 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/MySQL/db.sql</strong>"; $sMessage .= "You may want create these tables using the file <strong>Core/Resources/Db/{$dbBackendName}/db.sql</strong>";
$sMessage .= "<br /><br /><strong>Nothing has been saved</strong>"; $sMessage .= "<br /><br /><strong>Nothing has been saved</strong>";
$oForm->declareError($oMorpho->element("mysql"), $sMessage); $oForm->declareError($oMorpho->element("backend"), $sMessage);
return; return;
} }

View file

@ -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;
}
}

View file

@ -273,8 +273,12 @@ class Framework extends \Flake\Core\Framework {
if (defined("BAIKAL_CONTEXT_INSTALL") && (!isset($config['system']['configured_version']) || $config['system']['configured_version'] === BAIKAL_VERSION)) { if (defined("BAIKAL_CONTEXT_INSTALL") && (!isset($config['system']['configured_version']) || $config['system']['configured_version'] === BAIKAL_VERSION)) {
return true; return true;
} }
if ($config['database']['mysql'] === true) { # Config key 'mysql' kept for backwards compatibility
$legacyMysql = key_exists('mysql', $config['database']) && $config['database']['mysql'] === true;
if ($legacyMysql || $config['database']['backend'] === 'mysql') {
self::initDbMysql($config); self::initDbMysql($config);
} elseif ($config['database']['backend'] === 'pgsql') {
self::initDbPgsql($config);
} else { } else {
self::initDbSqlite($config); self::initDbSqlite($config);
} }
@ -339,6 +343,38 @@ class Framework extends \Flake\Core\Framework {
return true; return true;
} }
protected static function initDbPgsql(array $config) {
if (!$config['database']['pgsql_host']) {
exit("<h3>The constant PROJECT_DB_PGSQL_HOST, containing the PostgreSQL host name, is not set.<br />You should set it in config/baikal.yaml</h3>");
}
if (!$config['database']['pgsql_dbname']) {
exit("<h3>The constant PROJECT_DB_PGSQL_DBNAME, containing the PostgreSQL database name, is not set.<br />You should set it in config/baikal.yaml</h3>");
}
try {
$GLOBALS["DB"] = new \Flake\Core\Database\Pgsql(
$config['database']['pgsql_host'],
$config['database']['pgsql_dbname'],
$config['database']['pgsql_username'],
$config['database']['pgsql_password']
);
$GLOBALS["DB"]->query("SET NAMES 'UTF8'");
} catch (\Exception $e) {
$message = "Baïkal was not able to establish a connection to the configured PostgreSQL database (as configured in config/baikal.yaml).";
if (!$config['database']['pgsql_username']) {
exit("<h3>$message Note: The constant PROJECT_DB_PGSQL_USERNAME, containing the PostgreSQL database username, is not set. If your database requires a username you should set it in config/baikal.yaml.</h3>");
}
if ($config['database']['pgsql_password'] === null) {
exit("<h3>$message Note: The constant PROJECT_DB_PGSQL_PASSWORD, containing the PostgreSQL database password, is not set. If your database requires a password you should set it in config/baikal.yaml.</h3>");
}
exit("<h3>$message</h3>");
}
}
static function isDBInitialized() { static function isDBInitialized() {
return isset($GLOBALS["DB"]) && \Flake\Util\Tools::is_a($GLOBALS["DB"], "\Flake\Core\Database"); return isset($GLOBALS["DB"]) && \Flake\Util\Tools::is_a($GLOBALS["DB"], "\Flake\Core\Database");
} }

View file

@ -0,0 +1,142 @@
CREATE TABLE addressbooks (
id SERIAL PRIMARY KEY,
principaluri TEXT,
displayname VARCHAR(255),
uri TEXT,
description TEXT,
synctoken INT CHECK (synctoken > 0) NOT NULL DEFAULT '1'
);
CREATE TABLE cards (
id SERIAL PRIMARY KEY,
addressbookid INT CHECK (addressbookid > 0) NOT NULL,
carddata TEXT,
uri TEXT,
lastmodified INT CHECK (lastmodified > 0),
etag TEXT,
size INT CHECK (size > 0) NOT NULL
);
CREATE TABLE addressbookchanges (
id SERIAL PRIMARY KEY,
uri TEXT NOT NULL,
synctoken INT CHECK (synctoken > 0) NOT NULL,
addressbookid INT CHECK (addressbookid > 0) NOT NULL,
operation SMALLINT NOT NULL
);
CREATE INDEX addressbookid_synctoken ON addressbookchanges (addressbookid, synctoken);
CREATE TABLE calendarobjects (
id SERIAL PRIMARY KEY,
calendardata TEXT,
uri TEXT,
calendarid INTEGER CHECK (calendarid > 0) NOT NULL,
lastmodified INT CHECK (lastmodified > 0),
etag TEXT,
size INT CHECK (size > 0) NOT NULL,
componenttype TEXT,
firstoccurence INT CHECK (firstoccurence > 0),
lastoccurence INT CHECK (lastoccurence > 0),
uid TEXT
);
CREATE TABLE calendars (
id SERIAL PRIMARY KEY,
synctoken INTEGER CHECK (synctoken > 0) NOT NULL DEFAULT '1',
components TEXT
);
CREATE TABLE calendarinstances (
id SERIAL PRIMARY KEY,
calendarid INTEGER CHECK (calendarid > 0) NOT NULL,
principaluri TEXT,
access SMALLINT NOT NULL DEFAULT '1',
displayname VARCHAR(100),
uri TEXT,
description TEXT,
calendarorder INT CHECK (calendarorder >= 0) NOT NULL DEFAULT '0',
calendarcolor TEXT,
timezone TEXT,
transparent SMALLINT NOT NULL DEFAULT '0',
share_href TEXT,
share_displayname VARCHAR(100),
share_invitestatus SMALLINT NOT NULL DEFAULT '2'
);
CREATE TABLE calendarchanges (
id SERIAL PRIMARY KEY,
uri TEXT NOT NULL,
synctoken INT CHECK (synctoken > 0) NOT NULL,
calendarid INT CHECK (calendarid > 0) NOT NULL,
operation SMALLINT NOT NULL
);
CREATE INDEX calendarid_synctoken ON calendarchanges (calendarid, synctoken);
CREATE TABLE calendarsubscriptions (
id SERIAL PRIMARY KEY,
uri TEXT NOT NULL,
principaluri TEXT NOT NULL,
source TEXT,
displayname VARCHAR(100),
refreshrate VARCHAR(10),
calendarorder INT CHECK (calendarorder >= 0) NOT NULL DEFAULT '0',
calendarcolor TEXT,
striptodos SMALLINT NULL,
stripalarms SMALLINT NULL,
stripattachments SMALLINT NULL,
lastmodified INT CHECK (lastmodified > 0)
);
CREATE TABLE schedulingobjects (
id SERIAL PRIMARY KEY,
principaluri TEXT,
calendardata TEXT,
uri TEXT,
lastmodified INT CHECK (lastmodified > 0),
etag TEXT,
size INT CHECK (size > 0) NOT NULL
);
CREATE TABLE locks (
id SERIAL PRIMARY KEY,
owner VARCHAR(100),
timeout INTEGER CHECK (timeout > 0),
created INTEGER,
token TEXT,
scope SMALLINT,
depth SMALLINT,
uri TEXT
);
CREATE INDEX ON locks (token);
CREATE INDEX ON locks (uri);
CREATE TABLE principals (
id SERIAL PRIMARY KEY,
uri TEXT NOT NULL,
email TEXT,
displayname VARCHAR(80)
);
CREATE TABLE groupmembers (
id SERIAL PRIMARY KEY,
principal_id INTEGER CHECK (principal_id > 0) NOT NULL,
member_id INTEGER CHECK (member_id > 0) NOT NULL
);
CREATE TABLE propertystorage (
id SERIAL PRIMARY KEY,
path TEXT NOT NULL,
name TEXT NOT NULL,
valuetype INT CHECK (valuetype > 0),
value TEXT
);
CREATE UNIQUE INDEX path_property ON propertystorage (path, name);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT,
digesta1 TEXT
);

View file

@ -11,9 +11,13 @@ system:
base_uri: '' base_uri: ''
database: database:
encryption_key: 5d3f0fa0192e3058ea70f1bb20924add encryption_key: 5d3f0fa0192e3058ea70f1bb20924add
backend: 'mysql'
sqlite_file: "absolute/path/to/Specific/db/db.sqlite" sqlite_file: "absolute/path/to/Specific/db/db.sqlite"
mysql: true
mysql_host: 'localhost' mysql_host: 'localhost'
mysql_dbname: 'baikal' mysql_dbname: 'baikal'
mysql_username: 'baikal' mysql_username: 'baikal'
mysql_password: 'baikal' mysql_password: 'baikal'
pgsql_host: 'localhost'
pgsql_dbname: 'baikal'
pgsql_username: 'baikal'
pgsql_password: 'baikal'