diff --git a/app/Config/Database.php b/app/Config/Database.php index 2c092124550f..e2450ec16cf1 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -25,23 +25,24 @@ class Database extends Config * The default database connection. */ public array $default = [ - 'DSN' => '', - 'hostname' => 'localhost', - 'username' => '', - 'password' => '', - 'database' => '', - 'DBDriver' => 'MySQLi', - 'DBPrefix' => '', - 'pConnect' => false, - 'DBDebug' => true, - 'charset' => 'utf8', - 'DBCollat' => 'utf8_general_ci', - 'swapPre' => '', - 'encrypt' => false, - 'compress' => false, - 'strictOn' => false, - 'failover' => [], - 'port' => 3306, + 'DSN' => '', + 'hostname' => 'localhost', + 'username' => '', + 'password' => '', + 'database' => '', + 'DBDriver' => 'MySQLi', + 'DBPrefix' => '', + 'pConnect' => false, + 'DBDebug' => true, + 'charset' => 'utf8', + 'DBCollat' => 'utf8_general_ci', + 'swapPre' => '', + 'encrypt' => false, + 'compress' => false, + 'strictOn' => false, + 'failover' => [], + 'port' => 3306, + 'numberNative' => false, ]; /** diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 094fcfe7e3ec..45b306ce2a44 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -72,6 +72,13 @@ class Connection extends BaseConnection */ public $resultMode = MYSQLI_STORE_RESULT; + /** + * Use MYSQLI_OPT_INT_AND_FLOAT_NATIVE + * + * @var bool + */ + public $numberNative = false; + /** * Connect to the database. * @@ -99,6 +106,10 @@ public function connect(bool $persistent = false) $this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10); + if ($this->numberNative === true) { + $this->mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); + } + if (isset($this->strictOn)) { if ($this->strictOn) { $this->mysqli->options( diff --git a/tests/system/Database/Live/MySQLi/NumberNativeTest.php b/tests/system/Database/Live/MySQLi/NumberNativeTest.php new file mode 100644 index 000000000000..1f95dd4e7c59 --- /dev/null +++ b/tests/system/Database/Live/MySQLi/NumberNativeTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Database\Live\MySQLi; + +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\DatabaseTestTrait; +use Config\Database; +use Tests\Support\Database\Seeds\CITestSeeder; + +/** + * @group DatabaseLive + * + * @internal + */ +final class NumberNativeTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + + private $tests; + protected $refresh = true; + protected $seed = CITestSeeder::class; + + protected function setUp(): void + { + parent::setUp(); + + $config = config('Database'); + + $this->tests = $config->tests; + } + + public function testEnableNumberNative() + { + $this->tests['numberNative'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $this->assertTrue($db1->numberNative); + } + + public function testDisableNumberNative() + { + $this->tests['numberNative'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $this->assertFalse($db1->numberNative); + } + + public function testQueryDataAfterEnableNumberNative() + { + $this->tests['numberNative'] = true; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $data = $db1->table('db_type_test') + ->get() + ->getRow(); + + $this->assertIsFloat($data->type_float); + $this->assertIsInt($data->type_integer); + } + + public function testQueryDataAfterDisableNumberNative() + { + $this->tests['numberNative'] = false; + + $db1 = Database::connect($this->tests); + + if ($db1->DBDriver !== 'MySQLi') { + $this->markTestSkipped('Only MySQLi can complete this test.'); + } + + $data = $db1->table('db_type_test') + ->get() + ->getRow(); + + $this->assertIsString($data->type_float); + $this->assertIsString($data->type_integer); + } +} diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index a4d2afbafec2..24ef1efc1707 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -59,6 +59,9 @@ Forge Others ------ +- **MySQLi:** Added the ``numberNative`` attribute to the Database Config to keep the variable type obtained after SQL Query consistent with the type set in the database. + See :ref:`Database Configuration `. + Model ===== diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index b5a768439ccc..cfd666988ac3 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -104,53 +104,56 @@ default group's configuration settings. The values should be name following this database.default.password = ''; database.default.database = 'ci4'; +.. _database-configuration-explanation-of-values: + ********************** Explanation of Values: ********************** -=============== =========================================================================================================== - Name Config Description -=============== =========================================================================================================== -**dsn** The DSN connect string (an all-in-one configuration sequence). -**hostname** The hostname of your database server. Often this is 'localhost'. -**username** The username used to connect to the database. (``SQLite3`` does not use this.) -**password** The password used to connect to the database. (``SQLite3`` does not use this.) -**database** The name of the database you want to connect to. - - .. note:: CodeIgniter doesn't support dots (``.``) in the database, table, and column names. -**DBDriver** The database driver name. e.g.,: ``MySQLi``, ``Postgres``, etc. The case must match the driver name. - You can set a fully qualified classname to use your custom driver. -**DBPrefix** An optional table prefix which will added to the table name when running - :doc:`Query Builder ` queries. This permits multiple CodeIgniter - installations to share one database. -**pConnect** true/false (boolean) - Whether to use a persistent connection. -**DBDebug** true/false (boolean) - Whether to throw exceptions or not when database errors occur. -**charset** The character set used in communicating with the database. -**DBCollat** The character collation used in communicating with the database (``MySQLi`` only). -**swapPre** A default table prefix that should be swapped with ``DBPrefix``. This is useful for distributed - applications where you might run manually written queries, and need the prefix to still be - customizable by the end user. -**schema** The database schema, default value varies by driver. (Used by ``Postgres`` and ``SQLSRV``.) -**encrypt** Whether or not to use an encrypted connection. - ``SQLSRV`` driver accepts true/false - ``MySQLi`` driver accepts an array with the following options: - * ``ssl_key`` - Path to the private key file - * ``ssl_cert`` - Path to the public key certificate file - * ``ssl_ca`` - Path to the certificate authority file - * ``ssl_capath`` - Path to a directory containing trusted CA certificates in PEM format - * ``ssl_cipher`` - List of *allowed* ciphers to be used for the encryption, separated by colons (``:``) - * ``ssl_verify`` - true/false; Whether to verify the server certificate or not (``MySQLi`` only) -**compress** Whether or not to use client compression (``MySQLi`` only). -**strictOn** true/false (boolean) - Whether to force "Strict Mode" connections, good for ensuring strict SQL - while developing an application (``MySQLi`` only). -**port** The database port number. -**foreignKeys** true/false (boolean) - Whether or not to enable Foreign Key constraint (``SQLite3`` only). - - .. important:: SQLite3 Foreign Key constraint is disabled by default. - See `SQLite documentation `_. - To enforce Foreign Key constraint, set this config item to true. -**busyTimeout** milliseconds (int) - Sleeps for a specified amount of time when a table is locked (``SQLite3`` only). -=============== =========================================================================================================== +================ =========================================================================================================== + Name Config Description +================ =========================================================================================================== +**dsn** The DSN connect string (an all-in-one configuration sequence). +**hostname** The hostname of your database server. Often this is 'localhost'. +**username** The username used to connect to the database. (``SQLite3`` does not use this.) +**password** The password used to connect to the database. (``SQLite3`` does not use this.) +**database** The name of the database you want to connect to. + + .. note:: CodeIgniter doesn't support dots (``.``) in the database, table, and column names. +**DBDriver** The database driver name. e.g.,: ``MySQLi``, ``Postgres``, etc. The case must match the driver name. + You can set a fully qualified classname to use your custom driver. +**DBPrefix** An optional table prefix which will added to the table name when running + :doc:`Query Builder ` queries. This permits multiple CodeIgniter + installations to share one database. +**pConnect** true/false (boolean) - Whether to use a persistent connection. +**DBDebug** true/false (boolean) - Whether to throw exceptions or not when database errors occur. +**charset** The character set used in communicating with the database. +**DBCollat** The character collation used in communicating with the database (``MySQLi`` only). +**swapPre** A default table prefix that should be swapped with ``DBPrefix``. This is useful for distributed + applications where you might run manually written queries, and need the prefix to still be + customizable by the end user. +**schema** The database schema, default value varies by driver. (Used by ``Postgres`` and ``SQLSRV``.) +**encrypt** Whether or not to use an encrypted connection. + ``SQLSRV`` driver accepts true/false + ``MySQLi`` driver accepts an array with the following options: + * ``ssl_key`` - Path to the private key file + * ``ssl_cert`` - Path to the public key certificate file + * ``ssl_ca`` - Path to the certificate authority file + * ``ssl_capath`` - Path to a directory containing trusted CA certificates in PEM format + * ``ssl_cipher`` - List of *allowed* ciphers to be used for the encryption, separated by colons (``:``) + * ``ssl_verify`` - true/false; Whether to verify the server certificate or not (``MySQLi`` only) +**compress** Whether or not to use client compression (``MySQLi`` only). +**strictOn** true/false (boolean) - Whether to force "Strict Mode" connections, good for ensuring strict SQL + while developing an application (``MySQLi`` only). +**port** The database port number. +**foreignKeys** true/false (boolean) - Whether or not to enable Foreign Key constraint (``SQLite3`` only). + + .. important:: SQLite3 Foreign Key constraint is disabled by default. + See `SQLite documentation `_. + To enforce Foreign Key constraint, set this config item to true. +**busyTimeout** milliseconds (int) - Sleeps for a specified amount of time when a table is locked (``SQLite3`` only). +**numberNative** true/false (boolean) - Whether or not to enable MYSQLI_OPT_INT_AND_FLOAT_NATIVE (``MySQLi`` only). +================ =========================================================================================================== .. note:: Depending on what database driver you are using (``MySQLi``, ``Postgres``, etc.) not all values will be needed. For example, when using ``SQLite3`` you