Skip to content
Merged
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
101 changes: 65 additions & 36 deletions src/Database/PicoDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ private function connectSqlite()
* Connect to the RDMS (Relational Database Management System).
*
* Establishes a connection to an RDMS database using the provided credentials and optionally selects
* a specific database based on the provided flag. Sets the time zone, charset, and handles schema settings
* for PostgreSQL.
* a specific database based on the provided flag. Sets the time zone for the connection and handles
* schema settings for PostgreSQL. Charset is also set based on the provided configuration.
*
* @param bool $withDatabase Flag to select the database when connected (default is true).
* @return bool True if the connection is successful, false if it fails.
Expand All @@ -385,56 +385,83 @@ private function connectRDMS($withDatabase = true)
$timeZoneOffset = date("P");
try {
$connectionString = $this->constructConnectionString($withDatabase);
$charset = $this->databaseCredentials->getCharset();


// Check for database username configuration
if (!$this->databaseCredentials->issetUsername()) {
throw new InvalidDatabaseConfiguration("Database username may not be empty. Please check your database configuration!");
}

// Initialize the query to set the timezone
$initialQueries = "SET time_zone = '$timeZoneOffset';";

// Get charset from database credentials
$charset = addslashes($this->databaseCredentials->getCharset());

// Initial queries to set timezone and charset
$initialQueries = "SET time_zone = '$timeZoneOffset'; ";
if(isset($charset) && !empty($charset)) {
$charset = addslashes($charset);
$initialQueries .= "SET NAMES '$charset'; ";
}
// Handle PostgreSQL-specific connection settings
if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) {

// Set schema for PostgreSQL
if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL &&
$this->databaseCredentials->getDatabaseSchema() != null &&
$this->databaseCredentials->getDatabaseSchema() != "") {
$initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema() . ";";
}
// Set charset for PostgreSQL if provided (PostgreSQL does not use `SET NAMES`, but you can set the encoding)
if ($charset) {
$initialQueries .= "SET CLIENT_ENCODING TO '$charset';";
}

// Additional options for PDO
$pdoOptions = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
// Set schema for PostgreSQL if it is provided
if ($this->databaseCredentials->getDatabaseSchema() != null && $this->databaseCredentials->getDatabaseSchema() != "") {
$initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema() . ";";
}

// Additional options specific to MySQL/MariaDB
if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL ||
$this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MARIADB) {
$pdoOptions[PDO::MYSQL_ATTR_INIT_COMMAND] = $initialQueries;
}
// PostgreSQL connection setup
$this->databaseConnection = new PDO(
$connectionString,
$this->databaseCredentials->getUsername(),
$this->databaseCredentials->getPassword(),
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
);

// Execute the initial queries (timezone, charset, schema) in PostgreSQL
if (!empty($initialQueries)) {
$this->databaseConnection->exec($initialQueries);
}

$this->databaseConnection = new PDO(
$connectionString,
$this->databaseCredentials->getUsername(),
$this->databaseCredentials->getPassword(),
$pdoOptions
);
}
// Handle MySQL-specific connection settings
elseif ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) {

// Add charset to the initial queries for MySQL
if ($charset) {
$initialQueries .= "SET NAMES '$charset';"; // Set charset for MySQL
}

// Execute initial queries for PostgreSQL
if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) {
$this->databaseConnection->exec($initialQueries);
// MySQL connection setup
$this->databaseConnection = new PDO(
$connectionString,
$this->databaseCredentials->getUsername(),
$this->databaseCredentials->getPassword(),
[
PDO::MYSQL_ATTR_INIT_COMMAND => $initialQueries,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_FOUND_ROWS => true
]
);
}
// If the database type is neither MySQL nor PostgreSQL, throw an exception
else {
throw new PDOException("Unsupported database type: " . $this->getDatabaseType());
}

// Log successful connection
$connected = true;
$this->connected = $connected;
} catch (Exception $e) {
error_log('ERR ' . $e->getMessage());
// Handle connection errors
throw new PDOException($e->getMessage(), intval($e->getCode()));
}
return $connected;
}


