From 17d0979b1fa1cc336a5a99d978c4356a72722a70 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 08:59:20 +0700 Subject: [PATCH 1/8] Fixing PostgreSQL connection --- src/Database/PicoDatabase.php | 43 +++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Database/PicoDatabase.php b/src/Database/PicoDatabase.php index e2f67f49..2e5d3c20 100644 --- a/src/Database/PicoDatabase.php +++ b/src/Database/PicoDatabase.php @@ -370,14 +370,20 @@ 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 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. - * @throws InvalidDatabaseConfiguration If the database username is empty. - * @throws PDOException If the connection fails with an error. + * Establishes a connection to an RDMS database using the provided credentials. Optionally, a specific + * database is selected based on the provided flag. This method also configures the time zone, character set, + * and schema settings (for PostgreSQL) after the connection is established. + * + * - The time zone is set based on the current offset (`date("P")`), or a configured value. + * - For PostgreSQL, the client encoding (charset) is set using `SET CLIENT_ENCODING`, and the schema is set + * using `SET search_path`. + * - For MySQL, the time zone and charset are set using `SET time_zone` and `SET NAMES`. + * + * @param bool $withDatabase Flag to specify whether to select a database upon connection (default is true). + * If true, the database is selected; otherwise, only the connection is made. + * @return bool True if the connection is successfully established, false otherwise. + * @throws InvalidDatabaseConfiguration If the database username is missing from the configuration. + * @throws PDOException If an error occurs during the connection process. */ private function connectRDMS($withDatabase = true) { @@ -386,26 +392,28 @@ private function connectRDMS($withDatabase = true) try { $connectionString = $this->constructConnectionString($withDatabase); - // Check for database username configuration + // Check if the database username is provided 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';"; + $initialQueries = ""; - // Get charset from database credentials + // Get charset from the database credentials $charset = addslashes($this->databaseCredentials->getCharset()); - + // Handle PostgreSQL-specific connection settings if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) { - // Set charset for PostgreSQL if provided (PostgreSQL does not use `SET NAMES`, but you can set the encoding) + // Set time zone for PostgreSQL + $initialQueries = "SET TIMEZONE TO '$timeZoneOffset';"; + + // Set the client encoding (charset) for PostgreSQL if ($charset) { $initialQueries .= "SET CLIENT_ENCODING TO '$charset';"; } - // Set schema for PostgreSQL if it is provided + // Set schema if provided for PostgreSQL if ($this->databaseCredentials->getDatabaseSchema() != null && $this->databaseCredentials->getDatabaseSchema() != "") { $initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema() . ";"; } @@ -428,6 +436,8 @@ private function connectRDMS($withDatabase = true) } // Handle MySQL-specific connection settings elseif ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) { + // Set time zone for MySQL + $initialQueries = "SET time_zone = '$timeZoneOffset';"; // Add charset to the initial queries for MySQL if ($charset) { @@ -461,7 +471,6 @@ private function connectRDMS($withDatabase = true) } return $connected; } - /** * Determine the database type based on the provided database type string. @@ -1104,4 +1113,4 @@ public function setCallbackDebugQuery($callbackDebugQuery) return $this; } -} +} \ No newline at end of file From 38ba78e7a958bb3b17653b6966647eed482493c6 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 09:40:53 +0700 Subject: [PATCH 2/8] Update PicoDatabaseUtilPostgreSql.php --- src/Util/Database/PicoDatabaseUtilPostgreSql.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Util/Database/PicoDatabaseUtilPostgreSql.php b/src/Util/Database/PicoDatabaseUtilPostgreSql.php index f1582f38..f66ca792 100644 --- a/src/Util/Database/PicoDatabaseUtilPostgreSql.php +++ b/src/Util/Database/PicoDatabaseUtilPostgreSql.php @@ -495,15 +495,7 @@ public function getColumnType($columnType) return 'BOOLEAN'; } $type = $this->convertMySqlToPostgreSql($columnType); - if(stripos($type, 'integer(') === 0) - { - $type = 'INTEGER'; - } - else if(stripos($type, 'smallinteger(') === 0) - { - $type = 'INTEGER'; - } - else if(stripos($type, 'biginteger(') === 0) + if(stripos($type, 'biginteger(') === 0 || stripos($type, 'smallinteger(') === 0 || stripos($type, 'integer(') === 0 || stripos($type, 'int(') === 0) { $type = 'INTEGER'; } @@ -517,6 +509,8 @@ public function getColumnType($columnType) // Set the NVARCHAR length to the max length of enum values + 2 $type = 'CHARACTER VARYING(' . ($maxLength + 2) . ')'; } + } else if (stripos($type, 'varchar(') === 0) { + $type = str_ireplace('varchar', 'CHARACTER VARYING', $columnType); } else if (stripos($type, 'year(') === 0) { // Extract the enum values between the parentheses $type = "INTEGER"; From f007d3d49ae17620929037c3f1c805ec1c8efd77 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 09:43:18 +0700 Subject: [PATCH 3/8] Update PicoDatabaseUtilPostgreSql.php --- src/Util/Database/PicoDatabaseUtilPostgreSql.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Util/Database/PicoDatabaseUtilPostgreSql.php b/src/Util/Database/PicoDatabaseUtilPostgreSql.php index f66ca792..41a8ef66 100644 --- a/src/Util/Database/PicoDatabaseUtilPostgreSql.php +++ b/src/Util/Database/PicoDatabaseUtilPostgreSql.php @@ -494,12 +494,12 @@ public function getColumnType($columnType) { return 'BOOLEAN'; } - $type = $this->convertMySqlToPostgreSql($columnType); - if(stripos($type, 'biginteger(') === 0 || stripos($type, 'smallinteger(') === 0 || stripos($type, 'integer(') === 0 || stripos($type, 'int(') === 0) + else if(stripos($columnType, 'biginteger(') === 0 || stripos($columnType, 'smallinteger(') === 0 || stripos($columnType, 'integer(') === 0 || stripos($columnType, 'bigint(') === 0 || stripos($columnType, 'smallint(') === 0 || stripos($columnType, 'int(') === 0) { $type = 'INTEGER'; } - else if (stripos($type, 'enum(') === 0) { + $type = $this->convertMySqlToPostgreSql($columnType); + if (stripos($type, 'enum(') === 0) { // Extract the enum values between the parentheses if (preg_match('/^enum\((.+)\)$/i', $type, $matches)) { // Get the enum values as an array by splitting the string From d5013c09e4478d6b5792efe0d67b63a2d69297b4 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 10:04:47 +0700 Subject: [PATCH 4/8] Update --- src/Util/Database/PicoDatabaseUtilBase.php | 49 ++++++++++++------- .../Database/PicoDatabaseUtilPostgreSql.php | 9 +++- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/Util/Database/PicoDatabaseUtilBase.php b/src/Util/Database/PicoDatabaseUtilBase.php index c58f1c0b..9fb30e3e 100644 --- a/src/Util/Database/PicoDatabaseUtilBase.php +++ b/src/Util/Database/PicoDatabaseUtilBase.php @@ -695,23 +695,29 @@ public function showColumns($database, $tableName) // NOSONAR } /** - * Converts a MariaDB CREATE TABLE query to a PostgreSQL compatible query. + * Converts a MySQL CREATE TABLE query to a PostgreSQL compatible query. * - * This function takes a SQL CREATE TABLE statement written for MariaDB + * This function takes a SQL CREATE TABLE statement written for MySQL * and transforms it into a format compatible with PostgreSQL. It handles - * common data types and syntax differences between the two databases. + * common data types, constraints, and syntax differences between the two databases, + * such as converting data types, removing unsupported clauses (e.g., AUTO_INCREMENT, ENGINE), + * and adjusting default values and column types. * - * @param string $mariadbQuery The MariaDB CREATE TABLE query to be converted. - * @return string The converted PostgreSQL CREATE TABLE query. + * @param string $mysqlQuery The MySQL CREATE TABLE query to be converted. + * + * @return string The converted PostgreSQL CREATE TABLE query, with MySQL-specific syntax + * replaced by PostgreSQL equivalents, including type conversions and other adjustments. + * + * @throws InvalidArgumentException If the input query is not a valid MySQL CREATE TABLE query. */ - public function convertMariaDbToPostgreSql($mariadbQuery) { + public function convertMySqlToPostgreSql($mysqlQuery) { // Remove comments - $query = preg_replace('/--.*?\n|\/\*.*?\*\//s', '', $mariadbQuery); // NOSONAR + $query = preg_replace('/--.*?\n|\/\*.*?\*\//s', '', $mysqlQuery); // NOSONAR - // Replace MariaDB data types with PostgreSQL data types + // Replace MySQL data types with PostgreSQL data types $replacements = array( 'int' => 'INTEGER', - 'tinyint(1)' => 'BOOLEAN', // MariaDB TINYINT(1) as BOOLEAN + 'tinyint(1)' => 'BOOLEAN', // MySQL TINYINT(1) as BOOLEAN 'tinyint' => 'SMALLINT', 'smallint' => 'SMALLINT', 'mediumint' => 'INTEGER', // No direct equivalent, use INTEGER @@ -758,12 +764,18 @@ public function convertMariaDbToPostgreSql($mariadbQuery) { * * This function takes a SQL CREATE TABLE statement written for PostgreSQL * and transforms it into a format compatible with MySQL. It handles common - * data types and syntax differences between the two databases. + * data types, constraints, and syntax differences between the two databases. + * The function adjusts data types, removes PostgreSQL-specific clauses, + * and makes necessary adjustments for MySQL compatibility. * * @param string $postgresqlQuery The PostgreSQL CREATE TABLE query to be converted. - * @return string The converted MySQL CREATE TABLE query. - */ - public function convertMySqlToPostgreSql($postgresqlQuery) { + * + * @return string The converted MySQL CREATE TABLE query, with PostgreSQL-specific syntax + * replaced by MySQL equivalents, including type conversions and other adjustments. + * + * @throws InvalidArgumentException If the input query is not a valid PostgreSQL CREATE TABLE query. + */ + public function convertPostgreSqlToMySql($postgresqlQuery) { // Remove comments $query = preg_replace('/--.*?\n|\/\*.*?\*\//s', '', $postgresqlQuery); // NOSONAR @@ -788,24 +800,25 @@ public function convertMySqlToPostgreSql($postgresqlQuery) { 'bytea' => 'BLOB', // Added handling for bytea // Add more type conversions as needed ); - + $query = str_ireplace(array_keys($replacements), array_values($replacements), $query); - + // Replace DEFAULT on columns with strings to NULL in MySQL $query = preg_replace('/DEFAULT (\'[^\']*\')/', 'DEFAULT $1', $query); - + // Replace SERIAL with INT AUTO_INCREMENT $query = preg_replace('/\bSERIAL\b/', 'INT AUTO_INCREMENT', $query); // Modify "IF NOT EXISTS" for MySQL $query = preg_replace('/CREATE TABLE IF NOT EXISTS/', 'CREATE TABLE IF NOT EXISTS', $query); // NOSONAR - + // Remove UNIQUE constraints if necessary (optional) $query = preg_replace('/UNIQUE\s*\(.*?\),?\s*/i', '', $query); // NOSONAR // Remove 'USING BTREE' if present $query = preg_replace('/USING BTREE/', '', $query); // NOSONAR - + return $query; } + } \ No newline at end of file diff --git a/src/Util/Database/PicoDatabaseUtilPostgreSql.php b/src/Util/Database/PicoDatabaseUtilPostgreSql.php index 41a8ef66..ca88b340 100644 --- a/src/Util/Database/PicoDatabaseUtilPostgreSql.php +++ b/src/Util/Database/PicoDatabaseUtilPostgreSql.php @@ -494,9 +494,14 @@ public function getColumnType($columnType) { return 'BOOLEAN'; } - else if(stripos($columnType, 'biginteger(') === 0 || stripos($columnType, 'smallinteger(') === 0 || stripos($columnType, 'integer(') === 0 || stripos($columnType, 'bigint(') === 0 || stripos($columnType, 'smallint(') === 0 || stripos($columnType, 'int(') === 0) + else if(stripos($columnType, 'biginteger(') === 0 + || stripos($columnType, 'smallinteger') === 0 + || stripos($columnType, 'integer') === 0 + || stripos($columnType, 'bigint') === 0 + || stripos($columnType, 'smallint') === 0 + || stripos($columnType, 'int') === 0) { - $type = 'INTEGER'; + return 'INTEGER'; } $type = $this->convertMySqlToPostgreSql($columnType); if (stripos($type, 'enum(') === 0) { From 72c4c8ae423ba293fc76b2ec6578b819349526b4 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 18:25:47 +0700 Subject: [PATCH 5/8] Update --- src/Database/PicoDatabase.php | 112 +++++---------------- src/Database/PicoDatabaseCredentials.php | 30 ------ src/Database/PicoPageable.php | 66 ++---------- src/Database/PicoSqlite.php | 1 - src/Database/PicoTableInfo.php | 44 ++++++++ src/Database/PicoTableInfoExtended.php | 36 ------- src/Generator/PicoDatabaseDump.php | 4 +- src/Generator/PicoEntityGenerator.php | 92 ++++------------- src/Txt.php | 4 +- src/Util/Database/PicoDatabaseUtilBase.php | 14 +-- 10 files changed, 113 insertions(+), 290 deletions(-) diff --git a/src/Database/PicoDatabase.php b/src/Database/PicoDatabase.php index 2e5d3c20..a9de8835 100644 --- a/src/Database/PicoDatabase.php +++ b/src/Database/PicoDatabase.php @@ -370,20 +370,14 @@ private function connectSqlite() /** * Connect to the RDMS (Relational Database Management System). * - * Establishes a connection to an RDMS database using the provided credentials. Optionally, a specific - * database is selected based on the provided flag. This method also configures the time zone, character set, - * and schema settings (for PostgreSQL) after the connection is established. - * - * - The time zone is set based on the current offset (`date("P")`), or a configured value. - * - For PostgreSQL, the client encoding (charset) is set using `SET CLIENT_ENCODING`, and the schema is set - * using `SET search_path`. - * - For MySQL, the time zone and charset are set using `SET time_zone` and `SET NAMES`. - * - * @param bool $withDatabase Flag to specify whether to select a database upon connection (default is true). - * If true, the database is selected; otherwise, only the connection is made. - * @return bool True if the connection is successfully established, false otherwise. - * @throws InvalidDatabaseConfiguration If the database username is missing from the configuration. - * @throws PDOException If an error occurs during the connection process. + * 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 for the connection and handles + * schema settings for PostgreSQL. + * + * @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. + * @throws InvalidDatabaseConfiguration If the database username is empty. + * @throws PDOException If the connection fails with an error. */ private function connectRDMS($withDatabase = true) { @@ -391,82 +385,28 @@ private function connectRDMS($withDatabase = true) $timeZoneOffset = date("P"); try { $connectionString = $this->constructConnectionString($withDatabase); - - // Check if the database username is provided if (!$this->databaseCredentials->issetUsername()) { throw new InvalidDatabaseConfiguration("Database username may not be empty. Please check your database configuration!"); } - - $initialQueries = ""; - - // Get charset from the database credentials - $charset = addslashes($this->databaseCredentials->getCharset()); - - // Handle PostgreSQL-specific connection settings - if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) { - - // Set time zone for PostgreSQL - $initialQueries = "SET TIMEZONE TO '$timeZoneOffset';"; - - // Set the client encoding (charset) for PostgreSQL - if ($charset) { - $initialQueries .= "SET CLIENT_ENCODING TO '$charset';"; - } - - // Set schema if provided for PostgreSQL - if ($this->databaseCredentials->getDatabaseSchema() != null && $this->databaseCredentials->getDatabaseSchema() != "") { - $initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema() . ";"; - } - - // 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); - } - + $initialQueries = "SET time_zone = '$timeZoneOffset';"; + if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL && + $this->databaseCredentials->getDatabaseSchema() != null && + $this->databaseCredentials->getDatabaseSchema() != "") { + $initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema(); } - // Handle MySQL-specific connection settings - elseif ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) { - // Set time zone for MySQL - $initialQueries = "SET time_zone = '$timeZoneOffset';"; - - // Add charset to the initial queries for MySQL - if ($charset) { - $initialQueries .= "SET NAMES '$charset';"; // Set charset for MySQL - } - - // 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 + $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 + ] + ); $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; @@ -558,12 +498,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()); } } @@ -1113,4 +1053,4 @@ public function setCallbackDebugQuery($callbackDebugQuery) return $this; } -} \ No newline at end of file +} diff --git a/src/Database/PicoDatabaseCredentials.php b/src/Database/PicoDatabaseCredentials.php index 928d49f8..3e614ea6 100644 --- a/src/Database/PicoDatabaseCredentials.php +++ b/src/Database/PicoDatabaseCredentials.php @@ -105,13 +105,6 @@ class PicoDatabaseCredentials extends SecretObject */ protected $timeZone; - /** - * Charset - * - * @var string - */ - protected $charset; - /** * Get the database driver. * @@ -191,27 +184,4 @@ public function getTimeZone() { return $this->timeZone; } - - /** - * Get the charset. - * - * @return string The charset currently set. - */ - public function getCharset() - { - return $this->charset; - } - - /** - * Set the charset. - * - * @param string $charset The charset to set. - * @return self Returns the current instance for method chaining. - */ - public function setCharset($charset) - { - $this->charset = $charset; - return $this; - } - } diff --git a/src/Database/PicoPageable.php b/src/Database/PicoPageable.php index 05ead32a..6103fec2 100644 --- a/src/Database/PicoPageable.php +++ b/src/Database/PicoPageable.php @@ -36,59 +36,15 @@ class PicoPageable private $offsetLimit = null; /** - * Constructor for the Pageable class. - * - * This constructor allows initializing a Pageable object with pagination and sorting options. - * It supports different formats for both page and sortable parameters. - * - * Example 1: Using `PicoPage` and `PicoSortable` - * - * ```php - * - * ``` - * - * Example 2: Using `PicoLimit` and `PicoSortable` - * - * ```php - * - * ``` - * - * Example 3: Using an array for page and `PicoSortable` - * - * ```php - * - * ``` - * - * Example 4: Using an array for both page and sortable - * - * ```php - * - * ``` - * - * Example 5: Using `PicoPage` and an array for sortable - * - * ```php - * - * ``` - * - * @param PicoPage|PicoLimit|array|null $page The page or limit configuration. Can be: - * - `PicoPage` instance (for page number and size), - * - `PicoLimit` instance (for offset and limit), - * - array with two elements representing page and size (e.g., `[1, 100]`). - * - * @param PicoSortable|array|null $sortable The sorting configuration. Can be: - * - `PicoSortable` instance (for sorting by multiple fields), - * - array of field-direction pairs (e.g., `['userName', 'asc', 'email', 'desc']`). + * Constructor of Pageable + * Example: + * 1. $pageable = new Pageable(array(1, 100), array('userName', 'asc', 'email', 'desc', 'phone', 'asc')); + * 2. $pageable = new Pageable(new PicoPage(1, 100), array('userName', 'asc', 'email', 'desc', 'phone', 'asc')); + * 3. $pageable = new Pageable(array(1, 100), new PicoSortable('userName', 'asc', 'email', 'desc', 'phone', 'asc')); + * 4. $pageable = new Pageable(new PicoPage(1, 100), new PicoSortable('userName', 'asc', 'email', 'desc', 'phone', 'asc')); + * + * @param PicoPage|PicoLimit|array|null $page Page + * @param PicoSortable|array|null $sortable Sortable */ public function __construct($page = null, $sortable = null) { @@ -104,7 +60,7 @@ public function __construct($page = null, $sortable = null) } else if(is_array($page)) { - // Create from array + // create from array $this->page = new PicoPage($page[0], $page[1]); } } @@ -116,7 +72,7 @@ public function __construct($page = null, $sortable = null) } else if(is_array($sortable)) { - // Create from array + // create from array $this->sortable = new PicoSortable($sortable); } } diff --git a/src/Database/PicoSqlite.php b/src/Database/PicoSqlite.php index 3893a4a7..a3c7a87c 100644 --- a/src/Database/PicoSqlite.php +++ b/src/Database/PicoSqlite.php @@ -13,7 +13,6 @@ class PicoSqlite extends PicoDatabase { const LOGIC_AND = " and "; - /** * Database file path * diff --git a/src/Database/PicoTableInfo.php b/src/Database/PicoTableInfo.php index 6fb2cdaf..7e64ea44 100644 --- a/src/Database/PicoTableInfo.php +++ b/src/Database/PicoTableInfo.php @@ -87,6 +87,15 @@ class PicoTableInfo // NOSONAR */ protected $package; + /** + * List of sorted column names. + * + * This property stores an array of column names in their sorted order. + * + * @var array List of sorted column names. + */ + private $sortedColumnName = array(); + /** * Gets an instance of PicoTableInfo. * @@ -123,6 +132,13 @@ public function __construct($tableName, $columns, $joinColumns, $primaryKeys, $a $this->notNullColumns = $notNullColumns; $this->noCache = $noCache; $this->package = $package; + + // Store sorted column list + $res = array(); + $res = array_merge($res, array_keys($columns)); + $res = array_unique($res); + + $this->sortedColumnName = $res; } /** @@ -384,4 +400,32 @@ public function setPackage($package) return $this; } + + /** + * Get the sorted column names. + * + * This method retrieves the list of sorted column names. + * + * @return array List of sorted column names. + */ + public function getSortedColumnName() + { + return $this->sortedColumnName; + } + + /** + * Set the sorted column names. + * + * This method sets the list of sorted column names. + * + * @param array $sortedColumnName List of sorted column names. + * + * @return self Returns the current instance for method chaining. + */ + public function setSortedColumnName($sortedColumnName) + { + $this->sortedColumnName = $sortedColumnName; + + return $this; + } } diff --git a/src/Database/PicoTableInfoExtended.php b/src/Database/PicoTableInfoExtended.php index 48ceda45..90844099 100644 --- a/src/Database/PicoTableInfoExtended.php +++ b/src/Database/PicoTableInfoExtended.php @@ -19,15 +19,6 @@ class PicoTableInfoExtended extends PicoTableInfo const PREV_NAME = "prevColumnName"; // Key for the previous column name const ELEMENT = "element"; // Key for the element - /** - * List of sorted column names. - * - * This property stores an array of column names in their sorted order. - * - * @var array List of sorted column names. - */ - private $sortedColumnName = array(); - /** * Gets an instance of PicoTableInfoExtended. * @@ -303,32 +294,5 @@ public function mergeNotNullColumns($newList) return $this; } - /** - * Get the sorted column names. - * - * This method retrieves the list of sorted column names. - * - * @return array List of sorted column names. - */ - public function getSortedColumnName() - { - return $this->sortedColumnName; - } - - /** - * Set the sorted column names. - * - * This method sets the list of sorted column names. - * - * @param array $sortedColumnName List of sorted column names. - * - * @return self Returns the current instance for method chaining. - */ - public function setSortedColumnName($sortedColumnName) - { - $this->sortedColumnName = $sortedColumnName; - - return $this; - } } diff --git a/src/Generator/PicoDatabaseDump.php b/src/Generator/PicoDatabaseDump.php index bdcecd3a..3d5f2fe6 100644 --- a/src/Generator/PicoDatabaseDump.php +++ b/src/Generator/PicoDatabaseDump.php @@ -249,8 +249,8 @@ public function createQueryAlterTable($tableName, $columnName, $columnType) public function createAlterTableAddFromEntities($entities, $tableName = null, $database = null) { $tableInfo = $this->getMergedTableInfo($entities); - $clumnNameList = $this->getColumnNameList($entities); - $tableInfo->setSortedColumnName($clumnNameList); + $columnNameList = $this->getColumnNameList($entities); + $tableInfo->setSortedColumnName($columnNameList); $tableName = $this->getTableName($tableName, $tableInfo); $database = $this->getDatabase($database, $entities); diff --git a/src/Generator/PicoEntityGenerator.php b/src/Generator/PicoEntityGenerator.php index 17e98762..9d085bde 100644 --- a/src/Generator/PicoEntityGenerator.php +++ b/src/Generator/PicoEntityGenerator.php @@ -215,86 +215,36 @@ protected function getDataLength($str) } /** - * Get a mapping of database types to PHP types for MySQL, PostgreSQL and SQLite. + * Get a mapping of database types to PHP types. * * @return array Associative array of type mappings */ protected function getTypeMap() { return array( - // Numeric types - "double" => "float", // PostgreSQL: double precision - "float" => "float", // PostgreSQL: float - "bigint" => "int", // PostgreSQL: bigint - "smallint" => "int", // PostgreSQL: smallint - "tinyint(1)" => "bool", // MySQL-style, use boolean for tinyint(1) - "tinyint" => "int", // PostgreSQL/SQLite: tinyint, handled as INT - "int" => "int", // PostgreSQL/SQLite: integer - "serial" => "int", // PostgreSQL: auto-increment integer (equivalent to INT) - "bigserial" => "int", // PostgreSQL: bigserial (auto-incrementing large integer, mapped to int in PHP) - "mediumint" => "int", // MySQL: mediumint (3-byte integer) - "smallserial" => "int", // PostgreSQL: smallserial (auto-incrementing small integer) - "unsigned" => "int", // MySQL: unsigned integer (mapped to int in PHP) - - // String types - "varchar" => "string", // PostgreSQL: variable-length string - "character varying" => "string", // PostgreSQL: character varying (same as varchar) - "char" => "string", // PostgreSQL: fixed-length string - "text" => "string", // PostgreSQL/SQLite: unlimited length string - "varchar(255)" => "string", // PostgreSQL: same as varchar without length - "citext" => "string", // PostgreSQL: case-insensitive text (equivalent to string) - - // MySQL-style text types (these types are similar to `text`) - "tinytext" => "string", // MySQL: tinytext - "mediumtext" => "string", // MySQL: mediumtext - "longtext" => "string", // MySQL: longtext - "text" => "string", // PostgreSQL/SQLite: text (string) - - // Boolean types - "bool" => "bool", // PostgreSQL: boolean - "boolean" => "bool", // PostgreSQL: boolean (same as bool) - - // Date/Time types - "timestamp" => "string", // PostgreSQL: timestamp (datetime) - "datetime" => "string", // PostgreSQL/SQLite: datetime - "date" => "string", // PostgreSQL/SQLite: date - "time" => "string", // PostgreSQL/SQLite: time - "timestamp with time zone" => "string", // PostgreSQL: timestamp with time zone - "timestamp without time zone" => "string", // PostgreSQL: timestamp without time zone - "date" => "string", // PostgreSQL/SQLite: date - "time" => "string", // PostgreSQL/SQLite: time - "interval" => "string", // PostgreSQL: interval (for durations) - "year" => "int", // MySQL: year type (usually stored as an integer) - - // SQLite-specific types - "integer" => "int", // SQLite: integer - "real" => "float", // SQLite: real (floating-point) - "text" => "string", // SQLite: text (string) - "blob" => "resource", // SQLite: blob (binary data) - - // SQLite's special handling - "BOOLEAN" => "bool", // SQLite: boolean (same as PostgreSQL) - - // Special cases - "json" => "array", // PostgreSQL: JSON type, mapped to PHP array - "jsonb" => "array", // PostgreSQL: JSONB (binary JSON), mapped to PHP array - "uuid" => "string", // PostgreSQL/SQLite: UUID - "xml" => "string", // PostgreSQL: XML type - "cidr" => "string", // PostgreSQL: CIDR type (IPv4/IPv6) - "inet" => "string", // PostgreSQL: Inet type (IPv4/IPv6) - "macaddr" => "string", // PostgreSQL: MAC address type - "point" => "string", // PostgreSQL: point type (coordinates) - "polygon" => "string", // PostgreSQL: polygon type - "line" => "string", // PostgreSQL: line type - "lseg" => "string", // PostgreSQL: line segment type - "path" => "string", // PostgreSQL: path type (geometric shapes) - "circle" => "string", // PostgreSQL: circle type (geometric shapes) - "json" => "array", // PostgreSQL: JSON data type - "jsonb" => "array", // PostgreSQL: Binary JSON type (faster) + "double" => "double", + "float" => "double", + "bigint" => "integer", + "smallint" => "integer", + "tinyint(1)" => "boolean", + "tinyint" => "integer", + "int" => "integer", + "varchar" => "string", + "char" => "string", + "tinytext" => "string", + "mediumtext" => "string", + "longtext" => "string", + "text" => "string", + "enum" => "string", + "bool" => "boolean", + "boolean" => "boolean", + "timestamp" => "string", + "datetime" => "string", + "date" => "string", + "time" => "string" ); } - /** * Generate the entity class and save it to a file. * diff --git a/src/Txt.php b/src/Txt.php index 8a03d37e..e3b70638 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -34,7 +34,7 @@ public static function __callStatic($name, $arguments) // NOSONAR * Returns a new instance of the Txt class. * * This method allows you to retrieve an instance of the Txt class for non-static operations. - * This instance can be used to access dynamic properties via the `__get()` magic method. + * This instance can be used to access dynamic properties via the __get() magic method. * * @return Txt A new instance of the Txt class. */ @@ -47,7 +47,7 @@ public static function getInstance() * Creates and returns a new instance of the Txt class. * * Similar to getInstance(), this method allows you to retrieve an instance of the Txt class - * for non-static operations, such as dynamic property access using the `__get()` magic method. + * for non-static operations, such as dynamic property access using the __get() magic method. * * @return Txt A new instance of the Txt class. */ diff --git a/src/Util/Database/PicoDatabaseUtilBase.php b/src/Util/Database/PicoDatabaseUtilBase.php index 9fb30e3e..04f13096 100644 --- a/src/Util/Database/PicoDatabaseUtilBase.php +++ b/src/Util/Database/PicoDatabaseUtilBase.php @@ -716,24 +716,24 @@ public function convertMySqlToPostgreSql($mysqlQuery) { // Replace MySQL data types with PostgreSQL data types $replacements = array( - 'int' => 'INTEGER', 'tinyint(1)' => 'BOOLEAN', // MySQL TINYINT(1) as BOOLEAN - 'tinyint' => 'SMALLINT', - 'smallint' => 'SMALLINT', + 'tinyint' => 'INTEGER', + 'smallint' => 'INTEGER', 'mediumint' => 'INTEGER', // No direct equivalent, use INTEGER - 'bigint' => 'BIGINT', + 'bigint' => 'INTEGER', + 'int' => 'INTEGER', 'float' => 'REAL', 'double' => 'DOUBLE PRECISION', 'decimal' => 'NUMERIC', // Decimal types + 'datetime' => 'TIMESTAMP', // Use TIMESTAMP for datetime + 'timestamp' => 'TIMESTAMP WITH TIME ZONE', 'date' => 'DATE', 'time' => 'TIME', - 'datetime' => 'TIMESTAMP', // Use TIMESTAMP for datetime - 'timestamp' => 'TIMESTAMP', 'varchar' => 'VARCHAR', // Variable-length string - 'text' => 'TEXT', 'blob' => 'BYTEA', // Binary data 'mediumtext' => 'TEXT', // No direct equivalent 'longtext' => 'TEXT', // No direct equivalent + 'text' => 'TEXT', 'json' => 'JSONB', // Use JSONB for better performance in PostgreSQL // Add more type conversions as needed ); From 8fb47c13182a8b505ed525bd1493547b4fcef7b3 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 18:27:18 +0700 Subject: [PATCH 6/8] Update --- src/Database/PicoDatabase.php | 112 ++++++++++++++---- src/Database/PicoDatabaseCredentials.php | 30 +++++ src/Database/PicoPageable.php | 66 +++++++++-- src/Database/PicoSqlite.php | 1 + src/Generator/PicoEntityGenerator.php | 92 ++++++++++---- src/Txt.php | 4 +- .../Database/PicoDatabaseUtilPostgreSql.php | 3 +- 7 files changed, 247 insertions(+), 61 deletions(-) diff --git a/src/Database/PicoDatabase.php b/src/Database/PicoDatabase.php index a9de8835..2e5d3c20 100644 --- a/src/Database/PicoDatabase.php +++ b/src/Database/PicoDatabase.php @@ -370,14 +370,20 @@ 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 for the connection and handles - * schema settings for PostgreSQL. - * - * @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. - * @throws InvalidDatabaseConfiguration If the database username is empty. - * @throws PDOException If the connection fails with an error. + * Establishes a connection to an RDMS database using the provided credentials. Optionally, a specific + * database is selected based on the provided flag. This method also configures the time zone, character set, + * and schema settings (for PostgreSQL) after the connection is established. + * + * - The time zone is set based on the current offset (`date("P")`), or a configured value. + * - For PostgreSQL, the client encoding (charset) is set using `SET CLIENT_ENCODING`, and the schema is set + * using `SET search_path`. + * - For MySQL, the time zone and charset are set using `SET time_zone` and `SET NAMES`. + * + * @param bool $withDatabase Flag to specify whether to select a database upon connection (default is true). + * If true, the database is selected; otherwise, only the connection is made. + * @return bool True if the connection is successfully established, false otherwise. + * @throws InvalidDatabaseConfiguration If the database username is missing from the configuration. + * @throws PDOException If an error occurs during the connection process. */ private function connectRDMS($withDatabase = true) { @@ -385,28 +391,82 @@ private function connectRDMS($withDatabase = true) $timeZoneOffset = date("P"); try { $connectionString = $this->constructConnectionString($withDatabase); + + // Check if the database username is provided if (!$this->databaseCredentials->issetUsername()) { throw new InvalidDatabaseConfiguration("Database username may not be empty. Please check your database configuration!"); } - $initialQueries = "SET time_zone = '$timeZoneOffset';"; - if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL && - $this->databaseCredentials->getDatabaseSchema() != null && - $this->databaseCredentials->getDatabaseSchema() != "") { - $initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema(); + + $initialQueries = ""; + + // Get charset from the database credentials + $charset = addslashes($this->databaseCredentials->getCharset()); + + // Handle PostgreSQL-specific connection settings + if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) { + + // Set time zone for PostgreSQL + $initialQueries = "SET TIMEZONE TO '$timeZoneOffset';"; + + // Set the client encoding (charset) for PostgreSQL + if ($charset) { + $initialQueries .= "SET CLIENT_ENCODING TO '$charset';"; + } + + // Set schema if provided for PostgreSQL + if ($this->databaseCredentials->getDatabaseSchema() != null && $this->databaseCredentials->getDatabaseSchema() != "") { + $initialQueries .= "SET search_path TO " . $this->databaseCredentials->getDatabaseSchema() . ";"; + } + + // 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(), - [ - PDO::MYSQL_ATTR_INIT_COMMAND => $initialQueries, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::MYSQL_ATTR_FOUND_ROWS => true - ] - ); + // Handle MySQL-specific connection settings + elseif ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) { + // Set time zone for MySQL + $initialQueries = "SET time_zone = '$timeZoneOffset';"; + + // Add charset to the initial queries for MySQL + if ($charset) { + $initialQueries .= "SET NAMES '$charset';"; // Set charset for MySQL + } + + // 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; @@ -498,12 +558,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()); } } @@ -1053,4 +1113,4 @@ public function setCallbackDebugQuery($callbackDebugQuery) return $this; } -} +} \ No newline at end of file diff --git a/src/Database/PicoDatabaseCredentials.php b/src/Database/PicoDatabaseCredentials.php index 3e614ea6..928d49f8 100644 --- a/src/Database/PicoDatabaseCredentials.php +++ b/src/Database/PicoDatabaseCredentials.php @@ -105,6 +105,13 @@ class PicoDatabaseCredentials extends SecretObject */ protected $timeZone; + /** + * Charset + * + * @var string + */ + protected $charset; + /** * Get the database driver. * @@ -184,4 +191,27 @@ public function getTimeZone() { return $this->timeZone; } + + /** + * Get the charset. + * + * @return string The charset currently set. + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Set the charset. + * + * @param string $charset The charset to set. + * @return self Returns the current instance for method chaining. + */ + public function setCharset($charset) + { + $this->charset = $charset; + return $this; + } + } diff --git a/src/Database/PicoPageable.php b/src/Database/PicoPageable.php index 6103fec2..05ead32a 100644 --- a/src/Database/PicoPageable.php +++ b/src/Database/PicoPageable.php @@ -36,15 +36,59 @@ class PicoPageable private $offsetLimit = null; /** - * Constructor of Pageable - * Example: - * 1. $pageable = new Pageable(array(1, 100), array('userName', 'asc', 'email', 'desc', 'phone', 'asc')); - * 2. $pageable = new Pageable(new PicoPage(1, 100), array('userName', 'asc', 'email', 'desc', 'phone', 'asc')); - * 3. $pageable = new Pageable(array(1, 100), new PicoSortable('userName', 'asc', 'email', 'desc', 'phone', 'asc')); - * 4. $pageable = new Pageable(new PicoPage(1, 100), new PicoSortable('userName', 'asc', 'email', 'desc', 'phone', 'asc')); - * - * @param PicoPage|PicoLimit|array|null $page Page - * @param PicoSortable|array|null $sortable Sortable + * Constructor for the Pageable class. + * + * This constructor allows initializing a Pageable object with pagination and sorting options. + * It supports different formats for both page and sortable parameters. + * + * Example 1: Using `PicoPage` and `PicoSortable` + * + * ```php + * + * ``` + * + * Example 2: Using `PicoLimit` and `PicoSortable` + * + * ```php + * + * ``` + * + * Example 3: Using an array for page and `PicoSortable` + * + * ```php + * + * ``` + * + * Example 4: Using an array for both page and sortable + * + * ```php + * + * ``` + * + * Example 5: Using `PicoPage` and an array for sortable + * + * ```php + * + * ``` + * + * @param PicoPage|PicoLimit|array|null $page The page or limit configuration. Can be: + * - `PicoPage` instance (for page number and size), + * - `PicoLimit` instance (for offset and limit), + * - array with two elements representing page and size (e.g., `[1, 100]`). + * + * @param PicoSortable|array|null $sortable The sorting configuration. Can be: + * - `PicoSortable` instance (for sorting by multiple fields), + * - array of field-direction pairs (e.g., `['userName', 'asc', 'email', 'desc']`). */ public function __construct($page = null, $sortable = null) { @@ -60,7 +104,7 @@ public function __construct($page = null, $sortable = null) } else if(is_array($page)) { - // create from array + // Create from array $this->page = new PicoPage($page[0], $page[1]); } } @@ -72,7 +116,7 @@ public function __construct($page = null, $sortable = null) } else if(is_array($sortable)) { - // create from array + // Create from array $this->sortable = new PicoSortable($sortable); } } diff --git a/src/Database/PicoSqlite.php b/src/Database/PicoSqlite.php index a3c7a87c..3893a4a7 100644 --- a/src/Database/PicoSqlite.php +++ b/src/Database/PicoSqlite.php @@ -13,6 +13,7 @@ class PicoSqlite extends PicoDatabase { const LOGIC_AND = " and "; + /** * Database file path * diff --git a/src/Generator/PicoEntityGenerator.php b/src/Generator/PicoEntityGenerator.php index 9d085bde..17e98762 100644 --- a/src/Generator/PicoEntityGenerator.php +++ b/src/Generator/PicoEntityGenerator.php @@ -215,36 +215,86 @@ protected function getDataLength($str) } /** - * Get a mapping of database types to PHP types. + * Get a mapping of database types to PHP types for MySQL, PostgreSQL and SQLite. * * @return array Associative array of type mappings */ protected function getTypeMap() { return array( - "double" => "double", - "float" => "double", - "bigint" => "integer", - "smallint" => "integer", - "tinyint(1)" => "boolean", - "tinyint" => "integer", - "int" => "integer", - "varchar" => "string", - "char" => "string", - "tinytext" => "string", - "mediumtext" => "string", - "longtext" => "string", - "text" => "string", - "enum" => "string", - "bool" => "boolean", - "boolean" => "boolean", - "timestamp" => "string", - "datetime" => "string", - "date" => "string", - "time" => "string" + // Numeric types + "double" => "float", // PostgreSQL: double precision + "float" => "float", // PostgreSQL: float + "bigint" => "int", // PostgreSQL: bigint + "smallint" => "int", // PostgreSQL: smallint + "tinyint(1)" => "bool", // MySQL-style, use boolean for tinyint(1) + "tinyint" => "int", // PostgreSQL/SQLite: tinyint, handled as INT + "int" => "int", // PostgreSQL/SQLite: integer + "serial" => "int", // PostgreSQL: auto-increment integer (equivalent to INT) + "bigserial" => "int", // PostgreSQL: bigserial (auto-incrementing large integer, mapped to int in PHP) + "mediumint" => "int", // MySQL: mediumint (3-byte integer) + "smallserial" => "int", // PostgreSQL: smallserial (auto-incrementing small integer) + "unsigned" => "int", // MySQL: unsigned integer (mapped to int in PHP) + + // String types + "varchar" => "string", // PostgreSQL: variable-length string + "character varying" => "string", // PostgreSQL: character varying (same as varchar) + "char" => "string", // PostgreSQL: fixed-length string + "text" => "string", // PostgreSQL/SQLite: unlimited length string + "varchar(255)" => "string", // PostgreSQL: same as varchar without length + "citext" => "string", // PostgreSQL: case-insensitive text (equivalent to string) + + // MySQL-style text types (these types are similar to `text`) + "tinytext" => "string", // MySQL: tinytext + "mediumtext" => "string", // MySQL: mediumtext + "longtext" => "string", // MySQL: longtext + "text" => "string", // PostgreSQL/SQLite: text (string) + + // Boolean types + "bool" => "bool", // PostgreSQL: boolean + "boolean" => "bool", // PostgreSQL: boolean (same as bool) + + // Date/Time types + "timestamp" => "string", // PostgreSQL: timestamp (datetime) + "datetime" => "string", // PostgreSQL/SQLite: datetime + "date" => "string", // PostgreSQL/SQLite: date + "time" => "string", // PostgreSQL/SQLite: time + "timestamp with time zone" => "string", // PostgreSQL: timestamp with time zone + "timestamp without time zone" => "string", // PostgreSQL: timestamp without time zone + "date" => "string", // PostgreSQL/SQLite: date + "time" => "string", // PostgreSQL/SQLite: time + "interval" => "string", // PostgreSQL: interval (for durations) + "year" => "int", // MySQL: year type (usually stored as an integer) + + // SQLite-specific types + "integer" => "int", // SQLite: integer + "real" => "float", // SQLite: real (floating-point) + "text" => "string", // SQLite: text (string) + "blob" => "resource", // SQLite: blob (binary data) + + // SQLite's special handling + "BOOLEAN" => "bool", // SQLite: boolean (same as PostgreSQL) + + // Special cases + "json" => "array", // PostgreSQL: JSON type, mapped to PHP array + "jsonb" => "array", // PostgreSQL: JSONB (binary JSON), mapped to PHP array + "uuid" => "string", // PostgreSQL/SQLite: UUID + "xml" => "string", // PostgreSQL: XML type + "cidr" => "string", // PostgreSQL: CIDR type (IPv4/IPv6) + "inet" => "string", // PostgreSQL: Inet type (IPv4/IPv6) + "macaddr" => "string", // PostgreSQL: MAC address type + "point" => "string", // PostgreSQL: point type (coordinates) + "polygon" => "string", // PostgreSQL: polygon type + "line" => "string", // PostgreSQL: line type + "lseg" => "string", // PostgreSQL: line segment type + "path" => "string", // PostgreSQL: path type (geometric shapes) + "circle" => "string", // PostgreSQL: circle type (geometric shapes) + "json" => "array", // PostgreSQL: JSON data type + "jsonb" => "array", // PostgreSQL: Binary JSON type (faster) ); } + /** * Generate the entity class and save it to a file. * diff --git a/src/Txt.php b/src/Txt.php index e3b70638..8a03d37e 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -34,7 +34,7 @@ public static function __callStatic($name, $arguments) // NOSONAR * Returns a new instance of the Txt class. * * This method allows you to retrieve an instance of the Txt class for non-static operations. - * This instance can be used to access dynamic properties via the __get() magic method. + * This instance can be used to access dynamic properties via the `__get()` magic method. * * @return Txt A new instance of the Txt class. */ @@ -47,7 +47,7 @@ public static function getInstance() * Creates and returns a new instance of the Txt class. * * Similar to getInstance(), this method allows you to retrieve an instance of the Txt class - * for non-static operations, such as dynamic property access using the __get() magic method. + * for non-static operations, such as dynamic property access using the `__get()` magic method. * * @return Txt A new instance of the Txt class. */ diff --git a/src/Util/Database/PicoDatabaseUtilPostgreSql.php b/src/Util/Database/PicoDatabaseUtilPostgreSql.php index ca88b340..014675ef 100644 --- a/src/Util/Database/PicoDatabaseUtilPostgreSql.php +++ b/src/Util/Database/PicoDatabaseUtilPostgreSql.php @@ -494,11 +494,12 @@ public function getColumnType($columnType) { return 'BOOLEAN'; } - else if(stripos($columnType, 'biginteger(') === 0 + else if(stripos($columnType, 'biginteger') === 0 || stripos($columnType, 'smallinteger') === 0 || stripos($columnType, 'integer') === 0 || stripos($columnType, 'bigint') === 0 || stripos($columnType, 'smallint') === 0 + || stripos($columnType, 'tinyint') === 0 || stripos($columnType, 'int') === 0) { return 'INTEGER'; From 12abcad037086d7cc0e1e6357661b7e9d4e46b10 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 21:41:29 +0700 Subject: [PATCH 7/8] Update PicoDatabase.php --- src/Database/PicoDatabase.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Database/PicoDatabase.php b/src/Database/PicoDatabase.php index 2e5d3c20..e319a40d 100644 --- a/src/Database/PicoDatabase.php +++ b/src/Database/PicoDatabase.php @@ -175,7 +175,7 @@ private static function getDatabaseCredentialsFromPdo($pdo, $driver, $dbType) $schema = $stmt->fetchColumn(); // Fetch the schema name $timezone = self::convertOffsetToTimeZone(self::getTimeZoneOffset($pdo)); } - elseif ($dbType == PicoDatabaseType::DATABASE_TYPE_MYSQL || $dbType == PicoDatabaseType::DATABASE_TYPE_MARIADB) { + else if ($dbType == PicoDatabaseType::DATABASE_TYPE_MARIADB || $dbType == PicoDatabaseType::DATABASE_TYPE_MYSQL) { // For MySQL, the schema is the same as the database name $schema = $databaseName; // MySQL schema is the database name $timezone = self::convertOffsetToTimeZone(self::getTimeZoneOffset($pdo)); @@ -435,9 +435,9 @@ private function connectRDMS($withDatabase = true) } // Handle MySQL-specific connection settings - elseif ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) { + else if ($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MARIADB || $this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) { // Set time zone for MySQL - $initialQueries = "SET time_zone = '$timeZoneOffset';"; + $initialQueries = "SET time_zone='$timeZoneOffset';"; // Add charset to the initial queries for MySQL if ($charset) { @@ -590,8 +590,17 @@ public function disconnect() */ public function setTimeZoneOffset($timeZoneOffset) { - $sql = "SET time_zone='$timeZoneOffset';"; - $this->execute($sql); + if($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_PGSQL) + { + $sql = "SET TIMEZONE TO '$timeZoneOffset'"; + $this->execute($sql); + } + else if($this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MARIADB || $this->getDatabaseType() == PicoDatabaseType::DATABASE_TYPE_MYSQL) + { + $sql = "SET time_zone='$timeZoneOffset';"; + $this->execute($sql); + } + return $this; } From aa322897a772c8814e547df78c3bcd1a9a5d30ad Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Wed, 4 Dec 2024 21:47:06 +0700 Subject: [PATCH 8/8] Update Exceptions --- src/Exceptions/ClassNotFoundException.php | 2 +- src/Exceptions/CurlException.php | 2 +- src/Exceptions/EmptyResultException.php | 2 +- src/Exceptions/ErrorConnectionException.php | 2 +- src/Exceptions/FindOptionException.php | 2 +- src/Exceptions/InvalidAddressException.php | 2 +- src/Exceptions/InvalidAnnotationException.php | 2 +- src/Exceptions/InvalidFileFormatException.php | 2 +- src/Exceptions/InvalidFilterException.php | 2 +- src/Exceptions/InvalidInputFormatException.php | 2 +- src/Exceptions/InvalidParameterException.php | 2 +- src/Exceptions/InvalidPolygonException.php | 2 +- src/Exceptions/InvalidQueryInputException.php | 2 +- src/Exceptions/MandatoryTableNameException.php | 2 +- src/Exceptions/NoColumnMatchException.php | 2 +- src/Exceptions/NoColumnUpdatedException.php | 2 +- src/Exceptions/NoDatabaseConnectionException.php | 2 +- src/Exceptions/NoInsertableColumnException.php | 2 +- src/Exceptions/NoPrimaryKeyDefinedException.php | 2 +- src/Exceptions/NoRecordFoundException.php | 2 +- src/Exceptions/NoUpdatableColumnException.php | 2 +- src/Exceptions/NotNullColumnException.php | 2 +- src/Exceptions/NullPointerException.php | 2 +- src/Exceptions/UnknownErrorException.php | 2 +- src/Exceptions/ZeroArgumentException.php | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Exceptions/ClassNotFoundException.php b/src/Exceptions/ClassNotFoundException.php index c713954f..5ecc8aa5 100644 --- a/src/Exceptions/ClassNotFoundException.php +++ b/src/Exceptions/ClassNotFoundException.php @@ -33,7 +33,7 @@ class ClassNotFoundException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/CurlException.php b/src/Exceptions/CurlException.php index e4ddd808..2d5506f0 100644 --- a/src/Exceptions/CurlException.php +++ b/src/Exceptions/CurlException.php @@ -36,7 +36,7 @@ class CurlException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/EmptyResultException.php b/src/Exceptions/EmptyResultException.php index 3502beb5..086b2a9c 100644 --- a/src/Exceptions/EmptyResultException.php +++ b/src/Exceptions/EmptyResultException.php @@ -31,7 +31,7 @@ class EmptyResultException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/ErrorConnectionException.php b/src/Exceptions/ErrorConnectionException.php index 9e1336c4..60c28244 100644 --- a/src/Exceptions/ErrorConnectionException.php +++ b/src/Exceptions/ErrorConnectionException.php @@ -33,7 +33,7 @@ class ErrorConnectionException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/FindOptionException.php b/src/Exceptions/FindOptionException.php index 521ce656..d873ff3c 100644 --- a/src/Exceptions/FindOptionException.php +++ b/src/Exceptions/FindOptionException.php @@ -32,7 +32,7 @@ class FindOptionException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidAddressException.php b/src/Exceptions/InvalidAddressException.php index eeaf2e59..7ec31b1a 100644 --- a/src/Exceptions/InvalidAddressException.php +++ b/src/Exceptions/InvalidAddressException.php @@ -33,7 +33,7 @@ class InvalidAddressException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidAnnotationException.php b/src/Exceptions/InvalidAnnotationException.php index 4d9f1d95..c06bb450 100644 --- a/src/Exceptions/InvalidAnnotationException.php +++ b/src/Exceptions/InvalidAnnotationException.php @@ -32,7 +32,7 @@ class InvalidAnnotationException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidFileFormatException.php b/src/Exceptions/InvalidFileFormatException.php index 98e76718..862c8779 100644 --- a/src/Exceptions/InvalidFileFormatException.php +++ b/src/Exceptions/InvalidFileFormatException.php @@ -33,7 +33,7 @@ class InvalidFileFormatException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidFilterException.php b/src/Exceptions/InvalidFilterException.php index 5c76d6aa..1a8aba23 100644 --- a/src/Exceptions/InvalidFilterException.php +++ b/src/Exceptions/InvalidFilterException.php @@ -32,7 +32,7 @@ class InvalidFilterException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidInputFormatException.php b/src/Exceptions/InvalidInputFormatException.php index f5c4cecd..789cab68 100644 --- a/src/Exceptions/InvalidInputFormatException.php +++ b/src/Exceptions/InvalidInputFormatException.php @@ -33,7 +33,7 @@ class InvalidInputFormatException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidParameterException.php b/src/Exceptions/InvalidParameterException.php index d226cc73..a166a95d 100644 --- a/src/Exceptions/InvalidParameterException.php +++ b/src/Exceptions/InvalidParameterException.php @@ -32,7 +32,7 @@ class InvalidParameterException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidPolygonException.php b/src/Exceptions/InvalidPolygonException.php index 8ec91590..08ca9299 100644 --- a/src/Exceptions/InvalidPolygonException.php +++ b/src/Exceptions/InvalidPolygonException.php @@ -33,7 +33,7 @@ class InvalidPolygonException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/InvalidQueryInputException.php b/src/Exceptions/InvalidQueryInputException.php index 6508cc34..26ba845f 100644 --- a/src/Exceptions/InvalidQueryInputException.php +++ b/src/Exceptions/InvalidQueryInputException.php @@ -42,7 +42,7 @@ class InvalidQueryInputException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/MandatoryTableNameException.php b/src/Exceptions/MandatoryTableNameException.php index d9dc207b..b1014052 100644 --- a/src/Exceptions/MandatoryTableNameException.php +++ b/src/Exceptions/MandatoryTableNameException.php @@ -33,7 +33,7 @@ class MandatoryTableNameException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoColumnMatchException.php b/src/Exceptions/NoColumnMatchException.php index b1dcd7ca..f753183a 100644 --- a/src/Exceptions/NoColumnMatchException.php +++ b/src/Exceptions/NoColumnMatchException.php @@ -34,7 +34,7 @@ class NoColumnMatchException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoColumnUpdatedException.php b/src/Exceptions/NoColumnUpdatedException.php index d25328c4..ed7b6843 100644 --- a/src/Exceptions/NoColumnUpdatedException.php +++ b/src/Exceptions/NoColumnUpdatedException.php @@ -34,7 +34,7 @@ class NoColumnUpdatedException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoDatabaseConnectionException.php b/src/Exceptions/NoDatabaseConnectionException.php index 888bd8c4..e346924e 100644 --- a/src/Exceptions/NoDatabaseConnectionException.php +++ b/src/Exceptions/NoDatabaseConnectionException.php @@ -34,7 +34,7 @@ class NoDatabaseConnectionException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoInsertableColumnException.php b/src/Exceptions/NoInsertableColumnException.php index 54212bd1..ba943c70 100644 --- a/src/Exceptions/NoInsertableColumnException.php +++ b/src/Exceptions/NoInsertableColumnException.php @@ -35,7 +35,7 @@ class NoInsertableColumnException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoPrimaryKeyDefinedException.php b/src/Exceptions/NoPrimaryKeyDefinedException.php index 54e91e0c..8458b906 100644 --- a/src/Exceptions/NoPrimaryKeyDefinedException.php +++ b/src/Exceptions/NoPrimaryKeyDefinedException.php @@ -34,7 +34,7 @@ class NoPrimaryKeyDefinedException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoRecordFoundException.php b/src/Exceptions/NoRecordFoundException.php index 5203a089..4d47634a 100644 --- a/src/Exceptions/NoRecordFoundException.php +++ b/src/Exceptions/NoRecordFoundException.php @@ -35,7 +35,7 @@ class NoRecordFoundException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NoUpdatableColumnException.php b/src/Exceptions/NoUpdatableColumnException.php index 63e125a6..aada913e 100644 --- a/src/Exceptions/NoUpdatableColumnException.php +++ b/src/Exceptions/NoUpdatableColumnException.php @@ -35,7 +35,7 @@ class NoUpdatableColumnException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NotNullColumnException.php b/src/Exceptions/NotNullColumnException.php index 64548dec..ec958994 100644 --- a/src/Exceptions/NotNullColumnException.php +++ b/src/Exceptions/NotNullColumnException.php @@ -35,7 +35,7 @@ class NotNullColumnException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/NullPointerException.php b/src/Exceptions/NullPointerException.php index 098b64c9..e0e0a1b3 100644 --- a/src/Exceptions/NullPointerException.php +++ b/src/Exceptions/NullPointerException.php @@ -35,7 +35,7 @@ class NullPointerException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/UnknownErrorException.php b/src/Exceptions/UnknownErrorException.php index 4681e148..855a183f 100644 --- a/src/Exceptions/UnknownErrorException.php +++ b/src/Exceptions/UnknownErrorException.php @@ -35,7 +35,7 @@ class UnknownErrorException extends Exception * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous; diff --git a/src/Exceptions/ZeroArgumentException.php b/src/Exceptions/ZeroArgumentException.php index de9fdc08..a174e5cb 100644 --- a/src/Exceptions/ZeroArgumentException.php +++ b/src/Exceptions/ZeroArgumentException.php @@ -35,7 +35,7 @@ class ZeroArgumentException extends InvalidArgumentException * @param int $code Exception code * @param Throwable|null $previous Previous exception */ - public function __construct($message, $code = 0, Throwable $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); $this->previous = $previous;