/**
* Determine the database type based on the provided database type string.
Expand Down Expand Up @@ -497,6 +524,7 @@ private function getDbDriver($databaseType)
}
}


/**
* Create a connection string.
*
Expand All @@ -521,12 +549,12 @@ private function constructConnectionString($withDatabase = true)
$emptyValue .= $emptyName ? "{database_name}" : "";
throw new InvalidDatabaseConfiguration("Invalid database configuration. $emptyValue. Please check your database configuration!");
}
return $this->getDbDriver($this->databaseCredentials->getDriver()) . ':host=' . $this->databaseCredentials->getHost() . '; port=' . ((int) $this->databaseCredentials->getPort()) . '; dbname=' . $this->databaseCredentials->getDatabaseName();
return $this->getDbDriver($this->databaseCredentials->getDriver()) . ':host=' . $this->databaseCredentials->getHost() . ';port=' . ((int) $this->databaseCredentials->getPort()) . ';dbname=' . $this->databaseCredentials->getDatabaseName();
} else {
if ($invalidParam1) {
throw new InvalidDatabaseConfiguration("Invalid database configuration. $emptyValue. Please check your database configuration!");
}
return $this->getDbDriver($this->databaseCredentials->getDriver()) . ':host=' . $this->databaseCredentials->getHost() . '; port=' . ((int) $this->databaseCredentials->getPort());
return $this->getDbDriver($this->databaseCredentials->getDriver()) . ':host=' . $this->databaseCredentials->getHost() . ';port=' . ((int) $this->databaseCredentials->getPort());
}
}

Expand Down Expand Up @@ -1018,6 +1046,7 @@ public function __toString()
return json_encode($val);
}


/**
* Get the callback function to be executed when modifying data with queries.
*
Expand Down
97 changes: 52 additions & 45 deletions src/Util/Database/PicoDatabaseUtilMySql.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,11 @@ public function dumpStructure($tableInfo, $tableName, $createIfNotExists = false
$query[] = "-- DROP TABLE IF EXISTS `$tableName`;";
$query[] = "";
}
$createStatement = "";

$createStatement = "CREATE TABLE";
if($createIfNotExists)
{
$createStatement .= " IF NOT EXISTS";
}

$autoIncrementKeys = $this->getAutoIncrementKey($tableInfo);

$query[] = "$createStatement `$tableName` (";
Expand All @@ -101,75 +98,77 @@ public function dumpStructure($tableInfo, $tableName, $createIfNotExists = false
{
if(isset($cols[$columnName]))
{
$columns[] = $this->createColumn($cols[$columnName]);
$columns[] = $this->createColumn($cols[$columnName], $autoIncrementKeys, $tableInfo->getPrimaryKeys());
}
}
$query[] = implode(",\r\n", $columns);
$query[] = ") ENGINE=$engine DEFAULT CHARSET=$charset;";

$pk = $tableInfo->getPrimaryKeys();
if(isset($pk) && is_array($pk) && !empty($pk))
{
$query[] = "";
$query[] = "ALTER TABLE `$tableName`";
foreach($pk as $primaryKey)
{
$query[] = "\tADD PRIMARY KEY (`$primaryKey[name]`)";
}
$query[] = ";";
}

foreach($tableInfo->getColumns() as $column)
{
if(isset($autoIncrementKeys) && is_array($autoIncrementKeys) && in_array($column[parent::KEY_NAME], $autoIncrementKeys))
{
$query[] = "";
$query[] = "ALTER TABLE `$tableName` \r\n\tMODIFY ".trim($this->createColumn($column), " \r\n\t ")." AUTO_INCREMENT";
$query[] = ";";
}
}

return implode("\r\n", $query);
}

/**
* Creates a column definition for a SQL statement.
*
* This method constructs a SQL column definition based on the provided column details,
* including the column name, data type, nullability, and default value. The resulting
* definition is formatted for use in a CREATE TABLE statement.
* including the column name, data type, nullability, default value, primary key status,
* and auto-increment settings. The resulting definition is formatted for use in a CREATE TABLE statement.
*
* @param array $column An associative array containing details about the column:
* - string name: The name of the column.
* - string type: The data type of the column (e.g., VARCHAR, INT).
* - bool|string nullable: Indicates if the column allows NULL values (true or 'true' for NULL; otherwise, NOT NULL).
* - mixed default_value: The default value for the column (optional).
* - string 'name': The name of the column.
* - string 'type': The data type of the column (e.g., VARCHAR, INT).
* - bool|string 'nullable': Indicates if the column allows NULL values
* ('true' or true for NULL; otherwise, NOT NULL).
* - mixed 'default_value': The default value for the column (optional).
* @param array $autoIncrementKeys An array of column names that should have AUTO_INCREMENT property.
* @param array $primaryKeys An array of primary key columns, each being an associative array
* with at least a 'name' key.
*
* @return string The SQL column definition formatted as a string, suitable for inclusion in a CREATE TABLE statement.
*/
public function createColumn($column)
public function createColumn($column, $autoIncrementKeys, $primaryKeys)
{
$pkCols = array();
foreach ($primaryKeys as $col) {
$pkCols[] = $col['name'];
}

$col = array();
$col[] = "\t";
$col[] = "`".$column[parent::KEY_NAME]."`";
$col[] = $column['type'];
if(isset($column['nullable']) && strtolower(trim($column['nullable'])) == 'true')
{
$col[] = "NULL";
$col[] = "\t"; // Adding indentation for readability in SQL statements
$columnName = $column[parent::KEY_NAME];
$columnType = $column['type'];

$col[] = "`" . $columnName . "`"; // Enclose column name in backticks
$col[] = $columnType; // Add the column type (e.g., INT, VARCHAR)

// Check if the column is part of primary keys
if (in_array($columnName, $pkCols)) {
$col[] = 'PRIMARY KEY';
}
else
{

// Check if the column should auto-increment
if (isset($autoIncrementKeys) && is_array($autoIncrementKeys) && in_array($column[parent::KEY_NAME], $autoIncrementKeys)) {
$col[] = 'AUTO_INCREMENT';
}

// Determine if the column allows NULL values
if (isset($column['nullable']) && strtolower(trim($column['nullable'])) == 'true') {
$col[] = "NULL";
} else {
$col[] = "NOT NULL";
}
if(isset($column['default_value']))
{

// Set default value if specified
if (isset($column['default_value'])) {
$defaultValue = $column['default_value'];
$defaultValue = $this->fixDefaultValue($defaultValue, $column['type']);
$col[] = "DEFAULT $defaultValue";
}

return implode(" ", $col);
}


/**
* Fixes the default value for SQL insertion based on its type.
*
Expand All @@ -185,14 +184,22 @@ public function createColumn($column)
*/
public function fixDefaultValue($defaultValue, $type)
{
if(strtolower($defaultValue) == 'true' || strtolower($defaultValue) == 'false' || strtolower($defaultValue) == 'null')
if(stripos($type, 'bool') !== false || stripos($type, 'tinyint(1)') !== false)
{
return $defaultValue != 0 ? 'true' : 'false';
}
else if(strtolower($defaultValue) == 'true' || strtolower($defaultValue) == 'false' || strtolower($defaultValue) == 'null')
{
return $defaultValue;
}
if(stripos($type, 'enum') !== false || stripos($type, 'char') !== false || stripos($type, 'text') !== false || stripos($type, 'int') !== false || stripos($type, 'float') !== false || stripos($type, 'double') !== false)
else if(stripos($type, 'enum') !== false || stripos($type, 'char') !== false || stripos($type, 'text') !== false)
{
return "'".$defaultValue."'";
}
else if(stripos($type, 'int') !== false || stripos($type, 'decimal') !== false || stripos($type, 'float') !== false || stripos($type, 'double') !== false)
{
return $defaultValue * 1;
}
return $defaultValue;
}

Expand Down
Loading
Loading