From 52a346c8f5737b7f0969da8baf73c49040089109 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 01:12:09 +0200 Subject: [PATCH 01/53] ResultSet: added fetchList() as alias for fetchFields() & shortcuts --- src/Database/Connection.php | 14 ++++++++++++-- src/Database/Explorer.php | 14 ++++++++++++-- src/Database/ResultSet.php | 15 ++++++++++++--- tests/Database/Connection.fetch.phpt | 4 ++-- tests/Database/Explorer.fetch.phpt | 4 ++-- ...chFields().phpt => ResultSet.fetchList().phpt} | 4 ++-- 6 files changed, 42 insertions(+), 13 deletions(-) rename tests/Database/{ResultSet.fetchFields().phpt => ResultSet.fetchList().phpt} (85%) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 4d66a05a1..233c2d6c2 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -285,12 +285,22 @@ public function fetchField(#[Language('SQL')] string $sql, #[Language('GenericSQ /** - * Shortcut for query()->fetchFields() + * Shortcut for query()->fetchList() + * @param literal-string $sql + */ + public function fetchList(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array + { + return $this->query($sql, ...$params)->fetchList(); + } + + + /** + * Shortcut for query()->fetchList() * @param literal-string $sql */ public function fetchFields(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array { - return $this->query($sql, ...$params)->fetchFields(); + return $this->query($sql, ...$params)->fetchList(); } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 12cbd3f25..fe1a486c2 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -137,12 +137,22 @@ public function fetchField(#[Language('SQL')] string $sql, #[Language('GenericSQ /** - * Shortcut for query()->fetchFields() + * Shortcut for query()->fetchList() + * @param literal-string $sql + */ + public function fetchList(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array + { + return $this->connection->query($sql, ...$params)->fetchList(); + } + + + /** + * Shortcut for query()->fetchList() * @param literal-string $sql */ public function fetchFields(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array { - return $this->connection->query($sql, ...$params)->fetchFields(); + return $this->connection->query($sql, ...$params)->fetchList(); } diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 6c8c1cfe2..05a96ff06 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -221,7 +221,7 @@ public function fetch(): ?Row /** - * Fetches single field. + * Returns the first field of the next row or null if there are no more rows. */ public function fetchField(): mixed { @@ -231,15 +231,24 @@ public function fetchField(): mixed /** - * Fetches array of fields. + * Returns the next row as indexes array or null if there are no more rows. */ - public function fetchFields(): ?array + public function fetchList(): ?array { $row = $this->fetchAssoc(); return $row ? array_values($row) : null; } + /** + * Alias for fetchList(). + */ + public function fetchFields(): ?array + { + return $this->fetchList(); + } + + /** * Fetches all rows as associative array. */ diff --git a/tests/Database/Connection.fetch.phpt b/tests/Database/Connection.fetch.phpt index 745d76c80..e4e09421f 100644 --- a/tests/Database/Connection.fetch.phpt +++ b/tests/Database/Connection.fetch.phpt @@ -39,8 +39,8 @@ test('fetchField', function () use ($connection) { }); -test('fetchFields', function () use ($connection) { - Assert::same([11, 'Jakub Vrana'], $connection->fetchFields('SELECT id, name FROM author ORDER BY id')); +test('fetchList', function () use ($connection) { + Assert::same([11, 'Jakub Vrana'], $connection->fetchList('SELECT id, name FROM author ORDER BY id')); }); diff --git a/tests/Database/Explorer.fetch.phpt b/tests/Database/Explorer.fetch.phpt index bc1024032..8b07a8e1b 100644 --- a/tests/Database/Explorer.fetch.phpt +++ b/tests/Database/Explorer.fetch.phpt @@ -41,8 +41,8 @@ test('fetchField', function () use ($explorer) { }); -test('fetchFields', function () use ($explorer) { - Assert::same([11, 'Jakub Vrana'], $explorer->fetchFields('SELECT id, name FROM author ORDER BY id')); +test('fetchList', function () use ($explorer) { + Assert::same([11, 'Jakub Vrana'], $explorer->fetchList('SELECT id, name FROM author ORDER BY id')); }); diff --git a/tests/Database/ResultSet.fetchFields().phpt b/tests/Database/ResultSet.fetchList().phpt similarity index 85% rename from tests/Database/ResultSet.fetchFields().phpt rename to tests/Database/ResultSet.fetchList().phpt index 92457b389..f13e91b57 100644 --- a/tests/Database/ResultSet.fetchFields().phpt +++ b/tests/Database/ResultSet.fetchList().phpt @@ -18,12 +18,12 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($connection) { $res = $connection->query('SELECT name, id FROM author ORDER BY id'); - Assert::same(['Jakub Vrana', 11], $res->fetchFields()); + Assert::same(['Jakub Vrana', 11], $res->fetchList()); }); test('', function () use ($connection) { $res = $connection->query('SELECT id FROM author WHERE id = ?', 666); - Assert::null($res->fetchFields()); + Assert::null($res->fetchList()); }); From 7c4be75fbb27f400386c2ace2482be645ea455d4 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 2 Sep 2024 23:49:40 +0200 Subject: [PATCH 02/53] github actions updated --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 59e3468cf..7cf08b7d4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,7 +3,7 @@ name: Tests on: [push, pull_request] env: - php-extensions: mbstring, intl, pdo_sqlsrv-5.10.0beta2 + php-extensions: mbstring, intl, pdo_sqlsrv-5.12.0 php-tools: "composer:v2, pecl" jobs: From caaaa42eb969ebd2c2a92393c61edcb430c9fa61 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 03:49:25 +0200 Subject: [PATCH 03/53] cs --- src/Database/Driver.php | 56 ++++++++++++++------------- src/Database/Drivers/MsSqlDriver.php | 12 +++--- src/Database/Drivers/MySqlDriver.php | 20 +++++----- src/Database/Drivers/OciDriver.php | 12 +++--- src/Database/Drivers/OdbcDriver.php | 12 +++--- src/Database/Drivers/PgSqlDriver.php | 12 +++--- src/Database/Drivers/SqliteDriver.php | 12 +++--- src/Database/Drivers/SqlsrvDriver.php | 12 +++--- src/Database/Table/SqlBuilder.php | 2 +- 9 files changed, 77 insertions(+), 73 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 030674007..b69874bb9 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -19,7 +19,7 @@ interface Driver SupportSequence = 'sequence', SupportSelectUngroupedColumns = 'ungrouped_cols', SupportMultiInsertAsSelect = 'insert_as_select', - SupportMultiColumnAsOrCond = 'multi_column_as_or', + SupportMultiColumnAsOrCondition = 'multi_column_as_or', SupportSubselect = 'subselect', SupportSchema = 'schema'; @@ -32,6 +32,12 @@ interface Driver SUPPORT_SUBSELECT = 'subselect', SUPPORT_SCHEMA = 'schema'; + /** + * Checks if the engine supports a specific feature. + * @param self::Support* $feature + */ + function isSupported(string $feature): bool; + /** * Initializes connection. */ @@ -42,43 +48,47 @@ function initialize(Connection $connection, array $options): void; */ function convertException(\PDOException $e): DriverException; - /** - * Delimites identifier for use in a SQL statement. - */ + /********************* SQL utilities ****************d*g**/ + + /** Escapes an identifier for use in an SQL statement. */ function delimite(string $name): string; - /** - * Formats date-time for use in a SQL statement. - */ + /** Formats a date-time value for use in an SQL statement. */ function formatDateTime(\DateTimeInterface $value): string; - /** - * Formats date-time interval for use in a SQL statement. - */ + /** Formats a date-time interval for use in an SQL statement. */ function formatDateInterval(\DateInterval $value): string; - /** - * Encodes string for use in a LIKE statement. - */ + /** Encodes string for use in a LIKE statement. */ function formatLike(string $value, int $pos): string; - /** - * Injects LIMIT/OFFSET to the SQL query. - */ + /** Applies LIMIT and OFFSET clauses to an SQL query. */ function applyLimit(string &$sql, ?int $limit, ?int $offset): void; /********************* reflection ****************d*g**/ - /** @return list */ + /** + * Returns a list of all tables in the database. + * @return list + */ function getTables(): array; - /** @return list */ + /** + * Returns detailed information about columns in a table. + * @return list + */ function getColumns(string $table): array; - /** @return list, unique: bool, primary: bool}> */ + /** + * Returns information about indexes in a table. + * @return list, unique: bool, primary: bool}> + */ function getIndexes(string $table): array; - /** @return list */ + /** + * Returns information about foreign keys in a table. + * @return list + */ function getForeignKeys(string $table): array; /** @@ -86,12 +96,6 @@ function getForeignKeys(string $table): array; * @return array */ function getColumnTypes(\PDOStatement $statement): array; - - /** - * Cheks if driver supports specific property - * @param self::Support* $item - */ - function isSupported(string $item): bool; } diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 4ef886921..3509cb434 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -26,6 +26,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportSubselect; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { return Nette\Database\DriverException::from($e); @@ -216,10 +222,4 @@ public function getColumnTypes(\PDOStatement $statement): array { return Nette\Database\Helpers::detectTypes($statement); } - - - public function isSupported(string $item): bool - { - return $item === self::SupportSubselect; - } } diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index cca8735b4..9fab9c3ea 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -48,6 +48,16 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + // MULTI_COLUMN_AS_OR_COND due to mysql bugs: + // - http://bugs.mysql.com/bug.php?id=31188 + // - http://bugs.mysql.com/bug.php?id=35819 + // and more. + return $feature === self::SupportSelectUngroupedColumns || $feature === self::SupportMultiColumnAsOrCondition; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[1] ?? null; @@ -211,14 +221,4 @@ public function getColumnTypes(\PDOStatement $statement): array return $types; } - - - public function isSupported(string $item): bool - { - // MULTI_COLUMN_AS_OR_COND due to mysql bugs: - // - http://bugs.mysql.com/bug.php?id=31188 - // - http://bugs.mysql.com/bug.php?id=35819 - // and more. - return $item === self::SupportSelectUngroupedColumns || $item === self::SupportMultiColumnAsOrCond; - } } diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index 6f5fc7e57..ba48033ca 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -28,6 +28,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportSequence || $feature === self::SupportSubselect; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[1] ?? null; @@ -133,10 +139,4 @@ public function getColumnTypes(\PDOStatement $statement): array { return []; } - - - public function isSupported(string $item): bool - { - return $item === self::SupportSequence || $item === self::SupportSubselect; - } } diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index 078a202cc..d687fc915 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -22,6 +22,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportSubselect; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { return Nette\Database\DriverException::from($e); @@ -104,10 +110,4 @@ public function getColumnTypes(\PDOStatement $statement): array { return []; } - - - public function isSupported(string $item): bool - { - return $item === self::SupportSubselect; - } } diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index bda0fce9a..f2f8ebe1b 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -26,6 +26,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportSequence || $feature === self::SupportSubselect || $feature === self::SupportSchema; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[0] ?? null; @@ -244,12 +250,6 @@ public function getColumnTypes(\PDOStatement $statement): array } - public function isSupported(string $item): bool - { - return $item === self::SupportSequence || $item === self::SupportSubselect || $item === self::SupportSchema; - } - - /** * Converts: schema.name => "schema"."name" */ diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 801329fb7..db886de85 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -28,6 +28,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportMultiInsertAsSelect || $feature === self::SupportSubselect || $feature === self::SupportMultiColumnAsOrCondition; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[1] ?? null; @@ -243,10 +249,4 @@ public function getColumnTypes(\PDOStatement $statement): array return $types; } - - - public function isSupported(string $item): bool - { - return $item === self::SupportMultiInsertAsSelect || $item === self::SupportSubselect || $item === self::SupportMultiColumnAsOrCond; - } } diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index f5e45b360..e34a50bb3 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -26,6 +26,12 @@ public function initialize(Nette\Database\Connection $connection, array $options } + public function isSupported(string $feature): bool + { + return $feature === self::SupportSubselect; + } + + public function convertException(\PDOException $e): Nette\Database\DriverException { return Nette\Database\DriverException::from($e); @@ -234,10 +240,4 @@ public function getColumnTypes(\PDOStatement $statement): array return $types; } - - - public function isSupported(string $item): bool - { - return $item === self::SupportSubselect; - } } diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 4db2d3f09..6867e39a1 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -808,7 +808,7 @@ protected function addConditionComposition( array &$conditionsParameters, ): bool { - if ($this->driver->isSupported(Driver::SupportMultiColumnAsOrCond)) { + if ($this->driver->isSupported(Driver::SupportMultiColumnAsOrCondition)) { $conditionFragment = '(' . implode(' = ? AND ', $columns) . ' = ?) OR '; $condition = substr(str_repeat($conditionFragment, count($parameters)), 0, -4); return $this->addCondition($condition, [Nette\Utils\Arrays::flatten($parameters)], $conditions, $conditionsParameters); From 3a1030cbf87150b7ce0d9194589ef9a3bacfff65 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 15:28:10 +0100 Subject: [PATCH 04/53] opened 4.0-dev --- composer.json | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 4dbddd8c6..93277697a 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } } } diff --git a/readme.md b/readme.md index 2f4742165..0a26a191a 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ Nette Database ============== [![Downloads this Month](https://img.shields.io/packagist/dm/nette/database.svg)](https://packagist.org/packages/nette/database) -[![Tests](https://github.com/nette/database/actions/workflows/tests.yml/badge.svg?branch=v3.2)](https://github.com/nette/database/actions) +[![Tests](https://github.com/nette/database/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/nette/database/actions) [![Latest Stable Version](https://poser.pugx.org/nette/database/v/stable)](https://github.com/nette/database/releases) [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/database/blob/master/license.md) From e67b168a56936ac901b8ca10806215fc74a38d8e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 16 May 2024 15:05:25 +0200 Subject: [PATCH 05/53] readme: added jumbo --- readme.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 0a26a191a..647b53b74 100644 --- a/readme.md +++ b/readme.md @@ -1,25 +1,26 @@ -Nette Database -============== +[![Nette Database](https://github.com/nette/database/assets/194960/97d8f31b-096c-466c-a76f-f5b9e511ea8d)](https://doc.nette.org/database) [![Downloads this Month](https://img.shields.io/packagist/dm/nette/database.svg)](https://packagist.org/packages/nette/database) [![Tests](https://github.com/nette/database/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/nette/database/actions) [![Latest Stable Version](https://poser.pugx.org/nette/database/v/stable)](https://github.com/nette/database/releases) [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/database/blob/master/license.md) +  Introduction ------------ Nette provides a powerful layer for accessing your database easily. -- composes SQL queries with ease -- easily fetches data -- uses efficient queries and does not transmit unnecessary data +✅ composes SQL queries with ease
+✅ significantly simplifies retrieving data without writing SQL queries
+✅ uses efficient queries and does not transmit unnecessary data -The [Nette Database Core](https://doc.nette.org/database-core) is a wrapper around the PDO and provides core functionality. +The [Nette Database Core](https://doc.nette.org/en/database/core) is a wrapper around the PDO and provides core functionality. -The [Nette Database Explorer](https://doc.nette.org/database-explorer) layer helps you to fetch database data more easily and in a more optimized way. +The [Nette Database Explorer](https://doc.nette.org/en/database/explorer) layer helps you to fetch database data more easily and in a more optimized way. +  [Support Me](https://github.com/sponsors/dg) -------------------------------------------- @@ -30,6 +31,7 @@ Do you like Nette Database? Are you looking forward to the new features? Thank you! +  Installation ------------ @@ -42,12 +44,14 @@ composer require nette/database It requires PHP version 8.1 and supports PHP up to 8.4. +  Usage ----- This is just a piece of documentation. [Please see our website](https://doc.nette.org/database). +  Database Core ------------- @@ -71,6 +75,8 @@ $database->query('UPDATE users SET ? WHERE id=?', $data, $id); $database->query('SELECT * FROM categories WHERE id=?', 123)->dump(); ``` +  + Database Explorer ----------------- From 35cf35ab472ecb32d9a0ac1c891dc8bdfd9c3212 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 10 May 2024 16:24:09 +0200 Subject: [PATCH 06/53] removed old Driver::SUPPORT & MySqlDriver::Error constants (BC break) --- src/Database/Driver.php | 9 --------- src/Database/Drivers/MySqlDriver.php | 5 ----- 2 files changed, 14 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index b69874bb9..3ca786f90 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -23,15 +23,6 @@ interface Driver SupportSubselect = 'subselect', SupportSchema = 'schema'; - /** @deprecated use Driver::Support* */ - public const - SUPPORT_SEQUENCE = 'sequence', - SUPPORT_SELECT_UNGROUPED_COLUMNS = 'ungrouped_cols', - SUPPORT_MULTI_INSERT_AS_SELECT = 'insert_as_select', - SUPPORT_MULTI_COLUMN_AS_OR_COND = 'multi_column_as_or', - SUPPORT_SUBSELECT = 'subselect', - SUPPORT_SCHEMA = 'schema'; - /** * Checks if the engine supports a specific feature. * @param self::Support* $feature diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 9fab9c3ea..8f595a810 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -17,11 +17,6 @@ */ class MySqlDriver implements Nette\Database\Driver { - public const - ERROR_ACCESS_DENIED = 1045, - ERROR_DUPLICATE_ENTRY = 1062, - ERROR_DATA_TRUNCATED = 1265; - private Nette\Database\Connection $connection; private bool $convertBoolean; From 49c05dbb2cf67d3660f0e4e2981e2d7d92060768 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 14:05:47 +0100 Subject: [PATCH 07/53] deprecated methods trigger notices --- src/Database/Connection.php | 2 ++ src/Database/Explorer.php | 1 + src/Database/Helpers.php | 2 ++ src/Database/ResultSet.php | 1 + tests/Database.Tracy/ConnectionPanel.phpt | 2 +- tests/Database/Connection.query.phpt | 2 +- tests/Database/Explorer.query.phpt | 2 +- 7 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 233c2d6c2..aaa8352e6 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -110,6 +110,7 @@ public function getDriver(): Driver /** @deprecated use getDriver() */ public function getSupplementalDriver(): Driver { + trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED); $this->connect(); return $this->driver; } @@ -228,6 +229,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] /** @deprecated use query() */ public function queryArgs(string $sql, array $params): ResultSet { + trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED); return $this->query($sql, ...$params); } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index fe1a486c2..c280a02e2 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -75,6 +75,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] /** @deprecated use query() */ public function queryArgs(string $sql, array $params): ResultSet { + trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED); return $this->connection->query($sql, ...$params); } diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 7f9cf0623..3684a3eb1 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -303,6 +303,7 @@ public static function createDebugPanel( Tracy\BlueScreen $blueScreen, ): ?ConnectionPanel { + trigger_error(__METHOD__ . '() is deprecated, use Nette\Bridges\DatabaseTracy\ConnectionPanel::initialize()', E_USER_DEPRECATED); return ConnectionPanel::initialize($connection, true, $name, $explain, $bar, $blueScreen); } @@ -317,6 +318,7 @@ public static function initializeTracy( ?Tracy\BlueScreen $blueScreen = null, ): ?ConnectionPanel { + trigger_error(__METHOD__ . '() is deprecated, use Nette\Bridges\DatabaseTracy\ConnectionPanel::initialize()', E_USER_DEPRECATED); return ConnectionPanel::initialize($connection, $addBarPanel, $name, $explain, $bar, $blueScreen); } diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 05a96ff06..2d495aaab 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -69,6 +69,7 @@ public function __construct( /** @deprecated */ public function getConnection(): Connection { + trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); return $this->connection; } diff --git a/tests/Database.Tracy/ConnectionPanel.phpt b/tests/Database.Tracy/ConnectionPanel.phpt index 4dba1b30c..bf2ffe8a8 100644 --- a/tests/Database.Tracy/ConnectionPanel.phpt +++ b/tests/Database.Tracy/ConnectionPanel.phpt @@ -47,7 +47,7 @@ test('Bluescreen Panel', function () { test('deprecated initialization', function () { $connection = new Connection('sqlite::memory:'); - $panel = Nette\Database\Helpers::initializeTracy($connection, addBarPanel: true, name: 'foo'); + $panel = @Nette\Database\Helpers::initializeTracy($connection, addBarPanel: true, name: 'foo'); // deprecated $connection->beginTransaction(); $connection->query('SELECT 1'); diff --git a/tests/Database/Connection.query.phpt b/tests/Database/Connection.query.phpt index 1a1d60ce0..4465bd3fe 100644 --- a/tests/Database/Connection.query.phpt +++ b/tests/Database/Connection.query.phpt @@ -32,7 +32,7 @@ test('', function () use ($connection) { test('', function () use ($connection) { - $res = $connection->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); + $res = @$connection->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); Assert::same([11, 12], $res->getParameters()); }); diff --git a/tests/Database/Explorer.query.phpt b/tests/Database/Explorer.query.phpt index 254a38c7d..9a1f8d13f 100644 --- a/tests/Database/Explorer.query.phpt +++ b/tests/Database/Explorer.query.phpt @@ -33,7 +33,7 @@ test('', function () use ($explorer) { test('', function () use ($explorer) { - $res = $explorer->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); + $res = @$explorer->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); Assert::same([11, 12], $res->getParameters()); }); From 0e7f616eacce426bd72be55ecfcbeb1c4510a3e3 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 04:10:47 +0200 Subject: [PATCH 08/53] DatabaseExtension: removed obsolete option 'reflection' --- src/Bridges/DatabaseDI/DatabaseExtension.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Bridges/DatabaseDI/DatabaseExtension.php b/src/Bridges/DatabaseDI/DatabaseExtension.php index 703c18a55..e93e5a61b 100644 --- a/src/Bridges/DatabaseDI/DatabaseExtension.php +++ b/src/Bridges/DatabaseDI/DatabaseExtension.php @@ -77,6 +77,10 @@ public function beforeCompile(): void private function setupDatabase(\stdClass $config, string $name): void { + if (!empty($config->reflection)) { + throw new Nette\DeprecatedException('The "reflection" option is deprecated, use "conventions" instead.'); + } + $builder = $this->getContainerBuilder(); foreach ($config->options as $key => $value) { @@ -99,21 +103,11 @@ private function setupDatabase(\stdClass $config, string $name): void ->setArguments([$connection]) ->setAutowired($config->autowired); - if (!empty($config->reflection)) { - $conventionsServiceName = 'reflection'; - $config->conventions = $config->reflection; - if (is_string($config->conventions) && strtolower($config->conventions) === 'conventional') { - $config->conventions = 'Static'; - } - } else { - $conventionsServiceName = 'conventions'; - } - if (!$config->conventions) { $conventions = null; } elseif (is_string($config->conventions)) { - $conventions = $builder->addDefinition($this->prefix("$name.$conventionsServiceName")) + $conventions = $builder->addDefinition($this->prefix("$name.conventions")) ->setFactory(preg_match('#^[a-z]+$#Di', $config->conventions) ? 'Nette\Database\Conventions\\' . ucfirst($config->conventions) . 'Conventions' : $config->conventions) From 4d4ae57db751798a6531d85df4b732d7c170896d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:49:20 +0200 Subject: [PATCH 09/53] Driver::getColumns() nativetype -> nativeType, autoincrement -> autoIncrement (BC break) --- src/Database/Driver.php | 2 +- src/Database/Drivers/MsSqlDriver.php | 4 ++-- src/Database/Drivers/MySqlDriver.php | 4 ++-- src/Database/Drivers/PgSqlDriver.php | 4 ++-- src/Database/Drivers/SqliteDriver.php | 4 ++-- src/Database/Drivers/SqlsrvDriver.php | 6 +++--- src/Database/Reflection/Table.php | 3 ++- src/Database/Structure.php | 14 ++++++------- tests/Database/Reflection.driver.phpt | 24 +++++++++++------------ tests/Database/Reflection.postgre.10.phpt | 16 +++++++-------- tests/Database/Structure.phpt | 24 +++++++++++------------ 11 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 3ca786f90..d2cc64127 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -66,7 +66,7 @@ function getTables(): array; /** * Returns detailed information about columns in a table. - * @return list + * @return list */ function getColumns(string $table): array; diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 3509cb434..caeaf47d4 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -127,12 +127,12 @@ public function getColumns(string $table): array $columns[] = [ 'name' => $row['COLUMN_NAME'], 'table' => $table, - 'nativetype' => strtoupper($row['DATA_TYPE']), + 'nativeType' => strtoupper($row['DATA_TYPE']), 'size' => $row['CHARACTER_MAXIMUM_LENGTH'] ?? $row['NUMERIC_PRECISION'], 'unsigned' => false, 'nullable' => $row['IS_NULLABLE'] === 'YES', 'default' => $row['COLUMN_DEFAULT'], - 'autoincrement' => $row['DOMAIN_NAME'] === 'COUNTER', + 'autoIncrement' => $row['DOMAIN_NAME'] === 'COUNTER', 'primary' => $row['COLUMN_NAME'] === 'ID', 'vendor' => (array) $row, ]; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 8f595a810..8db876945 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -145,11 +145,11 @@ public function getColumns(string $table): array $columns[] = [ 'name' => $row['field'], 'table' => $table, - 'nativetype' => strtoupper($typeInfo['type']), + 'nativeType' => strtoupper($typeInfo['type']), 'size' => $typeInfo['length'], 'nullable' => $row['null'] === 'YES', 'default' => $row['default'], - 'autoincrement' => $row['extra'] === 'auto_increment', + 'autoIncrement' => $row['extra'] === 'auto_increment', 'primary' => $row['key'] === 'PRI', 'vendor' => $row, ]; diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index f2f8ebe1b..2d3c6cf40 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -139,7 +139,7 @@ public function getColumns(string $table): array SELECT a.attname::varchar AS name, c.relname::varchar AS table, - upper(t.typname) AS nativetype, + upper(t.typname) AS "nativeType", CASE WHEN a.atttypid IN (1700, 1231) THEN ((a.atttypmod - 4) >> 16) & 65535 -- precision for numeric/decimal WHEN a.atttypmod > 0 THEN a.atttypmod - 4 -- length for varchar etc. @@ -148,7 +148,7 @@ public function getColumns(string $table): array END AS size, NOT (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS nullable, pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass)::varchar AS default, - coalesce(co.contype = 'p' AND (seq.relname IS NOT NULL OR strpos(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid), 'nextval') = 1), FALSE) AS autoincrement, + coalesce(co.contype = 'p' AND (seq.relname IS NOT NULL OR strpos(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid), 'nextval') = 1), FALSE) AS "autoIncrement", coalesce(co.contype = 'p', FALSE) AS primary, coalesce(seq.relname, substring(pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass) from 'nextval[(]''"?([^''"]+)')) AS sequence FROM diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index db886de85..dfc39f5b6 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -155,11 +155,11 @@ public function getColumns(string $table): array $columns[] = [ 'name' => $column, 'table' => $table, - 'nativetype' => strtoupper($typeInfo['type']), + 'nativeType' => strtoupper($typeInfo['type']), 'size' => $typeInfo['length'], 'nullable' => $row['notnull'] == 0, 'default' => $row['dflt_value'], - 'autoincrement' => $createSql && preg_match($pattern, $createSql['sql']), + 'autoIncrement' => $createSql && preg_match($pattern, $createSql['sql']), 'primary' => $row['pk'] > 0, 'vendor' => (array) $row, ]; diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index e34a50bb3..7f52dd3ba 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -119,7 +119,7 @@ public function getColumns(string $table): array SELECT c.name AS name, o.name AS [table], - UPPER(t.name) AS nativetype, + UPPER(t.name) AS nativeType, CASE WHEN c.precision <> 0 THEN c.precision WHEN c.max_length <> -1 THEN c.max_length @@ -127,7 +127,7 @@ public function getColumns(string $table): array END AS size, c.is_nullable AS nullable, OBJECT_DEFINITION(c.default_object_id) AS [default], - c.is_identity AS autoincrement, + c.is_identity AS autoIncrement, CASE WHEN i.index_id IS NULL THEN 0 ELSE 1 @@ -147,7 +147,7 @@ public function getColumns(string $table): array $row = (array) $row; $row['vendor'] = $row; $row['nullable'] = (bool) $row['nullable']; - $row['autoincrement'] = (bool) $row['autoincrement']; + $row['autoIncrement'] = (bool) $row['autoIncrement']; $row['primary'] = (bool) $row['primary']; $columns[] = $row; diff --git a/src/Database/Reflection/Table.php b/src/Database/Reflection/Table.php index d4beb4cc3..a051de557 100644 --- a/src/Database/Reflection/Table.php +++ b/src/Database/Reflection/Table.php @@ -49,7 +49,8 @@ private function initColumns(): void { $res = []; foreach ($this->reflection->getDriver()->getColumns($this->name) as $row) { - $res[$row['name']] = new Column($row['name'], $this, $row['nativetype'], $row['size'], $row['nullable'], $row['default'], $row['autoincrement'], $row['primary'], $row['vendor']); + $row['table'] = $this; + $res[$row['name']] = new Column(...$row); } $this->columns = $res; } diff --git a/src/Database/Structure.php b/src/Database/Structure.php index bd5b7a3ce..580c2b820 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -66,11 +66,11 @@ public function getPrimaryAutoincrementKey(string $table): ?string return null; } - // Search for autoincrement key from multi primary key + // Search for autoIncrement key from multi primary key if (is_array($primaryKey)) { $keys = array_flip($primaryKey); foreach ($this->getColumns($table) as $column) { - if (isset($keys[$column['name']]) && $column['autoincrement']) { + if (isset($keys[$column['name']]) && $column['autoIncrement']) { return $column['name']; } } @@ -78,10 +78,10 @@ public function getPrimaryAutoincrementKey(string $table): ?string return null; } - // Search for autoincrement key from simple primary key + // Search for auto-increment key from simple primary key foreach ($this->getColumns($table) as $column) { if ($column['name'] === $primaryKey) { - return $column['autoincrement'] ? $column['name'] : null; + return $column['autoIncrement'] ? $column['name'] : null; } } @@ -98,14 +98,14 @@ public function getPrimaryKeySequence(string $table): ?string return null; } - $autoincrementPrimaryKeyName = $this->getPrimaryAutoincrementKey($table); - if (!$autoincrementPrimaryKeyName) { + $autoIncrementPrimaryKeyName = $this->getPrimaryAutoincrementKey($table); + if (!$autoIncrementPrimaryKeyName) { return null; } // Search for sequence from simple primary key foreach ($this->structure['columns'][$table] as $columnMeta) { - if ($columnMeta['name'] === $autoincrementPrimaryKeyName) { + if ($columnMeta['name'] === $autoIncrementPrimaryKeyName) { return $columnMeta['vendor']['sequence'] ?? null; } } diff --git a/tests/Database/Reflection.driver.phpt b/tests/Database/Reflection.driver.phpt index 04c676bf8..69c8dfc7d 100644 --- a/tests/Database/Reflection.driver.phpt +++ b/tests/Database/Reflection.driver.phpt @@ -53,41 +53,41 @@ $expectedColumns = [ [ 'name' => 'id', 'table' => 'author', - 'nativetype' => 'INT', + 'nativeType' => 'INT', 'size' => 11, 'nullable' => false, 'default' => null, - 'autoincrement' => true, + 'autoIncrement' => true, 'primary' => true, ], [ 'name' => 'name', 'table' => 'author', - 'nativetype' => 'VARCHAR', + 'nativeType' => 'VARCHAR', 'size' => 30, 'nullable' => false, 'default' => null, - 'autoincrement' => false, + 'autoIncrement' => false, 'primary' => false, ], [ 'name' => 'web', 'table' => 'author', - 'nativetype' => 'VARCHAR', + 'nativeType' => 'VARCHAR', 'size' => 100, 'nullable' => false, 'default' => null, - 'autoincrement' => false, + 'autoIncrement' => false, 'primary' => false, ], [ 'name' => 'born', 'table' => 'author', - 'nativetype' => 'DATE', + 'nativeType' => 'DATE', 'size' => null, 'nullable' => true, 'default' => null, - 'autoincrement' => false, + 'autoIncrement' => false, 'primary' => false, ], ]; @@ -100,17 +100,17 @@ switch ($driverName) { } break; case 'pgsql': - $expectedColumns[0]['nativetype'] = 'INT4'; + $expectedColumns[0]['nativeType'] = 'INT4'; $expectedColumns[0]['default'] = "nextval('author_id_seq'::regclass)"; $expectedColumns[0]['size'] = 4; $expectedColumns[3]['size'] = 4; break; case 'sqlite': - $expectedColumns[0]['nativetype'] = 'INTEGER'; + $expectedColumns[0]['nativeType'] = 'INTEGER'; $expectedColumns[0]['size'] = null; - $expectedColumns[1]['nativetype'] = 'TEXT'; + $expectedColumns[1]['nativeType'] = 'TEXT'; $expectedColumns[1]['size'] = null; - $expectedColumns[2]['nativetype'] = 'TEXT'; + $expectedColumns[2]['nativeType'] = 'TEXT'; $expectedColumns[2]['size'] = null; break; case 'sqlsrv': diff --git a/tests/Database/Reflection.postgre.10.phpt b/tests/Database/Reflection.postgre.10.phpt index 2b6191da0..2d9718ac3 100644 --- a/tests/Database/Reflection.postgre.10.phpt +++ b/tests/Database/Reflection.postgre.10.phpt @@ -24,13 +24,13 @@ function shortInfo(array $columns): array { return array_map(fn(array $col): array => [ 'name' => $col['name'], - 'autoincrement' => $col['autoincrement'], + 'autoIncrement' => $col['autoIncrement'], 'sequence' => $col['vendor']['sequence'], ], $columns); } -test('SERIAL and IDENTITY imply autoincrement on primary keys', function () use ($connection) { +test('SERIAL and IDENTITY imply autoIncrement on primary keys', function () use ($connection) { Nette\Database\Helpers::loadFromFile($connection, Tester\FileMock::create(' DROP SCHEMA IF EXISTS "reflection_10" CASCADE; CREATE SCHEMA "reflection_10"; @@ -61,37 +61,37 @@ test('SERIAL and IDENTITY imply autoincrement on primary keys', function () use Assert::same([ 'serial' => [[ 'name' => 'id', - 'autoincrement' => false, + 'autoIncrement' => false, 'sequence' => 'serial_id_seq', ]], 'serial_pk' => [[ 'name' => 'id', - 'autoincrement' => true, + 'autoIncrement' => true, 'sequence' => 'serial_pk_id_seq', ]], 'identity_always' => [[ 'name' => 'id', - 'autoincrement' => false, + 'autoIncrement' => false, 'sequence' => 'identity_always_id_seq', ]], 'identity_always_pk' => [[ 'name' => 'id', - 'autoincrement' => true, + 'autoIncrement' => true, 'sequence' => 'identity_always_pk_id_seq', ]], 'identity_by_default' => [[ 'name' => 'id', - 'autoincrement' => false, + 'autoIncrement' => false, 'sequence' => 'identity_by_default_id_seq', ]], 'identity_by_default_pk' => [[ 'name' => 'id', - 'autoincrement' => true, + 'autoIncrement' => true, 'sequence' => 'identity_by_default_pk_id_seq', ]], ], $columns); diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index 434908402..9e585e4c0 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -52,24 +52,24 @@ class StructureTestCase extends TestCase ['name' => 'books_view', 'view' => true], ]); $this->driver->shouldReceive('getColumns')->with('authors')->once()->andReturn([ - ['name' => 'id', 'primary' => true, 'autoincrement' => true, 'vendor' => ['sequence' => '"public"."authors_id_seq"']], - ['name' => 'name', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + ['name' => 'id', 'primary' => true, 'autoIncrement' => true, 'vendor' => ['sequence' => '"public"."authors_id_seq"']], + ['name' => 'name', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); $this->driver->shouldReceive('getColumns')->with('Books')->once()->andReturn([ - ['name' => 'id', 'primary' => true, 'autoincrement' => true, 'vendor' => ['sequence' => '"public"."Books_id_seq"']], - ['name' => 'title', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + ['name' => 'id', 'primary' => true, 'autoIncrement' => true, 'vendor' => ['sequence' => '"public"."Books_id_seq"']], + ['name' => 'title', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); $this->driver->shouldReceive('getColumns')->with('tags')->once()->andReturn([ - ['name' => 'id', 'primary' => true, 'autoincrement' => false, 'vendor' => []], - ['name' => 'name', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + ['name' => 'id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], + ['name' => 'name', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); $this->driver->shouldReceive('getColumns')->with('books_x_tags')->once()->andReturn([ - ['name' => 'book_id', 'primary' => true, 'autoincrement' => false, 'vendor' => []], - ['name' => 'tag_id', 'primary' => true, 'autoincrement' => false, 'vendor' => []], + ['name' => 'book_id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], + ['name' => 'tag_id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], ]); $this->driver->shouldReceive('getColumns')->with('books_view')->once()->andReturn([ - ['name' => 'id', 'primary' => false, 'autoincrement' => false, 'vendor' => []], - ['name' => 'title', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + ['name' => 'id', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], + ['name' => 'title', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); $this->connection->shouldReceive('getDriver')->times(4)->andReturn($this->driver); $this->driver->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]); @@ -102,8 +102,8 @@ class StructureTestCase extends TestCase public function testGetColumns() { $columns = [ - ['name' => 'id', 'primary' => true, 'autoincrement' => false, 'vendor' => []], - ['name' => 'name', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + ['name' => 'id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], + ['name' => 'name', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]; Assert::same($columns, $this->structure->getColumns('tags')); From c2fe3313a855209bfa59d8e4e6852b9f6e816b3e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:49:48 +0200 Subject: [PATCH 10/53] Reflection: added 'scale' field --- src/Database/Driver.php | 2 +- src/Database/Drivers/MsSqlDriver.php | 2 ++ src/Database/Drivers/MySqlDriver.php | 3 +- src/Database/Drivers/PgSqlDriver.php | 4 +++ src/Database/Drivers/SqliteDriver.php | 3 +- src/Database/Drivers/SqlsrvDriver.php | 3 ++ src/Database/Helpers.php | 4 +-- src/Database/Reflection/Column.php | 1 + tests/Database/Helpers.parseColumnType.phpt | 8 ++--- tests/Database/Reflection.columns.mysql.phpt | 32 ++++++++++++++++++ .../Database/Reflection.columns.postgre.phpt | 33 +++++++++++++++++++ tests/Database/Reflection.columns.sqlite.phpt | 28 ++++++++++++++++ tests/Database/Reflection.columns.sqlsrv.phpt | 31 +++++++++++++++++ tests/Database/Reflection.driver.phpt | 4 +++ tests/Database/Reflection.phpt | 5 +++ 15 files changed, 154 insertions(+), 9 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index d2cc64127..c9fec5298 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -66,7 +66,7 @@ function getTables(): array; /** * Returns detailed information about columns in a table. - * @return list + * @return list */ function getColumns(string $table): array; diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index caeaf47d4..1d2eeb7f7 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -113,6 +113,7 @@ public function getColumns(string $table): array DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, + NUMERIC_SCALE, IS_NULLABLE, COLUMN_DEFAULT, DOMAIN_NAME @@ -129,6 +130,7 @@ public function getColumns(string $table): array 'table' => $table, 'nativeType' => strtoupper($row['DATA_TYPE']), 'size' => $row['CHARACTER_MAXIMUM_LENGTH'] ?? $row['NUMERIC_PRECISION'], + 'scale' => $row['NUMERIC_SCALE'], 'unsigned' => false, 'nullable' => $row['IS_NULLABLE'] === 'YES', 'default' => $row['COLUMN_DEFAULT'], diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 8db876945..af9731789 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -146,7 +146,8 @@ public function getColumns(string $table): array 'name' => $row['field'], 'table' => $table, 'nativeType' => strtoupper($typeInfo['type']), - 'size' => $typeInfo['length'], + 'size' => $typeInfo['size'], + 'scale' => $typeInfo['scale'], 'nullable' => $row['null'] === 'YES', 'default' => $row['default'], 'autoIncrement' => $row['extra'] === 'auto_increment', diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 2d3c6cf40..0162091d1 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -146,6 +146,10 @@ public function getColumns(string $table): array WHEN t.typlen > 0 THEN t.typlen -- length for fixed-length types ELSE NULL END AS size, + CASE + WHEN a.atttypid IN (1700, 1231) THEN (a.atttypmod - 4) & 65535 + ELSE null + END AS scale, NOT (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS nullable, pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass)::varchar AS default, coalesce(co.contype = 'p' AND (seq.relname IS NOT NULL OR strpos(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid), 'nextval') = 1), FALSE) AS "autoIncrement", diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index dfc39f5b6..787f8a271 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -156,7 +156,8 @@ public function getColumns(string $table): array 'name' => $column, 'table' => $table, 'nativeType' => strtoupper($typeInfo['type']), - 'size' => $typeInfo['length'], + 'size' => $typeInfo['size'], + 'scale' => $typeInfo['scale'], 'nullable' => $row['notnull'] == 0, 'default' => $row['dflt_value'], 'autoIncrement' => $createSql && preg_match($pattern, $createSql['sql']), diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 7f52dd3ba..3c6402ab9 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -125,6 +125,7 @@ public function getColumns(string $table): array WHEN c.max_length <> -1 THEN c.max_length ELSE NULL END AS size, + c.scale AS scale, c.is_nullable AS nullable, OBJECT_DEFINITION(c.default_object_id) AS [default], c.is_identity AS autoIncrement, @@ -146,6 +147,8 @@ public function getColumns(string $table): array while ($row = $rows->fetch()) { $row = (array) $row; $row['vendor'] = $row; + $row['size'] = $row['size'] ?: null; + $row['scale'] = $row['scale'] ?: null; $row['nullable'] = (bool) $row['nullable']; $row['autoIncrement'] = (bool) $row['autoIncrement']; $row['primary'] = (bool) $row['primary']; diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 3684a3eb1..f30597c6c 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -390,13 +390,13 @@ public static function findDuplicates(\PDOStatement $statement): string } - /** @return array{type: string, length: ?null, scale: ?null, parameters: ?string} */ + /** @return array{type: string, size: ?int, scale: ?int, parameters: ?string} */ public static function parseColumnType(string $type): array { preg_match('/^([^(]+)(?:\((?:(\d+)(?:,(\d+))?|([^)]+))\))?/', $type, $m, PREG_UNMATCHED_AS_NULL); return [ 'type' => $m[1], - 'length' => isset($m[2]) ? (int) $m[2] : null, + 'size' => isset($m[2]) ? (int) $m[2] : null, 'scale' => isset($m[3]) ? (int) $m[3] : null, 'parameters' => $m[4] ?? null, ]; diff --git a/src/Database/Reflection/Column.php b/src/Database/Reflection/Column.php index 48eaa87c7..acb0043e0 100644 --- a/src/Database/Reflection/Column.php +++ b/src/Database/Reflection/Column.php @@ -21,6 +21,7 @@ public function __construct( public readonly ?Table $table = null, public readonly string $nativeType = '', public readonly ?int $size = null, + public readonly ?int $scale = null, public readonly bool $nullable = false, public readonly mixed $default = null, public readonly bool $autoIncrement = false, diff --git a/tests/Database/Helpers.parseColumnType.phpt b/tests/Database/Helpers.parseColumnType.phpt index fe665d92e..a8bca9003 100644 --- a/tests/Database/Helpers.parseColumnType.phpt +++ b/tests/Database/Helpers.parseColumnType.phpt @@ -15,16 +15,16 @@ require __DIR__ . '/../bootstrap.php'; // Test basic type $result = Helpers::parseColumnType('UNSIGNED INT'); -Assert::same(['type' => 'UNSIGNED INT', 'length' => null, 'scale' => null, 'parameters' => null], $result); +Assert::same(['type' => 'UNSIGNED INT', 'size' => null, 'scale' => null, 'parameters' => null], $result); // Test type with length $result = Helpers::parseColumnType('VARCHAR(255)'); -Assert::same(['type' => 'VARCHAR', 'length' => 255, 'scale' => null, 'parameters' => null], $result); +Assert::same(['type' => 'VARCHAR', 'size' => 255, 'scale' => null, 'parameters' => null], $result); // Test type with precision and scale $result = Helpers::parseColumnType('DECIMAL(10,2)'); -Assert::same(['type' => 'DECIMAL', 'length' => 10, 'scale' => 2, 'parameters' => null], $result); +Assert::same(['type' => 'DECIMAL', 'size' => 10, 'scale' => 2, 'parameters' => null], $result); // Test type with additional parameters $result = Helpers::parseColumnType("ENUM('value1','value2')"); -Assert::same(['type' => 'ENUM', 'length' => null, 'scale' => null, 'parameters' => "'value1','value2'"], $result); +Assert::same(['type' => 'ENUM', 'size' => null, 'scale' => null, 'parameters' => "'value1','value2'"], $result); diff --git a/tests/Database/Reflection.columns.mysql.phpt b/tests/Database/Reflection.columns.mysql.phpt index 14d913162..aedf6a793 100644 --- a/tests/Database/Reflection.columns.mysql.phpt +++ b/tests/Database/Reflection.columns.mysql.phpt @@ -25,6 +25,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => $version80 ? 'INT UNSIGNED' : 'INT', 'size' => $version80 ? null : 11, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -35,6 +36,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT', 'size' => $version80 ? null : 11, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -45,6 +47,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SMALLINT', 'size' => $version80 ? null : 6, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -55,6 +58,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYINT', 'size' => $version80 ? null : 4, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -65,6 +69,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MEDIUMINT', 'size' => $version80 ? null : 9, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -75,6 +80,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIGINT', 'size' => $version80 ? null : 20, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -85,6 +91,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYINT', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -95,6 +102,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIT', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -105,6 +113,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DECIMAL', 'size' => 10, + 'scale' => 0, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -115,6 +124,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DECIMAL', 'size' => 10, + 'scale' => 2, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -125,6 +135,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'FLOAT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -135,6 +146,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DOUBLE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -145,6 +157,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -155,6 +168,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIME', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -165,6 +179,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATETIME', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -175,6 +190,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIMESTAMP', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -185,6 +201,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'YEAR', 'size' => $version80 ? null : 4, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -195,6 +212,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CHAR', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -205,6 +223,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARCHAR', 'size' => 30, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -215,6 +234,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BINARY', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -225,6 +245,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARBINARY', 'size' => 30, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -235,6 +256,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -245,6 +267,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYBLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -255,6 +278,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MEDIUMBLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -265,6 +289,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'LONGBLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -275,6 +300,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -285,6 +311,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYTEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -295,6 +322,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MEDIUMTEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -305,6 +333,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'LONGTEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -315,6 +344,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'ENUM', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -325,6 +355,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SET', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -339,6 +370,7 @@ Assert::same( 'table' => $c->table->name, 'nativeType' => $c->nativeType, 'size' => $c->size, + 'scale' => $c->scale, 'nullable' => $c->nullable, 'default' => $c->default, 'autoIncrement' => $c->autoIncrement, diff --git a/tests/Database/Reflection.columns.postgre.phpt b/tests/Database/Reflection.columns.postgre.phpt index b404de843..49d5ca894 100644 --- a/tests/Database/Reflection.columns.postgre.phpt +++ b/tests/Database/Reflection.columns.postgre.phpt @@ -24,6 +24,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT2', 'size' => 2, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -34,6 +35,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT4', 'size' => 4, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -44,6 +46,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT8', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -54,6 +57,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NUMERIC', 'size' => 3, + 'scale' => 2, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -64,6 +68,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'FLOAT4', 'size' => 4, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -74,6 +79,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'FLOAT8', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -84,6 +90,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MONEY', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -94,6 +101,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BOOL', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -104,6 +112,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATE', 'size' => 4, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -114,6 +123,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIME', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -124,6 +134,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIMESTAMP', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -134,6 +145,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIMESTAMPTZ', 'size' => 8, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -144,6 +156,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INTERVAL', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -154,6 +167,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BPCHAR', 'size' => 30, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -164,6 +178,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARCHAR', 'size' => 30, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -174,6 +189,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -184,6 +200,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TSQUERY', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -194,6 +211,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TSVECTOR', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -204,6 +222,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'UUID', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -214,6 +233,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'XML', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -224,6 +244,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CIDR', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -234,6 +255,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INET', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -244,6 +266,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MACADDR', 'size' => 6, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -254,6 +277,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIT', 'size' => -3, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -264,6 +288,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARBIT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -274,6 +299,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BYTEA', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -284,6 +310,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BOX', 'size' => 32, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -294,6 +321,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CIRCLE', 'size' => 24, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -304,6 +332,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'LSEG', 'size' => 32, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -314,6 +343,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'PATH', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -324,6 +354,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'POINT', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -334,6 +365,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'POLYGON', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -348,6 +380,7 @@ Assert::same( 'table' => $c->table->name, 'nativeType' => $c->nativeType, 'size' => $c->size, + 'scale' => $c->scale, 'nullable' => $c->nullable, 'default' => $c->default, 'autoIncrement' => $c->autoIncrement, diff --git a/tests/Database/Reflection.columns.sqlite.phpt b/tests/Database/Reflection.columns.sqlite.phpt index 4f32b02a5..bd8b6ecb6 100644 --- a/tests/Database/Reflection.columns.sqlite.phpt +++ b/tests/Database/Reflection.columns.sqlite.phpt @@ -24,6 +24,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -34,6 +35,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INTEGER', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -44,6 +46,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYINT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -54,6 +57,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SMALLINT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -64,6 +68,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MEDIUMINT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -74,6 +79,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIGINT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -84,6 +90,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'UNSIGNED BIG INT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -94,6 +101,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT2', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -104,6 +112,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT8', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -114,6 +123,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CHARACTER', 'size' => 20, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -124,6 +134,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARCHAR', 'size' => 255, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -134,6 +145,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARYING CHARACTER', 'size' => 255, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -144,6 +156,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NCHAR', 'size' => 55, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -154,6 +167,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NATIVE CHARACTER', 'size' => 70, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -164,6 +178,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NVARCHAR', 'size' => 100, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -174,6 +189,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TEXT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -184,6 +200,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -194,6 +211,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BLOB', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -204,6 +222,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'REAL', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -214,6 +233,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DOUBLE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -224,6 +244,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DOUBLE PRECISION', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -234,6 +255,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'FLOAT', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -244,6 +266,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NUMERIC', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -254,6 +277,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DECIMAL', 'size' => 10, + 'scale' => 5, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -264,6 +288,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BOOLEAN', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -274,6 +299,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -284,6 +310,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATETIME', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -298,6 +325,7 @@ Assert::same( 'table' => $c->table->name, 'nativeType' => $c->nativeType, 'size' => $c->size, + 'scale' => $c->scale, 'nullable' => $c->nullable, 'default' => $c->default, 'autoIncrement' => $c->autoIncrement, diff --git a/tests/Database/Reflection.columns.sqlsrv.phpt b/tests/Database/Reflection.columns.sqlsrv.phpt index 57109e4aa..246007031 100644 --- a/tests/Database/Reflection.columns.sqlsrv.phpt +++ b/tests/Database/Reflection.columns.sqlsrv.phpt @@ -24,6 +24,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIGINT', 'size' => 19, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -34,6 +35,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BINARY', 'size' => 3, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -44,6 +46,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'BIT', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -54,6 +57,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'CHAR', 'size' => 5, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -64,6 +68,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATE', 'size' => 10, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -74,6 +79,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATETIME', 'size' => 23, + 'scale' => 3, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -84,6 +90,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DATETIME2', 'size' => 27, + 'scale' => 7, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -94,6 +101,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'DECIMAL', 'size' => 18, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -104,6 +112,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'FLOAT', 'size' => 53, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -114,6 +123,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'GEOGRAPHY', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -124,6 +134,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'GEOMETRY', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -134,6 +145,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'HIERARCHYID', 'size' => 892, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -144,6 +156,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'INT', 'size' => 10, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -154,6 +167,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'MONEY', 'size' => 19, + 'scale' => 4, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -164,6 +178,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NCHAR', 'size' => 2, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -174,6 +189,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NTEXT', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -184,6 +200,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NUMERIC', 'size' => 10, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -194,6 +211,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NUMERIC', 'size' => 10, + 'scale' => 2, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -204,6 +222,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'NVARCHAR', 'size' => 2, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -214,6 +233,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'REAL', 'size' => 24, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -224,6 +244,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SMALLDATETIME', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -234,6 +255,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SMALLINT', 'size' => 5, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -244,6 +266,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'SMALLMONEY', 'size' => 10, + 'scale' => 4, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -254,6 +277,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TEXT', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -264,6 +288,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TIME', 'size' => 16, + 'scale' => 7, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -274,6 +299,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'TINYINT', 'size' => 3, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -284,6 +310,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'UNIQUEIDENTIFIER', 'size' => 16, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -294,6 +321,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARBINARY', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -304,6 +332,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'VARCHAR', 'size' => 1, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -314,6 +343,7 @@ $expectedColumns = [ 'table' => 'types', 'nativeType' => 'XML', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -328,6 +358,7 @@ Assert::same( 'table' => $c->table->name, 'nativeType' => $c->nativeType, 'size' => $c->size, + 'scale' => $c->scale, 'nullable' => $c->nullable, 'default' => $c->default, 'autoIncrement' => $c->autoIncrement, diff --git a/tests/Database/Reflection.driver.phpt b/tests/Database/Reflection.driver.phpt index 69c8dfc7d..0098c5777 100644 --- a/tests/Database/Reflection.driver.phpt +++ b/tests/Database/Reflection.driver.phpt @@ -55,6 +55,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'INT', 'size' => 11, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => true, @@ -65,6 +66,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'VARCHAR', 'size' => 30, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => false, @@ -75,6 +77,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'VARCHAR', 'size' => 100, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => false, @@ -85,6 +88,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'DATE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index ebbc1f9c6..80fa8ab6b 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -75,6 +75,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'INT', 'size' => 11, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => true, @@ -85,6 +86,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'VARCHAR', 'size' => 30, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => false, @@ -95,6 +97,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'VARCHAR', 'size' => 100, + 'scale' => null, 'nullable' => false, 'default' => null, 'autoIncrement' => false, @@ -105,6 +108,7 @@ $expectedColumns = [ 'table' => 'author', 'nativeType' => 'DATE', 'size' => null, + 'scale' => null, 'nullable' => true, 'default' => null, 'autoIncrement' => false, @@ -149,6 +153,7 @@ Assert::same( 'table' => $c->table->name, 'nativeType' => $c->nativeType, 'size' => $c->size, + 'scale' => $c->scale, 'nullable' => $c->nullable, 'default' => $c->default, 'autoIncrement' => $c->autoIncrement, From c26d1e1a87ab80462c8b4ba97bce94557fefc277 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 27 Aug 2024 19:44:43 +0200 Subject: [PATCH 11/53] returns date-time as immutable Nette\Database\DateTime by default (BC break) can be disabled by option newDateTime = false --- src/Database/Connection.php | 3 ++- src/Database/Helpers.php | 2 +- src/Database/Table/ActiveRow.php | 2 +- tests/Database/connection.options.mysql.phpt | 2 +- tests/databases.github.ini | 6 ------ tests/databases.sqlite.ini | 1 - 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index aaa8352e6..24f333c41 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -11,6 +11,7 @@ use JetBrains\PhpStorm\Language; use Nette\Utils\Arrays; +use Nette\Utils\DateTime; use PDO; use PDOException; @@ -43,7 +44,7 @@ public function __construct( private readonly ?string $password = null, private readonly array $options = [], ) { - if (!empty($options['newDateTime'])) { + if (($options['newDateTime'] ?? null) === false) { $this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class); } if (empty($options['lazy'])) { diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index f30597c6c..4df4f9d4b 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -205,7 +205,7 @@ public static function detectType(string $type): string public static function normalizeRow( array $row, ResultSet $resultSet, - $dateTimeClass = Nette\Utils\DateTime::class, + $dateTimeClass = DateTime::class, ): array { foreach ($resultSet->getColumnTypes() as $key => $type) { diff --git a/src/Database/Table/ActiveRow.php b/src/Database/Table/ActiveRow.php index 91778c96a..bdf6cc719 100644 --- a/src/Database/Table/ActiveRow.php +++ b/src/Database/Table/ActiveRow.php @@ -61,7 +61,7 @@ public function toArray(): array /** * Returns primary key value. - * @return mixed possible int, string, array, object (Nette\Utils\DateTime) + * @return mixed possible int, string, array, object (Nette\Database\DateTime) */ public function getPrimary(bool $throw = true): mixed { diff --git a/tests/Database/connection.options.mysql.phpt b/tests/Database/connection.options.mysql.phpt index f95c202c7..15f7c558a 100644 --- a/tests/Database/connection.options.mysql.phpt +++ b/tests/Database/connection.options.mysql.phpt @@ -58,7 +58,7 @@ test('convertBoolean = false', function () { test('default newDateTime', function () { $connection = connectToDB(['newDateTime' => null])->getConnection(); $field = $connection->fetchField('SELECT NOW()'); - Assert::type(Nette\Utils\DateTime::class, $field); + Assert::type(Nette\Database\DateTime::class, $field); }); test('newDateTime = false', function () { diff --git a/tests/databases.github.ini b/tests/databases.github.ini index d00f44271..78802fef4 100644 --- a/tests/databases.github.ini +++ b/tests/databases.github.ini @@ -3,33 +3,27 @@ dsn = "mysql:host=127.0.0.1;port=3306;dbname=nette_test" username = root password = root options[convertBoolean] = yes -options[newDateTime] = yes [mysql 8.0] dsn = "mysql:host=127.0.0.1;port=3307;dbname=nette_test" username = root password = root options[convertBoolean] = yes -options[newDateTime] = yes [postgresql 9.6] dsn = "pgsql:host=127.0.0.1;port=5432;dbname=nette_test" username = postgres password = postgres -options[newDateTime] = yes [postgresql 13] dsn = "pgsql:host=127.0.0.1;port=5433;dbname=nette_test" username = postgres password = postgres -options[newDateTime] = yes [sqlsrv] dsn = "sqlsrv:Server=localhost,1433;Database=nette_test" username = SA password = "YourStrong!Passw0rd" -options[newDateTime] = yes [sqlite] dsn = "sqlite::memory:" -options[newDateTime] = yes diff --git a/tests/databases.sqlite.ini b/tests/databases.sqlite.ini index 2e076f7e8..2d89c58ce 100644 --- a/tests/databases.sqlite.ini +++ b/tests/databases.sqlite.ini @@ -1,3 +1,2 @@ [sqlite] dsn = "sqlite::memory:" -options[newDateTime] = yes From 5059ce6d3ddd4eb5f02e34c6f0a4a8f10459d695 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 28 Aug 2024 03:04:31 +0200 Subject: [PATCH 12/53] MySQL: convertBoolean is true by default (BC break) --- src/Database/Drivers/MySqlDriver.php | 2 +- tests/Database/connection.options.mysql.phpt | 2 +- tests/databases.github.ini | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index af9731789..7664b4865 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -39,7 +39,7 @@ public function initialize(Nette\Database\Connection $connection, array $options $connection->query('SET sql_mode=?', $options['sqlmode']); } - $this->convertBoolean = (bool) ($options['convertBoolean'] ?? $options['supportBooleans'] ?? false); + $this->convertBoolean = (bool) ($options['convertBoolean'] ?? true); } diff --git a/tests/Database/connection.options.mysql.phpt b/tests/Database/connection.options.mysql.phpt index 15f7c558a..43f232480 100644 --- a/tests/Database/connection.options.mysql.phpt +++ b/tests/Database/connection.options.mysql.phpt @@ -37,7 +37,7 @@ test('default convertBoolean', function () { $connection = connectToDB(['convertBoolean' => null])->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); - Assert::same(1, $row->bool); + Assert::same(true, $row->bool); }); test('convertBoolean = true', function () { diff --git a/tests/databases.github.ini b/tests/databases.github.ini index 78802fef4..1c722a84c 100644 --- a/tests/databases.github.ini +++ b/tests/databases.github.ini @@ -2,13 +2,11 @@ dsn = "mysql:host=127.0.0.1;port=3306;dbname=nette_test" username = root password = root -options[convertBoolean] = yes [mysql 8.0] dsn = "mysql:host=127.0.0.1;port=3307;dbname=nette_test" username = root password = root -options[convertBoolean] = yes [postgresql 9.6] dsn = "pgsql:host=127.0.0.1;port=5432;dbname=nette_test" From 923bc755bd890c92ce4c956c97963793fe708cc7 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 10 Aug 2024 03:43:16 +0200 Subject: [PATCH 13/53] removed aliases for ISupplementalDriver, IConventions (BC break) --- src/Database/Conventions.php | 3 --- src/Database/Driver.php | 3 --- src/compatibility-intf.php | 28 ---------------------------- 3 files changed, 34 deletions(-) delete mode 100644 src/compatibility-intf.php diff --git a/src/Database/Conventions.php b/src/Database/Conventions.php index 3e7a68710..72e743667 100644 --- a/src/Database/Conventions.php +++ b/src/Database/Conventions.php @@ -39,6 +39,3 @@ function getHasManyReference(string $table, string $key): ?array; */ function getBelongsToReference(string $table, string $key): ?array; } - - -interface_exists(IConventions::class); diff --git a/src/Database/Driver.php b/src/Database/Driver.php index c9fec5298..bfa1251f4 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -88,6 +88,3 @@ function getForeignKeys(string $table): array; */ function getColumnTypes(\PDOStatement $statement): array; } - - -interface_exists(ISupplementalDriver::class); diff --git a/src/compatibility-intf.php b/src/compatibility-intf.php deleted file mode 100644 index fe3ab8558..000000000 --- a/src/compatibility-intf.php +++ /dev/null @@ -1,28 +0,0 @@ - Date: Sat, 10 Aug 2024 07:02:43 +0200 Subject: [PATCH 14/53] removed IRow & IRowContainer (BC break) --- src/Database/IRow.php | 16 ---------------- src/Database/IRowContainer.php | 16 ---------------- src/Database/ResultSet.php | 2 +- src/Database/Row.php | 5 ++++- src/Database/Table/ActiveRow.php | 2 +- src/Database/Table/IRow.php | 18 ------------------ src/Database/Table/IRowContainer.php | 18 ------------------ src/Database/Table/Selection.php | 2 +- 8 files changed, 7 insertions(+), 72 deletions(-) delete mode 100644 src/Database/IRow.php delete mode 100644 src/Database/IRowContainer.php delete mode 100644 src/Database/Table/IRow.php delete mode 100644 src/Database/Table/IRowContainer.php diff --git a/src/Database/IRow.php b/src/Database/IRow.php deleted file mode 100644 index 8c5ab16f5..000000000 --- a/src/Database/IRow.php +++ /dev/null @@ -1,16 +0,0 @@ - * @implements \ArrayAccess */ -class Selection implements \Iterator, IRowContainer, \ArrayAccess, \Countable +class Selection implements \Iterator, \ArrayAccess, \Countable { protected readonly Explorer $explorer; From 5c5b60733a71b287c618fe1fc0424ed60c476bd9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:50:38 +0200 Subject: [PATCH 15/53] removed formatLike() (BC break) --- src/Database/Driver.php | 3 -- src/Database/Drivers/MsSqlDriver.php | 7 ---- src/Database/Drivers/MySqlDriver.php | 8 ---- src/Database/Drivers/OciDriver.php | 6 --- src/Database/Drivers/OdbcDriver.php | 7 ---- src/Database/Drivers/PgSqlDriver.php | 9 ---- src/Database/Drivers/SqliteDriver.php | 7 ---- src/Database/Drivers/SqlsrvDriver.php | 8 ---- .../Drivers/MySqlDriver.formatLike.phpt | 32 -------------- .../Drivers/PgSqlDriver.formatLike.phpt | 42 ------------------- .../Drivers/SqliteDriver.formatLike.phpt | 32 -------------- .../Drivers/SqlsrvDriver.formatLike.phpt | 35 ---------------- 12 files changed, 196 deletions(-) delete mode 100644 tests/Database/Drivers/MySqlDriver.formatLike.phpt delete mode 100644 tests/Database/Drivers/PgSqlDriver.formatLike.phpt delete mode 100644 tests/Database/Drivers/SqliteDriver.formatLike.phpt delete mode 100644 tests/Database/Drivers/SqlsrvDriver.formatLike.phpt diff --git a/src/Database/Driver.php b/src/Database/Driver.php index bfa1251f4..2397be328 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -50,9 +50,6 @@ function formatDateTime(\DateTimeInterface $value): string; /** Formats a date-time interval for use in an SQL statement. */ function formatDateInterval(\DateInterval $value): string; - /** Encodes string for use in a LIKE statement. */ - function formatLike(string $value, int $pos): string; - /** Applies LIMIT and OFFSET clauses to an SQL query. */ function applyLimit(string &$sql, ?int $limit, ?int $offset): void; diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 1d2eeb7f7..ae797e3b7 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -60,13 +60,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($offset) { diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 7664b4865..bcda41f73 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -96,14 +96,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - $value = str_replace('\\', '\\\\', $value); - $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_'); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index ba48033ca..108cc6b2f 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -74,12 +74,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - throw new Nette\NotImplementedException; - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index d687fc915..329b2a0f9 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -55,13 +55,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($offset) { diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 0162091d1..865b9b834 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -78,15 +78,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - $bs = substr($this->connection->quote('\\'), 1, -1); // standard_conforming_strings = on/off - $value = substr($this->connection->quote($value), 1, -1); - $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 787f8a271..881dfa0f6 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -87,13 +87,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\'); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 3c6402ab9..0e5d34714 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -61,14 +61,6 @@ public function formatDateInterval(\DateInterval $value): string } - public function formatLike(string $value, int $pos): string - { - /** @see https://msdn.microsoft.com/en-us/library/ms179859.aspx */ - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); - } - - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { diff --git a/tests/Database/Drivers/MySqlDriver.formatLike.phpt b/tests/Database/Drivers/MySqlDriver.formatLike.phpt deleted file mode 100644 index 45d7fa032..000000000 --- a/tests/Database/Drivers/MySqlDriver.formatLike.phpt +++ /dev/null @@ -1,32 +0,0 @@ -getConnection(); -$driver = $connection->getDriver(); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA_BB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA%BB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA''BB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\"BB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\\\\BB' LIKE", $connection::literal($driver->formatLike('A\B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\\\\%BB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)))->fetchField()); diff --git a/tests/Database/Drivers/PgSqlDriver.formatLike.phpt b/tests/Database/Drivers/PgSqlDriver.formatLike.phpt deleted file mode 100644 index 2e85ec7bb..000000000 --- a/tests/Database/Drivers/PgSqlDriver.formatLike.phpt +++ /dev/null @@ -1,42 +0,0 @@ -getConnection(); - -$tests = function ($connection) { - $driver = $connection->getDriver(); - - Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); - Assert::true($connection->query("SELECT 'AA_BB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); - - Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); - Assert::true($connection->query("SELECT 'AA%BB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); - - Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); - Assert::true($connection->query("SELECT 'AA''BB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); - - Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); - Assert::true($connection->query("SELECT 'AA\"BB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); -}; - -$driver = $connection->getDriver(); -$connection->query('SET escape_string_warning TO off'); // do not log warnings - -$connection->query('SET standard_conforming_strings TO on'); -$tests($connection); -Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\\B', 0)))->fetchField()); -Assert::true($connection->query("SELECT 'AA\\BB' LIKE", $connection::literal($driver->formatLike('A\\B', 0)))->fetchField()); - -$connection->query('SET standard_conforming_strings TO off'); -$tests($connection); -Assert::false($connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\\B', 0)))->fetchField()); -Assert::true($connection->query("SELECT 'AA\\\\BB' LIKE", $connection::literal($driver->formatLike('A\\B', 0)))->fetchField()); diff --git a/tests/Database/Drivers/SqliteDriver.formatLike.phpt b/tests/Database/Drivers/SqliteDriver.formatLike.phpt deleted file mode 100644 index 2b2443957..000000000 --- a/tests/Database/Drivers/SqliteDriver.formatLike.phpt +++ /dev/null @@ -1,32 +0,0 @@ -getConnection(); -$driver = $connection->getDriver(); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA_BB' LIKE", $connection::literal($driver->formatLike('A_B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA%BB' LIKE", $connection::literal($driver->formatLike('A%B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA''BB' LIKE", $connection::literal($driver->formatLike("A'B", 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\"BB' LIKE", $connection::literal($driver->formatLike('A"B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\\BB' LIKE", $connection::literal($driver->formatLike('A\B', 0)))->fetchField()); - -Assert::same(0, $connection->query("SELECT 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)))->fetchField()); -Assert::same(1, $connection->query("SELECT 'AA\\%BB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)))->fetchField()); diff --git a/tests/Database/Drivers/SqlsrvDriver.formatLike.phpt b/tests/Database/Drivers/SqlsrvDriver.formatLike.phpt deleted file mode 100644 index cd5ccc3f1..000000000 --- a/tests/Database/Drivers/SqlsrvDriver.formatLike.phpt +++ /dev/null @@ -1,35 +0,0 @@ -getConnection(); -$driver = $connection->getDriver(); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A_B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA_BB' LIKE", $connection::literal($driver->formatLike('A_B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A%B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA%BB' LIKE", $connection::literal($driver->formatLike('A%B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike("A'B", 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA''BB' LIKE", $connection::literal($driver->formatLike("A'B", 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A"B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA\"BB' LIKE", $connection::literal($driver->formatLike('A"B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA\\BB' LIKE", $connection::literal($driver->formatLike('A\B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA\\%BB' LIKE", $connection::literal($driver->formatLike('A\%B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); - -Assert::same(0, $connection->query("SELECT CASE WHEN 'AAxBB' LIKE", $connection::literal($driver->formatLike('A[a-z]B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); -Assert::same(1, $connection->query("SELECT CASE WHEN 'AA[a-z]BB' LIKE", $connection::literal($driver->formatLike('A[a-z]B', 0)), 'THEN 1 ELSE 0 END AS col')->fetchField()); From 6cffe4244ccf6f6d29292ca53bb0e6bba1a70359 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 28 Aug 2024 01:45:49 +0200 Subject: [PATCH 16/53] ConnectionPanel: refactoring, initialize($addBarPanel) is true by default --- src/Bridges/DatabaseTracy/ConnectionPanel.php | 30 +++++++------------ .../templates/ConnectionPanel.panel.phtml | 6 ++-- tests/Database.Tracy/ConnectionPanel.phpt | 2 +- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 2460f46e4..4c7586987 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -21,27 +21,19 @@ class ConnectionPanel implements Tracy\IBarPanel { public int $maxQueries = 100; - public string $name; - public bool|string $explain = true; - public bool $disabled = false; - public float $performanceScale = 0.25; - private float $totalTime = 0; - private int $count = 0; - - private array $queries = []; - + private array $events = []; private Tracy\BlueScreen $blueScreen; public static function initialize( Connection $connection, - bool $addBarPanel = false, + bool $addBarPanel = true, string $name = '', bool $explain = true, ?Tracy\Bar $bar = null, @@ -98,10 +90,10 @@ private function logQuery(Connection $connection, $result): void if ($result instanceof Nette\Database\ResultSet) { $this->totalTime += $result->getTime(); if ($this->count < $this->maxQueries) { - $this->queries[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null]; + $this->events[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null]; } } elseif ($result instanceof \PDOException && $this->count < $this->maxQueries) { - $this->queries[] = [$connection, $result->queryString, null, $source, null, null, $result->getMessage()]; + $this->events[] = [$connection, $result->queryString, null, $source, null, null, $result->getMessage()]; } } @@ -143,9 +135,9 @@ public function getPanel(): ?string return null; } - $queries = []; - foreach ($this->queries as $query) { - [$connection, $sql, $params, , , , $error] = $query; + $events = []; + foreach ($this->events as $event) { + [$connection, $sql, $params, , , , $error] = $event; $explain = null; $command = preg_match('#\s*\(?\s*(SELECT|INSERT|UPDATE|DELETE)\s#iA', $sql, $m) ? strtolower($m[1]) @@ -160,12 +152,12 @@ public function getPanel(): ?string } } - $query[] = $command; - $query[] = $explain; - $queries[] = $query; + $event[] = $command; + $event[] = $explain; + $events[] = $event; } - return Nette\Utils\Helpers::capture(function () use ($queries, $connection) { + return Nette\Utils\Helpers::capture(function () use ($events, $connection) { $name = $this->name; $count = $this->count; $totalTime = $this->totalTime; diff --git a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml index df359af94..b6af47406 100644 --- a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml +++ b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml @@ -24,8 +24,8 @@ use Tracy\Helpers;
Time msSQL QueryRows
@@ -61,6 +61,6 @@ use Tracy\Helpers;
-

...and more

+

...and more

diff --git a/tests/Database.Tracy/ConnectionPanel.phpt b/tests/Database.Tracy/ConnectionPanel.phpt index bf2ffe8a8..42374a537 100644 --- a/tests/Database.Tracy/ConnectionPanel.phpt +++ b/tests/Database.Tracy/ConnectionPanel.phpt @@ -15,7 +15,7 @@ require __DIR__ . '/../bootstrap.php'; test('Tracy Bar', function () { $connection = new Connection('sqlite::memory:'); - $panel = ConnectionPanel::initialize($connection, addBarPanel: true, name: 'foo'); + $panel = ConnectionPanel::initialize($connection, name: 'foo'); $connection->beginTransaction(); $connection->query('SELECT 1'); From 40ab5c67455a79bf97cdb4201180bfe786eea208 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 12:04:54 +0200 Subject: [PATCH 17/53] connections are always lazy (BC break) --- src/Database/Connection.php | 3 -- .../Database.DI/DatabaseExtension.basic.phpt | 2 +- .../DatabaseExtension.multiple.phpt | 4 -- .../Database/Connection.exceptions.mysql.phpt | 2 +- tests/Database/Connection.exceptions.phpt | 22 ----------- .../Connection.exceptions.postgre.phpt | 2 +- .../Connection.exceptions.sqlite.phpt | 2 +- ...n.lazy.phpt => connection.disconnect.phpt} | 37 ++----------------- tests/bootstrap.php | 4 +- 9 files changed, 10 insertions(+), 68 deletions(-) delete mode 100644 tests/Database/Connection.exceptions.phpt rename tests/Database/{connection.option.lazy.phpt => connection.disconnect.phpt} (53%) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 24f333c41..09da4c24e 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -47,9 +47,6 @@ public function __construct( if (($options['newDateTime'] ?? null) === false) { $this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class); } - if (empty($options['lazy'])) { - $this->connect(); - } } diff --git a/tests/Database.DI/DatabaseExtension.basic.phpt b/tests/Database.DI/DatabaseExtension.basic.phpt index cefa28c74..23bf6c332 100644 --- a/tests/Database.DI/DatabaseExtension.basic.phpt +++ b/tests/Database.DI/DatabaseExtension.basic.phpt @@ -23,7 +23,7 @@ test('', function () { password: secret debugger: no options: - lazy: yes + lazy: no services: cache: Nette\Caching\Storages\DevNullStorage diff --git a/tests/Database.DI/DatabaseExtension.multiple.phpt b/tests/Database.DI/DatabaseExtension.multiple.phpt index 463349587..fb5b72e67 100644 --- a/tests/Database.DI/DatabaseExtension.multiple.phpt +++ b/tests/Database.DI/DatabaseExtension.multiple.phpt @@ -23,16 +23,12 @@ test('', function () { user: name password: secret debugger: no - options: - lazy: yes second: dsn: "sqlite::memory:" user: name password: secret debugger: no - options: - lazy: yes services: cache: Nette\Caching\Storages\DevNullStorage diff --git a/tests/Database/Connection.exceptions.mysql.phpt b/tests/Database/Connection.exceptions.mysql.phpt index 1d60d5adc..bd265534d 100644 --- a/tests/Database/Connection.exceptions.mysql.phpt +++ b/tests/Database/Connection.exceptions.mysql.phpt @@ -17,7 +17,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('Exception thrown for invalid database credentials', function () { $options = Tester\Environment::loadData(); $e = Assert::exception( - fn() => new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'), + fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, '%a% Access denied for user %a%', ); diff --git a/tests/Database/Connection.exceptions.phpt b/tests/Database/Connection.exceptions.phpt deleted file mode 100644 index 8efbc2871..000000000 --- a/tests/Database/Connection.exceptions.phpt +++ /dev/null @@ -1,22 +0,0 @@ - new Nette\Database\Connection('unknown'), - Nette\Database\ConnectionException::class, - '%a%valid data source %a%', - 0, -); - -Assert::same(null, $e->getDriverCode()); -Assert::same(null, $e->getSqlState()); diff --git a/tests/Database/Connection.exceptions.postgre.phpt b/tests/Database/Connection.exceptions.postgre.phpt index 717245d38..248571641 100644 --- a/tests/Database/Connection.exceptions.postgre.phpt +++ b/tests/Database/Connection.exceptions.postgre.phpt @@ -18,7 +18,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('Exception thrown for invalid database credentials', function () { $options = Tester\Environment::loadData(); $e = Assert::exception( - fn() => new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'), + fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, null, '08006', diff --git a/tests/Database/Connection.exceptions.sqlite.phpt b/tests/Database/Connection.exceptions.sqlite.phpt index dbd66d5cb..b9cacb494 100644 --- a/tests/Database/Connection.exceptions.sqlite.phpt +++ b/tests/Database/Connection.exceptions.sqlite.phpt @@ -17,7 +17,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('Exception thrown for unable to open database file', function () { $e = Assert::exception( - fn() => new Nette\Database\Connection('sqlite:.'), + fn() => (new Nette\Database\Connection('sqlite:.'))->connect(), Nette\Database\ConnectionException::class, 'SQLSTATE[HY000] [14] unable to open database file', 'HY000', diff --git a/tests/Database/connection.option.lazy.phpt b/tests/Database/connection.disconnect.phpt similarity index 53% rename from tests/Database/connection.option.lazy.phpt rename to tests/Database/connection.disconnect.phpt index 1561dc39f..8b0659454 100644 --- a/tests/Database/connection.option.lazy.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -1,55 +1,24 @@ new Nette\Database\Connection('dsn', 'user', 'password'), - Nette\Database\DriverException::class, - '%a%valid data source %a%', - ); -}); - - -test('lazy', function () { - $connection = new Nette\Database\Connection('dsn', 'user', 'password', ['lazy' => true]); - $explorer = new Nette\Database\Explorer($connection, new Structure($connection, new DevNullStorage)); - Assert::exception( - fn() => $explorer->query('SELECT ?', 10), - Nette\Database\DriverException::class, - '%a%valid data source %a%', - ); -}); - - -test('', function () { - $connection = new Nette\Database\Connection('dsn', 'user', 'password', ['lazy' => true]); - Assert::exception( - fn() => $connection->quote('x'), - Nette\Database\DriverException::class, - '%a%valid data source %a%', - ); -}); - - test('connect & disconnect', function () { $options = Tester\Environment::loadData() + ['username' => null, 'password' => null]; $connections = 1; + $connection = new Nette\Database\Connection($options['dsn'], $options['username'], $options['password']); try { - $connection = new Nette\Database\Connection($options['dsn'], $options['username'], $options['password']); + $connection->connect(); } catch (PDOException $e) { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index f112e33ce..7867867e5 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -34,8 +34,10 @@ function connectToDB(array $options = []): Nette\Database\Explorer Tester\Environment::lock($args['dsn'], getTempDir()); } + $connection = new Nette\Database\Connection($args['dsn'], $args['username'], $args['password'], $args['options']); + try { - $connection = new Nette\Database\Connection($args['dsn'], $args['username'], $args['password'], $args['options']); + $connection->connect(); } catch (PDOException $e) { Tester\Environment::skip("Connection to '$args[dsn]' failed. Reason: " . $e->getMessage()); } From 8c8622838bdce804bd4f2e1f3a3f2e45d5988167 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 01:37:26 +0200 Subject: [PATCH 18/53] renamed Nette\Database\ResultSet -> Result as it represents not only result of queries but also of commands --- src/Bridges/DatabaseTracy/ConnectionPanel.php | 5 +++-- src/Database/Connection.php | 10 +++++----- src/Database/Explorer.php | 4 ++-- src/Database/Helpers.php | 4 ++-- src/Database/{ResultSet.php => Result.php} | 7 +++++-- src/Database/Table/Selection.php | 2 +- src/compatibility.php | 9 +++++++++ tests/Database/Connection.query.phpt | 2 +- tests/Database/Explorer.query.phpt | 2 +- tests/Database/Explorer/Explorer.cache.observer.phpt | 4 ++-- .../{ResultSet.fetch().phpt => Result.fetch().phpt} | 2 +- ...esultSet.fetchAll().phpt => Result.fetchAll().phpt} | 2 +- ...tSet.fetchAssoc().phpt => Result.fetchAssoc().phpt} | 2 +- ...tSet.fetchField().phpt => Result.fetchField().phpt} | 2 +- ...ultSet.fetchList().phpt => Result.fetchList().phpt} | 2 +- ...tSet.fetchPairs().phpt => Result.fetchPairs().phpt} | 2 +- tests/Database/ResultSet.customNormalizer.phpt | 2 +- 17 files changed, 38 insertions(+), 25 deletions(-) rename src/Database/{ResultSet.php => Result.php} (97%) rename tests/Database/{ResultSet.fetch().phpt => Result.fetch().phpt} (98%) rename tests/Database/{ResultSet.fetchAll().phpt => Result.fetchAll().phpt} (96%) rename tests/Database/{ResultSet.fetchAssoc().phpt => Result.fetchAssoc().phpt} (96%) rename tests/Database/{ResultSet.fetchField().phpt => Result.fetchField().phpt} (92%) rename tests/Database/{ResultSet.fetchList().phpt => Result.fetchList().phpt} (92%) rename tests/Database/{ResultSet.fetchPairs().phpt => Result.fetchPairs().phpt} (98%) diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 4c7586987..8a703a8e5 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Connection; use Nette\Database\Helpers; +use Nette\Database\Result; use Tracy; @@ -87,7 +88,7 @@ private function logQuery(Connection $connection, $result): void } } - if ($result instanceof Nette\Database\ResultSet) { + if ($result instanceof Result) { $this->totalTime += $result->getTime(); if ($this->count < $this->maxQueries) { $this->events[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null]; @@ -147,7 +148,7 @@ public function getPanel(): ?string $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN'; - $explain = (new Nette\Database\ResultSet($connection, "$cmd $sql", $params))->fetchAll(); + $explain = (new Result($connection, "$cmd $sql", $params))->fetchAll(); } catch (\PDOException) { } } diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 09da4c24e..fc532a005 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -24,13 +24,13 @@ class Connection /** @var array Occurs after connection is established */ public array $onConnect = []; - /** @var array Occurs after query is executed */ + /** @var array Occurs after query is executed */ public array $onQuery = []; private Driver $driver; private SqlPreprocessor $preprocessor; private ?PDO $pdo = null; - /** @var callable(array, ResultSet): array */ + /** @var callable(array, Result): array */ private $rowNormalizer = [Helpers::class, 'normalizeRow']; private ?string $sql = null; private int $transactionDepth = 0; @@ -209,11 +209,11 @@ public function transaction(callable $callback): mixed * Generates and executes SQL query. * @param literal-string $sql */ - public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ResultSet + public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result { [$this->sql, $params] = $this->preprocess($sql, ...$params); try { - $result = new ResultSet($this, $this->sql, $params, $this->rowNormalizer); + $result = new Result($this, $this->sql, $params, $this->rowNormalizer); } catch (PDOException $e) { Arrays::invoke($this->onQuery, $this, $e); throw $e; @@ -225,7 +225,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] /** @deprecated use query() */ - public function queryArgs(string $sql, array $params): ResultSet + public function queryArgs(string $sql, array $params): Result { trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED); return $this->query($sql, ...$params); diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index c280a02e2..b13b02cf7 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -66,14 +66,14 @@ public function getInsertId(?string $sequence = null): string * Generates and executes SQL query. * @param literal-string $sql */ - public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ResultSet + public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result { return $this->connection->query($sql, ...$params); } /** @deprecated use query() */ - public function queryArgs(string $sql, array $params): ResultSet + public function queryArgs(string $sql, array $params): Result { trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED); return $this->connection->query($sql, ...$params); diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 4df4f9d4b..5c17be465 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -40,7 +40,7 @@ class Helpers /** * Displays complete result set as HTML table for debug purposes. */ - public static function dumpResult(ResultSet $result): void + public static function dumpResult(Result $result): void { echo "\n\n\n"; if (!$result->getColumnCount()) { @@ -204,7 +204,7 @@ public static function detectType(string $type): string /** @internal */ public static function normalizeRow( array $row, - ResultSet $resultSet, + Result $resultSet, $dateTimeClass = DateTime::class, ): array { diff --git a/src/Database/ResultSet.php b/src/Database/Result.php similarity index 97% rename from src/Database/ResultSet.php rename to src/Database/Result.php index 788ed8012..7b9e42f9b 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/Result.php @@ -17,11 +17,11 @@ /** * Represents a result set. */ -class ResultSet implements \Iterator +class Result implements \Iterator { private ?\PDOStatement $pdoStatement = null; - /** @var callable(array, ResultSet): array */ + /** @var callable(array, Result): array */ private readonly mixed $normalizer; private Row|false|null $lastRow = null; private int $lastRowKey = -1; @@ -269,3 +269,6 @@ public function fetchAll(): array return $this->rows; } } + + +class_exists(ResultSet::class); diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index c8ec53ab0..921a8af32 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -565,7 +565,7 @@ protected function createGroupedSelectionInstance(string $table, string $column) } - protected function query(string $query): Nette\Database\ResultSet + protected function query(string $query): Nette\Database\Result { return $this->explorer->query($query, ...$this->sqlBuilder->getParameters()); } diff --git a/src/compatibility.php b/src/compatibility.php index 0166a7a01..135582033 100644 --- a/src/compatibility.php +++ b/src/compatibility.php @@ -17,3 +17,12 @@ class Context extends Explorer } elseif (!class_exists(Context::class)) { class_alias(Explorer::class, Context::class); } + +if (false) { + /** @deprecated use Nette\Database\Result */ + class ResultSet extends Result + { + } +} elseif (!class_exists(ResultSet::class)) { + class_alias(Result::class, ResultSet::class); +} diff --git a/tests/Database/Connection.query.phpt b/tests/Database/Connection.query.phpt index 4465bd3fe..e8f14b74f 100644 --- a/tests/Database/Connection.query.phpt +++ b/tests/Database/Connection.query.phpt @@ -17,7 +17,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($connection) { $res = $connection->query('SELECT id FROM author WHERE id = ?', 11); - Assert::type(Nette\Database\ResultSet::class, $res); + Assert::type(Nette\Database\Result::class, $res); Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString()); Assert::same([11], $res->getParameters()); Assert::same('SELECT id FROM author WHERE id = ?', $connection->getLastQueryString()); diff --git a/tests/Database/Explorer.query.phpt b/tests/Database/Explorer.query.phpt index 9a1f8d13f..feaeac8e1 100644 --- a/tests/Database/Explorer.query.phpt +++ b/tests/Database/Explorer.query.phpt @@ -19,7 +19,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($explorer) { $res = $explorer->query('SELECT id FROM author WHERE id = ?', 11); - Assert::type(Nette\Database\ResultSet::class, $res); + Assert::type(Nette\Database\Result::class, $res); Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString()); Assert::same([11], $res->getParameters()); }); diff --git a/tests/Database/Explorer/Explorer.cache.observer.phpt b/tests/Database/Explorer/Explorer.cache.observer.phpt index 1c52c4296..eee6c028f 100644 --- a/tests/Database/Explorer/Explorer.cache.observer.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\ResultSet; +use Nette\Database\Result; use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; @@ -26,7 +26,7 @@ $cacheStorage->shouldReceive('write')->with(Mockery::any(), ['id' => true, 'auth $explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cacheStorage); $queries = 0; -$connection->onQuery[] = function ($dao, ResultSet $result) use (&$queries) { +$connection->onQuery[] = function ($dao, Result $result) use (&$queries) { if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQueryString())) { $queries++; } diff --git a/tests/Database/ResultSet.fetch().phpt b/tests/Database/Result.fetch().phpt similarity index 98% rename from tests/Database/ResultSet.fetch().phpt rename to tests/Database/Result.fetch().phpt index 376bcb085..ab65f2725 100644 --- a/tests/Database/ResultSet.fetch().phpt +++ b/tests/Database/Result.fetch().phpt @@ -1,7 +1,7 @@ setRowNormalizer(function (array $row, Nette\Database\ResultSet $resultSet) { + $connection->setRowNormalizer(function (array $row) { foreach ($row as $key => $value) { unset($row[$key]); $row['_' . $key . '_'] = (string) $value; From 96c2e8a80edbcd5afcd5dd393b2c63bee663b15d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 28 Aug 2024 03:05:10 +0200 Subject: [PATCH 19/53] renamed Nette\Database\Driver -> Nette\Database\Drivers\Engine (BC break) --- src/Database/Connection.php | 22 ++++++------ .../{Driver.php => Drivers/Engine.php} | 12 ++++--- src/Database/Drivers/MsSqlDriver.php | 2 +- src/Database/Drivers/MySqlDriver.php | 2 +- src/Database/Drivers/OciDriver.php | 2 +- src/Database/Drivers/OdbcDriver.php | 2 +- src/Database/Drivers/PgSqlDriver.php | 2 +- src/Database/Drivers/SqliteDriver.php | 2 +- src/Database/Drivers/SqlsrvDriver.php | 2 +- src/Database/Explorer.php | 6 ++++ src/Database/Reflection.php | 8 ++--- src/Database/Reflection/Table.php | 6 ++-- src/Database/Result.php | 4 +-- src/Database/SqlPreprocessor.php | 12 +++---- src/Database/Structure.php | 10 +++--- src/Database/Table/Selection.php | 2 +- src/Database/Table/SqlBuilder.php | 26 +++++++------- ...postgre.10.phpt => Engine.postgre.10.phpt} | 30 ++++++++-------- ...ction.postgre.phpt => Engine.postgre.phpt} | 28 +++++++-------- ...ion.driver.phpt => Engine.reflection.phpt} | 12 +++---- .../Database/Explorer/Explorer.backjoin.phpt | 8 ++--- .../Explorer/Explorer.join-condition.phpt | 12 +++---- tests/Database/Explorer/Explorer.join.phpt | 12 +++---- .../Explorer/SqlBuilder.addAlias().phpt | 16 ++++----- .../Explorer/SqlBuilder.addWhere().phpt | 4 +-- .../SqlBuilder.parseJoinConditions().phpt | 8 ++--- .../Explorer/SqlBuilder.parseJoins().phpt | 12 +++---- tests/Database/Explorer/bugs/view.bug.phpt | 4 +-- tests/Database/Reflection.phpt | 4 +-- tests/Database/Row.phpt | 2 +- tests/Database/Structure.phpt | 34 +++++++++---------- tests/Database/Structure.schemas.phpt | 18 +++++----- tests/Database/connection.disconnect.phpt | 8 ++--- tests/Database/connection.options.sqlite.phpt | 8 ++--- 34 files changed, 175 insertions(+), 167 deletions(-) rename src/Database/{Driver.php => Drivers/Engine.php} (88%) rename tests/Database/{Reflection.postgre.10.phpt => Engine.postgre.10.phpt} (83%) rename tests/Database/{Reflection.postgre.phpt => Engine.postgre.phpt} (69%) rename tests/Database/{Reflection.driver.phpt => Engine.reflection.phpt} (94%) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index fc532a005..a1e207ea3 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -26,7 +26,7 @@ class Connection /** @var array Occurs after query is executed */ public array $onQuery = []; - private Driver $driver; + private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; private ?PDO $pdo = null; @@ -65,9 +65,9 @@ public function connect(): void $class = empty($this->options['driverClass']) ? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver' : $this->options['driverClass']; - $this->driver = new $class; + $this->engine = new $class; $this->preprocessor = new SqlPreprocessor($this); - $this->driver->initialize($this, $this->options); + $this->engine->initialize($this, $this->options); Arrays::invoke($this->onConnect, $this); } @@ -98,25 +98,25 @@ public function getPdo(): PDO } - public function getDriver(): Driver + /** @deprecated use getDriver() */ + public function getSupplementalDriver(): Drivers\Engine { + trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED); $this->connect(); - return $this->driver; + return $this->engine; } - /** @deprecated use getDriver() */ - public function getSupplementalDriver(): Driver + public function getDatabaseEngine(): Drivers\Engine { - trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED); $this->connect(); - return $this->driver; + return $this->engine; } public function getReflection(): Reflection { - return new Reflection($this->getDriver()); + return new Reflection($this->getDatabaseEngine()); } @@ -133,7 +133,7 @@ public function getInsertId(?string $sequence = null): string $res = $this->getPdo()->lastInsertId($sequence); return $res === false ? '0' : $res; } catch (PDOException $e) { - throw $this->driver->convertException($e); + throw $this->engine->convertException($e); } } diff --git a/src/Database/Driver.php b/src/Database/Drivers/Engine.php similarity index 88% rename from src/Database/Driver.php rename to src/Database/Drivers/Engine.php index 2397be328..1b8645bf7 100644 --- a/src/Database/Driver.php +++ b/src/Database/Drivers/Engine.php @@ -7,13 +7,15 @@ declare(strict_types=1); -namespace Nette\Database; +namespace Nette\Database\Drivers; + +use Nette\Database; /** - * Supplemental PDO database driver. + * Database platform specific operations and reflection capabilities. */ -interface Driver +interface Engine { public const SupportSequence = 'sequence', @@ -32,12 +34,12 @@ function isSupported(string $feature): bool; /** * Initializes connection. */ - function initialize(Connection $connection, array $options): void; + function initialize(Database\Connection $connection, array $options): void; /** * Converts PDOException to DriverException or its descendant. */ - function convertException(\PDOException $e): DriverException; + function convertException(\PDOException $e): Database\DriverException; /********************* SQL utilities ****************d*g**/ diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index ae797e3b7..14fe2104d 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -15,7 +15,7 @@ /** * Supplemental MS SQL database driver. */ -class MsSqlDriver implements Nette\Database\Driver +class MsSqlDriver implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index bcda41f73..adaedb1cc 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -15,7 +15,7 @@ /** * Supplemental MySQL database driver. */ -class MySqlDriver implements Nette\Database\Driver +class MySqlDriver implements Engine { private Nette\Database\Connection $connection; private bool $convertBoolean; diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index 108cc6b2f..6c4ff3754 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -15,7 +15,7 @@ /** * Supplemental Oracle database driver. */ -class OciDriver implements Nette\Database\Driver +class OciDriver implements Engine { private Nette\Database\Connection $connection; private string $fmtDateTime; diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index 329b2a0f9..ee6e42fa2 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -15,7 +15,7 @@ /** * Supplemental ODBC database driver. */ -class OdbcDriver implements Nette\Database\Driver +class OdbcDriver implements Engine { public function initialize(Nette\Database\Connection $connection, array $options): void { diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 865b9b834..2753b3ae7 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -15,7 +15,7 @@ /** * Supplemental PostgreSQL database driver. */ -class PgSqlDriver implements Nette\Database\Driver +class PgSqlDriver implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 881dfa0f6..c5d33b0dd 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -15,7 +15,7 @@ /** * Supplemental SQLite3 database driver. */ -class SqliteDriver implements Nette\Database\Driver +class SqliteDriver implements Engine { private Nette\Database\Connection $connection; private string $fmtDateTime; diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 0e5d34714..9187ca392 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -15,7 +15,7 @@ /** * Supplemental SQL Server 2005 and later database driver. */ -class SqlsrvDriver implements Nette\Database\Driver +class SqlsrvDriver implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index b13b02cf7..a6f084a10 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -92,6 +92,12 @@ public function getConnection(): Connection } + public function getDatabaseEngine(): Drivers\Engine + { + return $this->connection->getDatabaseEngine(); + } + + public function getStructure(): IStructure { return $this->structure; diff --git a/src/Database/Reflection.php b/src/Database/Reflection.php index bc288b091..d85be54ad 100644 --- a/src/Database/Reflection.php +++ b/src/Database/Reflection.php @@ -19,7 +19,7 @@ final class Reflection public function __construct( - private readonly Driver $driver, + private readonly Drivers\Engine $engine, ) { unset($this->tables); } @@ -67,16 +67,16 @@ private function getFullName(string $name): string /** @internal */ - public function getDriver(): Driver + public function getDatabaseEngine(): Drivers\Engine { - return $this->driver; + return $this->engine; } private function initTables(): void { $res = []; - foreach ($this->driver->getTables() as $row) { + foreach ($this->engine->getTables() as $row) { $res[$row['fullName'] ?? $row['name']] = new Table($this, $row['name'], $row['view'], $row['fullName'] ?? null); } $this->tables = $res; diff --git a/src/Database/Reflection/Table.php b/src/Database/Reflection/Table.php index a051de557..da684cc7b 100644 --- a/src/Database/Reflection/Table.php +++ b/src/Database/Reflection/Table.php @@ -48,7 +48,7 @@ public function getColumn(string $name): Column private function initColumns(): void { $res = []; - foreach ($this->reflection->getDriver()->getColumns($this->name) as $row) { + foreach ($this->reflection->getDatabaseEngine()->getColumns($this->name) as $row) { $row['table'] = $this; $res[$row['name']] = new Column(...$row); } @@ -65,7 +65,7 @@ private function initIndexes(): void $row['primary'], is_string($row['name']) ? $row['name'] : null, ), - $this->reflection->getDriver()->getIndexes($this->name), + $this->reflection->getDatabaseEngine()->getIndexes($this->name), ); } @@ -83,7 +83,7 @@ private function initPrimaryKey(): void private function initForeignKeys(): void { $tmp = []; - foreach ($this->reflection->getDriver()->getForeignKeys($this->name) as $row) { + foreach ($this->reflection->getDatabaseEngine()->getForeignKeys($this->name) as $row) { $id = $row['name']; $foreignTable = $this->reflection->getTable($row['table']); $tmp[$id][0] = $foreignTable; diff --git a/src/Database/Result.php b/src/Database/Result.php index 7b9e42f9b..f5846033b 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -56,7 +56,7 @@ public function __construct( $this->pdoStatement->execute(); } } catch (\PDOException $e) { - $e = $connection->getDriver()->convertException($e); + $e = $connection->getDatabaseEngine()->convertException($e); $e->queryString = $queryString; $e->params = $params; throw $e; @@ -109,7 +109,7 @@ public function getRowCount(): ?int public function getColumnTypes(): array { - $this->types ??= $this->connection->getDriver()->getColumnTypes($this->pdoStatement); + $this->types ??= $this->connection->getDatabaseEngine()->getColumnTypes($this->pdoStatement); return $this->types; } diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 876ef3ca3..6f002182f 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -49,7 +49,7 @@ class SqlPreprocessor ]; private readonly Connection $connection; - private readonly Driver $driver; + private readonly Drivers\Engine $engine; private array $params; private array $remaining; private int $counter; @@ -62,7 +62,7 @@ class SqlPreprocessor public function __construct(Connection $connection) { $this->connection = $connection; - $this->driver = $connection->getDriver(); + $this->engine = $connection->getDatabaseEngine(); } @@ -177,10 +177,10 @@ private function formatValue(mixed $value, ?string $mode = null): string return $res; } elseif ($value instanceof \DateTimeInterface) { - return $this->driver->formatDateTime($value); + return $this->engine->formatDateTime($value); } elseif ($value instanceof \DateInterval) { - return $this->driver->formatDateInterval($value); + return $this->engine->formatDateInterval($value); } elseif ($value instanceof \BackedEnum && is_scalar($value->value)) { $this->remaining[] = $value->value; @@ -231,7 +231,7 @@ private function formatValue(mixed $value, ?string $mode = null): string $vx[] = implode(', ', $vx2); } - $select = $this->driver->isSupported(Driver::SupportMultiInsertAsSelect); + $select = $this->engine->isSupported(Drivers\Engine::SupportMultiInsertAsSelect); return '(' . implode(', ', $kx) . ($select ? ') SELECT ' : ') VALUES (') . implode($select ? ' UNION ALL SELECT ' : '), (', $vx) . ($select ? '' : ')'); } @@ -320,6 +320,6 @@ private function formatValue(mixed $value, ?string $mode = null): string private function delimite(string $name): string { - return implode('.', array_map($this->driver->delimite(...), explode('.', $name))); + return implode('.', array_map($this->engine->delimite(...), explode('.', $name))); } } diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 580c2b820..2b3ae3076 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -94,7 +94,7 @@ public function getPrimaryKeySequence(string $table): ?string $this->needStructure(); $table = $this->resolveFQTableName($table); - if (!$this->connection->getDriver()->isSupported(Driver::SupportSequence)) { + if (!$this->connection->getDatabaseEngine()->isSupported(Drivers\Engine::SupportSequence)) { return null; } @@ -177,10 +177,10 @@ protected function needStructure(): void protected function loadStructure(): array { - $driver = $this->connection->getDriver(); + $engine = $this->connection->getDatabaseEngine(); $structure = []; - $structure['tables'] = $driver->getTables(); + $structure['tables'] = $engine->getTables(); foreach ($structure['tables'] as $tablePair) { if (isset($tablePair['fullName'])) { @@ -190,7 +190,7 @@ protected function loadStructure(): array $table = $tablePair['name']; } - $structure['columns'][strtolower($table)] = $columns = $driver->getColumns($table); + $structure['columns'][strtolower($table)] = $columns = $engine->getColumns($table); if (!$tablePair['view']) { $structure['primary'][strtolower($table)] = $this->analyzePrimaryKey($columns); @@ -233,7 +233,7 @@ protected function analyzeForeignKeys(array &$structure, string $table): void { $lowerTable = strtolower($table); - $foreignKeys = $this->connection->getDriver()->getForeignKeys($table); + $foreignKeys = $this->connection->getDatabaseEngine()->getForeignKeys($table); $fksColumnsCounts = []; foreach ($foreignKeys as $foreignKey) { diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 921a8af32..5b3abb056 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -801,7 +801,7 @@ public function insert(iterable $data): ActiveRow|array|int|bool // First check sequence if (!empty($primarySequenceName) && $primaryAutoincrementKey) { - $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getConnection()->getDriver()->delimite($primarySequenceName)); + $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getConnection()->getDatabaseEngine()->delimite($primarySequenceName)); // Autoincrement primary without sequence } elseif ($primaryAutoincrementKey) { diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 6867e39a1..00beaa59a 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -11,7 +11,7 @@ use Nette; use Nette\Database\Conventions; -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Nette\Database\Explorer; use Nette\Database\IStructure; use Nette\Database\SqlLiteral; @@ -46,7 +46,7 @@ class SqlBuilder protected array $reservedTableNames = []; protected array $aliases = []; protected string $currentAlias = ''; - private readonly Driver $driver; + private readonly Engine $engine; private readonly IStructure $structure; private array $cacheTableList = []; private array $expandingJoins = []; @@ -55,11 +55,11 @@ class SqlBuilder public function __construct(string $tableName, Explorer $explorer) { $this->tableName = $tableName; - $this->driver = $explorer->getConnection()->getDriver(); + $this->engine = $explorer->getConnection()->getDatabaseEngine(); $this->conventions = $explorer->getConventions(); $this->structure = $explorer->getStructure(); $tableNameParts = explode('.', $tableName); - $this->delimitedTable = implode('.', array_map($this->driver->delimite(...), $tableNameParts)); + $this->delimitedTable = implode('.', array_map($this->engine->delimite(...), $tableNameParts)); $this->checkUniqueTableName(end($tableNameParts), $tableName); } @@ -85,7 +85,7 @@ public function buildUpdateQuery(): string } if ($this->limit !== null || $this->offset) { - $this->driver->applyLimit($query, $this->limit, $this->offset); + $this->engine->applyLimit($query, $this->limit, $this->offset); } return $query; @@ -96,7 +96,7 @@ public function buildDeleteQuery(): string { $query = "DELETE FROM {$this->delimitedTable}" . $this->tryDelimite($this->buildConditions()); if ($this->limit !== null || $this->offset) { - $this->driver->applyLimit($query, $this->limit, $this->offset); + $this->engine->applyLimit($query, $this->limit, $this->offset); } return $query; @@ -119,7 +119,7 @@ public function getSelectQueryHash(?array $columns = null): string $parts[] = $this->select; } elseif ($columns) { $parts[] = [$this->delimitedTable, $columns]; - } elseif ($this->group && !$this->driver->isSupported(Driver::SupportSelectUngroupedColumns)) { + } elseif ($this->group && !$this->engine->isSupported(Engine::SupportSelectUngroupedColumns)) { $parts[] = [$this->group]; } else { $parts[] = "{$this->delimitedTable}.*"; @@ -171,7 +171,7 @@ public function buildSelectQuery(?array $columns = null): string $querySelect = $this->buildSelect($cols); - } elseif ($this->group && !$this->driver->isSupported(Driver::SupportSelectUngroupedColumns)) { + } elseif ($this->group && !$this->engine->isSupported(Engine::SupportSelectUngroupedColumns)) { $querySelect = $this->buildSelect([$this->group]); $this->parseJoins($joins, $querySelect); @@ -183,7 +183,7 @@ public function buildSelectQuery(?array $columns = null): string $queryJoins = $this->buildQueryJoins($joins, $finalJoinConditions); $query = "{$querySelect} FROM {$this->delimitedTable}{$queryJoins}{$queryCondition}{$queryEnd}"; - $this->driver->applyLimit($query, $this->limit, $this->offset); + $this->engine->applyLimit($query, $this->limit, $this->offset); return $this->tryDelimite($query); } @@ -343,7 +343,7 @@ protected function addCondition( } } - if ($this->driver->isSupported(Driver::SupportSubselect)) { + if ($this->engine->isSupported(Engine::SupportSubselect)) { $arg = null; $subSelectPlaceholderCount = substr_count($clone->getSql(), '?'); $replace = $match[2][0] . '(' . $clone->getSql() . (!$subSelectPlaceholderCount && count($clone->getSqlBuilder()->getParameters()) === 1 ? ' ?' : '') . ')'; @@ -634,7 +634,7 @@ public function parseJoinsCb(array &$joins, array $match): string $parentAlias = preg_replace('#^(.*\.)?(.*)$#', '$2', $this->tableName); // join schema keyMatch and table keyMatch to schema.table keyMatch - if ($this->driver->isSupported(Driver::SupportSchema) && count($keyMatches) > 1) { + if ($this->engine->isSupported(Engine::SupportSchema) && count($keyMatches) > 1) { $tables = $this->getCachedTableList(); if ( !isset($tables[$keyMatches[0]['key']]) @@ -795,7 +795,7 @@ protected function tryDelimite(string $s): string '#(?<=[^\w`"\[?:]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#Di', fn(array $m): string => strtoupper($m[0]) === $m[0] ? $m[0] - : $this->driver->delimite($m[0]), + : $this->engine->delimite($m[0]), $s, ); } @@ -808,7 +808,7 @@ protected function addConditionComposition( array &$conditionsParameters, ): bool { - if ($this->driver->isSupported(Driver::SupportMultiColumnAsOrCondition)) { + if ($this->engine->isSupported(Engine::SupportMultiColumnAsOrCondition)) { $conditionFragment = '(' . implode(' = ? AND ', $columns) . ' = ?) OR '; $condition = substr(str_repeat($conditionFragment, count($parameters)), 0, -4); return $this->addCondition($condition, [Nette\Utils\Arrays::flatten($parameters)], $conditions, $conditionsParameters); diff --git a/tests/Database/Reflection.postgre.10.phpt b/tests/Database/Engine.postgre.10.phpt similarity index 83% rename from tests/Database/Reflection.postgre.10.phpt rename to tests/Database/Engine.postgre.10.phpt index 2d9718ac3..6d4519adc 100644 --- a/tests/Database/Reflection.postgre.10.phpt +++ b/tests/Database/Engine.postgre.10.phpt @@ -45,17 +45,17 @@ test('SERIAL and IDENTITY imply autoIncrement on primary keys', function () use CREATE TABLE "reflection_10"."identity_by_default_pk" ("id" INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY); ')); - $driver = $connection->getDriver(); + $engine = $connection->getDatabaseEngine(); $connection->query('SET search_path TO reflection_10'); $columns = [ - 'serial' => shortInfo($driver->getColumns('serial')), - 'serial_pk' => shortInfo($driver->getColumns('serial_pk')), - 'identity_always' => shortInfo($driver->getColumns('identity_always')), - 'identity_always_pk' => shortInfo($driver->getColumns('identity_always_pk')), - 'identity_by_default' => shortInfo($driver->getColumns('identity_by_default')), - 'identity_by_default_pk' => shortInfo($driver->getColumns('identity_by_default_pk')), + 'serial' => shortInfo($engine->getColumns('serial')), + 'serial_pk' => shortInfo($engine->getColumns('serial_pk')), + 'identity_always' => shortInfo($engine->getColumns('identity_always')), + 'identity_always_pk' => shortInfo($engine->getColumns('identity_always_pk')), + 'identity_by_default' => shortInfo($engine->getColumns('identity_by_default')), + 'identity_by_default_pk' => shortInfo($engine->getColumns('identity_by_default_pk')), ]; Assert::same([ @@ -111,18 +111,18 @@ test('Materialized view columns', function () use ($connection) { CREATE MATERIALIZED VIEW "reflection_10"."source_mt" AS SELECT "name", "id" FROM "reflection_10"."source"; ')); - $driver = $connection->getDriver(); + $engine = $connection->getDatabaseEngine(); $connection->query('SET search_path TO reflection_10'); Assert::same([ ['name' => 'source', 'view' => false, 'fullName' => 'reflection_10.source'], ['name' => 'source_mt', 'view' => true, 'fullName' => 'reflection_10.source_mt'], - ], $driver->getTables()); + ], $engine->getTables()); Assert::same( ['name', 'id'], - array_column($driver->getColumns('source_mt'), 'name'), + array_column($engine->getColumns('source_mt'), 'name'), ); }); @@ -140,22 +140,22 @@ test('Partitioned table', function () use ($connection) { CREATE TABLE "reflection_10"."part_1" PARTITION OF "reflection_10"."parted" FOR VALUES FROM (1) TO (10); ')); - $driver = $connection->getDriver(); + $engine = $connection->getDatabaseEngine(); $connection->query('SET search_path TO reflection_10'); Assert::same([ ['name' => 'part_1', 'view' => false, 'fullName' => 'reflection_10.part_1'], ['name' => 'parted', 'view' => false, 'fullName' => 'reflection_10.parted'], - ], $driver->getTables()); + ], $engine->getTables()); - Assert::same(['id', 'value'], array_column($driver->getColumns('parted'), 'name')); - Assert::same(['id', 'value'], array_column($driver->getColumns('part_1'), 'name')); + Assert::same(['id', 'value'], array_column($engine->getColumns('parted'), 'name')); + Assert::same(['id', 'value'], array_column($engine->getColumns('part_1'), 'name')); Assert::same([[ 'name' => 'parted_pkey', 'unique' => true, 'primary' => true, 'columns' => ['id'], - ]], $driver->getIndexes('parted')); + ]], $engine->getIndexes('parted')); }); diff --git a/tests/Database/Reflection.postgre.phpt b/tests/Database/Engine.postgre.phpt similarity index 69% rename from tests/Database/Reflection.postgre.phpt rename to tests/Database/Engine.postgre.phpt index 53393629a..9ca883377 100644 --- a/tests/Database/Reflection.postgre.phpt +++ b/tests/Database/Engine.postgre.phpt @@ -43,26 +43,26 @@ test('Tables in schema', function () use ($connection) { ALTER TABLE "two"."slave" ADD CONSTRAINT "two_slave_fk" FOREIGN KEY ("two_id") REFERENCES "two"."master"("two_id"); ')); - $driver = $connection->getDriver(); + $engine = $connection->getDatabaseEngine(); // Reflection for tables with the same name but different schema $connection->query('SET search_path TO one, two'); - Assert::same(['master', 'slave'], names($driver->getTables())); - Assert::same(['one_id'], names($driver->getColumns('master'))); - Assert::same(['one_master_pkey'], names($driver->getIndexes('master'))); - Assert::same(['one_slave_fk'], names($driver->getForeignKeys('slave'))); + Assert::same(['master', 'slave'], names($engine->getTables())); + Assert::same(['one_id'], names($engine->getColumns('master'))); + Assert::same(['one_master_pkey'], names($engine->getIndexes('master'))); + Assert::same(['one_slave_fk'], names($engine->getForeignKeys('slave'))); $connection->query('SET search_path TO two, one'); - Assert::same(['master', 'slave'], names($driver->getTables())); - Assert::same(['two_id'], names($driver->getColumns('master'))); - Assert::same(['two_master_pkey'], names($driver->getIndexes('master'))); - Assert::same(['two_slave_fk'], names($driver->getForeignKeys('slave'))); + Assert::same(['master', 'slave'], names($engine->getTables())); + Assert::same(['two_id'], names($engine->getColumns('master'))); + Assert::same(['two_master_pkey'], names($engine->getIndexes('master'))); + Assert::same(['two_slave_fk'], names($engine->getForeignKeys('slave'))); // Reflection for FQN - Assert::same(['one_id'], names($driver->getColumns('one.master'))); - Assert::same(['one_master_pkey'], names($driver->getIndexes('one.master'))); - $foreign = $driver->getForeignKeys('one.slave'); + Assert::same(['one_id'], names($engine->getColumns('one.master'))); + Assert::same(['one_master_pkey'], names($engine->getIndexes('one.master'))); + $foreign = $engine->getForeignKeys('one.slave'); Assert::same([ 'name' => 'one_slave_fk', 'local' => 'one_id', @@ -74,7 +74,7 @@ test('Tables in schema', function () use ($connection) { // Limit foreign keys for current schemas only $connection->query('ALTER TABLE "one"."slave" ADD CONSTRAINT "one_two_fk" FOREIGN KEY ("one_id") REFERENCES "two"."master"("two_id")'); $connection->query('SET search_path TO one'); - Assert::same(['one_slave_fk'], names($driver->getForeignKeys('slave'))); + Assert::same(['one_slave_fk'], names($engine->getForeignKeys('slave'))); $connection->query('SET search_path TO one, two'); - Assert::same(['one_slave_fk', 'one_two_fk'], names($driver->getForeignKeys('slave'))); + Assert::same(['one_slave_fk', 'one_two_fk'], names($engine->getForeignKeys('slave'))); }); diff --git a/tests/Database/Reflection.driver.phpt b/tests/Database/Engine.reflection.phpt similarity index 94% rename from tests/Database/Reflection.driver.phpt rename to tests/Database/Engine.reflection.phpt index 0098c5777..ea07bcc63 100644 --- a/tests/Database/Reflection.driver.phpt +++ b/tests/Database/Engine.reflection.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -18,12 +18,12 @@ $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); -$driver = $connection->getDriver(); -$tables = $driver->getTables(); +$engine = $connection->getDatabaseEngine(); +$tables = $engine->getTables(); $tables = array_filter($tables, fn($t) => in_array($t['name'], ['author', 'book', 'book_tag', 'tag'], true)); usort($tables, fn($a, $b) => strcmp($a['name'], $b['name'])); -if ($driver->isSupported(Driver::SupportSchema)) { +if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( [ ['name' => 'author', 'view' => false, 'fullName' => 'public.author'], @@ -43,7 +43,7 @@ if ($driver->isSupported(Driver::SupportSchema)) { } -$columns = $driver->getColumns('author'); +$columns = $engine->getColumns('author'); array_walk($columns, function (&$item) { Assert::type('array', $item['vendor']); unset($item['vendor']); @@ -128,7 +128,7 @@ switch ($driverName) { Assert::same($expectedColumns, $columns); -$indexes = $driver->getIndexes('book_tag'); +$indexes = $engine->getIndexes('book_tag'); switch ($driverName) { case 'pgsql': Assert::same([ diff --git a/tests/Database/Explorer/Explorer.backjoin.phpt b/tests/Database/Explorer/Explorer.backjoin.phpt index a466b3404..73f982dd0 100644 --- a/tests/Database/Explorer/Explorer.backjoin.phpt +++ b/tests/Database/Explorer/Explorer.backjoin.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; @@ -16,7 +16,7 @@ $explorer = connectToDB(); $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$driver = $connection->getDriver(); +$engine = $connection->getDatabaseEngine(); test('', function () use ($explorer) { @@ -39,10 +39,10 @@ test('', function () use ($explorer) { }); -test('', function () use ($explorer, $driver) { +test('', function () use ($explorer, $engine) { $authorsSelection = $explorer->table('author')->where(':book.translator_id IS NOT NULL')->wherePrimary(12); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( reformat('SELECT [author].* FROM [author] LEFT JOIN [public].[book] [book] ON [author].[id] = [book].[author_id] WHERE ([book].[translator_id] IS NOT NULL) AND ([author].[id] = ?)'), $authorsSelection->getSql(), diff --git a/tests/Database/Explorer/Explorer.join-condition.phpt b/tests/Database/Explorer/Explorer.join-condition.phpt index 34af228b9..1e92b43e6 100644 --- a/tests/Database/Explorer/Explorer.join-condition.phpt +++ b/tests/Database/Explorer/Explorer.join-condition.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; @@ -16,9 +16,9 @@ $explorer = connectToDB(); $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$driver = $connection->getDriver(); -test('', function () use ($explorer, $driver) { - $schema = $driver->isSupported(Driver::SupportSchema) +$engine = $connection->getDatabaseEngine(); +test('', function () use ($explorer, $engine) { + $schema = $engine->isSupported(Engine::SupportSchema) ? '[public].' : ''; $sql = $explorer->table('book')->joinWhere('translator', 'translator.name', 'Geek')->select('book.*')->getSql(); @@ -29,7 +29,7 @@ test('', function () use ($explorer, $driver) { ), $sql); }); -test('', function () use ($explorer, $driver) { +test('', function () use ($explorer, $engine) { $sql = $explorer->table('tag') ->select('tag.name, COUNT(:book_tag.book.id) AS count_of_next_volume_written_by_younger_author') ->joinWhere(':book_tag.book.author', ':book_tag.book.author.born < next_volume_author.born') @@ -37,7 +37,7 @@ test('', function () use ($explorer, $driver) { ->where('tag.name', 'PHP') ->group('tag.name') ->getSql(); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( reformat( 'SELECT [tag].[name], COUNT([book].[id]) AS [count_of_next_volume_written_by_younger_author] FROM [tag] ' . diff --git a/tests/Database/Explorer/Explorer.join.phpt b/tests/Database/Explorer/Explorer.join.phpt index 897975fb7..d0638f2b1 100644 --- a/tests/Database/Explorer/Explorer.join.phpt +++ b/tests/Database/Explorer/Explorer.join.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; @@ -16,7 +16,7 @@ $explorer = connectToDB(); $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$driver = $connection->getDriver(); +$engine = $connection->getDatabaseEngine(); test('', function () use ($explorer) { @@ -34,10 +34,10 @@ test('', function () use ($explorer) { }); -test('', function () use ($explorer, $driver) { +test('', function () use ($explorer, $engine) { $joinSql = $explorer->table('book_tag')->where('book_id', 1)->select('tag.*')->getSql(); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( reformat('SELECT [tag].* FROM [book_tag] LEFT JOIN [public].[tag] [tag] ON [book_tag].[tag_id] = [tag].[id] WHERE ([book_id] = ?)'), $joinSql, @@ -51,10 +51,10 @@ test('', function () use ($explorer, $driver) { }); -test('', function () use ($explorer, $driver) { +test('', function () use ($explorer, $engine) { $joinSql = $explorer->table('book_tag')->where('book_id', 1)->select('Tag.id')->getSql(); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( reformat('SELECT [Tag].[id] FROM [book_tag] LEFT JOIN [public].[tag] [Tag] ON [book_tag].[tag_id] = [Tag].[id] WHERE ([book_id] = ?)'), $joinSql, diff --git a/tests/Database/Explorer/SqlBuilder.addAlias().phpt b/tests/Database/Explorer/SqlBuilder.addAlias().phpt index f3fbd4a06..3b253b6da 100644 --- a/tests/Database/Explorer/SqlBuilder.addAlias().phpt +++ b/tests/Database/Explorer/SqlBuilder.addAlias().phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Nette\Database\Table\SqlBuilder; use Tester\Assert; @@ -32,11 +32,11 @@ class SqlBuilderMock extends SqlBuilder } } -$driver = $connection->getDriver(); +$engine = $connection->getDatabaseEngine(); -test('test duplicated table names throw exception', function () use ($explorer, $driver) { - $authorTable = ($driver->isSupported(Driver::SupportSchema) ? 'public.' : '') . 'author'; +test('test duplicated table names throw exception', function () use ($explorer, $engine) { + $authorTable = ($engine->isSupported(Engine::SupportSchema) ? 'public.' : '') . 'author'; $sqlBuilder = new SqlBuilderMock($authorTable, $explorer); $sqlBuilder->addAlias(':book(translator)', 'book1'); $sqlBuilder->addAlias(':book:book_tag', 'book2'); @@ -73,7 +73,7 @@ test('test duplicated table names throw exception', function () use ($explorer, }); -test('test same table chain with another alias', function () use ($explorer, $driver) { +test('test same table chain with another alias', function () use ($explorer, $engine) { $sqlBuilder = new SqlBuilderMock('author', $explorer); $sqlBuilder->addAlias(':book(translator)', 'translated_book'); $sqlBuilder->addAlias(':book(translator)', 'translated_book2'); @@ -90,8 +90,8 @@ test('test same table chain with another alias', function () use ($explorer, $dr }); -test('test nested alias', function () use ($explorer, $driver) { - $sqlBuilder = $driver->isSupported(Driver::SupportSchema) +test('test nested alias', function () use ($explorer, $engine) { + $sqlBuilder = $engine->isSupported(Engine::SupportSchema) ? new SqlBuilderMock('public.author', $explorer) : new SqlBuilderMock('author', $explorer); $sqlBuilder->addAlias(':book(translator)', 'translated_book'); @@ -100,7 +100,7 @@ test('test nested alias', function () use ($explorer, $driver) { $joins = []; $sqlBuilder->parseJoins($joins, $query); $join = $sqlBuilder->buildQueryJoins($joins); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( 'LEFT JOIN book translated_book ON author.id = translated_book.translator_id ' . 'LEFT JOIN public.book next ON translated_book.next_volume = next.id', diff --git a/tests/Database/Explorer/SqlBuilder.addWhere().phpt b/tests/Database/Explorer/SqlBuilder.addWhere().phpt index 12e81c04b..9c3541bef 100644 --- a/tests/Database/Explorer/SqlBuilder.addWhere().phpt +++ b/tests/Database/Explorer/SqlBuilder.addWhere().phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Nette\Database\SqlLiteral; use Nette\Database\Table\SqlBuilder; use Tester\Assert; @@ -82,7 +82,7 @@ test('test more ActiveRow as a parameter', function () use ($explorer) { test('test Selection with parameters as a parameter', function () use ($explorer) { $sqlBuilder = new SqlBuilder('book', $explorer); $sqlBuilder->addWhere('id', $explorer->table('book')->having('COUNT(:book_tag.tag_id) >', 1)); - $schemaSupported = $explorer->getConnection()->getDriver()->isSupported(Driver::SupportSchema); + $schemaSupported = $explorer->getDatabaseEngine()->isSupported(Engine::SupportSchema); Assert::same(reformat([ 'mysql' => 'SELECT * FROM `book` WHERE (`id` IN (?))', 'SELECT * FROM [book] WHERE ([id] IN (SELECT [id] FROM [book] LEFT JOIN ' . ($schemaSupported ? '[public].[book_tag] ' : '') . '[book_tag] ON [book].[id] = [book_tag].[book_id] HAVING COUNT([book_tag].[tag_id]) > ?))', diff --git a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt index cb0d83ff0..17e6909e7 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Nette\Database\Table\SqlBuilder; use Tester\Assert; @@ -44,7 +44,7 @@ class SqlBuilderMock extends SqlBuilder } } -$driver = $connection->getDriver(); +$engine = $connection->getDatabaseEngine(); test('test circular reference', function () use ($explorer) { $sqlBuilder = new SqlBuilderMock('author', $explorer); @@ -75,7 +75,7 @@ test('test circular reference', function () use ($explorer) { ); }); -test('', function () use ($explorer, $driver) { +test('', function () use ($explorer, $engine) { $sqlBuilder = new SqlBuilderMock('author', $explorer); $sqlBuilder->addJoinCondition(':book(translator)', ':book(translator).id > ?', 2); $sqlBuilder->addJoinCondition(':book(translator):book_tag_alt', ':book(translator):book_tag_alt.state ?', 'private'); @@ -83,7 +83,7 @@ test('', function () use ($explorer, $driver) { $leftJoinConditions = $sqlBuilder->parseJoinConditions($joins, $sqlBuilder->buildJoinConditions()); $join = $sqlBuilder->buildQueryJoins($joins, $leftJoinConditions); - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( 'LEFT JOIN book ON author.id = book.translator_id AND (book.id > ?) ' . 'LEFT JOIN public.book_tag_alt book_tag_alt ON book.id = book_tag_alt.book_id AND (book_tag_alt.state = ?)', diff --git a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt index 847f32ec4..c77b37f44 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt @@ -8,7 +8,7 @@ declare(strict_types=1); use Nette\Database\Conventions\DiscoveredConventions; -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Nette\Database\Table\SqlBuilder; use Tester\Assert; @@ -37,7 +37,7 @@ class SqlBuilderMock extends SqlBuilder $structure = $explorer->getStructure(); $conventions = new DiscoveredConventions($structure); $sqlBuilder = new SqlBuilderMock('nUsers', $explorer); -$driver = $connection->getDriver(); +$engine = $connection->getDatabaseEngine(); $joins = []; @@ -46,9 +46,9 @@ $sqlBuilder->parseJoins($joins, $query); $join = $sqlBuilder->buildQueryJoins($joins); Assert::same('WHERE priorit.id IS NULL', $query); -$tables = $connection->getDriver()->getTables(); +$tables = $connection->getDatabaseEngine()->getTables(); if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_ntopics', 'nusers_ntopics_alt'], true)) { - if ($driver->isSupported(Driver::SupportSchema)) { + if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( 'LEFT JOIN public.nUsers_nTopics nusers_ntopics ON nUsers.nUserId = nusers_ntopics.nUserId ' . 'LEFT JOIN public.nTopics topic ON nusers_ntopics.nTopicId = topic.nTopicId ' . @@ -91,7 +91,7 @@ Assert::same( ); -$sqlBuilder = $driver->isSupported(Driver::SupportSchema) +$sqlBuilder = $engine->isSupported(Engine::SupportSchema) ? new SqlBuilderMock('public.book', $explorer) : new SqlBuilderMock('book', $explorer); @@ -101,7 +101,7 @@ $sqlBuilder->parseJoins($joins, $query); $join = $sqlBuilder->buildQueryJoins($joins); Assert::same('WHERE book_ref.translator_id IS NULL AND book_ref_ref.translator_id IS NULL', $query); -if ($driver->isSupported(Driver::SupportSchema)) { +if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( 'LEFT JOIN public.book book_ref ON book.id = book_ref.next_volume ' . 'LEFT JOIN public.book book_ref_ref ON book_ref.id = book_ref_ref.next_volume', diff --git a/tests/Database/Explorer/bugs/view.bug.phpt b/tests/Database/Explorer/bugs/view.bug.phpt index 0a555757b..0837c9d93 100644 --- a/tests/Database/Explorer/bugs/view.bug.phpt +++ b/tests/Database/Explorer/bugs/view.bug.phpt @@ -23,8 +23,8 @@ test('', function () use ($explorer) { }); test('', function () use ($connection) { - $driver = $connection->getDriver(); - $columns = $driver->getColumns('books_view'); + $engine = $connection->getDatabaseEngine(); + $columns = $engine->getColumns('books_view'); $columnsNames = array_map(fn($item) => $item['name'], $columns); Assert::same(['id', 'author_id', 'translator_id', 'title', 'next_volume'], $columnsNames); }); diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index 80fa8ab6b..dc06d824f 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Database\Driver; +use Nette\Database\Drivers\Engine; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -17,7 +17,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName $reflection = $connection->getReflection(); -$schemaSupported = $connection->getDriver()->isSupported(Driver::SupportSchema); +$schemaSupported = $connection->getDatabaseEngine()->isSupported(Engine::SupportSchema); // table names $tableNames = array_keys($reflection->tables); diff --git a/tests/Database/Row.phpt b/tests/Database/Row.phpt index 9dfc65170..ea63dbe3b 100644 --- a/tests/Database/Row.phpt +++ b/tests/Database/Row.phpt @@ -14,7 +14,7 @@ require __DIR__ . '/../bootstrap.php'; $connection = connectToDB()->getConnection(); test('numeric field', function () use ($connection) { - $row = $connection->fetch("SELECT 123 AS {$connection->getDriver()->delimite('123')}, NULL as nullcol"); + $row = $connection->fetch("SELECT 123 AS {$connection->getDatabaseEngine()->delimite('123')}, NULL as nullcol"); Assert::same(123, $row->{123}); Assert::same(123, $row->{'123'}); Assert::true(isset($row->{123})); diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index 9e585e4c0..b9e2b559a 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -30,7 +30,7 @@ class StructureMock extends Structure class StructureTestCase extends TestCase { private Nette\Database\Connection $connection; - private Nette\Database\Driver $driver; + private Nette\Database\Drivers\Engine $engine; private Nette\Caching\Storage $storage; private Structure $structure; @@ -38,47 +38,47 @@ class StructureTestCase extends TestCase protected function setUp() { parent::setUp(); - $this->driver = Mockery::mock(Nette\Database\Driver::class); + $this->engine = Mockery::mock(Nette\Database\Drivers\Engine::class); $this->connection = Mockery::mock(Nette\Database\Connection::class); $this->storage = Mockery::mock(Nette\Caching\Storage::class); $this->connection->shouldReceive('getDsn')->once()->andReturn(''); - $this->connection->shouldReceive('getDriver')->once()->andReturn($this->driver); - $this->driver->shouldReceive('getTables')->once()->andReturn([ + $this->connection->shouldReceive('getDatabaseEngine')->once()->andReturn($this->engine); + $this->engine->shouldReceive('getTables')->once()->andReturn([ ['name' => 'authors', 'view' => false], ['name' => 'Books', 'view' => false], ['name' => 'tags', 'view' => false], ['name' => 'books_x_tags', 'view' => false], ['name' => 'books_view', 'view' => true], ]); - $this->driver->shouldReceive('getColumns')->with('authors')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('authors')->once()->andReturn([ ['name' => 'id', 'primary' => true, 'autoIncrement' => true, 'vendor' => ['sequence' => '"public"."authors_id_seq"']], ['name' => 'name', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); - $this->driver->shouldReceive('getColumns')->with('Books')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('Books')->once()->andReturn([ ['name' => 'id', 'primary' => true, 'autoIncrement' => true, 'vendor' => ['sequence' => '"public"."Books_id_seq"']], ['name' => 'title', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); - $this->driver->shouldReceive('getColumns')->with('tags')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('tags')->once()->andReturn([ ['name' => 'id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], ['name' => 'name', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); - $this->driver->shouldReceive('getColumns')->with('books_x_tags')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('books_x_tags')->once()->andReturn([ ['name' => 'book_id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], ['name' => 'tag_id', 'primary' => true, 'autoIncrement' => false, 'vendor' => []], ]); - $this->driver->shouldReceive('getColumns')->with('books_view')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('books_view')->once()->andReturn([ ['name' => 'id', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ['name' => 'title', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); - $this->connection->shouldReceive('getDriver')->times(4)->andReturn($this->driver); - $this->driver->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]); - $this->driver->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([ + $this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine); + $this->engine->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]); + $this->engine->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([ ['local' => 'author_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk1'], ['local' => 'translator_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk2'], ]); - $this->driver->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]); - $this->driver->shouldReceive('getForeignKeys')->with('books_x_tags')->once()->andReturn([ + $this->engine->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]); + $this->engine->shouldReceive('getForeignKeys')->with('books_x_tags')->once()->andReturn([ ['local' => 'book_id', 'table' => 'Books', 'foreign' => 'id', 'name' => 'books_x_tags_fk1'], ['local' => 'tag_id', 'table' => 'tags', 'foreign' => 'id', 'name' => 'books_x_tags_fk2'], ]); @@ -132,9 +132,9 @@ class StructureTestCase extends TestCase public function testGetPrimaryKeySequence() { - $this->connection->shouldReceive('getDriver')->times(4)->andReturn($this->driver); - $this->driver->shouldReceive('isSupported')->with('sequence')->once()->andReturn(false); - $this->driver->shouldReceive('isSupported')->with('sequence')->times(3)->andReturn(true); + $this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine); + $this->engine->shouldReceive('isSupported')->with('sequence')->once()->andReturn(false); + $this->engine->shouldReceive('isSupported')->with('sequence')->times(3)->andReturn(true); Assert::null($this->structure->getPrimaryKeySequence('Authors')); Assert::same('"public"."authors_id_seq"', $this->structure->getPrimaryKeySequence('Authors')); diff --git a/tests/Database/Structure.schemas.phpt b/tests/Database/Structure.schemas.phpt index 0a145a56c..406b47fc7 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -30,7 +30,7 @@ class StructureMock extends Structure class StructureSchemasTestCase extends TestCase { private Nette\Database\Connection $connection; - private Nette\Database\Driver $driver; + private Nette\Database\Drivers\Engine $engine; private Nette\Caching\Storage $storage; private Structure $structure; @@ -38,28 +38,28 @@ class StructureSchemasTestCase extends TestCase protected function setUp() { parent::setUp(); - $this->driver = Mockery::mock(Nette\Database\Driver::class); + $this->engine = Mockery::mock(Nette\Database\Drivers\Engine::class); $this->connection = Mockery::mock(Nette\Database\Connection::class); $this->storage = Mockery::mock(Nette\Caching\Storage::class); $this->connection->shouldReceive('getDsn')->once()->andReturn(''); - $this->connection->shouldReceive('getDriver')->once()->andReturn($this->driver); - $this->driver->shouldReceive('getTables')->once()->andReturn([ + $this->connection->shouldReceive('getDatabaseEngine')->once()->andReturn($this->engine); + $this->engine->shouldReceive('getTables')->once()->andReturn([ ['name' => 'authors', 'view' => false, 'fullName' => 'authors.authors'], ['name' => 'books', 'view' => false, 'fullName' => 'books.books'], ]); - $this->driver->shouldReceive('getColumns')->with('authors.authors')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('authors.authors')->once()->andReturn([ ['name' => 'id', 'primary' => true, 'vendor' => ['sequence' => '"authors"."authors_id_seq"']], ['name' => 'name', 'primary' => false, 'vendor' => []], ]); - $this->driver->shouldReceive('getColumns')->with('books.books')->once()->andReturn([ + $this->engine->shouldReceive('getColumns')->with('books.books')->once()->andReturn([ ['name' => 'id', 'primary' => true, 'vendor' => ['sequence' => '"books"."books_id_seq"']], ['name' => 'title', 'primary' => false, 'vendor' => []], ]); - $this->connection->shouldReceive('getDriver')->times(2)->andReturn($this->driver); - $this->driver->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]); - $this->driver->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([ + $this->connection->shouldReceive('getDatabaseEngine')->times(2)->andReturn($this->engine); + $this->engine->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]); + $this->engine->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([ ['local' => 'author_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk1'], ['local' => 'translator_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk2'], ]); diff --git a/tests/Database/connection.disconnect.phpt b/tests/Database/connection.disconnect.phpt index 8b0659454..52d836ea0 100644 --- a/tests/Database/connection.disconnect.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -29,19 +29,19 @@ test('connect & disconnect', function () { // first connection $pdo = $connection->getPdo(); - $driver = $connection->getDriver(); + $driver = $connection->getDatabaseEngine(); Assert::same(1, $connections); // still first connection $connection->connect(); Assert::same($pdo, $connection->getPdo()); - Assert::same($driver, $connection->getDriver()); + Assert::same($driver, $connection->getDatabaseEngine()); Assert::same(1, $connections); // second connection $connection->reconnect(); $pdo2 = $connection->getPdo(); - $driver2 = $connection->getDriver(); + $driver2 = $connection->getDatabaseEngine(); Assert::notSame($pdo, $pdo2); Assert::notSame($driver, $driver2); @@ -50,6 +50,6 @@ test('connect & disconnect', function () { // third connection $connection->disconnect(); Assert::notSame($pdo2, $connection->getPdo()); - Assert::notSame($driver2, $connection->getDriver()); + Assert::notSame($driver2, $connection->getDatabaseEngine()); Assert::same(3, $connections); }); diff --git a/tests/Database/connection.options.sqlite.phpt b/tests/Database/connection.options.sqlite.phpt index 63b8536d4..887e1caee 100644 --- a/tests/Database/connection.options.sqlite.phpt +++ b/tests/Database/connection.options.sqlite.phpt @@ -14,12 +14,12 @@ require __DIR__ . '/../bootstrap.php'; test('formatDateTime', function () { $connection = connectToDB(['formatDateTime' => 'U'])->getConnection(); - $driver = $connection->getDriver(); - Assert::same('254358000', $driver->formatDateTime(new DateTime('1978-01-23 00:00:00'))); + $engine = $connection->getDatabaseEngine(); + Assert::same('254358000', $engine->formatDateTime(new DateTime('1978-01-23 00:00:00'))); }); test('formatDateTime', function () { $connection = connectToDB(['formatDateTime' => 'Y-m-d'])->getConnection(); - $driver = $connection->getDriver(); - Assert::same('1978-01-23', $driver->formatDateTime(new DateTime('1978-01-23 00:00:00'))); + $engine = $connection->getDatabaseEngine(); + Assert::same('1978-01-23', $engine->formatDateTime(new DateTime('1978-01-23 00:00:00'))); }); From f3e3cd98dfd3cfdfbd48657d1de39146c5a4190d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 23:26:28 +0200 Subject: [PATCH 20/53] renamed drivers to *Engine --- src/Database/Connection.php | 13 ++++++- .../MSSQLEngine.php} | 7 ++-- .../MySQLEngine.php} | 7 ++-- .../ODBCEngine.php} | 7 ++-- .../OracleEngine.php} | 7 ++-- .../PostgreSQLEngine.php} | 7 ++-- .../SQLServerEngine.php} | 7 ++-- .../SQLiteEngine.php} | 7 ++-- tests/Database.Tracy/panel.html | 2 +- .../MSSQLEngine.applyLimit.phpt} | 37 +++++++++---------- .../MySQLEngine.applyLimit.phpt} | 25 +++++-------- .../ODBCEngine.applyLimit.phpt} | 37 +++++++++---------- .../OracleEngine.applyLimit.phpt} | 21 +++++------ .../PostgreSQLEngine.applyLimit.phpt} | 21 +++++------ .../SQLServerEngine.applyLimit.phpt} | 23 +++++------- .../SQLiteEngine.applyLimit.phpt} | 21 +++++------ 16 files changed, 127 insertions(+), 122 deletions(-) rename src/Database/Drivers/{MsSqlDriver.php => Engines/MSSQLEngine.php} (97%) rename src/Database/Drivers/{MySqlDriver.php => Engines/MySQLEngine.php} (97%) rename src/Database/Drivers/{OdbcDriver.php => Engines/ODBCEngine.php} (93%) rename src/Database/Drivers/{OciDriver.php => Engines/OracleEngine.php} (95%) rename src/Database/Drivers/{PgSqlDriver.php => Engines/PostgreSQLEngine.php} (97%) rename src/Database/Drivers/{SqlsrvDriver.php => Engines/SQLServerEngine.php} (97%) rename src/Database/Drivers/{SqliteDriver.php => Engines/SQLiteEngine.php} (97%) rename tests/Database/{Drivers/OdbcDriver.applyLimit.phpt => Engines/MSSQLEngine.applyLimit.phpt} (61%) rename tests/Database/{Drivers/MySqlDriver.applyLimit.phpt => Engines/MySQLEngine.applyLimit.phpt} (60%) rename tests/Database/{Drivers/MsSqlDriver.applyLimit.phpt => Engines/ODBCEngine.applyLimit.phpt} (61%) rename tests/Database/{Drivers/OciDriver.applyLimit.phpt => Engines/OracleEngine.applyLimit.phpt} (70%) rename tests/Database/{Drivers/PgSqlDriver.applyLimit.phpt => Engines/PostgreSQLEngine.applyLimit.phpt} (62%) rename tests/Database/{Drivers/SqlsrvDriver.applyLimit.phpt => Engines/SQLServerEngine.applyLimit.phpt} (66%) rename tests/Database/{Drivers/SqliteDriver.applyLimit.phpt => Engines/SQLiteEngine.applyLimit.phpt} (63%) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index a1e207ea3..8930da554 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -21,6 +21,16 @@ */ class Connection { + private const Drivers = [ + 'pdo-mssql' => Drivers\Engines\MSSQLEngine::class, + 'pdo-mysql' => Drivers\Engines\MySQLEngine::class, + 'pdo-oci' => Drivers\Engines\OracleEngine::class, + 'pdo-odbc' => Drivers\Engines\ODBCEngine::class, + 'pdo-pgsql' => Drivers\Engines\PostgreSQLEngine::class, + 'pdo-sqlite' => Drivers\Engines\SQLiteEngine::class, + 'pdo-sqlsrv' => Drivers\Engines\SQLServerEngine::class, + ]; + /** @var array Occurs after connection is established */ public array $onConnect = []; @@ -62,8 +72,9 @@ public function connect(): void throw ConnectionException::from($e); } + $driver = explode(':', $this->dsn)[0]; $class = empty($this->options['driverClass']) - ? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver' + ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) : $this->options['driverClass']; $this->engine = new $class; $this->preprocessor = new SqlPreprocessor($this); diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/Engines/MSSQLEngine.php similarity index 97% rename from src/Database/Drivers/MsSqlDriver.php rename to src/Database/Drivers/Engines/MSSQLEngine.php index 14fe2104d..9ee4212bd 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental MS SQL database driver. + * MS SQL database platform. */ -class MsSqlDriver implements Engine +class MSSQLEngine implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/Engines/MySQLEngine.php similarity index 97% rename from src/Database/Drivers/MySqlDriver.php rename to src/Database/Drivers/Engines/MySQLEngine.php index adaedb1cc..22513ed4a 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental MySQL database driver. + * MySQL-like database platform. */ -class MySqlDriver implements Engine +class MySQLEngine implements Engine { private Nette\Database\Connection $connection; private bool $convertBoolean; diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/Engines/ODBCEngine.php similarity index 93% rename from src/Database/Drivers/OdbcDriver.php rename to src/Database/Drivers/Engines/ODBCEngine.php index ee6e42fa2..91aa61f31 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental ODBC database driver. + * Microsoft ODBC database platform. */ -class OdbcDriver implements Engine +class ODBCEngine implements Engine { public function initialize(Nette\Database\Connection $connection, array $options): void { diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/Engines/OracleEngine.php similarity index 95% rename from src/Database/Drivers/OciDriver.php rename to src/Database/Drivers/Engines/OracleEngine.php index 6c4ff3754..dfcaba0ba 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental Oracle database driver. + * Oracle database platform. */ -class OciDriver implements Engine +class OracleEngine implements Engine { private Nette\Database\Connection $connection; private string $fmtDateTime; diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php similarity index 97% rename from src/Database/Drivers/PgSqlDriver.php rename to src/Database/Drivers/Engines/PostgreSQLEngine.php index 2753b3ae7..0605d4101 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental PostgreSQL database driver. + * PostgreSQL database platform. */ -class PgSqlDriver implements Engine +class PostgreSQLEngine implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/Engines/SQLServerEngine.php similarity index 97% rename from src/Database/Drivers/SqlsrvDriver.php rename to src/Database/Drivers/Engines/SQLServerEngine.php index 9187ca392..ec3fe52ff 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental SQL Server 2005 and later database driver. + * Microsoft SQL Server database platform. */ -class SqlsrvDriver implements Engine +class SQLServerEngine implements Engine { private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/Engines/SQLiteEngine.php similarity index 97% rename from src/Database/Drivers/SqliteDriver.php rename to src/Database/Drivers/Engines/SQLiteEngine.php index c5d33b0dd..311964537 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Nette\Database\Drivers; +namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Engine; /** - * Supplemental SQLite3 database driver. + * SQLite database platform. */ -class SqliteDriver implements Engine +class SQLiteEngine implements Engine { private Nette\Database\Connection $connection; private string $fmtDateTime; diff --git a/tests/Database.Tracy/panel.html b/tests/Database.Tracy/panel.html index 1ec032563..b6274a51a 100644 --- a/tests/Database.Tracy/panel.html +++ b/tests/Database.Tracy/panel.html @@ -28,7 +28,7 @@

Queries: 4, time: %a% ms, foo

ERROR
+ %a%SQLiteEngine.php:%d%
" . htmlspecialchars($result->getQueryString(), ENT_IGNORE, 'UTF-8') . "
SELECT
- %a%SqliteDriver.php:%d%
diff --git a/tests/Database/Drivers/OdbcDriver.applyLimit.phpt b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt similarity index 61% rename from tests/Database/Drivers/OdbcDriver.applyLimit.phpt rename to tests/Database/Engines/MSSQLEngine.applyLimit.phpt index b40e3f03e..6018c7d1f 100644 --- a/tests/Database/Drivers/OdbcDriver.applyLimit.phpt +++ b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt @@ -6,55 +6,54 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; +$engine = new Nette\Database\Drivers\Engines\MSSQLEngine; -$driver = new Nette\Database\Drivers\OdbcDriver; - -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, 10, 20); + $engine->applyLimit($query, 10, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, 0, 20); + $engine->applyLimit($query, 0, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT TOP 10 1 FROM t', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, 20); + $engine->applyLimit($query, null, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT TOP 10 1 FROM t', $query); $query = ' select distinct 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same(' select distinct TOP 10 1 FROM t', $query); $query = 'UPDATE t SET'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('UPDATE TOP 10 t SET', $query); $query = 'DELETE FROM t SET'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('DELETE TOP 10 FROM t SET', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SET FROM t'; - $driver->applyLimit($query, 10, null); + $engine->applyLimit($query, 10, null); }, Nette\InvalidArgumentException::class, 'SQL query must begin with SELECT, UPDATE or DELETE command.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); diff --git a/tests/Database/Drivers/MySqlDriver.applyLimit.phpt b/tests/Database/Engines/MySQLEngine.applyLimit.phpt similarity index 60% rename from tests/Database/Drivers/MySqlDriver.applyLimit.phpt rename to tests/Database/Engines/MySQLEngine.applyLimit.phpt index 8434fe2f5..c2df8d321 100644 --- a/tests/Database/Drivers/MySqlDriver.applyLimit.phpt +++ b/tests/Database/Engines/MySQLEngine.applyLimit.phpt @@ -1,44 +1,39 @@ getConnection(); -$driver = $connection->getDriver(); +$engine = new Nette\Database\Drivers\Engines\MySQLEngine; $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 20); +$engine->applyLimit($query, 10, 20); Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 0, 20); +$engine->applyLimit($query, 0, 20); Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT 1 FROM t LIMIT 10', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, null, 20); +$engine->applyLimit($query, null, 20); Assert::same('SELECT 1 FROM t LIMIT 18446744073709551615 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT 1 FROM t LIMIT 10', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); diff --git a/tests/Database/Drivers/MsSqlDriver.applyLimit.phpt b/tests/Database/Engines/ODBCEngine.applyLimit.phpt similarity index 61% rename from tests/Database/Drivers/MsSqlDriver.applyLimit.phpt rename to tests/Database/Engines/ODBCEngine.applyLimit.phpt index 636142214..4fb8ba1d1 100644 --- a/tests/Database/Drivers/MsSqlDriver.applyLimit.phpt +++ b/tests/Database/Engines/ODBCEngine.applyLimit.phpt @@ -6,55 +6,54 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; +$engine = new Nette\Database\Drivers\Engines\ODBCEngine; -$driver = new Nette\Database\Drivers\MsSqlDriver; - -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, 10, 20); + $engine->applyLimit($query, 10, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, 0, 20); + $engine->applyLimit($query, 0, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT TOP 10 1 FROM t', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, 20); + $engine->applyLimit($query, null, 20); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT TOP 10 1 FROM t', $query); $query = ' select distinct 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same(' select distinct TOP 10 1 FROM t', $query); $query = 'UPDATE t SET'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('UPDATE TOP 10 t SET', $query); $query = 'DELETE FROM t SET'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('DELETE TOP 10 FROM t SET', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SET FROM t'; - $driver->applyLimit($query, 10, null); + $engine->applyLimit($query, 10, null); }, Nette\InvalidArgumentException::class, 'SQL query must begin with SELECT, UPDATE or DELETE command.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); diff --git a/tests/Database/Drivers/OciDriver.applyLimit.phpt b/tests/Database/Engines/OracleEngine.applyLimit.phpt similarity index 70% rename from tests/Database/Drivers/OciDriver.applyLimit.phpt rename to tests/Database/Engines/OracleEngine.applyLimit.phpt index c446108f5..829210b2e 100644 --- a/tests/Database/Drivers/OciDriver.applyLimit.phpt +++ b/tests/Database/Engines/OracleEngine.applyLimit.phpt @@ -6,35 +6,34 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; - -$driver = new Nette\Database\Drivers\OciDriver; +$engine = new Nette\Database\Drivers\Engines\OracleEngine; $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 20); +$engine->applyLimit($query, 10, 20); Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 30) WHERE "__rnum" > 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 0, 20); +$engine->applyLimit($query, 0, 20); Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 20) WHERE "__rnum" > 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, null, 20); +$engine->applyLimit($query, null, 20); Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t ) WHERE "__rnum" > 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); diff --git a/tests/Database/Drivers/PgSqlDriver.applyLimit.phpt b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt similarity index 62% rename from tests/Database/Drivers/PgSqlDriver.applyLimit.phpt rename to tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt index 6516e93d6..0b87c74bd 100644 --- a/tests/Database/Drivers/PgSqlDriver.applyLimit.phpt +++ b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt @@ -6,35 +6,34 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; - -$driver = new Nette\Database\Drivers\PgSqlDriver; +$engine = new Nette\Database\Drivers\Engines\PostgreSQLEngine; $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 20); +$engine->applyLimit($query, 10, 20); Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 0, 20); +$engine->applyLimit($query, 0, 20); Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT 1 FROM t LIMIT 10', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, null, 20); +$engine->applyLimit($query, null, 20); Assert::same('SELECT 1 FROM t OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT 1 FROM t LIMIT 10', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); diff --git a/tests/Database/Drivers/SqlsrvDriver.applyLimit.phpt b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt similarity index 66% rename from tests/Database/Drivers/SqlsrvDriver.applyLimit.phpt rename to tests/Database/Engines/SQLServerEngine.applyLimit.phpt index 3a06219af..830be4521 100644 --- a/tests/Database/Drivers/SqlsrvDriver.applyLimit.phpt +++ b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt @@ -6,37 +6,34 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; - -$driver = new Nette\Database\Drivers\SqlsrvDriver; +$engine = new Nette\Database\Drivers\Engines\SQLServerEngine; $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 20); +$engine->applyLimit($query, 10, 20); Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 0, 20); +$engine->applyLimit($query, 0, 20); Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, null, 20); +$engine->applyLimit($query, null, 20); Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $query); - - -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); diff --git a/tests/Database/Drivers/SqliteDriver.applyLimit.phpt b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt similarity index 63% rename from tests/Database/Drivers/SqliteDriver.applyLimit.phpt rename to tests/Database/Engines/SQLiteEngine.applyLimit.phpt index 662cb5d76..b6b72dac9 100644 --- a/tests/Database/Drivers/SqliteDriver.applyLimit.phpt +++ b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt @@ -6,35 +6,34 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; - -$driver = new Nette\Database\Drivers\SqliteDriver; +$engine = new Nette\Database\Drivers\Engines\SQLiteEngine; $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 20); +$engine->applyLimit($query, 10, 20); Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 0, 20); +$engine->applyLimit($query, 0, 20); Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, 0); +$engine->applyLimit($query, 10, 0); Assert::same('SELECT 1 FROM t LIMIT 10', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, null, 20); +$engine->applyLimit($query, null, 20); Assert::same('SELECT 1 FROM t LIMIT -1 OFFSET 20', $query); $query = 'SELECT 1 FROM t'; -$driver->applyLimit($query, 10, null); +$engine->applyLimit($query, 10, null); Assert::same('SELECT 1 FROM t LIMIT 10', $query); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, -1, null); + $engine->applyLimit($query, -1, null); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); -Assert::exception(function () use ($driver) { +Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; - $driver->applyLimit($query, null, -1); + $engine->applyLimit($query, null, -1); }, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); From 362e62cba8918c4eb6d16f315588fb5b62e8cc91 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 16 Aug 2024 04:44:00 +0200 Subject: [PATCH 21/53] added new drivers --- src/Database/Connection.php | 18 +++++++++------- src/Database/Drivers/Driver.php | 20 +++++++++++++++++ src/Database/Drivers/PDO/Driver.php | 20 +++++++++++++++++ src/Database/Drivers/PDO/MSSQL/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/MySQL/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/OCI/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/ODBC/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/PgSQL/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/SQLSrv/Driver.php | 25 ++++++++++++++++++++++ src/Database/Drivers/PDO/SQLite/Driver.php | 25 ++++++++++++++++++++++ 10 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 src/Database/Drivers/Driver.php create mode 100644 src/Database/Drivers/PDO/Driver.php create mode 100644 src/Database/Drivers/PDO/MSSQL/Driver.php create mode 100644 src/Database/Drivers/PDO/MySQL/Driver.php create mode 100644 src/Database/Drivers/PDO/OCI/Driver.php create mode 100644 src/Database/Drivers/PDO/ODBC/Driver.php create mode 100644 src/Database/Drivers/PDO/PgSQL/Driver.php create mode 100644 src/Database/Drivers/PDO/SQLSrv/Driver.php create mode 100644 src/Database/Drivers/PDO/SQLite/Driver.php diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 8930da554..c03db1f33 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -22,13 +22,13 @@ class Connection { private const Drivers = [ - 'pdo-mssql' => Drivers\Engines\MSSQLEngine::class, - 'pdo-mysql' => Drivers\Engines\MySQLEngine::class, - 'pdo-oci' => Drivers\Engines\OracleEngine::class, - 'pdo-odbc' => Drivers\Engines\ODBCEngine::class, - 'pdo-pgsql' => Drivers\Engines\PostgreSQLEngine::class, - 'pdo-sqlite' => Drivers\Engines\SQLiteEngine::class, - 'pdo-sqlsrv' => Drivers\Engines\SQLServerEngine::class, + 'pdo-mssql' => Drivers\PDO\MSSQL\Driver::class, + 'pdo-mysql' => Drivers\PDO\MySQL\Driver::class, + 'pdo-oci' => Drivers\PDO\OCI\Driver::class, + 'pdo-odbc' => Drivers\PDO\ODBC\Driver::class, + 'pdo-pgsql' => Drivers\PDO\PgSQL\Driver::class, + 'pdo-sqlite' => Drivers\PDO\SQLite\Driver::class, + 'pdo-sqlsrv' => Drivers\PDO\SQLSrv\Driver::class, ]; /** @var array Occurs after connection is established */ @@ -36,6 +36,7 @@ class Connection /** @var array Occurs after query is executed */ public array $onQuery = []; + private Drivers\Driver $driver; private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; private ?PDO $pdo = null; @@ -76,7 +77,8 @@ public function connect(): void $class = empty($this->options['driverClass']) ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) : $this->options['driverClass']; - $this->engine = new $class; + $this->driver = new $class; + $this->engine = $this->driver->createEngine(); $this->preprocessor = new SqlPreprocessor($this); $this->engine->initialize($this, $this->options); Arrays::invoke($this->onConnect, $this); diff --git a/src/Database/Drivers/Driver.php b/src/Database/Drivers/Driver.php new file mode 100644 index 000000000..212a21ca1 --- /dev/null +++ b/src/Database/Drivers/Driver.php @@ -0,0 +1,20 @@ + Date: Wed, 4 Sep 2024 01:52:01 +0200 Subject: [PATCH 22/53] Connection: the driver is created in the constructor --- src/Database/Connection.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index c03db1f33..b25f8c1b6 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -58,6 +58,12 @@ public function __construct( if (($options['newDateTime'] ?? null) === false) { $this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class); } + + $driver = explode(':', $dsn)[0]; + $class = empty($options['driverClass']) + ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) + : $options['driverClass']; + $this->driver = new $class; } @@ -73,13 +79,7 @@ public function connect(): void throw ConnectionException::from($e); } - $driver = explode(':', $this->dsn)[0]; - $class = empty($this->options['driverClass']) - ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) - : $this->options['driverClass']; - $this->driver = new $class; $this->engine = $this->driver->createEngine(); - $this->preprocessor = new SqlPreprocessor($this); $this->engine->initialize($this, $this->options); Arrays::invoke($this->onConnect, $this); } @@ -252,6 +252,7 @@ public function queryArgs(string $sql, array $params): Result public function preprocess(string $sql, ...$params): array { $this->connect(); + $this->preprocessor ??= new SqlPreprocessor($this); return $params ? $this->preprocessor->process(func_get_args()) : [$sql, []]; From e511b4456cceaa6aaf94e5245dc87c8aa85c8d5b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:40:11 +0200 Subject: [PATCH 23/53] connections & engines are created by Driver --- src/Database/Connection.php | 29 ++++++++------- src/Database/Drivers/Driver.php | 5 ++- src/Database/Drivers/Engine.php | 5 --- src/Database/Drivers/Engines/MSSQLEngine.php | 9 ++--- src/Database/Drivers/Engines/MySQLEngine.php | 25 +++---------- src/Database/Drivers/Engines/ODBCEngine.php | 5 --- src/Database/Drivers/Engines/OracleEngine.php | 12 +++---- .../Drivers/Engines/PostgreSQLEngine.php | 9 ++--- .../Drivers/Engines/SQLServerEngine.php | 9 ++--- src/Database/Drivers/Engines/SQLiteEngine.php | 14 ++++---- src/Database/Drivers/PDO/Driver.php | 14 ++++++++ src/Database/Drivers/PDO/MSSQL/Driver.php | 4 +-- src/Database/Drivers/PDO/MySQL/Driver.php | 35 +++++++++++++++++-- src/Database/Drivers/PDO/OCI/Driver.php | 19 ++++++++-- src/Database/Drivers/PDO/ODBC/Driver.php | 2 +- src/Database/Drivers/PDO/PgSQL/Driver.php | 4 +-- src/Database/Drivers/PDO/SQLSrv/Driver.php | 4 +-- src/Database/Drivers/PDO/SQLite/Driver.php | 19 ++++++++-- .../Engines/MSSQLEngine.applyLimit.phpt | 3 +- .../Engines/MySQLEngine.applyLimit.phpt | 3 +- .../Engines/ODBCEngine.applyLimit.phpt | 3 +- .../Engines/OracleEngine.applyLimit.phpt | 3 +- .../Engines/PostgreSQLEngine.applyLimit.phpt | 3 +- .../Engines/SQLServerEngine.applyLimit.phpt | 3 +- .../Engines/SQLiteEngine.applyLimit.phpt | 3 +- tests/Database/connection.disconnect.phpt | 4 +-- 26 files changed, 148 insertions(+), 100 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index b25f8c1b6..8abecfa56 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -49,11 +49,10 @@ class Connection public function __construct( private readonly string $dsn, + ?string $username = null, #[\SensitiveParameter] - private readonly ?string $user = null, - #[\SensitiveParameter] - private readonly ?string $password = null, - private readonly array $options = [], + ?string $password = null, + array $options = [], ) { if (($options['newDateTime'] ?? null) === false) { $this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class); @@ -63,7 +62,15 @@ public function __construct( $class = empty($options['driverClass']) ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) : $options['driverClass']; - $this->driver = new $class; + $args = compact('dsn', 'username', 'password', 'options'); + unset($options['lazy'], $options['newDateTime'], $options['driverClass']); + foreach ($options as $key => $value) { + if (!is_int($key) && $value !== null) { + $args[$key] = $value; + unset($args['options'][$key]); + } + } + $this->driver = new $class(...$args); } @@ -74,13 +81,11 @@ public function connect(): void } try { - $this->pdo = new PDO($this->dsn, $this->user, $this->password, $this->options); + $this->pdo = $this->driver->connect(); } catch (PDOException $e) { throw ConnectionException::from($e); } - $this->engine = $this->driver->createEngine(); - $this->engine->initialize($this, $this->options); Arrays::invoke($this->onConnect, $this); } @@ -115,15 +120,13 @@ public function getPdo(): PDO public function getSupplementalDriver(): Drivers\Engine { trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED); - $this->connect(); - return $this->engine; + return $this->getDatabaseEngine(); } public function getDatabaseEngine(): Drivers\Engine { - $this->connect(); - return $this->engine; + return $this->engine ??= $this->driver->createEngine($this); } @@ -146,7 +149,7 @@ public function getInsertId(?string $sequence = null): string $res = $this->getPdo()->lastInsertId($sequence); return $res === false ? '0' : $res; } catch (PDOException $e) { - throw $this->engine->convertException($e); + throw $this->getDatabaseEngine()->convertException($e); } } diff --git a/src/Database/Drivers/Driver.php b/src/Database/Drivers/Driver.php index 212a21ca1..428c998ab 100644 --- a/src/Database/Drivers/Driver.php +++ b/src/Database/Drivers/Driver.php @@ -15,6 +15,9 @@ */ interface Driver { + /** Establishes a connection to the database. */ + function connect(); + /** Creates a engine instance for the specific database platform. */ - function createEngine(): Engine; + function createEngine($connection): Engine; } diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index 1b8645bf7..41088cac6 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -31,11 +31,6 @@ interface Engine */ function isSupported(string $feature): bool; - /** - * Initializes connection. - */ - function initialize(Database\Connection $connection, array $options): void; - /** * Converts PDOException to DriverException or its descendant. */ diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index 9ee4212bd..729eddd6e 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -18,12 +18,9 @@ */ class MSSQLEngine implements Engine { - private Nette\Database\Connection $connection; - - - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index 22513ed4a..660af9113 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -18,29 +18,12 @@ */ class MySQLEngine implements Engine { - private Nette\Database\Connection $connection; - private bool $convertBoolean; + public bool $convertBoolean = true; - /** - * Driver options: - * - charset => character encoding to set (default is utf8mb4) - * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html - * - convertBoolean => converts INT(1) to boolean - */ - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; - $charset = $options['charset'] ?? 'utf8mb4'; - if ($charset) { - $connection->query('SET NAMES ?', $charset); - } - - if (isset($options['sqlmode'])) { - $connection->query('SET sql_mode=?', $options['sqlmode']); - } - - $this->convertBoolean = (bool) ($options['convertBoolean'] ?? true); + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } diff --git a/src/Database/Drivers/Engines/ODBCEngine.php b/src/Database/Drivers/Engines/ODBCEngine.php index 91aa61f31..55a22562c 100644 --- a/src/Database/Drivers/Engines/ODBCEngine.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -18,11 +18,6 @@ */ class ODBCEngine implements Engine { - public function initialize(Nette\Database\Connection $connection, array $options): void - { - } - - public function isSupported(string $feature): bool { return $feature === self::SupportSubselect; diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index dfcaba0ba..b122a2e5e 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -18,14 +18,12 @@ */ class OracleEngine implements Engine { - private Nette\Database\Connection $connection; - private string $fmtDateTime; + public string $formatDateTime = 'U'; - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; - $this->fmtDateTime = $options['formatDateTime'] ?? 'U'; + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } @@ -65,7 +63,7 @@ public function delimite(string $name): string public function formatDateTime(\DateTimeInterface $value): string { - return $value->format($this->fmtDateTime); + return $value->format($this->formatDateTime); } diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 0605d4101..797a51e10 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -18,12 +18,9 @@ */ class PostgreSQLEngine implements Engine { - private Nette\Database\Connection $connection; - - - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index ec3fe52ff..b1a5be504 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -18,12 +18,9 @@ */ class SQLServerEngine implements Engine { - private Nette\Database\Connection $connection; - - - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 311964537..d26d41f8a 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -18,14 +18,12 @@ */ class SQLiteEngine implements Engine { - private Nette\Database\Connection $connection; - private string $fmtDateTime; + public string $formatDateTime = 'U'; - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; - $this->fmtDateTime = $options['formatDateTime'] ?? 'U'; + public function __construct( + private readonly Nette\Database\Connection $connection, + ) { } @@ -78,7 +76,7 @@ public function delimite(string $name): string public function formatDateTime(\DateTimeInterface $value): string { - return $value->format($this->fmtDateTime); + return $value->format($this->formatDateTime); } @@ -234,7 +232,7 @@ public function getColumnTypes(\PDOStatement $statement): array for ($col = 0; $col < $count; $col++) { $meta = $statement->getColumnMeta($col); if (isset($meta['sqlite:decl_type'])) { - $types[$meta['name']] = $this->fmtDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true) + $types[$meta['name']] = $this->formatDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true) ? Nette\Database\IStructure::FIELD_UNIX_TIMESTAMP : Nette\Database\Helpers::detectType($meta['sqlite:decl_type']); } elseif (isset($meta['native_type'])) { diff --git a/src/Database/Drivers/PDO/Driver.php b/src/Database/Drivers/PDO/Driver.php index feb46e196..a63d95406 100644 --- a/src/Database/Drivers/PDO/Driver.php +++ b/src/Database/Drivers/PDO/Driver.php @@ -17,4 +17,18 @@ */ abstract class Driver implements Drivers\Driver { + public function __construct( + protected readonly string $dsn, + protected readonly ?string $username = null, + #[\SensitiveParameter] + protected readonly ?string $password = null, + protected readonly array $options = [], + ) { + } + + + public function connect() + { + return new \PDO($this->dsn, $this->username, $this->password, $this->options); + } } diff --git a/src/Database/Drivers/PDO/MSSQL/Driver.php b/src/Database/Drivers/PDO/MSSQL/Driver.php index 4e88c9a6b..3bac0a1d3 100644 --- a/src/Database/Drivers/PDO/MSSQL/Driver.php +++ b/src/Database/Drivers/PDO/MSSQL/Driver.php @@ -18,8 +18,8 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): MSSQLEngine + public function createEngine($connection): MSSQLEngine { - return new MSSQLEngine; + return new MSSQLEngine($connection); } } diff --git a/src/Database/Drivers/PDO/MySQL/Driver.php b/src/Database/Drivers/PDO/MySQL/Driver.php index 1cb590b1f..cbf7bf48c 100644 --- a/src/Database/Drivers/PDO/MySQL/Driver.php +++ b/src/Database/Drivers/PDO/MySQL/Driver.php @@ -18,8 +18,39 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): MySQLEngine + private const DefaultCharset = 'utf8mb4'; + + + public function __construct( + protected readonly string $dsn, + protected readonly ?string $username = null, + #[\SensitiveParameter] + protected readonly ?string $password = null, + protected readonly array $options = [], + protected readonly ?string $charset = self::DefaultCharset, + protected readonly ?string $sqlmode = null, + protected readonly ?bool $convertBoolean = null, + ) { + } + + + public function connect() + { + $connection = parent::connect(); + if ($this->charset) { + $connection->query('SET NAMES ' . $connection->quote($this->charset)); + } + if ($this->sqlmode) { + $connection->query('SET sql_mode=' . $connection->quote($this->sqlmode)); + } + return $connection; + } + + + public function createEngine($connection): MySQLEngine { - return new MySQLEngine; + $engine = new MySQLEngine($connection); + $engine->convertBoolean = $this->convertBoolean ?? $engine->convertBoolean; + return $engine; } } diff --git a/src/Database/Drivers/PDO/OCI/Driver.php b/src/Database/Drivers/PDO/OCI/Driver.php index 131c82cb1..54e9763e5 100644 --- a/src/Database/Drivers/PDO/OCI/Driver.php +++ b/src/Database/Drivers/PDO/OCI/Driver.php @@ -18,8 +18,23 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): OracleEngine + public function __construct( + protected readonly string $dsn, + protected readonly ?string $username = null, + #[\SensitiveParameter] + protected readonly ?string $password = null, + protected readonly array $options = [], + protected readonly ?string $formatDateTime = null, + ) { + } + + + public function createEngine($connection): OracleEngine { - return new OracleEngine; + $engine = new OracleEngine($connection); + if ($this->formatDateTime) { + $engine->formatDateTime = $this->formatDateTime; + } + return $engine; } } diff --git a/src/Database/Drivers/PDO/ODBC/Driver.php b/src/Database/Drivers/PDO/ODBC/Driver.php index 12da119cf..99a1b605c 100644 --- a/src/Database/Drivers/PDO/ODBC/Driver.php +++ b/src/Database/Drivers/PDO/ODBC/Driver.php @@ -18,7 +18,7 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): ODBCEngine + public function createEngine($connection): ODBCEngine { return new ODBCEngine; } diff --git a/src/Database/Drivers/PDO/PgSQL/Driver.php b/src/Database/Drivers/PDO/PgSQL/Driver.php index 80c504011..488d98f00 100644 --- a/src/Database/Drivers/PDO/PgSQL/Driver.php +++ b/src/Database/Drivers/PDO/PgSQL/Driver.php @@ -18,8 +18,8 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): PostgreSQLEngine + public function createEngine($connection): PostgreSQLEngine { - return new PostgreSQLEngine; + return new PostgreSQLEngine($connection); } } diff --git a/src/Database/Drivers/PDO/SQLSrv/Driver.php b/src/Database/Drivers/PDO/SQLSrv/Driver.php index 3c6caad08..3a5417929 100644 --- a/src/Database/Drivers/PDO/SQLSrv/Driver.php +++ b/src/Database/Drivers/PDO/SQLSrv/Driver.php @@ -18,8 +18,8 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): SQLServerEngine + public function createEngine($connection): SQLServerEngine { - return new SQLServerEngine; + return new SQLServerEngine($connection); } } diff --git a/src/Database/Drivers/PDO/SQLite/Driver.php b/src/Database/Drivers/PDO/SQLite/Driver.php index d01898a34..8123fdf2a 100644 --- a/src/Database/Drivers/PDO/SQLite/Driver.php +++ b/src/Database/Drivers/PDO/SQLite/Driver.php @@ -18,8 +18,23 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine(): SQLiteEngine + public function __construct( + protected readonly string $dsn, + protected readonly ?string $username = null, + #[\SensitiveParameter] + protected readonly ?string $password = null, + protected readonly array $options = [], + protected readonly ?string $formatDateTime = null, + ) { + } + + + public function createEngine($connection): SQLiteEngine { - return new SQLiteEngine; + $engine = new SQLiteEngine($connection); + if ($this->formatDateTime) { + $engine->formatDateTime = $this->formatDateTime; + } + return $engine; } } diff --git a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt index 6018c7d1f..3554d3518 100644 --- a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\MSSQLEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\MSSQLEngine($connection); Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/MySQLEngine.applyLimit.phpt b/tests/Database/Engines/MySQLEngine.applyLimit.phpt index c2df8d321..64cf7e0c5 100644 --- a/tests/Database/Engines/MySQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MySQLEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\MySQLEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\MySQLEngine($connection); $query = 'SELECT 1 FROM t'; $engine->applyLimit($query, 10, 20); diff --git a/tests/Database/Engines/ODBCEngine.applyLimit.phpt b/tests/Database/Engines/ODBCEngine.applyLimit.phpt index 4fb8ba1d1..f1482d4a0 100644 --- a/tests/Database/Engines/ODBCEngine.applyLimit.phpt +++ b/tests/Database/Engines/ODBCEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\ODBCEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\ODBCEngine($connection); Assert::exception(function () use ($engine) { $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/OracleEngine.applyLimit.phpt b/tests/Database/Engines/OracleEngine.applyLimit.phpt index 829210b2e..ccb46d5aa 100644 --- a/tests/Database/Engines/OracleEngine.applyLimit.phpt +++ b/tests/Database/Engines/OracleEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\OracleEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\OracleEngine($connection); $query = 'SELECT 1 FROM t'; $engine->applyLimit($query, 10, 20); diff --git a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt index 0b87c74bd..dbc9b8482 100644 --- a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\PostgreSQLEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\PostgreSQLEngine($connection); $query = 'SELECT 1 FROM t'; $engine->applyLimit($query, 10, 20); diff --git a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt index 830be4521..3768231d1 100644 --- a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\SQLServerEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\SQLServerEngine($connection); $query = 'SELECT 1 FROM t'; $engine->applyLimit($query, 10, 20); diff --git a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt index b6b72dac9..404c08ee5 100644 --- a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt @@ -6,7 +6,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$engine = new Nette\Database\Drivers\Engines\SQLiteEngine; +$connection = Mockery::mock(Nette\Database\Connection::class); +$engine = new Nette\Database\Drivers\Engines\SQLiteEngine($connection); $query = 'SELECT 1 FROM t'; $engine->applyLimit($query, 10, 20); diff --git a/tests/Database/connection.disconnect.phpt b/tests/Database/connection.disconnect.phpt index 52d836ea0..77da6ab2d 100644 --- a/tests/Database/connection.disconnect.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -44,12 +44,12 @@ test('connect & disconnect', function () { $driver2 = $connection->getDatabaseEngine(); Assert::notSame($pdo, $pdo2); - Assert::notSame($driver, $driver2); + Assert::same($driver, $driver2); Assert::same(2, $connections); // third connection $connection->disconnect(); Assert::notSame($pdo2, $connection->getPdo()); - Assert::notSame($driver2, $connection->getDatabaseEngine()); + Assert::same($driver2, $connection->getDatabaseEngine()); Assert::same(3, $connections); }); From 9718880aff69930497d9c42cf906cd877f482d86 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:40:52 +0200 Subject: [PATCH 24/53] PDO replaced by Connection --- src/Bridges/DatabaseTracy/ConnectionPanel.php | 3 +- src/Database/Connection.php | 40 +++++----- .../Drivers/Accessory/LazyConnection.php | 75 ++++++++++++++++++ src/Database/Drivers/Connection.php | 39 ++++++++++ src/Database/Drivers/Driver.php | 4 +- src/Database/Drivers/Engines/MSSQLEngine.php | 11 +-- src/Database/Drivers/Engines/MySQLEngine.php | 8 +- src/Database/Drivers/Engines/OracleEngine.php | 4 +- .../Drivers/Engines/PostgreSQLEngine.php | 15 ++-- .../Drivers/Engines/SQLServerEngine.php | 16 ++-- src/Database/Drivers/Engines/SQLiteEngine.php | 7 +- src/Database/Drivers/PDO/Connection.php | 78 +++++++++++++++++++ src/Database/Drivers/PDO/Driver.php | 4 +- src/Database/Drivers/PDO/MSSQL/Driver.php | 2 +- src/Database/Drivers/PDO/MySQL/Driver.php | 4 +- src/Database/Drivers/PDO/OCI/Driver.php | 2 +- src/Database/Drivers/PDO/ODBC/Driver.php | 2 +- src/Database/Drivers/PDO/PgSQL/Driver.php | 2 +- src/Database/Drivers/PDO/SQLSrv/Driver.php | 2 +- src/Database/Drivers/PDO/SQLite/Driver.php | 2 +- src/Database/Helpers.php | 6 +- src/Database/Result.php | 13 +--- src/Database/SqlPreprocessor.php | 4 +- src/Database/Table/Selection.php | 2 +- src/Database/Table/SqlBuilder.php | 2 +- .../Engines/MSSQLEngine.applyLimit.phpt | 2 +- .../Engines/MySQLEngine.applyLimit.phpt | 2 +- .../Engines/ODBCEngine.applyLimit.phpt | 2 +- .../Engines/OracleEngine.applyLimit.phpt | 2 +- .../Engines/PostgreSQLEngine.applyLimit.phpt | 2 +- .../Engines/SQLServerEngine.applyLimit.phpt | 2 +- .../Engines/SQLiteEngine.applyLimit.phpt | 2 +- 32 files changed, 276 insertions(+), 85 deletions(-) create mode 100644 src/Database/Drivers/Accessory/LazyConnection.php create mode 100644 src/Database/Drivers/Connection.php create mode 100644 src/Database/Drivers/PDO/Connection.php diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 8a703a8e5..3c2ffdfdc 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -148,7 +148,8 @@ public function getPanel(): ?string $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN'; - $explain = (new Result($connection, "$cmd $sql", $params))->fetchAll(); + $rows = $connection->getConnection()->query("$cmd $sql", $params); + for ($explain = []; $row = $rows->fetch(); $explain[] = $row); } catch (\PDOException) { } } diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 8abecfa56..01d93a02c 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -12,7 +12,6 @@ use JetBrains\PhpStorm\Language; use Nette\Utils\Arrays; use Nette\Utils\DateTime; -use PDO; use PDOException; @@ -37,9 +36,9 @@ class Connection /** @var array Occurs after query is executed */ public array $onQuery = []; private Drivers\Driver $driver; + private ?Drivers\Connection $connection = null; private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; - private ?PDO $pdo = null; /** @var callable(array, Result): array */ private $rowNormalizer = [Helpers::class, 'normalizeRow']; @@ -76,12 +75,12 @@ public function __construct( public function connect(): void { - if ($this->pdo) { + if ($this->connection) { return; } try { - $this->pdo = $this->driver->connect(); + $this->connection = $this->driver->connect(); } catch (PDOException $e) { throw ConnectionException::from($e); } @@ -99,7 +98,7 @@ public function reconnect(): void public function disconnect(): void { - $this->pdo = null; + $this->connection = null; } @@ -109,24 +108,30 @@ public function getDsn(): string } - public function getPdo(): PDO + public function getPdo(): \PDO + { + return $this->getConnection()->getNativeConnection(); + } + + + public function getConnection(): Drivers\Connection { $this->connect(); - return $this->pdo; + return $this->connection; } - /** @deprecated use getDriver() */ - public function getSupplementalDriver(): Drivers\Engine + /** @deprecated use getConnection() */ + public function getSupplementalDriver(): Drivers\Connection { - trigger_error(__METHOD__ . '() is deprecated, use getDriver()', E_USER_DEPRECATED); - return $this->getDatabaseEngine(); + trigger_error(__METHOD__ . '() is deprecated, use getConnection()', E_USER_DEPRECATED); + return $this->getConnection(); } public function getDatabaseEngine(): Drivers\Engine { - return $this->engine ??= $this->driver->createEngine($this); + return $this->engine ??= $this->driver->createEngine(new Drivers\Accessory\LazyConnection($this->getConnection(...))); } @@ -146,21 +151,16 @@ public function setRowNormalizer(?callable $normalizer): static public function getInsertId(?string $sequence = null): string { try { - $res = $this->getPdo()->lastInsertId($sequence); - return $res === false ? '0' : $res; + return $this->getConnection()->getInsertId($sequence); } catch (PDOException $e) { throw $this->getDatabaseEngine()->convertException($e); } } - public function quote(string $string, int $type = PDO::PARAM_STR): string + public function quote(string $string): string { - try { - return $this->getPdo()->quote($string, $type); - } catch (PDOException $e) { - throw DriverException::from($e); - } + return $this->getConnection()->quote($string); } diff --git a/src/Database/Drivers/Accessory/LazyConnection.php b/src/Database/Drivers/Accessory/LazyConnection.php new file mode 100644 index 000000000..5d4e5eade --- /dev/null +++ b/src/Database/Drivers/Accessory/LazyConnection.php @@ -0,0 +1,75 @@ +callback)(); + } + + + public function query(string $sql, array $params = []) + { + return $this->getConnection()->query($sql, $params); + } + + + public function getNativeConnection(): mixed + { + return $this->getConnection()->getNativeConnection(); + } + + + public function beginTransaction(): void + { + $this->getConnection()->beginTransaction(); + } + + + public function commit(): void + { + $this->getConnection()->commit(); + } + + + public function rollBack(): void + { + $this->getConnection()->rollBack(); + } + + + public function getInsertId(?string $sequence = null): int|string + { + return $this->getConnection()->getInsertId($sequence); + } + + + public function quote(string $string): string + { + return $this->getConnection()->quote($string); + } + + + public function getServerVersion(): string + { + return $this->getConnection()->getServerVersion(); + } +} diff --git a/src/Database/Drivers/Connection.php b/src/Database/Drivers/Connection.php new file mode 100644 index 000000000..1859b059d --- /dev/null +++ b/src/Database/Drivers/Connection.php @@ -0,0 +1,39 @@ +fetch()) { $columns[] = [ @@ -127,7 +128,7 @@ public function getColumns(string $table): array 'default' => $row['COLUMN_DEFAULT'], 'autoIncrement' => $row['DOMAIN_NAME'] === 'COUNTER', 'primary' => $row['COLUMN_NAME'] === 'ID', - 'vendor' => (array) $row, + 'vendor' => $row, ]; } @@ -156,7 +157,7 @@ public function getIndexes(string $table): array t.name = ? ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id - X, $table_name); + X, [$table_name]); while ($row = $rows->fetch()) { $id = $row['name_index']; @@ -197,7 +198,7 @@ public function getForeignKeys(string $table): array ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id WHERE tab1.name = ? - X, $table_name); + X, [$table_name]); $id = 0; while ($row = $rows->fetch()) { diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index 660af9113..2f23cedec 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; @@ -22,7 +23,7 @@ class MySQLEngine implements Engine public function __construct( - private readonly Nette\Database\Connection $connection, + private readonly Connection $connection, ) { } @@ -101,6 +102,7 @@ public function getTables(): array $tables = []; $rows = $this->connection->query('SHOW FULL TABLES'); while ($row = $rows->fetch()) { + $row = array_values($row); $tables[] = [ 'name' => $row[0], 'view' => ($row[1] ?? null) === 'VIEW', @@ -116,7 +118,7 @@ public function getColumns(string $table): array $columns = []; $rows = $this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table)); while ($row = $rows->fetch()) { - $row = array_change_key_case((array) $row, CASE_LOWER); + $row = array_change_key_case($row); $typeInfo = Nette\Database\Helpers::parseColumnType($row['type']); $columns[] = [ 'name' => $row['field'], @@ -161,7 +163,7 @@ public function getForeignKeys(string $table): array WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ? - X, $table); + X, [$table]); $id = 0; while ($row = $rows->fetch()) { diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index b122a2e5e..2fb9a4a05 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; @@ -22,7 +23,7 @@ class OracleEngine implements Engine public function __construct( - private readonly Nette\Database\Connection $connection, + private readonly Connection $connection, ) { } @@ -98,6 +99,7 @@ public function getTables(): array $tables = []; $rows = $this->connection->query('SELECT * FROM cat'); while ($row = $rows->fetch()) { + $row = array_values($row); if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { $tables[] = [ 'name' => $row[0], diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 797a51e10..0beccd26c 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; @@ -19,7 +20,7 @@ class PostgreSQLEngine implements Engine { public function __construct( - private readonly Nette\Database\Connection $connection, + private readonly Connection $connection, ) { } @@ -114,7 +115,7 @@ public function getTables(): array X); while ($row = $rows->fetch()) { - $tables[] = (array) $row; + $tables[] = $row; } return $tables; @@ -159,10 +160,10 @@ public function getColumns(string $table): array AND NOT a.attisdropped ORDER BY a.attnum - X, $this->delimiteFQN($table)); + X, [$this->delimiteFQN($table)]); while ($row = $rows->fetch()) { - $column = (array) $row; + $column = $row; $column['vendor'] = $column; unset($column['sequence']); @@ -190,7 +191,7 @@ public function getIndexes(string $table): array WHERE c1.relkind IN ('r', 'p') AND c1.oid = ?::regclass - X, $this->delimiteFQN($table)); + X, [$this->delimiteFQN($table)]); while ($row = $rows->fetch()) { $id = $row['name']; @@ -225,10 +226,10 @@ public function getForeignKeys(string $table): array co.contype = 'f' AND cl.oid = ?::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) - X, $this->delimiteFQN($table)); + X, [$this->delimiteFQN($table)]); while ($row = $rows->fetch()) { - $keys[] = (array) $row; + $keys[] = $row; } return $keys; } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index b1a5be504..b955bc50b 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; @@ -19,7 +20,7 @@ class SQLServerEngine implements Engine { public function __construct( - private readonly Nette\Database\Connection $connection, + private readonly Connection $connection, ) { } @@ -132,13 +133,12 @@ public function getColumns(string $table): array WHERE o.type IN ('U', 'V') AND o.name = ? - X, $table); + X, [$table]); while ($row = $rows->fetch()) { - $row = (array) $row; $row['vendor'] = $row; - $row['size'] = $row['size'] ?: null; - $row['scale'] = $row['scale'] ?: null; + $row['size'] = $row['size'] ? (int) $row['size'] : null; + $row['scale'] = $row['scale'] ? (int) $row['scale'] : null; $row['nullable'] = (bool) $row['nullable']; $row['autoIncrement'] = (bool) $row['autoIncrement']; $row['primary'] = (bool) $row['primary']; @@ -172,7 +172,7 @@ public function getIndexes(string $table): array ORDER BY i.index_id, ic.index_column_id - X, $table); + X, [$table]); while ($row = $rows->fetch()) { $id = $row['name']; @@ -205,10 +205,10 @@ public function getForeignKeys(string $table): array JOIN sys.columns cf ON fkc.referenced_object_id = cf.object_id AND fkc.referenced_column_id = cf.column_id WHERE tl.name = ? - X, $table); + X, [$table]); while ($row = $rows->fetch()) { - $keys[$row['name']] = (array) $row; + $keys[$row['name']] = $row; } return array_values($keys); diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index d26d41f8a..fed2b7d5e 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; @@ -22,7 +23,7 @@ class SQLiteEngine implements Engine public function __construct( - private readonly Nette\Database\Connection $connection, + private readonly Connection $connection, ) { } @@ -136,7 +137,7 @@ public function getColumns(string $table): array SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = ? - X, $table, $table)->fetch(); + X, [$table, $table])->fetch(); $columns = []; $rows = $this->connection->query("PRAGMA table_info({$this->delimite($table)})"); @@ -154,7 +155,7 @@ public function getColumns(string $table): array 'default' => $row['dflt_value'], 'autoIncrement' => $createSql && preg_match($pattern, $createSql['sql']), 'primary' => $row['pk'] > 0, - 'vendor' => (array) $row, + 'vendor' => $row, ]; } diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php new file mode 100644 index 000000000..cfb2437a3 --- /dev/null +++ b/src/Database/Drivers/PDO/Connection.php @@ -0,0 +1,78 @@ + PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; + + $statement = $this->pdo->prepare($sql); + foreach ($params as $key => $value) { + $statement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[gettype($value)] ?? PDO::PARAM_STR); + } + + $statement->setFetchMode(PDO::FETCH_ASSOC); + $statement->execute(); + return $statement; + } + + + public function beginTransaction(): void + { + $this->pdo->beginTransaction(); + + } + + + public function commit(): void + { + $this->pdo->commit(); + + } + + + public function rollBack(): void + { + $this->pdo->rollBack(); + + } + + + public function getInsertId(?string $sequence = null): string + { + $res = $this->pdo->lastInsertId($sequence); + return $res === false ? '0' : $res; + + } + + + public function quote(string $string): string + { + return $this->pdo->quote($string); + } + + + public function getNativeConnection(): PDO + { + return $this->pdo; + } +} diff --git a/src/Database/Drivers/PDO/Driver.php b/src/Database/Drivers/PDO/Driver.php index a63d95406..0b93232a7 100644 --- a/src/Database/Drivers/PDO/Driver.php +++ b/src/Database/Drivers/PDO/Driver.php @@ -27,8 +27,8 @@ public function __construct( } - public function connect() + public function connect(): Connection { - return new \PDO($this->dsn, $this->username, $this->password, $this->options); + return new Connection(new \PDO($this->dsn, $this->username, $this->password, $this->options)); } } diff --git a/src/Database/Drivers/PDO/MSSQL/Driver.php b/src/Database/Drivers/PDO/MSSQL/Driver.php index 3bac0a1d3..4c3acd205 100644 --- a/src/Database/Drivers/PDO/MSSQL/Driver.php +++ b/src/Database/Drivers/PDO/MSSQL/Driver.php @@ -18,7 +18,7 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine($connection): MSSQLEngine + public function createEngine(Drivers\Connection $connection): MSSQLEngine { return new MSSQLEngine($connection); } diff --git a/src/Database/Drivers/PDO/MySQL/Driver.php b/src/Database/Drivers/PDO/MySQL/Driver.php index cbf7bf48c..c4badabcb 100644 --- a/src/Database/Drivers/PDO/MySQL/Driver.php +++ b/src/Database/Drivers/PDO/MySQL/Driver.php @@ -34,7 +34,7 @@ public function __construct( } - public function connect() + public function connect(): Drivers\PDO\Connection { $connection = parent::connect(); if ($this->charset) { @@ -47,7 +47,7 @@ public function connect() } - public function createEngine($connection): MySQLEngine + public function createEngine(Drivers\Connection $connection): MySQLEngine { $engine = new MySQLEngine($connection); $engine->convertBoolean = $this->convertBoolean ?? $engine->convertBoolean; diff --git a/src/Database/Drivers/PDO/OCI/Driver.php b/src/Database/Drivers/PDO/OCI/Driver.php index 54e9763e5..606f7df6a 100644 --- a/src/Database/Drivers/PDO/OCI/Driver.php +++ b/src/Database/Drivers/PDO/OCI/Driver.php @@ -29,7 +29,7 @@ public function __construct( } - public function createEngine($connection): OracleEngine + public function createEngine(Drivers\Connection $connection): OracleEngine { $engine = new OracleEngine($connection); if ($this->formatDateTime) { diff --git a/src/Database/Drivers/PDO/ODBC/Driver.php b/src/Database/Drivers/PDO/ODBC/Driver.php index 99a1b605c..47371287e 100644 --- a/src/Database/Drivers/PDO/ODBC/Driver.php +++ b/src/Database/Drivers/PDO/ODBC/Driver.php @@ -18,7 +18,7 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine($connection): ODBCEngine + public function createEngine(Drivers\Connection $connection): ODBCEngine { return new ODBCEngine; } diff --git a/src/Database/Drivers/PDO/PgSQL/Driver.php b/src/Database/Drivers/PDO/PgSQL/Driver.php index 488d98f00..23d444a7b 100644 --- a/src/Database/Drivers/PDO/PgSQL/Driver.php +++ b/src/Database/Drivers/PDO/PgSQL/Driver.php @@ -18,7 +18,7 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine($connection): PostgreSQLEngine + public function createEngine(Drivers\Connection $connection): PostgreSQLEngine { return new PostgreSQLEngine($connection); } diff --git a/src/Database/Drivers/PDO/SQLSrv/Driver.php b/src/Database/Drivers/PDO/SQLSrv/Driver.php index 3a5417929..667dec1cf 100644 --- a/src/Database/Drivers/PDO/SQLSrv/Driver.php +++ b/src/Database/Drivers/PDO/SQLSrv/Driver.php @@ -18,7 +18,7 @@ */ class Driver extends Drivers\PDO\Driver { - public function createEngine($connection): SQLServerEngine + public function createEngine(Drivers\Connection $connection): SQLServerEngine { return new SQLServerEngine($connection); } diff --git a/src/Database/Drivers/PDO/SQLite/Driver.php b/src/Database/Drivers/PDO/SQLite/Driver.php index 8123fdf2a..892ba0f19 100644 --- a/src/Database/Drivers/PDO/SQLite/Driver.php +++ b/src/Database/Drivers/PDO/SQLite/Driver.php @@ -29,7 +29,7 @@ public function __construct( } - public function createEngine($connection): SQLiteEngine + public function createEngine(Drivers\Connection $connection): SQLiteEngine { $engine = new SQLiteEngine($connection); if ($this->formatDateTime) { diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 5c17be465..ee51a29bb 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -262,7 +262,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla $count = $size = 0; $delimiter = ';'; $sql = ''; - $pdo = $connection->getPdo(); // native query without logging + $connection = $connection->getConnection(); // native query without logging while (($s = fgets($handle)) !== false) { $size += strlen($s); if (!strncasecmp($s, 'DELIMITER ', 10)) { @@ -270,7 +270,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla } elseif (str_ends_with($ts = rtrim($s), $delimiter)) { $sql .= substr($ts, 0, -strlen($delimiter)); - $pdo->exec($sql); + $connection->query($sql); $sql = ''; $count++; if ($onProgress) { @@ -282,7 +282,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla } if (rtrim($sql) !== '') { - $pdo->exec($sql); + $connection->query($sql); $count++; if ($onProgress) { $onProgress($count, isset($stat['size']) ? 100 : null); diff --git a/src/Database/Result.php b/src/Database/Result.php index f5846033b..8cd0fb310 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -11,7 +11,6 @@ use Nette; use Nette\Utils\Arrays; -use PDO; /** @@ -40,20 +39,12 @@ public function __construct( ) { $time = microtime(true); $this->normalizer = $normalizer; - $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; try { if (str_starts_with($queryString, '::')) { - $connection->getPdo()->{substr($queryString, 2)}(); + $connection->getConnection()->{substr($queryString, 2)}(); } else { - $this->pdoStatement = $connection->getPdo()->prepare($queryString); - foreach ($params as $key => $value) { - $type = gettype($value); - $this->pdoStatement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[$type] ?? PDO::PARAM_STR); - } - - $this->pdoStatement->setFetchMode(PDO::FETCH_ASSOC); - $this->pdoStatement->execute(); + $this->pdoStatement = $connection->getConnection()->query($queryString, $params); } } catch (\PDOException $e) { $e = $connection->getDatabaseEngine()->convertException($e); diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 6f002182f..690b6075f 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -48,7 +48,7 @@ class SqlPreprocessor 'EXPLAIN' => 1, ]; - private readonly Connection $connection; + private readonly Drivers\Connection $connection; private readonly Drivers\Engine $engine; private array $params; private array $remaining; @@ -61,7 +61,7 @@ class SqlPreprocessor public function __construct(Connection $connection) { - $this->connection = $connection; + $this->connection = $connection->getConnection(); $this->engine = $connection->getDatabaseEngine(); } diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 5b3abb056..d0469c723 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -801,7 +801,7 @@ public function insert(iterable $data): ActiveRow|array|int|bool // First check sequence if (!empty($primarySequenceName) && $primaryAutoincrementKey) { - $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getConnection()->getDatabaseEngine()->delimite($primarySequenceName)); + $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getDatabaseEngine()->delimite($primarySequenceName)); // Autoincrement primary without sequence } elseif ($primaryAutoincrementKey) { diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 00beaa59a..eb33bfd28 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -55,7 +55,7 @@ class SqlBuilder public function __construct(string $tableName, Explorer $explorer) { $this->tableName = $tableName; - $this->engine = $explorer->getConnection()->getDatabaseEngine(); + $this->engine = $explorer->getDatabaseEngine(); $this->conventions = $explorer->getConventions(); $this->structure = $explorer->getStructure(); $tableNameParts = explode('.', $tableName); diff --git a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt index 3554d3518..5c1766d31 100644 --- a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\MSSQLEngine($connection); Assert::exception(function () use ($engine) { diff --git a/tests/Database/Engines/MySQLEngine.applyLimit.phpt b/tests/Database/Engines/MySQLEngine.applyLimit.phpt index 64cf7e0c5..d07234d28 100644 --- a/tests/Database/Engines/MySQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MySQLEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\MySQLEngine($connection); $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/ODBCEngine.applyLimit.phpt b/tests/Database/Engines/ODBCEngine.applyLimit.phpt index f1482d4a0..2d3c22902 100644 --- a/tests/Database/Engines/ODBCEngine.applyLimit.phpt +++ b/tests/Database/Engines/ODBCEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\ODBCEngine($connection); Assert::exception(function () use ($engine) { diff --git a/tests/Database/Engines/OracleEngine.applyLimit.phpt b/tests/Database/Engines/OracleEngine.applyLimit.phpt index ccb46d5aa..370f0615f 100644 --- a/tests/Database/Engines/OracleEngine.applyLimit.phpt +++ b/tests/Database/Engines/OracleEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\OracleEngine($connection); $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt index dbc9b8482..59a867319 100644 --- a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\PostgreSQLEngine($connection); $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt index 3768231d1..917bbc325 100644 --- a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\SQLServerEngine($connection); $query = 'SELECT 1 FROM t'; diff --git a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt index 404c08ee5..280298ee4 100644 --- a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt @@ -6,7 +6,7 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; -$connection = Mockery::mock(Nette\Database\Connection::class); +$connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\SQLiteEngine($connection); $query = 'SELECT 1 FROM t'; From b77522622643a753b52e053a44d4e5f806f35d22 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 15 Aug 2024 07:11:18 +0200 Subject: [PATCH 25/53] added Connection::getServerVersion() --- src/Database/Connection.php | 6 ++++++ src/Database/Drivers/Connection.php | 3 +++ src/Database/Drivers/PDO/Connection.php | 6 ++++++ tests/Database/Engine.reflection.phpt | 2 +- tests/Database/Explorer/Explorer.limit.sqlsrv.phpt | 2 +- tests/Database/Explorer/Selection.page().phpt | 2 +- tests/Database/Explorer/bugs/bug1356.phpt | 2 +- tests/Database/Reflection.columns.mysql.phpt | 2 +- tests/Database/Reflection.phpt | 2 +- 9 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 01d93a02c..f7f93e916 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -135,6 +135,12 @@ public function getDatabaseEngine(): Drivers\Engine } + public function getServerVersion(): string + { + return $this->getConnection()->getServerVersion(); + } + + public function getReflection(): Reflection { return new Reflection($this->getDatabaseEngine()); diff --git a/src/Database/Drivers/Connection.php b/src/Database/Drivers/Connection.php index 1859b059d..aec6ca191 100644 --- a/src/Database/Drivers/Connection.php +++ b/src/Database/Drivers/Connection.php @@ -36,4 +36,7 @@ function getInsertId(?string $sequence = null): int|string; /** Quotes a string for use in an SQL statement. */ function quote(string $string): string; + + /** Returns the version of the database server. */ + function getServerVersion(): string; } diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index cfb2437a3..70822dd52 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -71,6 +71,12 @@ public function quote(string $string): string } + public function getServerVersion(): string + { + return $this->pdo->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + public function getNativeConnection(): PDO { return $this->pdo; diff --git a/tests/Database/Engine.reflection.phpt b/tests/Database/Engine.reflection.phpt index ea07bcc63..a5b39538a 100644 --- a/tests/Database/Engine.reflection.phpt +++ b/tests/Database/Engine.reflection.phpt @@ -98,7 +98,7 @@ $expectedColumns = [ switch ($driverName) { case 'mysql': - $version = $connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION); + $version = $connection->getServerVersion(); if (version_compare($version, '8.0', '>=')) { $expectedColumns[0]['size'] = null; } diff --git a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt index e5218ff98..c8ef3a7b2 100644 --- a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt +++ b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt @@ -16,7 +16,7 @@ $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$version2008 = $connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION) < 11; +$version2008 = $connection->getServerVersion() < 11; Assert::same( $version2008 diff --git a/tests/Database/Explorer/Selection.page().phpt b/tests/Database/Explorer/Selection.page().phpt index ea05cad0a..614b84b02 100644 --- a/tests/Database/Explorer/Selection.page().phpt +++ b/tests/Database/Explorer/Selection.page().phpt @@ -14,7 +14,7 @@ require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); $connection = $explorer->getConnection(); -if ($driverName === 'sqlsrv' && $connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION) < 11) { +if ($driverName === 'sqlsrv' && $connection->getServerVersion() < 11) { Tester\Environment::skip('Offset is supported since SQL Server 2012'); } diff --git a/tests/Database/Explorer/bugs/bug1356.phpt b/tests/Database/Explorer/bugs/bug1356.phpt index 479b5ae32..9e70d15cd 100644 --- a/tests/Database/Explorer/bugs/bug1356.phpt +++ b/tests/Database/Explorer/bugs/bug1356.phpt @@ -31,7 +31,7 @@ foreach ($books as $book) { } Assert::same(reformat([ - 'sqlsrv' => $connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION) < 11 + 'sqlsrv' => $connection->getServerVersion() < 11 ? 'SELECT TOP 1 * FROM [book] ORDER BY [book].[id]' : 'SELECT * FROM [book] ORDER BY [book].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY', 'SELECT * FROM [book] ORDER BY [book].[id] LIMIT 1', diff --git a/tests/Database/Reflection.columns.mysql.phpt b/tests/Database/Reflection.columns.mysql.phpt index aedf6a793..9f969eced 100644 --- a/tests/Database/Reflection.columns.mysql.phpt +++ b/tests/Database/Reflection.columns.mysql.phpt @@ -15,7 +15,7 @@ $connection = connectToDB()->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); -$version80 = version_compare($connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0', '>='); +$version80 = version_compare($connection->getServerVersion(), '8.0', '>='); $reflection = $connection->getReflection(); $columns = $reflection->getTable('types')->columns; diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index dc06d824f..d73dc3580 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -118,7 +118,7 @@ $expectedColumns = [ switch ($driverName) { case 'mysql': - $version = $connection->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION); + $version = $connection->getServerVersion(); if (version_compare($version, '8.0', '>=')) { $expectedColumns['id']['size'] = null; } From b11c0bf8ac8f41a2fd16b31f00d996147478ff36 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:33:18 +0200 Subject: [PATCH 26/53] added Connection::execute() allows under PDO SQLsrv driver to load SQL dump containing 'SET IDENTITY_INSERT products ON;' --- .../Drivers/Accessory/LazyConnection.php | 6 ++++++ src/Database/Drivers/Connection.php | 3 +++ src/Database/Drivers/PDO/Connection.php | 6 ++++++ src/Database/Drivers/PDO/MySQL/Driver.php | 4 ++-- src/Database/Helpers.php | 4 ++-- tests/Database/files/sqlsrv-loadFromFile.sql | 13 +++++++++++++ tests/Database/sqlsrv-loadFromFile.phpt | 18 ++++++++++++++++++ 7 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 tests/Database/files/sqlsrv-loadFromFile.sql create mode 100644 tests/Database/sqlsrv-loadFromFile.phpt diff --git a/src/Database/Drivers/Accessory/LazyConnection.php b/src/Database/Drivers/Accessory/LazyConnection.php index 5d4e5eade..d99da9b73 100644 --- a/src/Database/Drivers/Accessory/LazyConnection.php +++ b/src/Database/Drivers/Accessory/LazyConnection.php @@ -32,6 +32,12 @@ public function query(string $sql, array $params = []) } + public function execute(string $sql): int + { + return $this->getConnection()->execute($sql); + } + + public function getNativeConnection(): mixed { return $this->getConnection()->getNativeConnection(); diff --git a/src/Database/Drivers/Connection.php b/src/Database/Drivers/Connection.php index aec6ca191..f180e1137 100644 --- a/src/Database/Drivers/Connection.php +++ b/src/Database/Drivers/Connection.php @@ -19,6 +19,9 @@ interface Connection /** Executes an SQL query with optional parameters and returns a result set. */ function query(string $sql, array $params = []); + /** Executes an SQL command and returns the number of affected rows. */ + function execute(string $sql): int; + /** Returns the underlying database connection object. */ function getNativeConnection(): mixed; diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index 70822dd52..726fd6304 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -36,6 +36,12 @@ public function query(string $sql, array $params = []) } + public function execute(string $sql): int + { + return $this->pdo->exec($sql); + } + + public function beginTransaction(): void { $this->pdo->beginTransaction(); diff --git a/src/Database/Drivers/PDO/MySQL/Driver.php b/src/Database/Drivers/PDO/MySQL/Driver.php index c4badabcb..c9e730572 100644 --- a/src/Database/Drivers/PDO/MySQL/Driver.php +++ b/src/Database/Drivers/PDO/MySQL/Driver.php @@ -38,10 +38,10 @@ public function connect(): Drivers\PDO\Connection { $connection = parent::connect(); if ($this->charset) { - $connection->query('SET NAMES ' . $connection->quote($this->charset)); + $connection->execute('SET NAMES ' . $connection->quote($this->charset)); } if ($this->sqlmode) { - $connection->query('SET sql_mode=' . $connection->quote($this->sqlmode)); + $connection->execute('SET sql_mode=' . $connection->quote($this->sqlmode)); } return $connection; } diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index ee51a29bb..5cdbb8c6d 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -270,7 +270,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla } elseif (str_ends_with($ts = rtrim($s), $delimiter)) { $sql .= substr($ts, 0, -strlen($delimiter)); - $connection->query($sql); + $connection->execute($sql); $sql = ''; $count++; if ($onProgress) { @@ -282,7 +282,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla } if (rtrim($sql) !== '') { - $connection->query($sql); + $connection->execute($sql); $count++; if ($onProgress) { $onProgress($count, isset($stat['size']) ? 100 : null); diff --git a/tests/Database/files/sqlsrv-loadFromFile.sql b/tests/Database/files/sqlsrv-loadFromFile.sql new file mode 100644 index 000000000..d4336c2a0 --- /dev/null +++ b/tests/Database/files/sqlsrv-loadFromFile.sql @@ -0,0 +1,13 @@ +IF OBJECT_ID('products', 'U') IS NOT NULL DROP TABLE products; + +CREATE TABLE products ( + product_id int NOT NULL IDENTITY(11,1), + title varchar(50) NOT NULL, + PRIMARY KEY(product_id) +); + +SET IDENTITY_INSERT products ON; +INSERT INTO products (product_id, title) VALUES (1, 'Chair'); +INSERT INTO products (product_id, title) VALUES (2, 'Table'); +INSERT INTO products (product_id, title) VALUES (3, 'Computer'); +SET IDENTITY_INSERT products OFF; diff --git a/tests/Database/sqlsrv-loadFromFile.phpt b/tests/Database/sqlsrv-loadFromFile.phpt new file mode 100644 index 000000000..2289d6fd5 --- /dev/null +++ b/tests/Database/sqlsrv-loadFromFile.phpt @@ -0,0 +1,18 @@ + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-loadFromFile.sql') +); From be8936fccf0c89d70ca8c8962004fb916e1c4e4e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 15 Aug 2024 05:59:35 +0200 Subject: [PATCH 27/53] Connection::getPdo() deprecated --- src/Database/Connection.php | 2 ++ tests/Database/Result.fetch().phpt | 2 +- .../ResultSet.normalizeRow.mysql.phpt | 2 +- tests/Database/connection.disconnect.phpt | 20 +++++++++---------- tests/bootstrap.php | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index f7f93e916..2d8e094a8 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -108,8 +108,10 @@ public function getDsn(): string } + /** @deprecated use getConnection()->getNativeConnection() */ public function getPdo(): \PDO { + trigger_error(__METHOD__ . '() is deprecated, use getConnection()->getNativeConnection()', E_USER_DEPRECATED); return $this->getConnection()->getNativeConnection(); } diff --git a/tests/Database/Result.fetch().phpt b/tests/Database/Result.fetch().phpt index ab65f2725..bafe5bff4 100644 --- a/tests/Database/Result.fetch().phpt +++ b/tests/Database/Result.fetch().phpt @@ -38,7 +38,7 @@ test('', function () use ($connection, $driverName) { test('tests closeCursor()', function () use ($connection, $driverName) { if ($driverName === 'mysql') { $connection->query('CREATE DEFINER = CURRENT_USER PROCEDURE `testProc`(IN param int(10) unsigned) BEGIN SELECT * FROM book WHERE id != param; END;;'); - $connection->getPdo()->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + $connection->getConnection()->getNativeConnection()->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); $res = $connection->query('CALL testProc(1)'); foreach ($res as $row) { diff --git a/tests/Database/ResultSet.normalizeRow.mysql.phpt b/tests/Database/ResultSet.normalizeRow.mysql.phpt index f78e86154..b3c6388b4 100644 --- a/tests/Database/ResultSet.normalizeRow.mysql.phpt +++ b/tests/Database/ResultSet.normalizeRow.mysql.phpt @@ -151,7 +151,7 @@ Assert::same( ); -$connection->getPdo()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); +$connection->getConnection()->getNativeConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $res = $connection->query('SELECT `int`, `decimal`, `decimal2`, `float`, `double` FROM types'); Assert::same([ 'int' => 1, diff --git a/tests/Database/connection.disconnect.phpt b/tests/Database/connection.disconnect.phpt index 77da6ab2d..4fab40976 100644 --- a/tests/Database/connection.disconnect.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -28,28 +28,28 @@ test('connect & disconnect', function () { }; // first connection - $pdo = $connection->getPdo(); - $driver = $connection->getDatabaseEngine(); + $native = $connection->getConnection()->getNativeConnection(); + $driver = $connection->getConnection(); Assert::same(1, $connections); // still first connection $connection->connect(); - Assert::same($pdo, $connection->getPdo()); - Assert::same($driver, $connection->getDatabaseEngine()); + Assert::same($native, $connection->getConnection()->getNativeConnection()); + Assert::same($driver, $connection->getConnection()); Assert::same(1, $connections); // second connection $connection->reconnect(); - $pdo2 = $connection->getPdo(); - $driver2 = $connection->getDatabaseEngine(); + $native2 = $connection->getConnection()->getNativeConnection(); + $driver2 = $connection->getConnection(); - Assert::notSame($pdo, $pdo2); - Assert::same($driver, $driver2); + Assert::notSame($native, $native2); + Assert::notSame($driver, $driver2); Assert::same(2, $connections); // third connection $connection->disconnect(); - Assert::notSame($pdo2, $connection->getPdo()); - Assert::same($driver2, $connection->getDatabaseEngine()); + Assert::notSame($native2, $connection->getConnection()->getNativeConnection()); + Assert::notSame($driver2, $connection->getConnection()); Assert::same(3, $connections); }); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 7867867e5..8ce5fd37b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -42,7 +42,7 @@ function connectToDB(array $options = []): Nette\Database\Explorer Tester\Environment::skip("Connection to '$args[dsn]' failed. Reason: " . $e->getMessage()); } - $driverName = $connection->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME); + $driverName = $connection->getConnection()->getNativeConnection()->getAttribute(PDO::ATTR_DRIVER_NAME); $cacheMemoryStorage = new Nette\Caching\Storages\MemoryStorage; $structure = new Nette\Database\Structure($connection, $cacheMemoryStorage); From 0cf26624404be0a6c9466ea14fa08889590adf84 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 17 Aug 2024 07:08:56 +0200 Subject: [PATCH 28/53] added TypeConverter --- src/Database/Drivers/Engines/MySQLEngine.php | 3 +- .../Drivers/Engines/SQLServerEngine.php | 5 +- src/Database/Drivers/Engines/SQLiteEngine.php | 5 +- src/Database/Helpers.php | 34 +------------- src/Database/TypeConverter.php | 46 +++++++++++++++++++ 5 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 src/Database/TypeConverter.php diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index 2f23cedec..c84b6499f 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -188,7 +189,7 @@ public function getColumnTypes(\PDOStatement $statement): array $meta['native_type'] === 'NEWDECIMAL' && $meta['precision'] === 0 => Nette\Database\IStructure::FIELD_INTEGER, $meta['native_type'] === 'TINY' && $meta['len'] === 1 && $this->convertBoolean => Nette\Database\IStructure::FIELD_BOOL, $meta['native_type'] === 'TIME' => Nette\Database\IStructure::FIELD_TIME_INTERVAL, - default => Nette\Database\Helpers::detectType($meta['native_type']), + default => TypeConverter::detectType($meta['native_type']), }; } } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index b955bc50b..d2316bbcd 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -225,9 +226,9 @@ public function getColumnTypes(\PDOStatement $statement): array isset($meta['sqlsrv:decl_type']) && $meta['sqlsrv:decl_type'] !== 'timestamp' ) { // timestamp does not mean time in sqlsrv - $types[$meta['name']] = Nette\Database\Helpers::detectType($meta['sqlsrv:decl_type']); + $types[$meta['name']] = TypeConverter::detectType($meta['sqlsrv:decl_type']); } elseif (isset($meta['native_type'])) { - $types[$meta['name']] = Nette\Database\Helpers::detectType($meta['native_type']); + $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); } } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index fed2b7d5e..21132d7a9 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -235,9 +236,9 @@ public function getColumnTypes(\PDOStatement $statement): array if (isset($meta['sqlite:decl_type'])) { $types[$meta['name']] = $this->formatDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true) ? Nette\Database\IStructure::FIELD_UNIX_TIMESTAMP - : Nette\Database\Helpers::detectType($meta['sqlite:decl_type']); + : TypeConverter::detectType($meta['sqlite:decl_type']); } elseif (isset($meta['native_type'])) { - $types[$meta['name']] = Nette\Database\Helpers::detectType($meta['native_type']); + $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); } } diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 5cdbb8c6d..61da77010 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -24,18 +24,6 @@ class Helpers /** maximum SQL length */ public static int $maxLength = 100; - public static array $typePatterns = [ - '^_' => IStructure::FIELD_TEXT, // PostgreSQL arrays - '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => IStructure::FIELD_INTEGER, - '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_DECIMAL, - 'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => IStructure::FIELD_FLOAT, - 'BOOL(EAN)?' => IStructure::FIELD_BOOL, - 'TIME' => IStructure::FIELD_TIME, - 'DATE' => IStructure::FIELD_DATE, - '(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => IStructure::FIELD_DATETIME, - 'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => IStructure::FIELD_BINARY, - ]; - /** * Displays complete result set as HTML table for debug purposes. @@ -173,7 +161,7 @@ public static function detectTypes(\PDOStatement $statement): array for ($col = 0; $col < $count; $col++) { $meta = $statement->getColumnMeta($col); if (isset($meta['native_type'])) { - $types[$meta['name']] = self::detectType($meta['native_type']); + $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); } } @@ -181,26 +169,6 @@ public static function detectTypes(\PDOStatement $statement): array } - /** - * Heuristic column type detection. - * @internal - */ - public static function detectType(string $type): string - { - static $cache; - if (!isset($cache[$type])) { - $cache[$type] = 'string'; - foreach (self::$typePatterns as $s => $val) { - if (preg_match("#^($s)$#i", $type)) { - return $cache[$type] = $val; - } - } - } - - return $cache[$type]; - } - - /** @internal */ public static function normalizeRow( array $row, diff --git a/src/Database/TypeConverter.php b/src/Database/TypeConverter.php new file mode 100644 index 000000000..f915c48dc --- /dev/null +++ b/src/Database/TypeConverter.php @@ -0,0 +1,46 @@ + IStructure::FIELD_TEXT, // PostgreSQL arrays + '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => IStructure::FIELD_INTEGER, + '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_DECIMAL, + 'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => IStructure::FIELD_FLOAT, + 'BOOL(EAN)?' => IStructure::FIELD_BOOL, + 'TIME' => IStructure::FIELD_TIME, + 'DATE' => IStructure::FIELD_DATE, + '(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => IStructure::FIELD_DATETIME, + 'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => IStructure::FIELD_BINARY, + ]; + + + /** + * Heuristic column type detection. + * @return Type::* + */ + public static function detectType(string $type): string + { + static $cache; + if (!isset($cache[$type])) { + $cache[$type] = IStructure::FIELD_TEXT; + foreach (self::$typePatterns as $s => $val) { + if (preg_match("#^($s)$#i", $type)) { + return $cache[$type] = $val; + } + } + } + + return $cache[$type]; + } +} From 4415f62259828abc83c58f07aca8becdfce52069 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:09:07 +0200 Subject: [PATCH 29/53] uses TypeConverter to convert values to PHP --- src/Database/Connection.php | 18 ++- src/Database/Drivers/Engine.php | 10 +- src/Database/Drivers/Engines/MSSQLEngine.php | 5 +- src/Database/Drivers/Engines/MySQLEngine.php | 33 +++--- src/Database/Drivers/Engines/ODBCEngine.php | 5 +- src/Database/Drivers/Engines/OracleEngine.php | 5 +- .../Drivers/Engines/PostgreSQLEngine.php | 10 +- .../Drivers/Engines/SQLServerEngine.php | 21 +--- src/Database/Drivers/Engines/SQLiteEngine.php | 20 +--- src/Database/Drivers/PDO/Connection.php | 3 + src/Database/Drivers/PDO/MySQL/Driver.php | 5 +- src/Database/Drivers/PDO/SQLSrv/Driver.php | 8 ++ src/Database/Drivers/PDO/SQLite/Driver.php | 8 ++ src/Database/Helpers.php | 55 +-------- src/Database/Result.php | 32 ++++-- src/Database/TypeConverter.php | 105 +++++++++++++++--- 16 files changed, 191 insertions(+), 152 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 2d8e094a8..87e287b2d 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -11,7 +11,6 @@ use JetBrains\PhpStorm\Language; use Nette\Utils\Arrays; -use Nette\Utils\DateTime; use PDOException; @@ -29,6 +28,7 @@ class Connection 'pdo-sqlite' => Drivers\PDO\SQLite\Driver::class, 'pdo-sqlsrv' => Drivers\PDO\SQLSrv\Driver::class, ]; + private const TypeConverterOptions = ['convertBoolean', 'newDateTime']; /** @var array Occurs after connection is established */ public array $onConnect = []; @@ -39,6 +39,7 @@ class Connection private ?Drivers\Connection $connection = null; private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; + private TypeConverter $typeConverter; /** @var callable(array, Result): array */ private $rowNormalizer = [Helpers::class, 'normalizeRow']; @@ -53,23 +54,22 @@ public function __construct( ?string $password = null, array $options = [], ) { - if (($options['newDateTime'] ?? null) === false) { - $this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class); - } - $driver = explode(':', $dsn)[0]; $class = empty($options['driverClass']) ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) : $options['driverClass']; $args = compact('dsn', 'username', 'password', 'options'); - unset($options['lazy'], $options['newDateTime'], $options['driverClass']); + unset($options['lazy'], $options['driverClass']); foreach ($options as $key => $value) { if (!is_int($key) && $value !== null) { $args[$key] = $value; unset($args['options'][$key]); } } + $args = array_diff_key($args, array_flip(self::TypeConverterOptions)); $this->driver = new $class(...$args); + $this->typeConverter = new TypeConverter; + array_map(fn($opt) => isset($options[$opt]) && ($this->typeConverter->$opt = (bool) $options[$opt]), self::TypeConverterOptions); } @@ -149,6 +149,12 @@ public function getReflection(): Reflection } + public function getTypeConverter(): TypeConverter + { + return $this->typeConverter; + } + + public function setRowNormalizer(?callable $normalizer): static { $this->rowNormalizer = $normalizer; diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index 41088cac6..e12ef0e90 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers; use Nette\Database; +use Nette\Database\TypeConverter; /** @@ -36,6 +37,9 @@ function isSupported(string $feature): bool; */ function convertException(\PDOException $e): Database\DriverException; + /** Converts a value from the database to a PHP value. */ + function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed; + /********************* SQL utilities ****************d*g**/ /** Escapes an identifier for use in an SQL statement. */ @@ -75,10 +79,4 @@ function getIndexes(string $table): array; * @return list */ function getForeignKeys(string $table): array; - - /** - * Returns associative array of detected types (IStructure::FIELD_*) in result set. - * @return array - */ - function getColumnTypes(\PDOStatement $statement): array; } diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index f68a666f2..ecf513087 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -212,8 +213,8 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - return Nette\Database\Helpers::detectTypes($statement); + return $converter->convertToPhp($value, $meta); } } diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index c84b6499f..f51d16e73 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -20,9 +20,6 @@ */ class MySQLEngine implements Engine { - public bool $convertBoolean = true; - - public function __construct( private readonly Connection $connection, ) { @@ -178,22 +175,20 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - $types = []; - $count = $statement->columnCount(); - for ($col = 0; $col < $count; $col++) { - $meta = $statement->getColumnMeta($col); - if (isset($meta['native_type'])) { - $types[$meta['name']] = match (true) { - $meta['native_type'] === 'NEWDECIMAL' && $meta['precision'] === 0 => Nette\Database\IStructure::FIELD_INTEGER, - $meta['native_type'] === 'TINY' && $meta['len'] === 1 && $this->convertBoolean => Nette\Database\IStructure::FIELD_BOOL, - $meta['native_type'] === 'TIME' => Nette\Database\IStructure::FIELD_TIME_INTERVAL, - default => TypeConverter::detectType($meta['native_type']), - }; - } - } - - return $types; + return match ($meta['nativeType']) { + 'NEWDECIMAL' => $meta['scale'] === 0 + ? $converter->toInt($value) + : $converter->toFloat($value), + 'TINY' => $meta['size'] === 1 && $converter->convertBoolean + ? $converter->toBool($value) + : $converter->toInt($value), + 'TIME' => $converter->toInterval($value), + 'DATE', 'DATETIME', 'TIMESTAMP' => str_starts_with($value, '0000-00') + ? null + : $converter->toDateTime($value), + default => $converter->convertToPhp($value, $meta), + }; } } diff --git a/src/Database/Drivers/Engines/ODBCEngine.php b/src/Database/Drivers/Engines/ODBCEngine.php index 55a22562c..1dcf80f98 100644 --- a/src/Database/Drivers/Engines/ODBCEngine.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -11,6 +11,7 @@ use Nette; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -95,8 +96,8 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - return []; + return $converter->convertToPhp($value, $meta); } } diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index 2fb9a4a05..5905bd972 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -130,8 +131,8 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - return []; + return $converter->convertToPhp($value, $meta); } } diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 0beccd26c..1d73ea858 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -12,6 +12,7 @@ use Nette; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; +use Nette\Database\TypeConverter; /** @@ -235,12 +236,11 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - static $cache; - $item = &$cache[$statement->queryString]; - $item ??= Nette\Database\Helpers::detectTypes($statement); - return $item; + return $meta['nativeType'] === 'bool' + ? ($value && $value !== 'f' && $value !== 'F') + : $converter->convertToPhp($value, $meta); } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index d2316bbcd..d31f93be2 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -216,22 +216,11 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - $types = []; - $count = $statement->columnCount(); - for ($col = 0; $col < $count; $col++) { - $meta = $statement->getColumnMeta($col); - if ( - isset($meta['sqlsrv:decl_type']) - && $meta['sqlsrv:decl_type'] !== 'timestamp' - ) { // timestamp does not mean time in sqlsrv - $types[$meta['name']] = TypeConverter::detectType($meta['sqlsrv:decl_type']); - } elseif (isset($meta['native_type'])) { - $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); - } - } - - return $types; + return match ($meta['nativeType']) { + 'timestamp' => $value, // timestamp does not mean time in sqlsrv + default => $converter->convertToPhp($value, $meta), + }; } } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 21132d7a9..5157ff587 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers\Engines; use Nette; +use Nette\Database\DateTime; use Nette\Database\Drivers\Connection; use Nette\Database\Drivers\Engine; use Nette\Database\TypeConverter; @@ -227,21 +228,10 @@ public function getForeignKeys(string $table): array } - public function getColumnTypes(\PDOStatement $statement): array + public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - $types = []; - $count = $statement->columnCount(); - for ($col = 0; $col < $count; $col++) { - $meta = $statement->getColumnMeta($col); - if (isset($meta['sqlite:decl_type'])) { - $types[$meta['name']] = $this->formatDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true) - ? Nette\Database\IStructure::FIELD_UNIX_TIMESTAMP - : TypeConverter::detectType($meta['sqlite:decl_type']); - } elseif (isset($meta['native_type'])) { - $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); - } - } - - return $types; + return in_array($meta['nativeType'], ['DATE', 'DATETIME'], true) + ? (is_int($value) ? (new DateTime)->setTimestamp($value) : new DateTime($value)) + : $converter->convertToPhp($value, $meta); } } diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index 726fd6304..c0566c4ce 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -15,6 +15,9 @@ class Connection implements Drivers\Connection { + public string $metaTypeKey = 'native_type'; + + public function __construct( protected readonly PDO $pdo, ) { diff --git a/src/Database/Drivers/PDO/MySQL/Driver.php b/src/Database/Drivers/PDO/MySQL/Driver.php index c9e730572..c6e5ccb6f 100644 --- a/src/Database/Drivers/PDO/MySQL/Driver.php +++ b/src/Database/Drivers/PDO/MySQL/Driver.php @@ -29,7 +29,6 @@ public function __construct( protected readonly array $options = [], protected readonly ?string $charset = self::DefaultCharset, protected readonly ?string $sqlmode = null, - protected readonly ?bool $convertBoolean = null, ) { } @@ -49,8 +48,6 @@ public function connect(): Drivers\PDO\Connection public function createEngine(Drivers\Connection $connection): MySQLEngine { - $engine = new MySQLEngine($connection); - $engine->convertBoolean = $this->convertBoolean ?? $engine->convertBoolean; - return $engine; + return new MySQLEngine($connection); } } diff --git a/src/Database/Drivers/PDO/SQLSrv/Driver.php b/src/Database/Drivers/PDO/SQLSrv/Driver.php index 667dec1cf..c074c6dbb 100644 --- a/src/Database/Drivers/PDO/SQLSrv/Driver.php +++ b/src/Database/Drivers/PDO/SQLSrv/Driver.php @@ -18,6 +18,14 @@ */ class Driver extends Drivers\PDO\Driver { + public function connect(): Drivers\PDO\Connection + { + $connection = parent::connect(); + $connection->metaTypeKey = 'sqlsrv:decl_type'; + return $connection; + } + + public function createEngine(Drivers\Connection $connection): SQLServerEngine { return new SQLServerEngine($connection); diff --git a/src/Database/Drivers/PDO/SQLite/Driver.php b/src/Database/Drivers/PDO/SQLite/Driver.php index 892ba0f19..f73619b0c 100644 --- a/src/Database/Drivers/PDO/SQLite/Driver.php +++ b/src/Database/Drivers/PDO/SQLite/Driver.php @@ -29,6 +29,14 @@ public function __construct( } + public function connect(): Drivers\PDO\Connection + { + $connection = parent::connect(); + $connection->metaTypeKey = 'sqlite:decl_type'; + return $connection; + } + + public function createEngine(Drivers\Connection $connection): SQLiteEngine { $engine = new SQLiteEngine($connection); diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 61da77010..99b79541a 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -151,63 +151,20 @@ public static function dumpSql(string $sql, ?array $params = null, ?Connection $ } - /** - * Common column type detection. - */ - public static function detectTypes(\PDOStatement $statement): array - { - $types = []; - $count = $statement->columnCount(); // driver must be meta-aware, see PHP bugs #53782, #54695 - for ($col = 0; $col < $count; $col++) { - $meta = $statement->getColumnMeta($col); - if (isset($meta['native_type'])) { - $types[$meta['name']] = TypeConverter::detectType($meta['native_type']); - } - } - - return $types; - } - - /** @internal */ public static function normalizeRow( array $row, Result $resultSet, - $dateTimeClass = DateTime::class, ): array { - foreach ($resultSet->getColumnTypes() as $key => $type) { + $engine = @$resultSet->getConnection()->getDatabaseEngine(); + $converter = @$resultSet->getConnection()->getTypeConverter(); + foreach ($resultSet->getColumnsMeta() as $key => $meta) { $value = $row[$key]; - if ($value === null || $value === false || $type === IStructure::FIELD_TEXT) { - // do nothing - } elseif ($type === IStructure::FIELD_INTEGER) { - $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp; - - } elseif ($type === IStructure::FIELD_FLOAT || $type === IStructure::FIELD_DECIMAL) { - $row[$key] = (float) $value; - - } elseif ($type === IStructure::FIELD_BOOL) { - $row[$key] = $value && $value !== 'f' && $value !== 'F'; - - } elseif ($type === IStructure::FIELD_DATETIME || $type === IStructure::FIELD_DATE) { - $row[$key] = str_starts_with($value, '0000-00') - ? null - : new $dateTimeClass($value); - - } elseif ($type === IStructure::FIELD_TIME) { - $row[$key] = (new $dateTimeClass($value))->setDate(1, 1, 1); - - } elseif ($type === IStructure::FIELD_TIME_INTERVAL) { - preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m); - $row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S"); - $row[$key]->f = isset($m[5]) ? (float) $m[5] : 0.0; - $row[$key]->invert = (int) (bool) $m[1]; - - } elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) { - $row[$key] = (new $dateTimeClass)->setTimestamp($value); - } + $row[$key] = isset($value, $converter) + ? $engine->convertToPhp($value, $meta, $converter) + : $value; } - return $row; } diff --git a/src/Database/Result.php b/src/Database/Result.php index 8cd0fb310..f2b9c2f86 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -28,7 +28,7 @@ class Result implements \Iterator /** @var Row[] */ private array $rows; private float $time; - private array $types; + private array $meta; public function __construct( @@ -98,13 +98,6 @@ public function getRowCount(): ?int } - public function getColumnTypes(): array - { - $this->types ??= $this->connection->getDatabaseEngine()->getColumnTypes($this->pdoStatement); - return $this->types; - } - - public function getTime(): float { return $this->time; @@ -259,6 +252,29 @@ public function fetchAll(): array $this->rows ??= iterator_to_array($this); return $this->rows; } + + + public function getColumnsMeta(): array + { + if (isset($this->meta)) { + return $this->meta; + } + + $res = []; + $metaTypeKey = $this->connection->getConnection()->metaTypeKey; + $count = $this->pdoStatement->columnCount(); + for ($i = 0; $i < $count; $i++) { + $meta = $this->pdoStatement->getColumnMeta($i); + if (isset($meta[$metaTypeKey])) { + $res[$meta['name']] = [ + 'nativeType' => $meta[$metaTypeKey], + 'size' => $meta['len'], + 'scale' => $meta['precision'], + ]; + } + } + return $this->meta = $res; + } } diff --git a/src/Database/TypeConverter.php b/src/Database/TypeConverter.php index f915c48dc..7b86daf5f 100644 --- a/src/Database/TypeConverter.php +++ b/src/Database/TypeConverter.php @@ -12,35 +12,104 @@ final class TypeConverter { - public static array $typePatterns = [ - '^_' => IStructure::FIELD_TEXT, // PostgreSQL arrays - '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => IStructure::FIELD_INTEGER, - '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_DECIMAL, - 'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => IStructure::FIELD_FLOAT, - 'BOOL(EAN)?' => IStructure::FIELD_BOOL, - 'TIME' => IStructure::FIELD_TIME, - 'DATE' => IStructure::FIELD_DATE, - '(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => IStructure::FIELD_DATETIME, - 'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => IStructure::FIELD_BINARY, + private const + Binary = 1, + Boolean = 2, + Date = 3, + DateTime = 4, + Decimal = 5, + Float = 6, + Integer = 7, + Interval = 8, + Text = 9, + Time = 10; + + private const Patterns = [ + '^_' => self::Text, // PostgreSQL arrays + '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => self::Integer, + '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => self::Decimal, + 'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => self::Float, + 'BOOL(EAN)?' => self::Boolean, + 'TIME' => self::Time, + 'DATE' => self::Date, + '(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => self::DateTime, + 'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => self::Binary, ]; + public bool $convertBoolean = true; + public bool $newDateTime = true; + /** * Heuristic column type detection. - * @return Type::* */ - public static function detectType(string $type): string + private function detectType(string $nativeType): int { static $cache; - if (!isset($cache[$type])) { - $cache[$type] = IStructure::FIELD_TEXT; - foreach (self::$typePatterns as $s => $val) { - if (preg_match("#^($s)$#i", $type)) { - return $cache[$type] = $val; + if (!isset($cache[$nativeType])) { + $cache[$nativeType] = self::Text; + foreach (self::Patterns as $s => $val) { + if (preg_match("#^($s)$#i", $nativeType)) { + return $cache[$nativeType] = $val; } } } - return $cache[$type]; + return $cache[$nativeType]; + } + + + public function convertToPhp(mixed $value, array $meta): mixed + { + return match ($this->detectType($meta['nativeType'])) { + self::Integer => $this->toInt($value), + self::Float, + self::Decimal => $this->toFloat($value), + self::Boolean => $this->convertBoolean ? $this->toBool($value) : $value, + self::DateTime, self::Date => $this->toDateTime($value), + self::Time => $this->toTime($value), + self::Interval => self::toInterval($value), + default => $value, + }; + } + + + public function toInt(int|string $value): int|float + { + return is_float($tmp = $value * 1) ? $value : $tmp; + } + + + public function toFloat(float|string $value): float + { + return (float) $value; + } + + + public function toBool(bool|int|string $value): bool + { + return (bool) $value; + } + + + public function toDateTime(string $value): \DateTimeInterface + { + return $this->newDateTime ? new DateTime($value) : new \Nette\Utils\DateTime($value); + } + + + public function toTime(string $value): \DateTimeInterface + { + return $this->toDateTime($value)->setDate(1, 1, 1); + } + + + public function toInterval(string $value): \DateInterval + { + preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m); + $interval = new \DateInterval("PT$m[2]H$m[3]M$m[4]S"); + $interval->f = isset($m[5]) ? (float) $m[5] : 0.0; + $interval->invert = (int) (bool) $m[1]; + return $interval; } } From bb7f7fbe895ca085e54838bf48ec9a0ae1e3ad50 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 00:00:27 +0200 Subject: [PATCH 30/53] added option convertDateTime --- src/Database/Connection.php | 2 +- src/Database/Drivers/Engines/MySQLEngine.php | 8 +++---- src/Database/Drivers/Engines/SQLiteEngine.php | 2 +- src/Database/TypeConverter.php | 7 +++--- tests/Database/connection.options.mysql.phpt | 19 +++++++++++++++ tests/Database/connection.options.sqlite.phpt | 24 +++++++++++++++++++ 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 87e287b2d..607bf3a9a 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -28,7 +28,7 @@ class Connection 'pdo-sqlite' => Drivers\PDO\SQLite\Driver::class, 'pdo-sqlsrv' => Drivers\PDO\SQLSrv\Driver::class, ]; - private const TypeConverterOptions = ['convertBoolean', 'newDateTime']; + private const TypeConverterOptions = ['convertBoolean', 'convertDateTime', 'newDateTime']; /** @var array Occurs after connection is established */ public array $onConnect = []; diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index f51d16e73..d62e5ba87 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -184,10 +184,10 @@ public function convertToPhp(mixed $value, array $meta, TypeConverter $converter 'TINY' => $meta['size'] === 1 && $converter->convertBoolean ? $converter->toBool($value) : $converter->toInt($value), - 'TIME' => $converter->toInterval($value), - 'DATE', 'DATETIME', 'TIMESTAMP' => str_starts_with($value, '0000-00') - ? null - : $converter->toDateTime($value), + 'TIME' => $converter->convertDateTime ? $converter->toInterval($value) : $value, + 'DATE', 'DATETIME', 'TIMESTAMP' => $converter->convertDateTime + ? (str_starts_with($value, '0000-00') ? null : $converter->toDateTime($value)) + : $value, default => $converter->convertToPhp($value, $meta), }; } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 5157ff587..117a2248a 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -230,7 +230,7 @@ public function getForeignKeys(string $table): array public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { - return in_array($meta['nativeType'], ['DATE', 'DATETIME'], true) + return $converter->convertDateTime && in_array($meta['nativeType'], ['DATE', 'DATETIME'], true) ? (is_int($value) ? (new DateTime)->setTimestamp($value) : new DateTime($value)) : $converter->convertToPhp($value, $meta); } diff --git a/src/Database/TypeConverter.php b/src/Database/TypeConverter.php index 7b86daf5f..9334aec5b 100644 --- a/src/Database/TypeConverter.php +++ b/src/Database/TypeConverter.php @@ -37,6 +37,7 @@ final class TypeConverter ]; public bool $convertBoolean = true; + public bool $convertDateTime = true; public bool $newDateTime = true; @@ -66,9 +67,9 @@ public function convertToPhp(mixed $value, array $meta): mixed self::Float, self::Decimal => $this->toFloat($value), self::Boolean => $this->convertBoolean ? $this->toBool($value) : $value, - self::DateTime, self::Date => $this->toDateTime($value), - self::Time => $this->toTime($value), - self::Interval => self::toInterval($value), + self::DateTime, self::Date => $this->convertDateTime ? $this->toDateTime($value) : $value, + self::Time => $this->convertDateTime ? $this->toTime($value) : $value, + self::Interval => $this->convertDateTime ? self::toInterval($value) : $value, default => $value, }; } diff --git a/tests/Database/connection.options.mysql.phpt b/tests/Database/connection.options.mysql.phpt index 43f232480..390cf0462 100644 --- a/tests/Database/connection.options.mysql.phpt +++ b/tests/Database/connection.options.mysql.phpt @@ -72,3 +72,22 @@ test('newDateTime = true', function () { $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); + + +test('default convertDateTime', function () { + $connection = connectToDB(['convertDateTime' => null])->getConnection(); + $field = $connection->fetchField('SELECT NOW()'); + Assert::type(Nette\Database\DateTime::class, $field); +}); + +test('convertDateTime = false', function () { + $connection = connectToDB(['convertDateTime' => false])->getConnection(); + $field = $connection->fetchField('SELECT NOW()'); + Assert::type('string', $field); +}); + +test('convertDateTime = true', function () { + $connection = connectToDB(['convertDateTime' => true])->getConnection(); + $field = $connection->fetchField('SELECT NOW()'); + Assert::type(Nette\Database\DateTime::class, $field); +}); diff --git a/tests/Database/connection.options.sqlite.phpt b/tests/Database/connection.options.sqlite.phpt index 887e1caee..ce3967361 100644 --- a/tests/Database/connection.options.sqlite.phpt +++ b/tests/Database/connection.options.sqlite.phpt @@ -23,3 +23,27 @@ test('formatDateTime', function () { $engine = $connection->getDatabaseEngine(); Assert::same('1978-01-23', $engine->formatDateTime(new DateTime('1978-01-23 00:00:00'))); }); + + +test('default convertDateTime', function () { + $connection = connectToDB(['convertDateTime' => null])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::type(Nette\Database\DateTime::class, $row->date); + Assert::type(Nette\Database\DateTime::class, $row->datetime); +}); + +test('convertDateTime = false', function () { + $connection = connectToDB(['convertDateTime' => false])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::type('int', $row->date); + Assert::type('int', $row->datetime); +}); + +test('convertDateTime = true', function () { + $connection = connectToDB(['convertDateTime' => true])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::type(Nette\Database\DateTime::class, $row->date); +}); From 0eb80cf166d354deaeaec4ad2bcdba2ad102e314 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 00:00:54 +0200 Subject: [PATCH 31/53] added option convertDecimal & converts to int/float [Closes #257] --- src/Database/Connection.php | 2 +- src/Database/Drivers/Engines/MySQLEngine.php | 3 -- src/Database/TypeConverter.php | 9 +++-- .../ResultSet.normalizeRow.sqlsrv.phpt | 8 ++-- tests/Database/connection.options.mysql.phpt | 34 ++++++++++++++++ tests/Database/connection.options.sqlsrv.phpt | 40 +++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 tests/Database/connection.options.sqlsrv.phpt diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 607bf3a9a..c40febb82 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -28,7 +28,7 @@ class Connection 'pdo-sqlite' => Drivers\PDO\SQLite\Driver::class, 'pdo-sqlsrv' => Drivers\PDO\SQLSrv\Driver::class, ]; - private const TypeConverterOptions = ['convertBoolean', 'convertDateTime', 'newDateTime']; + private const TypeConverterOptions = ['convertBoolean', 'convertDateTime', 'convertDecimal', 'newDateTime']; /** @var array Occurs after connection is established */ public array $onConnect = []; diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index d62e5ba87..b6e8ed26c 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -178,9 +178,6 @@ public function getForeignKeys(string $table): array public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed { return match ($meta['nativeType']) { - 'NEWDECIMAL' => $meta['scale'] === 0 - ? $converter->toInt($value) - : $converter->toFloat($value), 'TINY' => $meta['size'] === 1 && $converter->convertBoolean ? $converter->toBool($value) : $converter->toInt($value), diff --git a/src/Database/TypeConverter.php b/src/Database/TypeConverter.php index 9334aec5b..c20bd9cc2 100644 --- a/src/Database/TypeConverter.php +++ b/src/Database/TypeConverter.php @@ -38,6 +38,7 @@ final class TypeConverter public bool $convertBoolean = true; public bool $convertDateTime = true; + public bool $convertDecimal = true; public bool $newDateTime = true; @@ -64,8 +65,10 @@ public function convertToPhp(mixed $value, array $meta): mixed { return match ($this->detectType($meta['nativeType'])) { self::Integer => $this->toInt($value), - self::Float, - self::Decimal => $this->toFloat($value), + self::Float => $this->toFloat($value), + self::Decimal => $this->convertDecimal + ? ($meta['scale'] === 0 ? $this->toInt($value) : $this->toFloat($value)) + : $value, self::Boolean => $this->convertBoolean ? $this->toBool($value) : $value, self::DateTime, self::Date => $this->convertDateTime ? $this->toDateTime($value) : $value, self::Time => $this->convertDateTime ? $this->toTime($value) : $value, @@ -75,7 +78,7 @@ public function convertToPhp(mixed $value, array $meta): mixed } - public function toInt(int|string $value): int|float + public function toInt(int|float|string $value): int|float|string { return is_float($tmp = $value * 1) ? $value : $tmp; } diff --git a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt index 09ea94fe7..1b7b39f98 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt @@ -26,7 +26,7 @@ Assert::equal([ 'date' => new DateTime('2012-10-13 00:00:00'), 'datetime' => new DateTime('2012-10-13 10:10:10'), 'datetime2' => new DateTime('2012-10-13 10:10:10'), - 'decimal' => 1.0, + 'decimal' => 1, 'float' => 1.1, 'geography' => "\xe6\x10\x00\x00\x01\x14\x87\x16\xd9\xce\xf7\xd3G@\xd7\xa3p=\n\x97^\xc0\x87\x16\xd9\xce\xf7\xd3G@\xcb\xa1E\xb6\xf3\x95^\xc0", 'geometry' => "\x00\x00\x00\x00\x01\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00Y@\x00\x00\x00\x00\x00\x00Y@\x00\x00\x00\x00\x00\x004@\x00\x00\x00\x00\x00\x80f@\x00\x00\x00\x00\x00\x80f@\x00\x00\x00\x00\x00\x80f@\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x02", @@ -35,7 +35,7 @@ Assert::equal([ 'money' => 1111.1, 'nchar' => 'a', 'ntext' => 'a', - 'numeric_10_0' => 1.0, + 'numeric_10_0' => 1, 'numeric_10_2' => 1.1, 'nvarchar' => 'a', 'real' => 1.1, @@ -59,7 +59,7 @@ Assert::equal([ 'date' => new DateTime('0001-01-01 00:00:00'), 'datetime' => new DateTime('1753-01-01 00:00:00'), 'datetime2' => new DateTime('0001-01-01 00:00:00'), - 'decimal' => 0.0, + 'decimal' => 0, 'float' => 0.5, 'geography' => null, 'geometry' => null, @@ -68,7 +68,7 @@ Assert::equal([ 'money' => 0.0, 'nchar' => ' ', 'ntext' => '', - 'numeric_10_0' => 0.0, + 'numeric_10_0' => 0, 'numeric_10_2' => 0.5, 'nvarchar' => '', 'real' => 0.0, diff --git a/tests/Database/connection.options.mysql.phpt b/tests/Database/connection.options.mysql.phpt index 390cf0462..9040f52fb 100644 --- a/tests/Database/connection.options.mysql.phpt +++ b/tests/Database/connection.options.mysql.phpt @@ -91,3 +91,37 @@ test('convertDateTime = true', function () { $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); + + +test('default convertDecimal', function () { + $connection = connectToDB(['convertDecimal' => null])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same(1, $row->decimal); + Assert::same(1.1, $row->decimal2); + + $fields = $connection->fetchFields('SELECT 10, 10.5'); + Assert::same([10, 10.5], $fields); +}); + +test('convertDecimal = false', function () { + $connection = connectToDB(['convertDecimal' => false])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same('1', $row->decimal); + Assert::same('1.10', $row->decimal2); + + $fields = $connection->fetchFields('SELECT 10, 10.5'); + Assert::same([10, '10.5'], $fields); +}); + +test('convertDecimal = true', function () { + $connection = connectToDB(['convertDecimal' => true])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same(1, $row->decimal); + Assert::same(1.1, $row->decimal2); + + $fields = $connection->fetchFields('SELECT 10, 10.5'); + Assert::same([10, 10.5], $fields); +}); diff --git a/tests/Database/connection.options.sqlsrv.phpt b/tests/Database/connection.options.sqlsrv.phpt new file mode 100644 index 000000000..e37744c6e --- /dev/null +++ b/tests/Database/connection.options.sqlsrv.phpt @@ -0,0 +1,40 @@ + null])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same(1, $row->decimal); + Assert::same(1, $row->numeric_10_0); + Assert::same(1.1, $row->numeric_10_2); +}); + +test('convertDecimal = true', function () { + $connection = connectToDB(['convertDecimal' => true])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same(1, $row->decimal); + Assert::same(1, $row->numeric_10_0); + Assert::same(1.1, $row->numeric_10_2); +}); + +test('convertDecimal = false', function () { + $connection = connectToDB(['convertDecimal' => false])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::same('1', $row->decimal); + Assert::same('1', $row->numeric_10_0); + Assert::same('1.10', $row->numeric_10_2); +}); From 68f45f8f3334e4534da8159d2f51ff01b7964e7b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 17 Aug 2024 19:10:27 +0200 Subject: [PATCH 32/53] SqlsrvDriver: converts BIT to boolean (BC break) --- .../Drivers/Engines/SQLServerEngine.php | 1 + src/Database/Drivers/PDO/SQLSrv/Driver.php | 2 ++ .../ResultSet.normalizeRow.sqlsrv.phpt | 4 ++-- tests/Database/connection.options.sqlsrv.phpt | 22 +++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index d31f93be2..db1957b04 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -220,6 +220,7 @@ public function convertToPhp(mixed $value, array $meta, TypeConverter $converter { return match ($meta['nativeType']) { 'timestamp' => $value, // timestamp does not mean time in sqlsrv + 'bit' => $converter->convertBoolean ? $converter->toBool($value) : $converter->toInt($value), default => $converter->convertToPhp($value, $meta), }; } diff --git a/src/Database/Drivers/PDO/SQLSrv/Driver.php b/src/Database/Drivers/PDO/SQLSrv/Driver.php index c074c6dbb..46909e970 100644 --- a/src/Database/Drivers/PDO/SQLSrv/Driver.php +++ b/src/Database/Drivers/PDO/SQLSrv/Driver.php @@ -15,6 +15,8 @@ /** * PDO SQL Server database driver. + * Options: + * - convertBoolean => converts BIT to boolean */ class Driver extends Drivers\PDO\Driver { diff --git a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt index 1b7b39f98..555e2131b 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt @@ -21,7 +21,7 @@ $res = $connection->query('SELECT * FROM types'); Assert::equal([ 'bigint' => 1, 'binary_3' => "\x00\x00\xFF", - 'bit' => '1', + 'bit' => true, 'char_5' => 'a ', 'date' => new DateTime('2012-10-13 00:00:00'), 'datetime' => new DateTime('2012-10-13 10:10:10'), @@ -54,7 +54,7 @@ Assert::equal([ Assert::equal([ 'bigint' => 0, 'binary_3' => "\x00\x00\x00", - 'bit' => '0', + 'bit' => false, 'char_5' => ' ', 'date' => new DateTime('0001-01-01 00:00:00'), 'datetime' => new DateTime('1753-01-01 00:00:00'), diff --git a/tests/Database/connection.options.sqlsrv.phpt b/tests/Database/connection.options.sqlsrv.phpt index e37744c6e..b23e8dd1f 100644 --- a/tests/Database/connection.options.sqlsrv.phpt +++ b/tests/Database/connection.options.sqlsrv.phpt @@ -38,3 +38,25 @@ test('convertDecimal = false', function () { Assert::same('1', $row->numeric_10_0); Assert::same('1.10', $row->numeric_10_2); }); + + +test('default convertBoolean', function () { + $connection = connectToDB(['convertBoolean' => null])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::equal(true, $row->bit); +}); + +test('convertBoolean = true', function () { + $connection = connectToDB(['convertBoolean' => true])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::equal(true, $row->bit); +}); + +test('convertBoolean = false', function () { + $connection = connectToDB(['convertBoolean' => false])->getConnection(); + Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); + $row = $connection->fetch('SELECT * FROM types'); + Assert::equal(1, $row->bit); +}); From 9075da38e873925c7731c543ce1a96e869cb52e9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:16:36 +0200 Subject: [PATCH 33/53] removed custom rowNormalizer (BC break) --- src/Database/Connection.php | 10 ++-- src/Database/Helpers.php | 18 ------- src/Database/Result.php | 34 ++++++------ src/Database/Table/Selection.php | 10 ++-- .../Database/ResultSet.customNormalizer.phpt | 53 ------------------- 5 files changed, 25 insertions(+), 100 deletions(-) delete mode 100644 tests/Database/ResultSet.customNormalizer.phpt diff --git a/src/Database/Connection.php b/src/Database/Connection.php index c40febb82..201e69982 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -10,6 +10,7 @@ namespace Nette\Database; use JetBrains\PhpStorm\Language; +use Nette; use Nette\Utils\Arrays; use PDOException; @@ -40,9 +41,6 @@ class Connection private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; private TypeConverter $typeConverter; - - /** @var callable(array, Result): array */ - private $rowNormalizer = [Helpers::class, 'normalizeRow']; private ?string $sql = null; private int $transactionDepth = 0; @@ -155,10 +153,10 @@ public function getTypeConverter(): TypeConverter } + /** @deprecated */ public function setRowNormalizer(?callable $normalizer): static { - $this->rowNormalizer = $normalizer; - return $this; + throw new Nette\DeprecatedException(__METHOD__ . "() is deprecated, configure 'convert*' options instead."); } @@ -243,7 +241,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] { [$this->sql, $params] = $this->preprocess($sql, ...$params); try { - $result = new Result($this, $this->sql, $params, $this->rowNormalizer); + $result = new Result($this, $this->sql, $params); } catch (PDOException $e) { Arrays::invoke($this->onQuery, $this, $e); throw $e; diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 99b79541a..83eb3a198 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -151,24 +151,6 @@ public static function dumpSql(string $sql, ?array $params = null, ?Connection $ } - /** @internal */ - public static function normalizeRow( - array $row, - Result $resultSet, - ): array - { - $engine = @$resultSet->getConnection()->getDatabaseEngine(); - $converter = @$resultSet->getConnection()->getTypeConverter(); - foreach ($resultSet->getColumnsMeta() as $key => $meta) { - $value = $row[$key]; - $row[$key] = isset($value, $converter) - ? $engine->convertToPhp($value, $meta, $converter) - : $value; - } - return $row; - } - - /** * Import SQL dump from file - extremely fast. * @param ?array $onProgress diff --git a/src/Database/Result.php b/src/Database/Result.php index f2b9c2f86..0ffeab5f1 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -19,9 +19,6 @@ class Result implements \Iterator { private ?\PDOStatement $pdoStatement = null; - - /** @var callable(array, Result): array */ - private readonly mixed $normalizer; private Row|false|null $lastRow = null; private int $lastRowKey = -1; @@ -35,10 +32,9 @@ public function __construct( private readonly Connection $connection, private readonly string $queryString, private readonly array $params, - ?callable $normalizer = null, ) { $time = microtime(true); - $this->normalizer = $normalizer; + try { if (str_starts_with($queryString, '::')) { @@ -104,15 +100,6 @@ public function getTime(): float } - /** @internal */ - public function normalizeRow(array $row): array - { - return $this->normalizer - ? ($this->normalizer)($row, $this) - : $row; - } - - /********************* misc tools ****************d*g**/ @@ -254,12 +241,23 @@ public function fetchAll(): array } - public function getColumnsMeta(): array + private function normalizeRow(array $row): array { - if (isset($this->meta)) { - return $this->meta; + $engine = $this->connection->getDatabaseEngine(); + $converter = $this->connection->getTypeConverter(); + $columnsMeta = $this->meta ??= $this->getColumnsMeta(); + foreach ($row as $key => $value) { + $row[$key] = isset($value, $columnsMeta[$key]) + ? $engine->convertToPhp($value, $columnsMeta[$key], $converter) + : $value; } + return $row; + } + + + private function getColumnsMeta(): array + { $res = []; $metaTypeKey = $this->connection->getConnection()->metaTypeKey; $count = $this->pdoStatement->columnCount(); @@ -273,7 +271,7 @@ public function getColumnsMeta(): array ]; } } - return $this->meta = $res; + return $res; } } diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index d0469c723..03ee2b61e 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -528,13 +528,13 @@ protected function execute(): void } } + $key = 0; $this->rows = []; $usedPrimary = true; - foreach ($result->getPdoStatement() as $key => $row) { - $row = $this->createRow($result->normalizeRow($row)); - $primary = $row->getSignature(false); - $usedPrimary = $usedPrimary && $primary !== ''; - $this->rows[$usedPrimary ? $primary : $key] = $row; + while ($row = @$result->fetchAssoc()) { // @ may contain duplicate columns + $row = $this->createRow($row); + $usedPrimary = $usedPrimary && ($primary = $row->getSignature(false)) !== ''; + $this->rows[$usedPrimary ? $primary : $key++] = $row; } $this->data = $this->rows; diff --git a/tests/Database/ResultSet.customNormalizer.phpt b/tests/Database/ResultSet.customNormalizer.phpt deleted file mode 100644 index b0bfb71e8..000000000 --- a/tests/Database/ResultSet.customNormalizer.phpt +++ /dev/null @@ -1,53 +0,0 @@ -getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); - -$connection->query('UPDATE author SET born=?', new DateTime('2022-01-23')); - - -test('disabled normalization', function () use ($connection) { - $driverName = $GLOBALS['driverName']; - - $connection->setRowNormalizer(null); - $res = $connection->query('SELECT * FROM author'); - $asInt = $driverName === 'pgsql' || ($driverName !== 'sqlsrv' && PHP_VERSION_ID >= 80100); - Assert::same([ - 'id' => $asInt ? 11 : '11', - 'name' => 'Jakub Vrana', - 'web' => 'http://www.vrana.cz/', - 'born' => $driverName === 'sqlite' ? ($asInt ? 1_642_892_400 : '1642892400') : '2022-01-23', - ], (array) $res->fetch()); -}); - - -test('custom normalization', function () use ($connection) { - $driverName = $GLOBALS['driverName']; - - $connection->setRowNormalizer(function (array $row) { - foreach ($row as $key => $value) { - unset($row[$key]); - $row['_' . $key . '_'] = (string) $value; - } - - return $row; - }); - - $res = $connection->query('SELECT * FROM author'); - Assert::same([ - '_id_' => '11', - '_name_' => 'Jakub Vrana', - '_web_' => 'http://www.vrana.cz/', - '_born_' => $driverName === 'sqlite' ? '1642892400' : '2022-01-23', - ], (array) $res->fetch()); -}); From 4bb25d210d0fa1e2b509cc59e36bc8e89d5afea9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 05:44:48 +0200 Subject: [PATCH 34/53] PDOStatement replaced by Result --- .../Drivers/Accessory/LazyConnection.php | 2 +- src/Database/Drivers/Connection.php | 2 +- src/Database/Drivers/PDO/Connection.php | 5 +- src/Database/Drivers/PDO/Result.php | 93 +++++++++++++++++++ src/Database/Drivers/Result.php | 38 ++++++++ src/Database/Helpers.php | 23 ----- src/Database/Result.php | 41 +++----- src/Database/TypeConverter.php | 2 +- tests/Database/Result.fetch().phpt | 8 +- 9 files changed, 151 insertions(+), 63 deletions(-) create mode 100644 src/Database/Drivers/PDO/Result.php create mode 100644 src/Database/Drivers/Result.php diff --git a/src/Database/Drivers/Accessory/LazyConnection.php b/src/Database/Drivers/Accessory/LazyConnection.php index d99da9b73..2755def89 100644 --- a/src/Database/Drivers/Accessory/LazyConnection.php +++ b/src/Database/Drivers/Accessory/LazyConnection.php @@ -26,7 +26,7 @@ private function getConnection(): Drivers\Connection } - public function query(string $sql, array $params = []) + public function query(string $sql, array $params = []): Drivers\Result { return $this->getConnection()->query($sql, $params); } diff --git a/src/Database/Drivers/Connection.php b/src/Database/Drivers/Connection.php index f180e1137..03dd4d412 100644 --- a/src/Database/Drivers/Connection.php +++ b/src/Database/Drivers/Connection.php @@ -17,7 +17,7 @@ interface Connection { /** Executes an SQL query with optional parameters and returns a result set. */ - function query(string $sql, array $params = []); + function query(string $sql, array $params = []): Result; /** Executes an SQL command and returns the number of affected rows. */ function execute(string $sql): int; diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index c0566c4ce..047978e17 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -24,7 +24,7 @@ public function __construct( } - public function query(string $sql, array $params = []) + public function query(string $sql, array $params = []): Result { $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; @@ -33,9 +33,8 @@ public function query(string $sql, array $params = []) $statement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[gettype($value)] ?? PDO::PARAM_STR); } - $statement->setFetchMode(PDO::FETCH_ASSOC); $statement->execute(); - return $statement; + return new Result($statement, $this); } diff --git a/src/Database/Drivers/PDO/Result.php b/src/Database/Drivers/PDO/Result.php new file mode 100644 index 000000000..f62a885ea --- /dev/null +++ b/src/Database/Drivers/PDO/Result.php @@ -0,0 +1,93 @@ +fetchList(); + if (!$row) { + return null; + } + + $res = []; + foreach ($this->getColumnsInfo() as $i => $meta) { + $res[$meta['name']] = $row[$i]; + } + return $res; + } + + + public function fetchList(): ?array + { + $row = $this->result->fetch(\PDO::FETCH_NUM); + if (!$row) { + $this->free(); + return null; + } + return $row; + } + + + public function getColumnCount(): int + { + return $this->result->columnCount(); + } + + + public function getRowCount(): int + { + return $this->result->rowCount(); + } + + + public function getColumnsInfo(): array + { + return $this->columns ??= $this->collectColumnsInfo(); + } + + + protected function collectColumnsInfo(): array + { + $res = []; + $count = $this->result->columnCount(); + for ($i = 0; $i < $count; $i++) { + $meta = $this->result->getColumnMeta($i) ?: throw new DriverException('Cannot fetch column metadata'); + $res[] = [ + 'name' => $meta['name'], + 'nativeType' => $meta[$this->connection->metaTypeKey] ?? null, + 'size' => $meta['len'], + 'scale' => $meta['precision'], + ]; + } + return $res; + } + + + public function free(): void + { + $this->result->closeCursor(); + } +} diff --git a/src/Database/Drivers/Result.php b/src/Database/Drivers/Result.php new file mode 100644 index 000000000..e17895e1f --- /dev/null +++ b/src/Database/Drivers/Result.php @@ -0,0 +1,38 @@ + + */ + function getColumnsInfo(): array; + + /** Frees the result set. */ + function free(): void; +} diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 83eb3a198..9a252850d 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -274,29 +274,6 @@ public static function toPairs(array $rows, string|int|\Closure|null $key, strin } - /** - * Finds duplicate columns in select statement - */ - public static function findDuplicates(\PDOStatement $statement): string - { - $cols = []; - for ($i = 0; $i < $statement->columnCount(); $i++) { - $meta = $statement->getColumnMeta($i); - $cols[$meta['name']][] = $meta['table'] ?? ''; - } - - $duplicates = []; - foreach ($cols as $name => $tables) { - if (count($tables) > 1) { - $tables = array_filter(array_unique($tables)); - $duplicates[] = "'$name'" . ($tables ? ' (from ' . implode(', ', $tables) . ')' : ''); - } - } - - return implode(', ', $duplicates); - } - - /** @return array{type: string, size: ?int, scale: ?int, parameters: ?string} */ public static function parseColumnType(string $type): array { diff --git a/src/Database/Result.php b/src/Database/Result.php index 0ffeab5f1..5a2497667 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -18,7 +18,7 @@ */ class Result implements \Iterator { - private ?\PDOStatement $pdoStatement = null; + private ?Drivers\Result $result = null; private Row|false|null $lastRow = null; private int $lastRowKey = -1; @@ -40,7 +40,7 @@ public function __construct( if (str_starts_with($queryString, '::')) { $connection->getConnection()->{substr($queryString, 2)}(); } else { - $this->pdoStatement = $connection->getConnection()->query($queryString, $params); + $this->result = $connection->getConnection()->query($queryString, $params); } } catch (\PDOException $e) { $e = $connection->getDatabaseEngine()->convertException($e); @@ -61,15 +61,6 @@ public function getConnection(): Connection } - /** - * @internal - */ - public function getPdoStatement(): ?\PDOStatement - { - return $this->pdoStatement; - } - - public function getQueryString(): string { return $this->queryString; @@ -84,13 +75,13 @@ public function getParameters(): array public function getColumnCount(): ?int { - return $this->pdoStatement ? $this->pdoStatement->columnCount() : null; + return $this->result?->getColumnCount(); } public function getRowCount(): ?int { - return $this->pdoStatement ? $this->pdoStatement->rowCount() : null; + return $this->result?->getRowCount(); } @@ -163,14 +154,13 @@ public function fetchAssoc(?string $path = null): ?array return Arrays::associate($this->fetchAll(), $path); } - $data = $this->pdoStatement ? $this->pdoStatement->fetch() : null; - if (!$data) { - $this->pdoStatement->closeCursor(); + $data = $this->result?->fetch(); + if ($data === null) { return null; - } elseif ($this->lastRow === null && count($data) !== $this->pdoStatement->columnCount()) { - $duplicates = Helpers::findDuplicates($this->pdoStatement); - trigger_error("Found duplicate columns in database result set: $duplicates."); + } elseif ($this->lastRow === null && count($data) !== $this->result->getColumnCount()) { + $duplicates = array_filter(array_count_values(array_column($this->result->getColumnsInfo(), 'name')), fn($val) => $val > 1); + trigger_error("Found duplicate columns in database result set: '" . implode("', '", array_keys($duplicates)) . "'."); } return $this->normalizeRow($data); @@ -259,17 +249,8 @@ private function normalizeRow(array $row): array private function getColumnsMeta(): array { $res = []; - $metaTypeKey = $this->connection->getConnection()->metaTypeKey; - $count = $this->pdoStatement->columnCount(); - for ($i = 0; $i < $count; $i++) { - $meta = $this->pdoStatement->getColumnMeta($i); - if (isset($meta[$metaTypeKey])) { - $res[$meta['name']] = [ - 'nativeType' => $meta[$metaTypeKey], - 'size' => $meta['len'], - 'scale' => $meta['precision'], - ]; - } + foreach ($this->result->getColumnsInfo() as $meta) { + $res[$meta['name']] = $meta; } return $res; } diff --git a/src/Database/TypeConverter.php b/src/Database/TypeConverter.php index c20bd9cc2..78d5edc2c 100644 --- a/src/Database/TypeConverter.php +++ b/src/Database/TypeConverter.php @@ -63,7 +63,7 @@ private function detectType(string $nativeType): int public function convertToPhp(mixed $value, array $meta): mixed { - return match ($this->detectType($meta['nativeType'])) { + return match ($this->detectType($meta['nativeType'] ?? '')) { self::Integer => $this->toInt($value), self::Float => $this->toFloat($value), self::Decimal => $this->convertDecimal diff --git a/tests/Database/Result.fetch().phpt b/tests/Database/Result.fetch().phpt index bafe5bff4..36d5adb2a 100644 --- a/tests/Database/Result.fetch().phpt +++ b/tests/Database/Result.fetch().phpt @@ -18,9 +18,9 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($connection, $driverName) { $res = $connection->query('SELECT name, name FROM author'); $message = match ($driverName) { - 'mysql' => "Found duplicate columns in database result set: 'name' (from author).", + 'mysql' => "Found duplicate columns in database result set: 'name'.", 'pgsql' => "Found duplicate columns in database result set: 'name'%a%", - 'sqlite' => "Found duplicate columns in database result set: 'name' (from author).", + 'sqlite' => "Found duplicate columns in database result set: 'name'.", 'sqlsrv' => "Found duplicate columns in database result set: 'name'.", default => Assert::fail("Unsupported driver $driverName"), }; @@ -54,9 +54,9 @@ test('tests closeCursor()', function () use ($connection, $driverName) { test('', function () use ($connection, $driverName) { $res = $connection->query('SELECT book.id, author.id, author.name, translator.name FROM book JOIN author ON (author.id = book.author_id) JOIN author translator ON (translator.id = book.translator_id)'); $message = match ($driverName) { - 'mysql' => "Found duplicate columns in database result set: 'id' (from book, author), 'name' (from author, translator).", + 'mysql' => "Found duplicate columns in database result set: 'id', 'name'.", 'pgsql' => "Found duplicate columns in database result set: 'id'%a% 'name'%a%", - 'sqlite' => "Found duplicate columns in database result set: 'id' (from book, author), 'name' (from author).", + 'sqlite' => "Found duplicate columns in database result set: 'id', 'name'.", 'sqlsrv' => "Found duplicate columns in database result set: 'id', 'name'.", default => Assert::fail("Unsupported driver $driverName"), }; From 1e40d23412c4848f895c0240c54feff5bbb5afb0 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 15 Aug 2024 05:45:20 +0200 Subject: [PATCH 35/53] Result::getColumnMeta is cached (ref #212) --- src/Database/Drivers/PDO/Connection.php | 3 ++- src/Database/Drivers/PDO/PgSQL/Driver.php | 8 ++++++++ src/Database/Drivers/PDO/PgSQL/Result.php | 24 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/Database/Drivers/PDO/PgSQL/Result.php diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index 047978e17..303264f3a 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -15,6 +15,7 @@ class Connection implements Drivers\Connection { + public string $resultClass = Result::class; public string $metaTypeKey = 'native_type'; @@ -34,7 +35,7 @@ public function query(string $sql, array $params = []): Result } $statement->execute(); - return new Result($statement, $this); + return new ($this->resultClass)($statement, $this); } diff --git a/src/Database/Drivers/PDO/PgSQL/Driver.php b/src/Database/Drivers/PDO/PgSQL/Driver.php index 23d444a7b..289707b15 100644 --- a/src/Database/Drivers/PDO/PgSQL/Driver.php +++ b/src/Database/Drivers/PDO/PgSQL/Driver.php @@ -18,6 +18,14 @@ */ class Driver extends Drivers\PDO\Driver { + public function connect(): Drivers\PDO\Connection + { + $connection = parent::connect(); + $connection->resultClass = Result::class; + return $connection; + } + + public function createEngine(Drivers\Connection $connection): PostgreSQLEngine { return new PostgreSQLEngine($connection); diff --git a/src/Database/Drivers/PDO/PgSQL/Result.php b/src/Database/Drivers/PDO/PgSQL/Result.php new file mode 100644 index 000000000..482161829 --- /dev/null +++ b/src/Database/Drivers/PDO/PgSQL/Result.php @@ -0,0 +1,24 @@ +result->queryString] ??= parent::collectColumnsInfo(); + } +} From 9bc920b873c0bc3ba1adc1815d3bdb08e016db22 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 11:48:06 +0200 Subject: [PATCH 36/53] try/catch checking and converting of PDOException moved to drivers --- src/Bridges/DatabaseTracy/ConnectionPanel.php | 21 +++---- src/Database/Connection.php | 16 ++++-- src/Database/DriverException.php | 41 ++++++-------- src/Database/Drivers/Engine.php | 8 +-- src/Database/Drivers/Engines/MSSQLEngine.php | 4 +- src/Database/Drivers/Engines/MySQLEngine.php | 26 +++------ src/Database/Drivers/Engines/ODBCEngine.php | 4 +- src/Database/Drivers/Engines/OracleEngine.php | 22 +++----- .../Drivers/Engines/PostgreSQLEngine.php | 29 +++------- .../Drivers/Engines/SQLServerEngine.php | 4 +- src/Database/Drivers/Engines/SQLiteEngine.php | 31 +++++------ src/Database/Drivers/PDO/Connection.php | 55 +++++++++++++------ src/Database/Drivers/PDO/Driver.php | 16 +++++- src/Database/Drivers/PDO/Result.php | 28 +++++++--- src/Database/Result.php | 8 +-- tests/Database.Tracy/panel.html | 2 +- .../Database/Connection.exceptions.mysql.phpt | 1 - .../Connection.exceptions.postgre.phpt | 1 - .../Connection.exceptions.sqlite.phpt | 1 - .../Database/Explorer/Explorer.update().phpt | 2 +- .../Database/Explorer/Selection.insert().phpt | 4 +- tests/Database/connection.disconnect.phpt | 2 +- tests/bootstrap.php | 2 +- 23 files changed, 170 insertions(+), 158 deletions(-) diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 3c2ffdfdc..30d17fba3 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -11,6 +11,7 @@ use Nette; use Nette\Database\Connection; +use Nette\Database\DriverException; use Nette\Database\Helpers; use Nette\Database\Result; use Tracy; @@ -72,7 +73,7 @@ private function logQuery(Connection $connection, $result): void $this->count++; $source = null; - $trace = $result instanceof \PDOException + $trace = $result instanceof DriverException ? $result->getTrace() : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); foreach ($trace as $row) { @@ -93,26 +94,20 @@ private function logQuery(Connection $connection, $result): void if ($this->count < $this->maxQueries) { $this->events[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null]; } - } elseif ($result instanceof \PDOException && $this->count < $this->maxQueries) { - $this->events[] = [$connection, $result->queryString, null, $source, null, null, $result->getMessage()]; + } elseif ($result instanceof DriverException && $this->count < $this->maxQueries) { + $this->events[] = [$connection, $result->getQueryString(), null, $source, null, null, $result->getMessage()]; } } public static function renderException(?\Throwable $e): ?array { - if (!$e instanceof \PDOException) { + if (!$e instanceof DriverException) { return null; } - if (isset($e->queryString)) { - $sql = $e->queryString; - - } elseif ($item = Tracy\Helpers::findTrace($e->getTrace(), 'PDO::prepare')) { - $sql = $item['args'][0]; - } - - return isset($sql) ? [ + $sql = $e->getQueryString(); + return $sql ? [ 'tab' => 'SQL', 'panel' => Helpers::dumpSql($sql, $e->params ?? []), ] : null; @@ -150,7 +145,7 @@ public function getPanel(): ?string : 'EXPLAIN'; $rows = $connection->getConnection()->query("$cmd $sql", $params); for ($explain = []; $row = $rows->fetch(); $explain[] = $row); - } catch (\PDOException) { + } catch (DriverException) { } } diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 201e69982..a2e847f26 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -12,7 +12,6 @@ use JetBrains\PhpStorm\Language; use Nette; use Nette\Utils\Arrays; -use PDOException; /** @@ -79,7 +78,7 @@ public function connect(): void try { $this->connection = $this->driver->connect(); - } catch (PDOException $e) { + } catch (DriverException $e) { throw ConnectionException::from($e); } @@ -164,8 +163,8 @@ public function getInsertId(?string $sequence = null): string { try { return $this->getConnection()->getInsertId($sequence); - } catch (PDOException $e) { - throw $this->getDatabaseEngine()->convertException($e); + } catch (DriverException $e) { + throw $this->convertException($e); } } @@ -242,7 +241,7 @@ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] [$this->sql, $params] = $this->preprocess($sql, ...$params); try { $result = new Result($this, $this->sql, $params); - } catch (PDOException $e) { + } catch (DriverException $e) { Arrays::invoke($this->onQuery, $this, $e); throw $e; } @@ -280,6 +279,13 @@ public function getLastQueryString(): ?string } + public function convertException(DriverException $e): DriverException + { + $class = $this->getDatabaseEngine()->classifyException($e); + return $class ? $class::from($e) : $e; + } + + /********************* shortcuts ****************d*g**/ diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index aad02cd6e..9bec0913a 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -13,51 +13,46 @@ /** * Base class for all errors in the driver or SQL server. */ -class DriverException extends \PDOException +class DriverException extends \Exception { - public ?string $queryString = null; - public ?array $params = null; + public static function from(self $e): static + { + return new static($e->getMessage(), $e->sqlState, $e->getDriverCode() ?? 0, $e->query, $e); + } - public static function from(\PDOException $src): static - { - $e = new static($src->message, 0, $src); - $e->file = $src->file; - $e->line = $src->line; - if (!$src->errorInfo && preg_match('#SQLSTATE\[(.*?)\] \[(.*?)\] (.*)#A', $src->message, $m)) { - $m[2] = (int) $m[2]; - $e->errorInfo = array_slice($m, 1); - $e->code = $m[1]; - } else { - $e->errorInfo = $src->errorInfo; - $e->code = $src->code; - $e->code = $e->errorInfo[0] ?? $src->code; - } - - return $e; + public function __construct( + string $message, + private readonly ?string $sqlState = null, + private int $driverCode = 0, + private readonly ?SqlLiteral $query = null, + ?\Throwable $previous = null, + ) { + parent::__construct($message, 0, $previous); + $this->code = $sqlState ?: null; } public function getDriverCode(): int|string|null { - return $this->errorInfo[1] ?? null; + return $this->driverCode ?: null; } public function getSqlState(): ?string { - return $this->errorInfo[0] ?? null; + return $this->sqlState; } public function getQueryString(): ?string { - return $this->queryString; + return $this->query?->getSql(); } public function getParameters(): ?array { - return $this->params; + return $this->query?->getParameters(); } } diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index e12ef0e90..ce8c770ac 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -9,7 +9,7 @@ namespace Nette\Database\Drivers; -use Nette\Database; +use Nette\Database\DriverException; use Nette\Database\TypeConverter; @@ -32,10 +32,8 @@ interface Engine */ function isSupported(string $feature): bool; - /** - * Converts PDOException to DriverException or its descendant. - */ - function convertException(\PDOException $e): Database\DriverException; + /** Maps a driver exception to an appropriate exception class. */ + function classifyException(DriverException $e): ?string; /** Converts a value from the database to a PHP value. */ function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed; diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index ecf513087..b1d412277 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -32,9 +32,9 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - return Nette\Database\DriverException::from($e); + return null; } diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index b6e8ed26c..fa6fdc9b9 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -36,24 +36,16 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->errorInfo[1] ?? null; - if (in_array($code, [1216, 1217, 1451, 1452, 1701], strict: true)) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); - - } elseif (in_array($code, [1062, 1557, 1569, 1586], strict: true)) { - return Nette\Database\UniqueConstraintViolationException::from($e); - - } elseif ($code >= 2001 && $code <= 2028) { - return Nette\Database\ConnectionException::from($e); - - } elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], strict: true)) { - return Nette\Database\NotNullConstraintViolationException::from($e); - - } else { - return Nette\Database\DriverException::from($e); - } + $code = $e->getDriverCode(); + return match (true) { + in_array($code, [1216, 1217, 1451, 1452, 1701], strict: true) => Nette\Database\ForeignKeyConstraintViolationException::class, + in_array($code, [1062, 1557, 1569, 1586], strict: true) => Nette\Database\UniqueConstraintViolationException::class, + $code >= 2001 && $code <= 2028 => Nette\Database\ConnectionException::class, + in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], strict: true) => Nette\Database\NotNullConstraintViolationException::class, + default => null, + }; } diff --git a/src/Database/Drivers/Engines/ODBCEngine.php b/src/Database/Drivers/Engines/ODBCEngine.php index 1dcf80f98..869338ea4 100644 --- a/src/Database/Drivers/Engines/ODBCEngine.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -25,9 +25,9 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - return Nette\Database\DriverException::from($e); + return null; } diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index 5905bd972..33d36c243 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -35,21 +35,15 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->errorInfo[1] ?? null; - if (in_array($code, [1, 2299, 38911], strict: true)) { - return Nette\Database\UniqueConstraintViolationException::from($e); - - } elseif (in_array($code, [1400], strict: true)) { - return Nette\Database\NotNullConstraintViolationException::from($e); - - } elseif (in_array($code, [2266, 2291, 2292], strict: true)) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); - - } else { - return Nette\Database\DriverException::from($e); - } + $code = $e->getDriverCode(); + return match (true) { + in_array($code, [1, 2299, 38911], strict: true) => Nette\Database\UniqueConstraintViolationException::class, + in_array($code, [1400], strict: true) => Nette\Database\NotNullConstraintViolationException::class, + in_array($code, [2266, 2291, 2292], strict: true) => Nette\Database\ForeignKeyConstraintViolationException::class, + default => null, + }; } diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 1d73ea858..799fe7ee4 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -32,27 +32,16 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->errorInfo[0] ?? null; - if ($code === '0A000' && str_contains($e->getMessage(), 'truncate')) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); - - } elseif ($code === '23502') { - return Nette\Database\NotNullConstraintViolationException::from($e); - - } elseif ($code === '23503') { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); - - } elseif ($code === '23505') { - return Nette\Database\UniqueConstraintViolationException::from($e); - - } elseif ($code === '08006') { - return Nette\Database\ConnectionException::from($e); - - } else { - return Nette\Database\DriverException::from($e); - } + return match ($e->getSqlState()) { + '0A000' => str_contains($e->getMessage(), 'truncate') ? Nette\Database\ForeignKeyConstraintViolationException::class : null, + '23502' => Nette\Database\NotNullConstraintViolationException::class, + '23503' => Nette\Database\ForeignKeyConstraintViolationException::class, + '23505' => Nette\Database\UniqueConstraintViolationException::class, + '08006' => Nette\Database\ConnectionException::class, + default => null, + }; } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index db1957b04..1f1d04b56 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -32,9 +32,9 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - return Nette\Database\DriverException::from($e); + return null; } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 117a2248a..bd25b3b4a 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -36,34 +36,33 @@ public function isSupported(string $feature): bool } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->errorInfo[1] ?? null; - $msg = $e->getMessage(); - if ($code !== 19) { - return Nette\Database\DriverException::from($e); + $message = $e->getMessage(); + if ($e->getDriverCode() !== 19) { + return null; } elseif ( - str_contains($msg, 'must be unique') - || str_contains($msg, 'is not unique') - || str_contains($msg, 'UNIQUE constraint failed') + str_contains($message, 'must be unique') + || str_contains($message, 'is not unique') + || str_contains($message, 'UNIQUE constraint failed') ) { - return Nette\Database\UniqueConstraintViolationException::from($e); + return Nette\Database\UniqueConstraintViolationException::class; } elseif ( - str_contains($msg, 'may not be null') - || str_contains($msg, 'NOT NULL constraint failed') + str_contains($message, 'may not be null') + || str_contains($message, 'NOT NULL constraint failed') ) { - return Nette\Database\NotNullConstraintViolationException::from($e); + return Nette\Database\NotNullConstraintViolationException::class; } elseif ( - str_contains($msg, 'foreign key constraint failed') - || str_contains($msg, 'FOREIGN KEY constraint failed') + str_contains($message, 'foreign key constraint failed') + || str_contains($message, 'FOREIGN KEY constraint failed') ) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); + return Nette\Database\ForeignKeyConstraintViolationException::class; } else { - return Nette\Database\ConstraintViolationException::from($e); + return Nette\Database\ConstraintViolationException::class; } } diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index 303264f3a..d6fb4e9e0 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -9,8 +9,11 @@ namespace Nette\Database\Drivers\PDO; +use Nette\Database\DriverException; use Nette\Database\Drivers; +use Nette\Database\SqlLiteral; use PDO; +use PDOException; class Connection implements Drivers\Connection @@ -29,48 +32,68 @@ public function query(string $sql, array $params = []): Result { $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; - $statement = $this->pdo->prepare($sql); - foreach ($params as $key => $value) { - $statement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[gettype($value)] ?? PDO::PARAM_STR); - } + try { + $statement = $this->pdo->prepare($sql); + foreach ($params as $key => $value) { + $statement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[gettype($value)] ?? PDO::PARAM_STR); + } + $statement->execute(); + return new ($this->resultClass)($statement, $this); - $statement->execute(); - return new ($this->resultClass)($statement, $this); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e, new SqlLiteral($sql, $params))); + } } public function execute(string $sql): int { - return $this->pdo->exec($sql); + try { + return $this->pdo->exec($sql); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e, new SqlLiteral($sql))); + } } public function beginTransaction(): void { - $this->pdo->beginTransaction(); - + try { + $this->pdo->beginTransaction(); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } public function commit(): void { - $this->pdo->commit(); - + try { + $this->pdo->commit(); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } public function rollBack(): void { - $this->pdo->rollBack(); - + try { + $this->pdo->rollBack(); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } public function getInsertId(?string $sequence = null): string { - $res = $this->pdo->lastInsertId($sequence); - return $res === false ? '0' : $res; - + try { + $res = $this->pdo->lastInsertId($sequence); + return $res === false ? '0' : $res; + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } diff --git a/src/Database/Drivers/PDO/Driver.php b/src/Database/Drivers/PDO/Driver.php index 0b93232a7..21b2f1139 100644 --- a/src/Database/Drivers/PDO/Driver.php +++ b/src/Database/Drivers/PDO/Driver.php @@ -9,7 +9,10 @@ namespace Nette\Database\Drivers\PDO; +use Nette\Database\DriverException; use Nette\Database\Drivers; +use Nette\Database\SqlLiteral; +use PDOException; /** @@ -29,6 +32,17 @@ public function __construct( public function connect(): Connection { - return new Connection(new \PDO($this->dsn, $this->username, $this->password, $this->options)); + try { + $pdo = new \PDO($this->dsn, $this->username, $this->password, $this->options); + } catch (PDOException $e) { + throw new DriverException(...self::exceptionArgs($e)); + } + return new Connection($pdo); + } + + + public static function exceptionArgs(PDOException $e, ?SqlLiteral $query = null): array + { + return [$e->getMessage(), $e->errorInfo[0] ?? null, $e->errorInfo[1] ?? 0, $query, $e]; } } diff --git a/src/Database/Drivers/PDO/Result.php b/src/Database/Drivers/PDO/Result.php index f62a885ea..f17b88cef 100644 --- a/src/Database/Drivers/PDO/Result.php +++ b/src/Database/Drivers/PDO/Result.php @@ -11,6 +11,7 @@ use Nette\Database\DriverException; use Nette\Database\Drivers; +use PDOException; class Result implements Drivers\Result @@ -42,24 +43,37 @@ public function fetch(): ?array public function fetchList(): ?array { - $row = $this->result->fetch(\PDO::FETCH_NUM); - if (!$row) { - $this->free(); - return null; + try { + $row = $this->result->fetch(\PDO::FETCH_NUM); + if (!$row) { + $this->free(); + return null; + } + return $row; + + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); } - return $row; } public function getColumnCount(): int { - return $this->result->columnCount(); + try { + return $this->result->columnCount(); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } public function getRowCount(): int { - return $this->result->rowCount(); + try { + return $this->result->rowCount(); + } catch (PDOException $e) { + throw new DriverException(...Driver::exceptionArgs($e)); + } } diff --git a/src/Database/Result.php b/src/Database/Result.php index 5a2497667..031d1c5fb 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -35,18 +35,14 @@ public function __construct( ) { $time = microtime(true); - try { if (str_starts_with($queryString, '::')) { $connection->getConnection()->{substr($queryString, 2)}(); } else { $this->result = $connection->getConnection()->query($queryString, $params); } - } catch (\PDOException $e) { - $e = $connection->getDatabaseEngine()->convertException($e); - $e->queryString = $queryString; - $e->params = $params; - throw $e; + } catch (DriverException $e) { + throw $connection->convertException($e); } $this->time = microtime(true) - $time; diff --git a/tests/Database.Tracy/panel.html b/tests/Database.Tracy/panel.html index b6274a51a..7bb0c3ecd 100644 --- a/tests/Database.Tracy/panel.html +++ b/tests/Database.Tracy/panel.html @@ -28,7 +28,7 @@

Queries: 4, time: %a% ms, foo

ERROR
SELECT
- %a%SQLiteEngine.php:%d% + %a%Result.php:%d% diff --git a/tests/Database/Connection.exceptions.mysql.phpt b/tests/Database/Connection.exceptions.mysql.phpt index bd265534d..07f236957 100644 --- a/tests/Database/Connection.exceptions.mysql.phpt +++ b/tests/Database/Connection.exceptions.mysql.phpt @@ -33,7 +33,6 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', - 0, ); Assert::same(null, $e->getDriverCode()); }); diff --git a/tests/Database/Connection.exceptions.postgre.phpt b/tests/Database/Connection.exceptions.postgre.phpt index 248571641..8eabaa821 100644 --- a/tests/Database/Connection.exceptions.postgre.phpt +++ b/tests/Database/Connection.exceptions.postgre.phpt @@ -34,7 +34,6 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', - 0, ); Assert::same(null, $e->getDriverCode()); diff --git a/tests/Database/Connection.exceptions.sqlite.phpt b/tests/Database/Connection.exceptions.sqlite.phpt index b9cacb494..3dd697167 100644 --- a/tests/Database/Connection.exceptions.sqlite.phpt +++ b/tests/Database/Connection.exceptions.sqlite.phpt @@ -33,7 +33,6 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', - 0, ); Assert::same(null, $e->getDriverCode()); diff --git a/tests/Database/Explorer/Explorer.update().phpt b/tests/Database/Explorer/Explorer.update().phpt index 11ffd1a81..008bae316 100644 --- a/tests/Database/Explorer/Explorer.update().phpt +++ b/tests/Database/Explorer/Explorer.update().phpt @@ -79,7 +79,7 @@ $tag2 = $explorer->table('tag')->insert([ 'name' => 'PS4 Game', ]); // INSERT INTO `tag` (`name`) VALUES ('PS4 Game') -// SQL Server throw PDOException because does not allow to update identity column +// SQL Server throw exception because does not allow to update identity column if ($driverName !== 'sqlsrv') { $tag2->update([ 'id' => 1, diff --git a/tests/Database/Explorer/Selection.insert().phpt b/tests/Database/Explorer/Selection.insert().phpt index f96e6f88c..69e219c90 100644 --- a/tests/Database/Explorer/Selection.insert().phpt +++ b/tests/Database/Explorer/Selection.insert().phpt @@ -41,7 +41,7 @@ $book2 = $books->insert([ Assert::same('eddard stark', $book2->author->name); // SELECT * FROM `author` WHERE (`author`.`id` IN (11, 15)) -// SQL Server throw PDOException because does not allow insert explicit value for IDENTITY column. +// SQL Server throw exception because does not allow insert explicit value for IDENTITY column. // This exception is about primary key violation. if ($driverName !== 'sqlsrv') { Assert::exception( @@ -50,7 +50,7 @@ if ($driverName !== 'sqlsrv') { 'name' => 'Jon Snow', 'web' => 'http://example.com', ]), - PDOException::class, + Nette\Database\UniqueConstraintViolationException::class, ); } diff --git a/tests/Database/connection.disconnect.phpt b/tests/Database/connection.disconnect.phpt index 4fab40976..b1c3b25f0 100644 --- a/tests/Database/connection.disconnect.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -19,7 +19,7 @@ test('connect & disconnect', function () { $connection = new Nette\Database\Connection($options['dsn'], $options['username'], $options['password']); try { $connection->connect(); - } catch (PDOException $e) { + } catch (Nette\Database\DriverException $e) { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8ce5fd37b..e5ebcbdd2 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -38,7 +38,7 @@ function connectToDB(array $options = []): Nette\Database\Explorer try { $connection->connect(); - } catch (PDOException $e) { + } catch (Nette\Database\ConnectionException $e) { Tester\Environment::skip("Connection to '$args[dsn]' failed. Reason: " . $e->getMessage()); } From 955b99273442e9f993dfa67ab98c488975e8b168 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 27 Aug 2024 19:40:27 +0200 Subject: [PATCH 37/53] DriverException::getCode() returns driver error code instead of SQLState (BC break) --- src/Database/DriverException.php | 12 +++---- src/Database/Drivers/Engines/MySQLEngine.php | 2 +- src/Database/Drivers/Engines/OracleEngine.php | 2 +- src/Database/Drivers/Engines/SQLiteEngine.php | 2 +- .../Database/Connection.exceptions.mysql.phpt | 31 ++++++----------- .../Connection.exceptions.postgre.phpt | 34 +++++++------------ .../Connection.exceptions.sqlite.phpt | 34 +++++++------------ 7 files changed, 44 insertions(+), 73 deletions(-) diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index 9bec0913a..4ed5ed127 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -17,25 +17,25 @@ class DriverException extends \Exception { public static function from(self $e): static { - return new static($e->getMessage(), $e->sqlState, $e->getDriverCode() ?? 0, $e->query, $e); + return new static($e->getMessage(), $e->sqlState, $e->getCode(), $e->query, $e); } public function __construct( string $message, private readonly ?string $sqlState = null, - private int $driverCode = 0, + int $code = 0, private readonly ?SqlLiteral $query = null, ?\Throwable $previous = null, ) { - parent::__construct($message, 0, $previous); - $this->code = $sqlState ?: null; + parent::__construct($message, $code, $previous); } - public function getDriverCode(): int|string|null + /** @deprecated use getCode() */ + public function getDriverCode(): int { - return $this->driverCode ?: null; + return $this->getCode(); } diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index fa6fdc9b9..66a363dad 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -38,7 +38,7 @@ public function isSupported(string $feature): bool public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->getDriverCode(); + $code = $e->getCode(); return match (true) { in_array($code, [1216, 1217, 1451, 1452, 1701], strict: true) => Nette\Database\ForeignKeyConstraintViolationException::class, in_array($code, [1062, 1557, 1569, 1586], strict: true) => Nette\Database\UniqueConstraintViolationException::class, diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index 33d36c243..c895d5293 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -37,7 +37,7 @@ public function isSupported(string $feature): bool public function classifyException(Nette\Database\DriverException $e): ?string { - $code = $e->getDriverCode(); + $code = $e->getCode(); return match (true) { in_array($code, [1, 2299, 38911], strict: true) => Nette\Database\UniqueConstraintViolationException::class, in_array($code, [1400], strict: true) => Nette\Database\NotNullConstraintViolationException::class, diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index bd25b3b4a..8b1ea051a 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -39,7 +39,7 @@ public function isSupported(string $feature): bool public function classifyException(Nette\Database\DriverException $e): ?string { $message = $e->getMessage(); - if ($e->getDriverCode() !== 19) { + if ($e->getCode() !== 19) { return null; } elseif ( diff --git a/tests/Database/Connection.exceptions.mysql.phpt b/tests/Database/Connection.exceptions.mysql.phpt index 07f236957..ad3f5f30a 100644 --- a/tests/Database/Connection.exceptions.mysql.phpt +++ b/tests/Database/Connection.exceptions.mysql.phpt @@ -20,11 +20,9 @@ test('Exception thrown for invalid database credentials', function () { fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, '%a% Access denied for user %a%', + 1045, ); - - Assert::same(1045, $e->getDriverCode()); Assert::contains($e->getSqlState(), ['HY000', '28000']); - Assert::same($e->getCode(), $e->getSqlState()); }); @@ -33,8 +31,9 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', + 0, ); - Assert::same(null, $e->getDriverCode()); + Assert::null($e->getSqlState()); }); @@ -43,11 +42,9 @@ test('Exception thrown for syntax error in SQL query', function () use ($connect fn() => $connection->query('SELECT'), Nette\Database\DriverException::class, '%a% Syntax error %a%', - '42000', + 1064, ); - - Assert::same(1064, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('42000', $e->getSqlState()); }); @@ -56,11 +53,9 @@ test('Exception thrown for unique constraint violation', function () use ($conne fn() => $connection->query('INSERT INTO author (id, name, web, born) VALUES (11, "", "", NULL)'), Nette\Database\UniqueConstraintViolationException::class, '%a% Integrity constraint violation: %a%', - '23000', + 1062, ); - - Assert::same(1062, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23000', $e->getSqlState()); }); @@ -69,11 +64,9 @@ test('Exception thrown for not null constraint violation', function () use ($con fn() => $connection->query('INSERT INTO author (name, web, born) VALUES (NULL, "", NULL)'), Nette\Database\NotNullConstraintViolationException::class, '%a% Integrity constraint violation: %a%', - '23000', + 1048, ); - - Assert::same(1048, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23000', $e->getSqlState()); }); @@ -82,9 +75,7 @@ test('Exception thrown for foreign key constraint violation', function () use ($ fn() => $connection->query('INSERT INTO book (author_id, translator_id, title) VALUES (999, 12, "")'), Nette\Database\ForeignKeyConstraintViolationException::class, '%a% a foreign key constraint fails %a%', - '23000', + 1452, ); - - Assert::same(1452, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23000', $e->getSqlState()); }); diff --git a/tests/Database/Connection.exceptions.postgre.phpt b/tests/Database/Connection.exceptions.postgre.phpt index 8eabaa821..c5d48f22b 100644 --- a/tests/Database/Connection.exceptions.postgre.phpt +++ b/tests/Database/Connection.exceptions.postgre.phpt @@ -21,11 +21,9 @@ test('Exception thrown for invalid database credentials', function () { fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, null, - '08006', + 7, ); - - Assert::same(7, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('08006', $e->getSqlState()); }); @@ -34,9 +32,9 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', + 0, ); - - Assert::same(null, $e->getDriverCode()); + Assert::null($e->getSqlState()); }); @@ -45,11 +43,9 @@ test('Exception thrown for syntax error in SQL query', function () use ($connect fn() => $connection->query('SELECT INTO'), Nette\Database\DriverException::class, '%a% syntax error %A%', - '42601', + 7, ); - - Assert::same(7, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('42601', $e->getSqlState()); }); @@ -58,11 +54,9 @@ test('Exception thrown for unique constraint violation', function () use ($conne fn() => $connection->query("INSERT INTO author (id, name, web, born) VALUES (11, '', '', NULL)"), Nette\Database\UniqueConstraintViolationException::class, '%a% Unique violation: %A%', - '23505', + 7, ); - - Assert::same(7, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23505', $e->getSqlState()); }); @@ -71,11 +65,9 @@ test('Exception thrown for not null constraint violation', function () use ($con fn() => $connection->query("INSERT INTO author (name, web, born) VALUES (NULL, '', NULL)"), Nette\Database\NotNullConstraintViolationException::class, '%a% Not null violation: %A%', - '23502', + 7, ); - - Assert::same(7, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23502', $e->getSqlState()); }); @@ -84,9 +76,7 @@ test('Exception thrown for foreign key constraint violation', function () use ($ fn() => $connection->query("INSERT INTO book (author_id, translator_id, title) VALUES (999, 12, '')"), Nette\Database\ForeignKeyConstraintViolationException::class, '%a% Foreign key violation: %A%', - '23503', + 7, ); - - Assert::same(7, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23503', $e->getSqlState()); }); diff --git a/tests/Database/Connection.exceptions.sqlite.phpt b/tests/Database/Connection.exceptions.sqlite.phpt index 3dd697167..b6fa35da9 100644 --- a/tests/Database/Connection.exceptions.sqlite.phpt +++ b/tests/Database/Connection.exceptions.sqlite.phpt @@ -20,11 +20,9 @@ test('Exception thrown for unable to open database file', function () { fn() => (new Nette\Database\Connection('sqlite:.'))->connect(), Nette\Database\ConnectionException::class, 'SQLSTATE[HY000] [14] unable to open database file', - 'HY000', + 14, ); - - Assert::same(14, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('HY000', $e->getSqlState()); }); @@ -33,9 +31,9 @@ test('Exception thrown when calling rollback with no active transaction', functi fn() => $connection->rollback(), Nette\Database\DriverException::class, 'There is no active transaction', + 0, ); - - Assert::same(null, $e->getDriverCode()); + Assert::null($e->getSqlState()); }); @@ -44,11 +42,9 @@ test('Exception thrown for error in SQL query', function () use ($connection) { fn() => $connection->query('SELECT'), Nette\Database\DriverException::class, '%a% error%a%', - 'HY000', + 1, ); - - Assert::same(1, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('HY000', $e->getSqlState()); }); @@ -57,11 +53,9 @@ test('Exception thrown for unique constraint violation', function () use ($conne fn() => $connection->query('INSERT INTO author (id, name, web, born) VALUES (11, "", "", NULL)'), Nette\Database\UniqueConstraintViolationException::class, '%a% Integrity constraint violation: %a%', - '23000', + 19, ); - - Assert::same(19, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23000', $e->getSqlState()); }); @@ -70,11 +64,9 @@ test('Exception thrown for not null constraint violation', function () use ($con fn() => $connection->query('INSERT INTO author (name, web, born) VALUES (NULL, "", NULL)'), Nette\Database\NotNullConstraintViolationException::class, '%a% Integrity constraint violation: %a%', - '23000', + 19, ); - - Assert::same(19, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + Assert::same('23000', $e->getSqlState()); }); @@ -82,8 +74,6 @@ test('Exception thrown for foreign key constraint violation', function () use ($ $e = Assert::exception(function () use ($connection) { $connection->query('PRAGMA foreign_keys=true'); $connection->query('INSERT INTO book (author_id, translator_id, title) VALUES (999, 12, "")'); - }, Nette\Database\ForeignKeyConstraintViolationException::class, '%a% Integrity constraint violation: %a%', '23000'); - - Assert::same(19, $e->getDriverCode()); - Assert::same($e->getCode(), $e->getSqlState()); + }, Nette\Database\ForeignKeyConstraintViolationException::class, '%a% Integrity constraint violation: %a%', 19); + Assert::same('23000', $e->getSqlState()); }); From 235def4634cf42e3f7d745c8c1437df160161314 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 15:07:38 +0200 Subject: [PATCH 38/53] Connection: calling query() moved here from Result reverts bc4bebce --- src/Database/Connection.php | 39 ++++++++++++++++++++++----------- src/Database/Result.php | 17 ++------------ tests/Database.Tracy/panel.html | 6 ++--- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index a2e847f26..fb239c53b 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -181,7 +181,7 @@ public function beginTransaction(): void throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback'); } - $this->query('::beginTransaction'); + $this->logOperation($this->getConnection()->beginTransaction(...), new SqlLiteral('BEGIN TRANSACTION')); } @@ -191,7 +191,7 @@ public function commit(): void throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback'); } - $this->query('::commit'); + $this->logOperation($this->getConnection()->commit(...), new SqlLiteral('COMMIT')); } @@ -201,7 +201,7 @@ public function rollBack(): void throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback'); } - $this->query('::rollBack'); + $this->logOperation($this->getConnection()->rollBack(...), new SqlLiteral('ROLLBACK')); } @@ -239,15 +239,10 @@ public function transaction(callable $callback): mixed public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result { [$this->sql, $params] = $this->preprocess($sql, ...$params); - try { - $result = new Result($this, $this->sql, $params); - } catch (DriverException $e) { - Arrays::invoke($this->onQuery, $this, $e); - throw $e; - } - - Arrays::invoke($this->onQuery, $this, $result); - return $result; + return $this->logOperation( + fn() => $this->connection->query($this->sql, $params), + new SqlLiteral($this->sql, $params), + ); } @@ -273,13 +268,31 @@ public function preprocess(string $sql, ...$params): array } + private function logOperation(\Closure $callback, SqlLiteral $query): Result + { + try { + $time = microtime(true); + $result = $callback(); + $time = microtime(true) - $time; + } catch (DriverException $e) { + $e = $this->convertException($e); + Arrays::invoke($this->onQuery, $this, $e); + throw $e; + } + + $result = new Result($this, $query->getSql(), $query->getParameters(), $result, $time); + Arrays::invoke($this->onQuery, $this, $result); + return $result; + } + + public function getLastQueryString(): ?string { return $this->sql; } - public function convertException(DriverException $e): DriverException + private function convertException(DriverException $e): DriverException { $class = $this->getDatabaseEngine()->classifyException($e); return $class ? $class::from($e) : $e; diff --git a/src/Database/Result.php b/src/Database/Result.php index 031d1c5fb..ffe6e66c9 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -18,13 +18,11 @@ */ class Result implements \Iterator { - private ?Drivers\Result $result = null; private Row|false|null $lastRow = null; private int $lastRowKey = -1; /** @var Row[] */ private array $rows; - private float $time; private array $meta; @@ -32,20 +30,9 @@ public function __construct( private readonly Connection $connection, private readonly string $queryString, private readonly array $params, + private readonly ?Drivers\Result $result, + private float $time, ) { - $time = microtime(true); - - try { - if (str_starts_with($queryString, '::')) { - $connection->getConnection()->{substr($queryString, 2)}(); - } else { - $this->result = $connection->getConnection()->query($queryString, $params); - } - } catch (DriverException $e) { - throw $connection->convertException($e); - } - - $this->time = microtime(true) - $time; } diff --git a/tests/Database.Tracy/panel.html b/tests/Database.Tracy/panel.html index 7bb0c3ecd..292e5d4e8 100644 --- a/tests/Database.Tracy/panel.html +++ b/tests/Database.Tracy/panel.html @@ -6,7 +6,7 @@

Queries: 4, time: %a% ms, foo

%A% - @@ -19,7 +19,7 @@

Queries: 4, time: %a% ms, foo

- @@ -28,7 +28,7 @@

Queries: 4, time: %a% ms, foo

ERROR + %a%Connection.php:%d%
%A%
::beginTransaction
+
BEGIN TRANSACTION
%a%Connection.php:%d%
%A%
::commit
+
COMMIT
%a%Connection.php:%d%
SELECT
- %a%Result.php:%d%
From 2478ffbf98868a8b912a87efcf828557bda13f03 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 15:39:51 +0200 Subject: [PATCH 39/53] added Connection::getLastQuery(), Result::getQuery(), DriverException::getQuery(), replaces getSql() and getParameters() pairs --- src/Bridges/DatabaseTracy/ConnectionPanel.php | 14 ++++++------- .../templates/ConnectionPanel.panel.phtml | 4 ++-- src/Database/Connection.php | 20 +++++++++++++------ src/Database/DriverException.php | 8 ++++++++ src/Database/Helpers.php | 13 ++++++------ src/Database/Result.php | 15 ++++++++++---- tests/Database/Connection.query.phpt | 14 ++++++------- tests/Database/Explorer.query.phpt | 12 +++++------ .../Explorer/Explorer.cache.observer.phpt | 2 +- tests/Database/Helpers.dumpSql.phpt | 18 ++++++++++------- tests/Database/Result.fetchAll().phpt | 2 +- 11 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 30d17fba3..542a7b7ae 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -92,10 +92,10 @@ private function logQuery(Connection $connection, $result): void if ($result instanceof Result) { $this->totalTime += $result->getTime(); if ($this->count < $this->maxQueries) { - $this->events[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null]; + $this->events[] = [$connection, $result->getQuery(), $source, $result->getTime(), $result->getRowCount(), null]; } } elseif ($result instanceof DriverException && $this->count < $this->maxQueries) { - $this->events[] = [$connection, $result->getQueryString(), null, $source, null, null, $result->getMessage()]; + $this->events[] = [$connection, $result->getQuery(), $source, null, null, $result->getMessage()]; } } @@ -106,10 +106,9 @@ public static function renderException(?\Throwable $e): ?array return null; } - $sql = $e->getQueryString(); - return $sql ? [ + return $e->getQuery() ? [ 'tab' => 'SQL', - 'panel' => Helpers::dumpSql($sql, $e->params ?? []), + 'panel' => Helpers::dumpSql($e->getQuery()), ] : null; } @@ -133,8 +132,9 @@ public function getPanel(): ?string $events = []; foreach ($this->events as $event) { - [$connection, $sql, $params, , , , $error] = $event; + [$connection, $query, , , , $error] = $event; $explain = null; + $sql = $query->getSql(); $command = preg_match('#\s*\(?\s*(SELECT|INSERT|UPDATE|DELETE)\s#iA', $sql, $m) ? strtolower($m[1]) : null; @@ -143,7 +143,7 @@ public function getPanel(): ?string $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN'; - $rows = $connection->getConnection()->query("$cmd $sql", $params); + $rows = $connection->getConnection()->query("$cmd $sql", $query->getParameters()); for ($explain = []; $row = $rows->fetch(); $explain[] = $row); } catch (DriverException) { } diff --git a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml index b6af47406..609ed0fd7 100644 --- a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml +++ b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml @@ -25,7 +25,7 @@ use Tracy\Helpers; Time msSQL QueryRows @@ -36,7 +36,7 @@ use Tracy\Helpers;
explain - + diff --git a/src/Database/Connection.php b/src/Database/Connection.php index fb239c53b..9f7794c68 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -40,7 +40,7 @@ class Connection private Drivers\Engine $engine; private SqlPreprocessor $preprocessor; private TypeConverter $typeConverter; - private ?string $sql = null; + private ?SqlLiteral $lastQuery = null; private int $transactionDepth = 0; @@ -238,10 +238,10 @@ public function transaction(callable $callback): mixed */ public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result { - [$this->sql, $params] = $this->preprocess($sql, ...$params); + [$sql, $params] = $this->preprocess($sql, ...$params); return $this->logOperation( - fn() => $this->connection->query($this->sql, $params), - new SqlLiteral($this->sql, $params), + fn() => $this->connection->query($sql, $params), + $this->lastQuery = new SqlLiteral($sql, $params), ); } @@ -280,15 +280,23 @@ private function logOperation(\Closure $callback, SqlLiteral $query): Result throw $e; } - $result = new Result($this, $query->getSql(), $query->getParameters(), $result, $time); + $result = new Result($this, $query, $result, $time); Arrays::invoke($this->onQuery, $this, $result); return $result; } + public function getLastQuery(): ?SqlLiteral + { + return $this->lastQuery; + } + + + /** @deprecated use getLastQuery()->getSql() */ public function getLastQueryString(): ?string { - return $this->sql; + trigger_error(__METHOD__ . '() is deprecated, use getLastQuery()->getSql()', E_USER_DEPRECATED); + return $this->lastQuery?->getSql(); } diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index 4ed5ed127..700c01186 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -45,12 +45,20 @@ public function getSqlState(): ?string } + public function getQuery(): ?SqlLiteral + { + return $this->query; + } + + + /** @deprecated use getQuery()->getSql() */ public function getQueryString(): ?string { return $this->query?->getSql(); } + /** @deprecated use getQuery()->getParameters() */ public function getParameters(): ?array { return $this->query?->getParameters(); diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 9a252850d..0f3c9082e 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -30,7 +30,7 @@ class Helpers */ public static function dumpResult(Result $result): void { - echo "\n
\n\n"; + echo "\n
" . htmlspecialchars($result->getQueryString(), ENT_IGNORE, 'UTF-8') . "
\n\n"; if (!$result->getColumnCount()) { echo "\t\n\t\t\n\t\t\n\t\n
" . htmlspecialchars($result->getQuery()->getSql(), ENT_IGNORE, 'UTF-8') . "
Affected rows:", $result->getRowCount(), "
\n"; return; @@ -75,12 +75,13 @@ public static function dumpResult(Result $result): void /** * Returns syntax highlighted SQL command. */ - public static function dumpSql(string $sql, ?array $params = null, ?Connection $connection = null): string + public static function dumpSql(SqlLiteral $query, ?Connection $connection = null): string { $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|[RI]?LIKE|REGEXP|TRUE|FALSE'; // insert new lines + $sql = $query->getSql(); $sql = " $sql "; $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); @@ -108,14 +109,14 @@ public static function dumpSql(string $sql, ?array $params = null, ?Connection $ }, $sql); // parameters + $params = $query->getParameters(); $sql = preg_replace_callback('#\?#', function () use ($params, $connection): string { static $i = 0; - if (!isset($params[$i])) { + $param = $params[$i++] ?? null; + if ($param === null) { return '?'; - } - $param = $params[$i++]; - if ( + } elseif ( is_string($param) && ( preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $param) diff --git a/src/Database/Result.php b/src/Database/Result.php index ffe6e66c9..2a453e4e2 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -28,8 +28,7 @@ class Result implements \Iterator public function __construct( private readonly Connection $connection, - private readonly string $queryString, - private readonly array $params, + private readonly SqlLiteral $query, private readonly ?Drivers\Result $result, private float $time, ) { @@ -44,15 +43,23 @@ public function getConnection(): Connection } + public function getQuery(): SqlLiteral + { + return $this->query; + } + + + /** @deprecated use getQuery()->getSql() */ public function getQueryString(): string { - return $this->queryString; + return $this->query->getSql(); } + /** @deprecated use getQuery()->getParameters() */ public function getParameters(): array { - return $this->params; + return $this->query->getParameters(); } diff --git a/tests/Database/Connection.query.phpt b/tests/Database/Connection.query.phpt index e8f14b74f..7fba6e7ff 100644 --- a/tests/Database/Connection.query.phpt +++ b/tests/Database/Connection.query.phpt @@ -18,21 +18,21 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($connection) { $res = $connection->query('SELECT id FROM author WHERE id = ?', 11); Assert::type(Nette\Database\Result::class, $res); - Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString()); - Assert::same([11], $res->getParameters()); - Assert::same('SELECT id FROM author WHERE id = ?', $connection->getLastQueryString()); + Assert::same('SELECT id FROM author WHERE id = ?', $res->getQuery()->getSql()); + Assert::same([11], $res->getQuery()->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ?', $connection->getLastQuery()->getSql()); }); test('', function () use ($connection) { $res = $connection->query('SELECT id FROM author WHERE id = ? OR id = ?', 11, 12); - Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); - Assert::same([11, 12], $res->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql()); + Assert::same([11, 12], $res->getQuery()->getParameters()); }); test('', function () use ($connection) { $res = @$connection->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated - Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); - Assert::same([11, 12], $res->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql()); + Assert::same([11, 12], $res->getQuery()->getParameters()); }); diff --git a/tests/Database/Explorer.query.phpt b/tests/Database/Explorer.query.phpt index feaeac8e1..bf4f9342c 100644 --- a/tests/Database/Explorer.query.phpt +++ b/tests/Database/Explorer.query.phpt @@ -20,20 +20,20 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('', function () use ($explorer) { $res = $explorer->query('SELECT id FROM author WHERE id = ?', 11); Assert::type(Nette\Database\Result::class, $res); - Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString()); - Assert::same([11], $res->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ?', $res->getQuery()->getSql()); + Assert::same([11], $res->getQuery()->getParameters()); }); test('', function () use ($explorer) { $res = $explorer->query('SELECT id FROM author WHERE id = ? OR id = ?', 11, 12); - Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); - Assert::same([11, 12], $res->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql()); + Assert::same([11, 12], $res->getQuery()->getParameters()); }); test('', function () use ($explorer) { $res = @$explorer->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated - Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString()); - Assert::same([11, 12], $res->getParameters()); + Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql()); + Assert::same([11, 12], $res->getQuery()->getParameters()); }); diff --git a/tests/Database/Explorer/Explorer.cache.observer.phpt b/tests/Database/Explorer/Explorer.cache.observer.phpt index eee6c028f..0e3b23e78 100644 --- a/tests/Database/Explorer/Explorer.cache.observer.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer.phpt @@ -27,7 +27,7 @@ $explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $queries = 0; $connection->onQuery[] = function ($dao, Result $result) use (&$queries) { - if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQueryString())) { + if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQuery()->getSql())) { $queries++; } }; diff --git a/tests/Database/Helpers.dumpSql.phpt b/tests/Database/Helpers.dumpSql.phpt index 907a870b5..e080bfa90 100644 --- a/tests/Database/Helpers.dumpSql.phpt +++ b/tests/Database/Helpers.dumpSql.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); +use Nette\Database\SqlLiteral; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -18,47 +19,50 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName test('int check', function () use ($connection) { Assert::same( "
SELECT id \nFROM author \nWHERE id = 10 OR id = 11
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE id = ? OR id = ?', [10, 11], $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE id = ? OR id = ?', [10, 11]), $connection), ); }); test('bool check', function () use ($connection) { Assert::same( "
SELECT id \nFROM author \nWHERE deleted = 0
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE deleted = ?', [false], $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE deleted = ?', [false]), $connection), ); }); test('string check', function () use ($connection) { Assert::same( "
SELECT id \nFROM author \nWHERE name = 'Alexej Chruščev'
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ['Alexej Chruščev'], $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ['Alexej Chruščev']), $connection), ); }); test('string check with \'', function () use ($connection) { Assert::same( "
SELECT id \nFROM author \nWHERE name = 'Alexej Ch\\'ruščev'
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection), ); }); test('string check without connection', function () { Assert::same( "
SELECT id \nFROM author \nWHERE name = 'Alexej Ch'ruščev'
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"])), ); }); test('string compare with $connection vs without', function () use ($connection) { - Assert::notSame(Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection), Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"])); + Assert::notSame( + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"])), + ); }); test('string check with \'', function () use ($connection) { Nette\Database\Helpers::$maxLength = 10; Assert::same( "
SELECT id \nFROM author \nWHERE name = 'Alexej Ch…'
\n", - Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection), + Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection), ); }); diff --git a/tests/Database/Result.fetchAll().phpt b/tests/Database/Result.fetchAll().phpt index d9adff617..6ae7375ab 100644 --- a/tests/Database/Result.fetchAll().phpt +++ b/tests/Database/Result.fetchAll().phpt @@ -24,7 +24,7 @@ match ($driverName) { }; Assert::same(1, $res->getColumnCount()); -Assert::same('SELECT id FROM book ORDER BY id', $res->getQueryString()); +Assert::same('SELECT id FROM book ORDER BY id', $res->getQuery()->getSql()); Assert::equal([ Nette\Database\Row::from(['id' => 1]), From 3cadf6ee36c4422523b238afc2deea19d4f7597b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 14 Aug 2024 15:51:10 +0200 Subject: [PATCH 40/53] Connection::getInsertId() returns int|string or exception (BC break) --- src/Database/Connection.php | 2 +- src/Database/Drivers/PDO/Connection.php | 11 ++++++-- src/Database/Explorer.php | 2 +- .../Connection.getInsertId().mysql.phpt | 28 +++++++++---------- .../Connection.getInsertId().postgre.phpt | 12 ++++---- .../Connection.getInsertId().sqlite.phpt | 14 +++++----- .../Connection.getInsertId().sqlsrv.phpt | 28 +++++++++---------- 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 9f7794c68..cd21a9674 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -159,7 +159,7 @@ public function setRowNormalizer(?callable $normalizer): static } - public function getInsertId(?string $sequence = null): string + public function getInsertId(?string $sequence = null): int|string { try { return $this->getConnection()->getInsertId($sequence); diff --git a/src/Database/Drivers/PDO/Connection.php b/src/Database/Drivers/PDO/Connection.php index d6fb4e9e0..540b725d5 100644 --- a/src/Database/Drivers/PDO/Connection.php +++ b/src/Database/Drivers/PDO/Connection.php @@ -86,11 +86,16 @@ public function rollBack(): void } - public function getInsertId(?string $sequence = null): string + public function getInsertId(?string $sequence = null): int|string { try { - $res = $this->pdo->lastInsertId($sequence); - return $res === false ? '0' : $res; + $id = $this->pdo->lastInsertId($sequence); + if ($id === '0' || $id === '' || $id === false) { + throw new DriverException('Cannot retrieve last generated ID.'); + } + $int = (int) $id; + return $id === (string) $int ? $int : $id; + } catch (PDOException $e) { throw new DriverException(...Driver::exceptionArgs($e)); } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index a6f084a10..312ae643f 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -56,7 +56,7 @@ public function transaction(callable $callback): mixed } - public function getInsertId(?string $sequence = null): string + public function getInsertId(?string $sequence = null): int|string { return $this->connection->getInsertId($sequence); } diff --git a/tests/Database/Connection.getInsertId().mysql.phpt b/tests/Database/Connection.getInsertId().mysql.phpt index 020ef596c..1dee7e83d 100644 --- a/tests/Database/Connection.getInsertId().mysql.phpt +++ b/tests/Database/Connection.getInsertId().mysql.phpt @@ -19,11 +19,11 @@ $connection->query(' ) ENGINE=InnoDB '); -$connection->query('INSERT INTO noprimarykey (col) VALUES (NULL)'); -Assert::same('0', $connection->getInsertId()); - $connection->query('INSERT INTO noprimarykey (col) VALUES (3)'); -Assert::same('0', $connection->getInsertId()); +Assert::exception( + fn() => $connection->getInsertId(), + Nette\Database\DriverException::class, +); $connection->query(' @@ -34,10 +34,10 @@ $connection->query(' '); $connection->query('INSERT INTO primarykey (prim) VALUES (5)'); -Assert::same('0', $connection->getInsertId()); - -$connection->query('INSERT INTO primarykey (prim) VALUES (6)'); -Assert::same('0', $connection->getInsertId()); +Assert::exception( + fn() => $connection->getInsertId(), + Nette\Database\DriverException::class, +); $connection->query(' @@ -49,13 +49,13 @@ $connection->query(' '); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query('INSERT INTO autoprimarykey (prim, col) VALUES (10, NULL)'); -Assert::same('10', $connection->getInsertId()); +Assert::same(10, $connection->getInsertId()); $connection->query(' @@ -67,10 +67,10 @@ $connection->query(' '); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query('INSERT INTO multiautoprimarykey (prim1, prim2) VALUES (10, 3)'); -Assert::same('10', $connection->getInsertId()); +Assert::same(10, $connection->getInsertId()); diff --git a/tests/Database/Connection.getInsertId().postgre.phpt b/tests/Database/Connection.getInsertId().postgre.phpt index 5c0a65c1c..60dc58b73 100644 --- a/tests/Database/Connection.getInsertId().postgre.phpt +++ b/tests/Database/Connection.getInsertId().postgre.phpt @@ -36,13 +36,13 @@ $connection->query(' '); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('1', $connection->getInsertId('autoprimarykey_prim_seq')); +Assert::same(1, $connection->getInsertId('autoprimarykey_prim_seq')); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('2', $connection->getInsertId('autoprimarykey_prim_seq')); +Assert::same(2, $connection->getInsertId('autoprimarykey_prim_seq')); $connection->query('INSERT INTO autoprimarykey (prim, col) VALUES (10, NULL)'); -Assert::same('2', $connection->getInsertId('autoprimarykey_prim_seq')); +Assert::same(2, $connection->getInsertId('autoprimarykey_prim_seq')); $connection->query(' @@ -54,10 +54,10 @@ $connection->query(' '); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('1', $connection->getInsertId('multiautoprimarykey_prim1_seq')); +Assert::same(1, $connection->getInsertId('multiautoprimarykey_prim1_seq')); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('2', $connection->getInsertId('multiautoprimarykey_prim1_seq')); +Assert::same(2, $connection->getInsertId('multiautoprimarykey_prim1_seq')); $connection->query('INSERT INTO multiautoprimarykey (prim1, prim2) VALUES (10, 3)'); -Assert::same('2', $connection->getInsertId('multiautoprimarykey_prim1_seq')); +Assert::same(2, $connection->getInsertId('multiautoprimarykey_prim1_seq')); diff --git a/tests/Database/Connection.getInsertId().sqlite.phpt b/tests/Database/Connection.getInsertId().sqlite.phpt index 26f57ac46..fcfac3213 100644 --- a/tests/Database/Connection.getInsertId().sqlite.phpt +++ b/tests/Database/Connection.getInsertId().sqlite.phpt @@ -20,10 +20,10 @@ $connection->query(' '); $connection->query('INSERT INTO noprimarykey (col) VALUES (NULL)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO noprimarykey (col) VALUES (3)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query(' @@ -33,10 +33,10 @@ $connection->query(' '); $connection->query('INSERT INTO primarykey (prim) VALUES (5)'); -Assert::same('5', $connection->getInsertId()); +Assert::same(5, $connection->getInsertId()); $connection->query('INSERT INTO primarykey (prim) VALUES (6)'); -Assert::same('6', $connection->getInsertId()); +Assert::same(6, $connection->getInsertId()); $connection->query(' @@ -47,10 +47,10 @@ $connection->query(' '); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query('INSERT INTO autoprimarykey (prim, col) VALUES (10, NULL)'); -Assert::same('10', $connection->getInsertId()); +Assert::same(10, $connection->getInsertId()); diff --git a/tests/Database/Connection.getInsertId().sqlsrv.phpt b/tests/Database/Connection.getInsertId().sqlsrv.phpt index e971245c7..c54444189 100644 --- a/tests/Database/Connection.getInsertId().sqlsrv.phpt +++ b/tests/Database/Connection.getInsertId().sqlsrv.phpt @@ -21,10 +21,10 @@ $connection->query(' '); $connection->query('INSERT INTO noprimarykey (col) VALUES (NULL)'); -Assert::same('', $connection->getInsertId()); - -$connection->query('INSERT INTO noprimarykey (col) VALUES (NULL)'); -Assert::same('', $connection->getInsertId()); +Assert::exception( + fn() => $connection->getInsertId(), + Nette\Database\DriverException::class, +); $connection->query("IF OBJECT_ID('primarykey', 'U') IS NOT NULL DROP TABLE primarykey"); @@ -36,10 +36,10 @@ $connection->query(' '); $connection->query('INSERT INTO primarykey (prim) VALUES (5)'); -Assert::same('', $connection->getInsertId()); - -$connection->query('INSERT INTO primarykey (prim) VALUES (6)'); -Assert::same('', $connection->getInsertId()); +Assert::exception( + fn() => $connection->getInsertId(), + Nette\Database\DriverException::class, +); $connection->query("IF OBJECT_ID('autoprimarykey', 'U') IS NOT NULL DROP TABLE autoprimarykey"); @@ -52,13 +52,13 @@ $connection->query(' '); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO autoprimarykey (col) VALUES (NULL)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query('SET IDENTITY_INSERT autoprimarykey ON; INSERT INTO autoprimarykey (prim, col) VALUES (10, NULL)'); -Assert::same('10', $connection->getInsertId()); +Assert::same(10, $connection->getInsertId()); $connection->query("IF OBJECT_ID('multiautoprimarykey', 'U') IS NOT NULL DROP TABLE multiautoprimarykey"); @@ -71,10 +71,10 @@ $connection->query(' '); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('1', $connection->getInsertId()); +Assert::same(1, $connection->getInsertId()); $connection->query('INSERT INTO multiautoprimarykey (prim2) VALUES (3)'); -Assert::same('2', $connection->getInsertId()); +Assert::same(2, $connection->getInsertId()); $connection->query('SET IDENTITY_INSERT multiautoprimarykey ON; INSERT INTO multiautoprimarykey (prim1, prim2) VALUES (10, 3)'); -Assert::same('10', $connection->getInsertId()); +Assert::same(10, $connection->getInsertId()); From 11f5d20993b1844565713073567962f9f3ad2741 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:51:43 +0200 Subject: [PATCH 41/53] drivers: getForeignKeys() works with multi-column foreign keys --- src/Database/Drivers/Engine.php | 2 +- src/Database/Drivers/Engines/MSSQLEngine.php | 8 ++++---- src/Database/Drivers/Engines/MySQLEngine.php | 8 ++++---- src/Database/Drivers/Engines/PostgreSQLEngine.php | 9 +++++++-- src/Database/Drivers/Engines/SQLServerEngine.php | 6 +++++- src/Database/Drivers/Engines/SQLiteEngine.php | 7 +++++-- src/Database/Reflection/Table.php | 4 ++-- src/Database/Structure.php | 12 +++--------- tests/Database/Engine.postgre.phpt | 4 ++-- tests/Database/Structure.phpt | 8 ++++---- tests/Database/Structure.schemas.phpt | 4 ++-- 11 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index ce8c770ac..320a5872d 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -74,7 +74,7 @@ function getIndexes(string $table): array; /** * Returns information about foreign keys in a table. - * @return list + * @return list, table: string, foreign: list}> */ function getForeignKeys(string $table): array; } diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index b1d412277..63c6e9f21 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -201,12 +201,12 @@ public function getForeignKeys(string $table): array tab1.name = ? X, [$table_name]); - $id = 0; while ($row = $rows->fetch()) { - $keys[$id]['name'] = $row['fk_name']; - $keys[$id]['local'] = $row['column']; + $id = $row['fk_name']; + $keys[$id]['name'] = $id; + $keys[$id]['local'][] = $row['column']; $keys[$id]['table'] = $table_schema . '.' . $row['referenced_table']; - $keys[$id++]['foreign'] = $row['referenced_column']; + $keys[$id]['foreign'][] = $row['referenced_column']; } return array_values($keys); diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index 66a363dad..1e23a98b4 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -155,12 +155,12 @@ public function getForeignKeys(string $table): array AND TABLE_NAME = ? X, [$table]); - $id = 0; while ($row = $rows->fetch()) { - $keys[$id]['name'] = $row['CONSTRAINT_NAME']; - $keys[$id]['local'] = $row['COLUMN_NAME']; + $id = $row['CONSTRAINT_NAME']; + $keys[$id]['name'] = $id; + $keys[$id]['local'][] = $row['COLUMN_NAME']; $keys[$id]['table'] = $row['REFERENCED_TABLE_NAME']; - $keys[$id++]['foreign'] = $row['REFERENCED_COLUMN_NAME']; + $keys[$id]['foreign'][] = $row['REFERENCED_COLUMN_NAME']; } return array_values($keys); diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 799fe7ee4..8f7869b74 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -219,9 +219,14 @@ public function getForeignKeys(string $table): array X, [$this->delimiteFQN($table)]); while ($row = $rows->fetch()) { - $keys[] = $row; + $id = $row['name']; + $keys[$id]['name'] = $id; + $keys[$id]['local'][] = $row['local']; + $keys[$id]['table'] = $row['table']; + $keys[$id]['foreign'][] = $row['foreign']; } - return $keys; + + return array_values($keys); } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index 1f1d04b56..da04de289 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -209,7 +209,11 @@ public function getForeignKeys(string $table): array X, [$table]); while ($row = $rows->fetch()) { - $keys[$row['name']] = $row; + $id = $row['name']; + $keys[$id]['name'] = $id; + $keys[$id]['local'][] = $row['local']; + $keys[$id]['table'] = $row['table']; + $keys[$id]['foreign'][] = $row['foreign']; } return array_values($keys); diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 8b1ea051a..c9245bab7 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -218,9 +218,12 @@ public function getForeignKeys(string $table): array while ($row = $rows->fetch()) { $id = $row['id']; $keys[$id]['name'] = $id; - $keys[$id]['local'] = $row['from']; + $keys[$id]['local'][] = $row['from']; $keys[$id]['table'] = $row['table']; - $keys[$id]['foreign'] = $row['to']; + $keys[$id]['foreign'][] = $row['to']; + if ($keys[$id]['foreign'][0] == null) { + $keys[$id]['foreign'] = []; + } } return array_values($keys); diff --git a/src/Database/Reflection/Table.php b/src/Database/Reflection/Table.php index da684cc7b..948ee2e03 100644 --- a/src/Database/Reflection/Table.php +++ b/src/Database/Reflection/Table.php @@ -87,8 +87,8 @@ private function initForeignKeys(): void $id = $row['name']; $foreignTable = $this->reflection->getTable($row['table']); $tmp[$id][0] = $foreignTable; - $tmp[$id][1][] = $this->getColumn($row['local']); - $tmp[$id][2][] = $foreignTable->getColumn($row['foreign']); + $tmp[$id][1] = array_map(fn($name) => $this->getColumn($name), $row['local']); + $tmp[$id][2] = array_map(fn($name) => $foreignTable->getColumn($name), $row['foreign']); $tmp[$id][3] = is_string($id) ? $id : null; } $this->foreignKeys = array_map(fn($row) => new ForeignKey(...$row), array_values($tmp)); diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 2b3ae3076..4a3b059f6 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -235,17 +235,11 @@ protected function analyzeForeignKeys(array &$structure, string $table): void $foreignKeys = $this->connection->getDatabaseEngine()->getForeignKeys($table); - $fksColumnsCounts = []; - foreach ($foreignKeys as $foreignKey) { - $tmp = &$fksColumnsCounts[$foreignKey['name']]; - $tmp++; - } - - usort($foreignKeys, fn($a, $b): int => $fksColumnsCounts[$b['name']] <=> $fksColumnsCounts[$a['name']]); + usort($foreignKeys, fn($a, $b): int => count($b['local']) <=> count($a['local'])); foreach ($foreignKeys as $row) { - $structure['belongsTo'][$lowerTable][$row['local']] = $row['table']; - $structure['hasMany'][strtolower($row['table'])][$table][] = $row['local']; + $structure['belongsTo'][$lowerTable][$row['local'][0]] = $row['table']; + $structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'][0]; } if (isset($structure['belongsTo'][$lowerTable])) { diff --git a/tests/Database/Engine.postgre.phpt b/tests/Database/Engine.postgre.phpt index 9ca883377..0084766ce 100644 --- a/tests/Database/Engine.postgre.phpt +++ b/tests/Database/Engine.postgre.phpt @@ -65,9 +65,9 @@ test('Tables in schema', function () use ($connection) { $foreign = $engine->getForeignKeys('one.slave'); Assert::same([ 'name' => 'one_slave_fk', - 'local' => 'one_id', + 'local' => ['one_id'], 'table' => 'one.master', - 'foreign' => 'one_id', + 'foreign' => ['one_id'], ], (array) $foreign[0]); diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index b9e2b559a..d54f720b4 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -74,13 +74,13 @@ class StructureTestCase extends TestCase $this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine); $this->engine->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]); $this->engine->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([ - ['local' => 'author_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk1'], - ['local' => 'translator_id', 'table' => 'authors', 'foreign' => 'id', 'name' => 'authors_fk2'], + ['local' => ['author_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk1'], + ['local' => ['translator_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk2'], ]); $this->engine->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]); $this->engine->shouldReceive('getForeignKeys')->with('books_x_tags')->once()->andReturn([ - ['local' => 'book_id', 'table' => 'Books', 'foreign' => 'id', 'name' => 'books_x_tags_fk1'], - ['local' => 'tag_id', 'table' => 'tags', 'foreign' => 'id', 'name' => 'books_x_tags_fk2'], + ['local' => ['book_id'], 'table' => 'Books', 'foreign' => ['id'], 'name' => 'books_x_tags_fk1'], + ['local' => ['tag_id'], 'table' => 'tags', 'foreign' => ['id'], 'name' => 'books_x_tags_fk2'], ]); $this->structure = new StructureMock($this->connection, $this->storage); diff --git a/tests/Database/Structure.schemas.phpt b/tests/Database/Structure.schemas.phpt index 406b47fc7..3f1fbf9f0 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -60,8 +60,8 @@ class StructureSchemasTestCase extends TestCase $this->connection->shouldReceive('getDatabaseEngine')->times(2)->andReturn($this->engine); $this->engine->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]); $this->engine->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([ - ['local' => 'author_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk1'], - ['local' => 'translator_id', 'table' => 'authors.authors', 'foreign' => 'id', 'name' => 'authors_authors_fk2'], + ['local' => ['author_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk1'], + ['local' => ['translator_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk2'], ]); $this->structure = new StructureMock($this->connection, $this->storage); From f981cccfba0b18eeb39cbc7f0f23ca07d3bc627a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 13 Aug 2024 18:44:04 +0200 Subject: [PATCH 42/53] Explorer, Selection, Structure: change of constructor dependencies (BC break) --- src/Bridges/DatabaseDI/DatabaseExtension.php | 8 ++++-- src/Database/Explorer.php | 10 ++++++-- src/Database/Structure.php | 21 ++++++---------- src/Database/Table/GroupedSelection.php | 5 +--- src/Database/Table/Selection.php | 25 ++++++------------- .../Explorer/Explorer.cache.observer.phpt | 10 ++++---- .../Explorer/Explorer.cache.observer2.phpt | 13 +++++----- .../staticReflection.undeclaredColumn.phpt | 4 +-- tests/Database/Structure.phpt | 17 +++++-------- tests/Database/Structure.schemas.phpt | 16 +++++------- tests/bootstrap.php | 4 +-- 11 files changed, 58 insertions(+), 75 deletions(-) diff --git a/src/Bridges/DatabaseDI/DatabaseExtension.php b/src/Bridges/DatabaseDI/DatabaseExtension.php index e93e5a61b..42a4bc6e1 100644 --- a/src/Bridges/DatabaseDI/DatabaseExtension.php +++ b/src/Bridges/DatabaseDI/DatabaseExtension.php @@ -10,6 +10,7 @@ namespace Nette\Bridges\DatabaseDI; use Nette; +use Nette\DI\Definitions\Statement; use Nette\Schema\Expect; use Tracy; @@ -94,13 +95,16 @@ private function setupDatabase(\stdClass $config, string $name): void } } + $cacheId = 'Nette.Database.' . hash('xxh128', $name . $config->dsn); + $cache = new Statement(Nette\Caching\Cache::class, [1 => $cacheId]); + $connection = $builder->addDefinition($this->prefix("$name.connection")) ->setFactory(Nette\Database\Connection::class, [$config->dsn, $config->user, $config->password, $config->options]) ->setAutowired($config->autowired); $structure = $builder->addDefinition($this->prefix("$name.structure")) ->setFactory(Nette\Database\Structure::class) - ->setArguments([$connection]) + ->setArguments([new Statement([$connection, 'getDatabaseEngine']), $cache]) ->setAutowired($config->autowired); if (!$config->conventions) { @@ -119,7 +123,7 @@ private function setupDatabase(\stdClass $config, string $name): void } $builder->addDefinition($this->prefix("$name.explorer")) - ->setFactory(Nette\Database\Explorer::class, [$connection, $structure, $conventions]) + ->setFactory(Nette\Database\Explorer::class, [$connection, $structure, $conventions, $cache]) ->setAutowired($config->autowired); $builder->addAlias($this->prefix("$name.context"), $this->prefix("$name.explorer")); diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 312ae643f..28c66eabd 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -26,7 +26,7 @@ public function __construct( private readonly Connection $connection, private readonly IStructure $structure, ?Conventions $conventions = null, - private readonly ?Nette\Caching\Storage $cacheStorage = null, + private readonly ?Nette\Caching\Cache $cache = null, ) { $this->conventions = $conventions ?: new StaticConventions; } @@ -82,7 +82,7 @@ public function queryArgs(string $sql, array $params): Result public function table(string $table): Table\Selection { - return new Table\Selection($this, $this->conventions, $table, $this->cacheStorage); + return new Table\Selection($this, $table); } @@ -110,6 +110,12 @@ public function getConventions(): Conventions } + public function getCache(): ?Nette\Caching\Cache + { + return $this->cache; + } + + /********************* shortcuts ****************d*g**/ diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 4a3b059f6..e219c842a 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -17,18 +17,15 @@ */ class Structure implements IStructure { - protected readonly Connection $connection; - protected readonly Nette\Caching\Cache $cache; - /** @var array{tables: array, columns: array, primary: array, aliases: array, hasMany: array, belongsTo: array} */ protected array $structure; protected bool $isRebuilt = false; - public function __construct(Connection $connection, Nette\Caching\Storage $cacheStorage) - { - $this->connection = $connection; - $this->cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Database.Structure.' . hash('xxh128', $connection->getDsn())); + public function __construct( + protected readonly Drivers\Engine $engine, + protected readonly Nette\Caching\Cache $cache, + ) { } @@ -94,7 +91,7 @@ public function getPrimaryKeySequence(string $table): ?string $this->needStructure(); $table = $this->resolveFQTableName($table); - if (!$this->connection->getDatabaseEngine()->isSupported(Drivers\Engine::SupportSequence)) { + if (!$this->engine->isSupported(Drivers\Engine::SupportSequence)) { return null; } @@ -177,10 +174,8 @@ protected function needStructure(): void protected function loadStructure(): array { - $engine = $this->connection->getDatabaseEngine(); - $structure = []; - $structure['tables'] = $engine->getTables(); + $structure['tables'] = $this->engine->getTables(); foreach ($structure['tables'] as $tablePair) { if (isset($tablePair['fullName'])) { @@ -190,7 +185,7 @@ protected function loadStructure(): array $table = $tablePair['name']; } - $structure['columns'][strtolower($table)] = $columns = $engine->getColumns($table); + $structure['columns'][strtolower($table)] = $columns = $this->engine->getColumns($table); if (!$tablePair['view']) { $structure['primary'][strtolower($table)] = $this->analyzePrimaryKey($columns); @@ -233,7 +228,7 @@ protected function analyzeForeignKeys(array &$structure, string $table): void { $lowerTable = strtolower($table); - $foreignKeys = $this->connection->getDatabaseEngine()->getForeignKeys($table); + $foreignKeys = $this->engine->getForeignKeys($table); usort($foreignKeys, fn($a, $b): int => count($b['local']) <=> count($a['local'])); diff --git a/src/Database/Table/GroupedSelection.php b/src/Database/Table/GroupedSelection.php index 507ac298a..fa29a86d0 100644 --- a/src/Database/Table/GroupedSelection.php +++ b/src/Database/Table/GroupedSelection.php @@ -10,7 +10,6 @@ namespace Nette\Database\Table; use Nette; -use Nette\Database\Conventions; use Nette\Database\Explorer; @@ -38,15 +37,13 @@ class GroupedSelection extends Selection */ public function __construct( Explorer $explorer, - Conventions $conventions, string $tableName, string $column, Selection $refTable, - ?Nette\Caching\Storage $cacheStorage = null, ) { $this->refTable = $refTable; $this->column = $column; - parent::__construct($explorer, $conventions, $tableName, $cacheStorage); + parent::__construct($explorer, $tableName); } diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 03ee2b61e..bf217e887 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -10,7 +10,6 @@ namespace Nette\Database\Table; use Nette; -use Nette\Database\Conventions; use Nette\Database\Explorer; @@ -24,10 +23,6 @@ class Selection implements \Iterator, \ArrayAccess, \Countable { protected readonly Explorer $explorer; - - /** back compatibility */ - protected Explorer $context; - protected readonly Conventions $conventions; protected readonly ?Nette\Caching\Cache $cache; protected SqlBuilder $sqlBuilder; @@ -72,18 +67,12 @@ class Selection implements \Iterator, \ArrayAccess, \Countable */ public function __construct( Explorer $explorer, - Conventions $conventions, string $tableName, - ?Nette\Caching\Storage $cacheStorage = null, ) { - $this->explorer = $this->context = $explorer; - $this->conventions = $conventions; + $this->explorer = $explorer; $this->name = $tableName; - - $this->cache = $cacheStorage - ? new Nette\Caching\Cache($cacheStorage, 'Nette.Database.' . hash('xxh128', $explorer->getConnection()->getDsn())) - : null; - $this->primary = $conventions->getPrimary($tableName); + $this->cache = $explorer->getCache(); + $this->primary = $explorer->getConventions()->getPrimary($tableName); $this->sqlBuilder = new SqlBuilder($tableName, $explorer); $this->refCache = &$this->getRefTable($refPath)->globalRefCache[$refPath]; } @@ -555,13 +544,13 @@ protected function createRow(array $row): ActiveRow public function createSelectionInstance(?string $table = null): self { - return new self($this->explorer, $this->conventions, $table ?: $this->name, $this->cache?->getStorage()); + return new self($this->explorer, $table ?: $this->name); } protected function createGroupedSelectionInstance(string $table, string $column): GroupedSelection { - return new GroupedSelection($this->explorer, $this->conventions, $table, $column, $this, $this->cache?->getStorage()); + return new GroupedSelection($this->explorer, $table, $column, $this); } @@ -886,7 +875,7 @@ public function delete(): int public function getReferencedTable(ActiveRow $row, ?string $table, ?string $column = null): ActiveRow|false|null { if (!$column) { - $belongsTo = $this->conventions->getBelongsToReference($this->name, $table); + $belongsTo = $this->explorer->getConventions()->getBelongsToReference($this->name, $table); if (!$belongsTo) { return false; } @@ -939,7 +928,7 @@ public function getReferencingTable( if (str_contains($table, '.')) { [$table, $column] = explode('.', $table); } elseif (!$column) { - $hasMany = $this->conventions->getHasManyReference($this->name, $table); + $hasMany = $this->explorer->getConventions()->getHasManyReference($this->name, $table); if (!$hasMany) { return null; } diff --git a/tests/Database/Explorer/Explorer.cache.observer.phpt b/tests/Database/Explorer/Explorer.cache.observer.phpt index 0e3b23e78..278972883 100644 --- a/tests/Database/Explorer/Explorer.cache.observer.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer.phpt @@ -18,12 +18,12 @@ $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$cacheStorage = Mockery::mock(Nette\Caching\Istorage::class); -$cacheStorage->shouldReceive('read')->withAnyArgs()->once()->andReturn(['id' => true]); -$cacheStorage->shouldReceive('read')->withAnyArgs()->times(4)->andReturn(['id' => true, 'author_id' => true]); -$cacheStorage->shouldReceive('write')->with(Mockery::any(), ['id' => true, 'author_id' => true, 'title' => true], []); +$cache = Mockery::mock(Nette\Caching\Cache::class); +$cache->shouldReceive('load')->withAnyArgs()->once()->andReturn(['id' => true]); +$cache->shouldReceive('load')->withAnyArgs()->times(4)->andReturn(['id' => true, 'author_id' => true]); +$cache->shouldReceive('save')->with(Mockery::any(), ['id' => true, 'author_id' => true, 'title' => true]); -$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cacheStorage); +$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cache); $queries = 0; $connection->onQuery[] = function ($dao, Result $result) use (&$queries) { diff --git a/tests/Database/Explorer/Explorer.cache.observer2.phpt b/tests/Database/Explorer/Explorer.cache.observer2.phpt index 5eff0cf66..e81555b38 100644 --- a/tests/Database/Explorer/Explorer.cache.observer2.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer2.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); +use Nette\Caching\Cache; use Nette\Caching\Storages\MemoryStorage; use Tester\Assert; @@ -18,20 +19,20 @@ $connection = $explorer->getConnection(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -class CacheMock extends MemoryStorage +class CacheMock extends Cache { public int $writes = 0; - public function write(string $key, $data, array $dependencies): void + public function save(mixed $key, mixed $data, ?array $dependencies = null): mixed { $this->writes++; - parent::write($key, $data, $dependencies); + return parent::save($key, $data, $dependencies); } } -$cacheStorage = new CacheMock; -$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cacheStorage); +$cache = new CacheMock(new MemoryStorage); +$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cache); for ($i = 0; $i < 2; ++$i) { $authors = $explorer->table('author'); @@ -52,4 +53,4 @@ for ($i = 0; $i < 2; ++$i) { } Assert::same(reformat('SELECT [id], [name] FROM [author]'), $sql); -Assert::same(2, $cacheStorage->writes); +Assert::same(2, $cache->writes); diff --git a/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt b/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt index 4780a937c..0ff25ccdf 100644 --- a/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt +++ b/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt @@ -15,8 +15,8 @@ $explorer = connectToDB(); $connection = $explorer->getConnection(); $conventions = new Nette\Database\Conventions\StaticConventions; -$cacheStorage = new Nette\Caching\Storages\MemoryStorage; -$explorer = new Nette\Database\Explorer($explorer->getConnection(), $explorer->getStructure(), $conventions, $cacheStorage); +$cache = new Nette\Caching\Cache(new Nette\Caching\Storages\MemoryStorage); +$explorer = new Nette\Database\Explorer($explorer->getConnection(), $explorer->getStructure(), $conventions, $cache); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index d54f720b4..61afde090 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -6,6 +6,7 @@ declare(strict_types=1); +use Mockery\MockInterface; use Nette\Database\Structure; use Tester\Assert; use Tester\TestCase; @@ -29,21 +30,17 @@ class StructureMock extends Structure */ class StructureTestCase extends TestCase { - private Nette\Database\Connection $connection; - private Nette\Database\Drivers\Engine $engine; - private Nette\Caching\Storage $storage; - private Structure $structure; + private Nette\Database\Drivers\Engine|MockInterface $engine; + private Nette\Caching\Cache|MockInterface $cache; + private Structure|MockInterface $structure; protected function setUp() { parent::setUp(); $this->engine = Mockery::mock(Nette\Database\Drivers\Engine::class); - $this->connection = Mockery::mock(Nette\Database\Connection::class); - $this->storage = Mockery::mock(Nette\Caching\Storage::class); + $this->cache = Mockery::mock(Nette\Caching\Cache::class); - $this->connection->shouldReceive('getDsn')->once()->andReturn(''); - $this->connection->shouldReceive('getDatabaseEngine')->once()->andReturn($this->engine); $this->engine->shouldReceive('getTables')->once()->andReturn([ ['name' => 'authors', 'view' => false], ['name' => 'Books', 'view' => false], @@ -71,7 +68,6 @@ class StructureTestCase extends TestCase ['name' => 'id', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ['name' => 'title', 'primary' => false, 'autoIncrement' => false, 'vendor' => []], ]); - $this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine); $this->engine->shouldReceive('getForeignKeys')->with('authors')->once()->andReturn([]); $this->engine->shouldReceive('getForeignKeys')->with('Books')->once()->andReturn([ ['local' => ['author_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk1'], @@ -83,7 +79,7 @@ class StructureTestCase extends TestCase ['local' => ['tag_id'], 'table' => 'tags', 'foreign' => ['id'], 'name' => 'books_x_tags_fk2'], ]); - $this->structure = new StructureMock($this->connection, $this->storage); + $this->structure = new StructureMock($this->engine, $this->cache); } @@ -132,7 +128,6 @@ class StructureTestCase extends TestCase public function testGetPrimaryKeySequence() { - $this->connection->shouldReceive('getDatabaseEngine')->times(4)->andReturn($this->engine); $this->engine->shouldReceive('isSupported')->with('sequence')->once()->andReturn(false); $this->engine->shouldReceive('isSupported')->with('sequence')->times(3)->andReturn(true); diff --git a/tests/Database/Structure.schemas.phpt b/tests/Database/Structure.schemas.phpt index 3f1fbf9f0..e92d333b5 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -6,6 +6,7 @@ declare(strict_types=1); +use Mockery\MockInterface; use Nette\Database\Structure; use Tester\Assert; use Tester\TestCase; @@ -29,21 +30,17 @@ class StructureMock extends Structure */ class StructureSchemasTestCase extends TestCase { - private Nette\Database\Connection $connection; - private Nette\Database\Drivers\Engine $engine; - private Nette\Caching\Storage $storage; - private Structure $structure; + private Nette\Database\Drivers\Engine|MockInterface $engine; + private Nette\Caching\Cache|MockInterface $cache; + private Structure|MockInterface $structure; protected function setUp() { parent::setUp(); $this->engine = Mockery::mock(Nette\Database\Drivers\Engine::class); - $this->connection = Mockery::mock(Nette\Database\Connection::class); - $this->storage = Mockery::mock(Nette\Caching\Storage::class); + $this->cache = Mockery::mock(Nette\Caching\Cache::class); - $this->connection->shouldReceive('getDsn')->once()->andReturn(''); - $this->connection->shouldReceive('getDatabaseEngine')->once()->andReturn($this->engine); $this->engine->shouldReceive('getTables')->once()->andReturn([ ['name' => 'authors', 'view' => false, 'fullName' => 'authors.authors'], ['name' => 'books', 'view' => false, 'fullName' => 'books.books'], @@ -57,14 +54,13 @@ class StructureSchemasTestCase extends TestCase ['name' => 'title', 'primary' => false, 'vendor' => []], ]); - $this->connection->shouldReceive('getDatabaseEngine')->times(2)->andReturn($this->engine); $this->engine->shouldReceive('getForeignKeys')->with('authors.authors')->once()->andReturn([]); $this->engine->shouldReceive('getForeignKeys')->with('books.books')->once()->andReturn([ ['local' => ['author_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk1'], ['local' => ['translator_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk2'], ]); - $this->structure = new StructureMock($this->connection, $this->storage); + $this->structure = new StructureMock($this->engine, $this->cache); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e5ebcbdd2..3edeba25a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -43,9 +43,9 @@ function connectToDB(array $options = []): Nette\Database\Explorer } $driverName = $connection->getConnection()->getNativeConnection()->getAttribute(PDO::ATTR_DRIVER_NAME); - $cacheMemoryStorage = new Nette\Caching\Storages\MemoryStorage; - $structure = new Nette\Database\Structure($connection, $cacheMemoryStorage); + $cacheMemoryStorage = new Nette\Caching\Cache(new Nette\Caching\Storages\MemoryStorage); + $structure = new Nette\Database\Structure($connection->getDatabaseEngine(), $cacheMemoryStorage); $conventions = new Nette\Database\Conventions\DiscoveredConventions($structure); $explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $cacheMemoryStorage); From 9b50bd215d1635eb18d9b2562f3b0db7c858c378 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 17 Aug 2024 07:01:50 +0200 Subject: [PATCH 43/53] Connection::getDsn() deprecated (BC break) --- .../DatabaseTracy/templates/ConnectionPanel.panel.phtml | 2 +- src/Database/Connection.php | 5 +++-- tests/Database.DI/DatabaseExtension.basic.phpt | 1 - tests/Database.DI/DatabaseExtension.multiple.phpt | 1 - tests/Database.Tracy/panel.html | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml index 609ed0fd7..a7f035eb0 100644 --- a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml +++ b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml @@ -16,7 +16,7 @@ use Tracy\Helpers; #tracy-debug td.nette-DbConnectionPanel-sql-update { background: #E7FBFF !important } -

Queries: Queries:

diff --git a/src/Database/Connection.php b/src/Database/Connection.php index cd21a9674..8bbae11d2 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -45,7 +45,7 @@ class Connection public function __construct( - private readonly string $dsn, + string $dsn, ?string $username = null, #[\SensitiveParameter] ?string $password = null, @@ -99,9 +99,10 @@ public function disconnect(): void } + /** @deprecated */ public function getDsn(): string { - return $this->dsn; + throw new Nette\DeprecatedException(__METHOD__ . '() is deprecated.'); } diff --git a/tests/Database.DI/DatabaseExtension.basic.phpt b/tests/Database.DI/DatabaseExtension.basic.phpt index 23bf6c332..abe51c70a 100644 --- a/tests/Database.DI/DatabaseExtension.basic.phpt +++ b/tests/Database.DI/DatabaseExtension.basic.phpt @@ -38,7 +38,6 @@ test('', function () { $connection = $container->getService('database.default'); Assert::type(Nette\Database\Connection::class, $connection); - Assert::same('sqlite::memory:', $connection->getDsn()); $explorer = $container->getService('database.default.explorer'); Assert::type(Nette\Database\Explorer::class, $explorer); diff --git a/tests/Database.DI/DatabaseExtension.multiple.phpt b/tests/Database.DI/DatabaseExtension.multiple.phpt index fb5b72e67..eccc9d3bd 100644 --- a/tests/Database.DI/DatabaseExtension.multiple.phpt +++ b/tests/Database.DI/DatabaseExtension.multiple.phpt @@ -44,7 +44,6 @@ test('', function () { $connection = $container->getService('database.first'); Assert::type(Nette\Database\Connection::class, $connection); Assert::same($connection, $container->getByType(Nette\Database\Connection::class)); - Assert::same('sqlite::memory:', $connection->getDsn()); $explorer = $container->getService('database.first.explorer'); Assert::type(Nette\Database\Explorer::class, $explorer); diff --git a/tests/Database.Tracy/panel.html b/tests/Database.Tracy/panel.html index 292e5d4e8..39316ae03 100644 --- a/tests/Database.Tracy/panel.html +++ b/tests/Database.Tracy/panel.html @@ -1,5 +1,5 @@ %A% -

Queries: 4, time: %a% ms, foo

+

Queries: 4, time: %a% ms, foo

From 45c712f9e4241a4b1d5a226afdf1dc7123d1f6ca Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 29 Aug 2024 04:53:21 +0200 Subject: [PATCH 44/53] auxiliary commit --- src/Database/Explorer.php | 199 -------------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 src/Database/Explorer.php diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php deleted file mode 100644 index 28c66eabd..000000000 --- a/src/Database/Explorer.php +++ /dev/null @@ -1,199 +0,0 @@ -conventions = $conventions ?: new StaticConventions; - } - - - public function beginTransaction(): void - { - $this->connection->beginTransaction(); - } - - - public function commit(): void - { - $this->connection->commit(); - } - - - public function rollBack(): void - { - $this->connection->rollBack(); - } - - - public function transaction(callable $callback): mixed - { - return $this->connection->transaction(fn() => $callback($this)); - } - - - public function getInsertId(?string $sequence = null): int|string - { - return $this->connection->getInsertId($sequence); - } - - - /** - * Generates and executes SQL query. - * @param literal-string $sql - */ - public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result - { - return $this->connection->query($sql, ...$params); - } - - - /** @deprecated use query() */ - public function queryArgs(string $sql, array $params): Result - { - trigger_error(__METHOD__ . '() is deprecated, use query()', E_USER_DEPRECATED); - return $this->connection->query($sql, ...$params); - } - - - public function table(string $table): Table\Selection - { - return new Table\Selection($this, $table); - } - - - public function getConnection(): Connection - { - return $this->connection; - } - - - public function getDatabaseEngine(): Drivers\Engine - { - return $this->connection->getDatabaseEngine(); - } - - - public function getStructure(): IStructure - { - return $this->structure; - } - - - public function getConventions(): Conventions - { - return $this->conventions; - } - - - public function getCache(): ?Nette\Caching\Cache - { - return $this->cache; - } - - - /********************* shortcuts ****************d*g**/ - - - /** - * Shortcut for query()->fetch() - * @param literal-string $sql - */ - public function fetch(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?Row - { - return $this->connection->query($sql, ...$params)->fetch(); - } - - - /** - * Shortcut for query()->fetchAssoc() - * @param literal-string $sql - */ - public function fetchAssoc(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array - { - return $this->connection->query($sql, ...$params)->fetchAssoc(); - } - - - /** - * Shortcut for query()->fetchField() - * @param literal-string $sql - */ - public function fetchField(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): mixed - { - return $this->connection->query($sql, ...$params)->fetchField(); - } - - - /** - * Shortcut for query()->fetchList() - * @param literal-string $sql - */ - public function fetchList(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array - { - return $this->connection->query($sql, ...$params)->fetchList(); - } - - - /** - * Shortcut for query()->fetchList() - * @param literal-string $sql - */ - public function fetchFields(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array - { - return $this->connection->query($sql, ...$params)->fetchList(); - } - - - /** - * Shortcut for query()->fetchPairs() - * @param literal-string $sql - */ - public function fetchPairs(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array - { - return $this->connection->query($sql, ...$params)->fetchPairs(); - } - - - /** - * Shortcut for query()->fetchAll() - * @param literal-string $sql - */ - public function fetchAll(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array - { - return $this->connection->query($sql, ...$params)->fetchAll(); - } - - - public static function literal(string $value, ...$params): SqlLiteral - { - return new SqlLiteral($value, $params); - } -} - - -class_exists(Context::class); From b9d5ce07f172b3acad17c508b4257e569b36615d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 6 Sep 2024 16:56:05 +0200 Subject: [PATCH 45/53] merging Connection & Explorer classes into one (BC break) --- readme.md | 2 +- src/Bridges/DatabaseDI/DatabaseExtension.php | 26 +++----- src/Bridges/DatabaseTracy/ConnectionPanel.php | 14 ++--- src/Database/{Connection.php => Explorer.php} | 61 ++++++++++++++++++- src/Database/Helpers.php | 14 ++--- src/Database/Result.php | 10 +-- src/Database/SqlPreprocessor.php | 6 +- src/compatibility.php | 8 +++ .../Database.DI/DatabaseExtension.basic.phpt | 14 ++--- .../DatabaseExtension.multiple.phpt | 17 ++---- tests/Database.Tracy/panel.html | 8 +-- .../Database/Connection.exceptions.mysql.phpt | 4 +- .../Connection.exceptions.postgre.phpt | 4 +- .../Connection.exceptions.sqlite.phpt | 4 +- tests/Database/Connection.fetch.phpt | 2 +- .../Connection.getInsertId().mysql.phpt | 2 +- .../Connection.getInsertId().postgre.phpt | 2 +- .../Connection.getInsertId().sqlite.phpt | 2 +- .../Connection.getInsertId().sqlsrv.phpt | 2 +- tests/Database/Connection.preprocess.phpt | 2 +- tests/Database/Connection.query.phpt | 2 +- tests/Database/Connection.transaction.phpt | 18 +++--- tests/Database/Engine.postgre.10.phpt | 2 +- tests/Database/Engine.postgre.phpt | 2 +- tests/Database/Engine.reflection.phpt | 7 +-- tests/Database/Explorer.fetch.phpt | 3 +- tests/Database/Explorer.query.phpt | 3 +- tests/Database/Explorer.transaction.phpt | 3 +- .../Explorer/ActiveRow.__toString().phpt | 3 +- .../Explorer/Explorer.aggregation.phpt | 3 +- .../Database/Explorer/Explorer.backjoin.phpt | 5 +- .../Explorer/Explorer.basic.camelCase.phpt | 3 +- tests/Database/Explorer/Explorer.basic.phpt | 3 +- .../Explorer/Explorer.cache.observer.phpt | 15 +++-- .../Explorer/Explorer.cache.observer2.phpt | 7 +-- tests/Database/Explorer/Explorer.cache.phpt | 5 +- .../Explorer/Explorer.cache.rows.phpt | 3 +- tests/Database/Explorer/Explorer.cache2.phpt | 3 +- .../Explorer/Explorer.columnRefetch.phpt | 3 +- .../Explorer.discoveredReflection.phpt | 7 +-- .../Explorer/Explorer.join-condition.phpt | 6 +- tests/Database/Explorer/Explorer.join.phpt | 5 +- tests/Database/Explorer/Explorer.limit.phpt | 3 +- .../Explorer/Explorer.limit.sqlsrv.phpt | 5 +- .../Explorer/Explorer.multi-primary-key.phpt | 3 +- .../Explorer/Explorer.placeholders.phpt | 3 +- tests/Database/Explorer/Explorer.ref().phpt | 7 +-- .../Explorer/Explorer.related().caching.phpt | 3 +- .../Database/Explorer/Explorer.related().phpt | 3 +- .../Explorer/Explorer.self-reference.phpt | 5 +- .../Database/Explorer/Explorer.subquery.phpt | 3 +- .../Database/Explorer/Explorer.update().phpt | 3 +- .../Explorer/GroupedSelection.insert().phpt | 3 +- .../Database/Explorer/Selection.delete().phpt | 3 +- .../Database/Explorer/Selection.fetch().phpt | 3 +- .../Explorer/Selection.fetchAssoc().phpt | 3 +- .../Explorer/Selection.fetchField().phpt | 3 +- .../Explorer/Selection.fetchPairs().phpt | 3 +- tests/Database/Explorer/Selection.get().phpt | 3 +- .../Database/Explorer/Selection.group().phpt | 3 +- .../Explorer/Selection.insert().multi.phpt | 3 +- .../Database/Explorer/Selection.insert().phpt | 3 +- .../Selection.insert().primaryKeys.phpt | 3 +- .../Database/Explorer/Selection.order().phpt | 3 +- tests/Database/Explorer/Selection.page().phpt | 5 +- .../Explorer/Selection.whereOr().phpt | 3 +- .../Explorer/SqlBuilder.addAlias().phpt | 5 +- .../Explorer/SqlBuilder.addWhere().phpt | 5 +- .../SqlBuilder.parseJoinConditions().phpt | 5 +- .../Explorer/SqlBuilder.parseJoins().phpt | 9 ++- .../Explorer/bugs/ActiveRow.__isset().phpt | 3 +- .../bugs/Selection.emptyResultSet.phpt | 3 +- ...Selection.getReferencingTable.pkTypes.phpt | 3 +- tests/Database/Explorer/bugs/bug1356.phpt | 5 +- tests/Database/Explorer/bugs/bug170.phpt | 3 +- tests/Database/Explorer/bugs/bug187.phpt | 3 +- tests/Database/Explorer/bugs/bug216.phpt | 3 +- .../Explorer/bugs/deleteCacheBug.phpt | 3 +- tests/Database/Explorer/bugs/query.count.phpt | 7 +-- .../staticReflection.undeclaredColumn.phpt | 7 +-- tests/Database/Explorer/bugs/view.bug.phpt | 7 +-- tests/Database/Helpers.dumpSql.phpt | 2 +- tests/Database/Helpers.loadFromFile.phpt | 2 +- tests/Database/Reflection.columns.mysql.phpt | 2 +- .../Database/Reflection.columns.postgre.phpt | 2 +- tests/Database/Reflection.columns.sqlite.phpt | 2 +- tests/Database/Reflection.columns.sqlsrv.phpt | 2 +- tests/Database/Reflection.phpt | 2 +- tests/Database/Result.fetch().phpt | 2 +- tests/Database/Result.fetchAll().phpt | 2 +- tests/Database/Result.fetchAssoc().phpt | 2 +- tests/Database/Result.fetchField().phpt | 2 +- tests/Database/Result.fetchList().phpt | 2 +- tests/Database/Result.fetchPairs().phpt | 2 +- .../ResultSet.normalizeRow.mysql.phpt | 2 +- .../ResultSet.normalizeRow.postgre.phpt | 2 +- .../ResultSet.normalizeRow.sqlite.phpt | 2 +- .../ResultSet.normalizeRow.sqlsrv.phpt | 2 +- tests/Database/Row.phpt | 2 +- tests/Database/SqlPreprocessor.enum.phpt | 2 +- tests/Database/SqlPreprocessor.phpt | 2 +- tests/Database/connection.disconnect.phpt | 2 +- tests/Database/connection.options.mysql.phpt | 30 ++++----- tests/Database/connection.options.sqlite.phpt | 10 +-- tests/Database/connection.options.sqlsrv.phpt | 12 ++-- tests/bootstrap.php | 12 ++-- 106 files changed, 279 insertions(+), 295 deletions(-) rename src/Database/{Connection.php => Explorer.php} (88%) diff --git a/readme.md b/readme.md index 647b53b74..43f65343d 100644 --- a/readme.md +++ b/readme.md @@ -59,7 +59,7 @@ Database Core To create a new database connection just create a new instance of `Nette\Database\Connection` class: ```php -$database = new Nette\Database\Connection($dsn, $user, $password); // the same arguments as uses PDO +$database = new Nette\Database\Explorer($dsn, $user, $password); // the same arguments as uses PDO ``` Connection allows you to easily query your database by calling `query` method: diff --git a/src/Bridges/DatabaseDI/DatabaseExtension.php b/src/Bridges/DatabaseDI/DatabaseExtension.php index 42a4bc6e1..35e5e8257 100644 --- a/src/Bridges/DatabaseDI/DatabaseExtension.php +++ b/src/Bridges/DatabaseDI/DatabaseExtension.php @@ -98,38 +98,30 @@ private function setupDatabase(\stdClass $config, string $name): void $cacheId = 'Nette.Database.' . hash('xxh128', $name . $config->dsn); $cache = new Statement(Nette\Caching\Cache::class, [1 => $cacheId]); - $connection = $builder->addDefinition($this->prefix("$name.connection")) - ->setFactory(Nette\Database\Connection::class, [$config->dsn, $config->user, $config->password, $config->options]) + $explorer = $builder->addDefinition($this->prefix($name)) + ->setFactory(Nette\Database\Explorer::class, [$config->dsn, $config->user, $config->password, $config->options]) + ->addSetup('setCache', [$cache]) ->setAutowired($config->autowired); - $structure = $builder->addDefinition($this->prefix("$name.structure")) - ->setFactory(Nette\Database\Structure::class) - ->setArguments([new Statement([$connection, 'getDatabaseEngine']), $cache]) - ->setAutowired($config->autowired); - - if (!$config->conventions) { - $conventions = null; + if (!$config->conventions || $config->conventions === 'discovered') { } elseif (is_string($config->conventions)) { $conventions = $builder->addDefinition($this->prefix("$name.conventions")) ->setFactory(preg_match('#^[a-z]+$#Di', $config->conventions) ? 'Nette\Database\Conventions\\' . ucfirst($config->conventions) . 'Conventions' : $config->conventions) - ->setArguments(strtolower($config->conventions) === 'discovered' ? [$structure] : []) ->setAutowired($config->autowired); + $explorer->addSetup('setConventions', [$conventions]); } else { - $conventions = Nette\DI\Helpers::filterArguments([$config->conventions])[0]; + $explorer->addSetup('setConventions', [Nette\DI\Helpers::filterArguments([$config->conventions])[0]]); } - $builder->addDefinition($this->prefix("$name.explorer")) - ->setFactory(Nette\Database\Explorer::class, [$connection, $structure, $conventions, $cache]) - ->setAutowired($config->autowired); - - $builder->addAlias($this->prefix("$name.context"), $this->prefix("$name.explorer")); + $builder->addAlias($this->prefix("$name.connection"), $this->prefix($name)); + $builder->addAlias($this->prefix("$name.context"), $this->prefix($name)); + $builder->addAlias($this->prefix("$name.explorer"), $this->prefix($name)); if ($this->name === 'database') { - $builder->addAlias($this->prefix($name), $this->prefix("$name.connection")); $builder->addAlias("nette.database.$name", $this->prefix($name)); $builder->addAlias("nette.database.$name.context", $this->prefix("$name.explorer")); } diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 542a7b7ae..ba6abdcc8 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -10,8 +10,8 @@ namespace Nette\Bridges\DatabaseTracy; use Nette; -use Nette\Database\Connection; use Nette\Database\DriverException; +use Nette\Database\Explorer; use Nette\Database\Helpers; use Nette\Database\Result; use Tracy; @@ -34,7 +34,7 @@ class ConnectionPanel implements Tracy\IBarPanel public static function initialize( - Connection $connection, + Explorer $explorer, bool $addBarPanel = true, string $name = '', bool $explain = true, @@ -46,7 +46,7 @@ public static function initialize( $blueScreen->addPanel(self::renderException(...)); if ($addBarPanel) { - $panel = new self($connection, $blueScreen); + $panel = new self($explorer, $blueScreen); $panel->explain = $explain; $panel->name = $name; $bar ??= Tracy\Debugger::getBar(); @@ -57,14 +57,14 @@ public static function initialize( } - public function __construct(Connection $connection, Tracy\BlueScreen $blueScreen) + public function __construct(Explorer $explorer, Tracy\BlueScreen $blueScreen) { - $connection->onQuery[] = $this->logQuery(...); + $explorer->onQuery[] = $this->logQuery(...); $this->blueScreen = $blueScreen; } - private function logQuery(Connection $connection, $result): void + private function logQuery(Explorer $connection, $result): void { if ($this->disabled) { return; @@ -82,7 +82,7 @@ private function logQuery(Connection $connection, $result): void && preg_match('~\.(php.?|phtml)$~', $row['file']) && !$this->blueScreen->isCollapsed($row['file'])) && ($row['class'] ?? '') !== self::class - && !is_a($row['class'] ?? '', Connection::class, allow_string: true) + && !is_a($row['class'] ?? '', Explorer::class, allow_string: true) ) { $source = [$row['file'], (int) $row['line']]; break; diff --git a/src/Database/Connection.php b/src/Database/Explorer.php similarity index 88% rename from src/Database/Connection.php rename to src/Database/Explorer.php index 8bbae11d2..bb2c6fb27 100644 --- a/src/Database/Connection.php +++ b/src/Database/Explorer.php @@ -11,13 +11,14 @@ use JetBrains\PhpStorm\Language; use Nette; +use Nette\Caching\Cache; use Nette\Utils\Arrays; /** - * Represents a connection between PHP and a database server. + * The central access point to Nette Database functionality. */ -class Connection +class Explorer { private const Drivers = [ 'pdo-mssql' => Drivers\PDO\MSSQL\Driver::class, @@ -42,6 +43,9 @@ class Connection private TypeConverter $typeConverter; private ?SqlLiteral $lastQuery = null; private int $transactionDepth = 0; + private ?Cache $cache = null; + private ?Conventions $conventions = null; + private ?IStructure $structure = null; public function __construct( @@ -385,4 +389,57 @@ public static function literal(string $value, ...$params): SqlLiteral { return new SqlLiteral($value, $params); } + + + /********************* active row ****************d*g**/ + + + public function table(string $table): Table\Selection + { + return new Table\Selection($this, $table); + } + + + public function setCache(Cache $cache): static + { + if (isset($this->structure)) { + throw new \LogicException('Cannot set cache after structure is created.'); + } + $this->cache = $cache; + return $this; + } + + + /** @internal */ + public function getCache(): ?Cache + { + return $this->cache; + } + + + public function setConventions(Conventions $conventions): static + { + if (isset($this->conventions)) { + throw new \LogicException('Conventions are already set.'); + } + $this->conventions = $conventions; + return $this; + } + + + /** @internal */ + public function getConventions(): Conventions + { + return $this->conventions ??= new Conventions\DiscoveredConventions($this->getStructure()); + } + + + /** @internal */ + public function getStructure(): IStructure + { + return $this->structure ??= new Structure($this->getDatabaseEngine(), $this->getCache()); + } } + + +class_exists(Connection::class); diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 0f3c9082e..588356ff0 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -75,7 +75,7 @@ public static function dumpResult(Result $result): void /** * Returns syntax highlighted SQL command. */ - public static function dumpSql(SqlLiteral $query, ?Connection $connection = null): string + public static function dumpSql(SqlLiteral $query, ?Explorer $explorer = null): string { $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|[RI]?LIKE|REGEXP|TRUE|FALSE'; @@ -110,7 +110,7 @@ public static function dumpSql(SqlLiteral $query, ?Connection $connection = null // parameters $params = $query->getParameters(); - $sql = preg_replace_callback('#\?#', function () use ($params, $connection): string { + $sql = preg_replace_callback('#\?#', function () use ($params, $explorer): string { static $i = 0; $param = $params[$i++] ?? null; if ($param === null) { @@ -128,7 +128,7 @@ public static function dumpSql(SqlLiteral $query, ?Connection $connection = null } elseif (is_string($param)) { $length = Nette\Utils\Strings::length($param); $truncated = Nette\Utils\Strings::truncate($param, self::$maxLength); - $text = htmlspecialchars($connection ? $connection->quote($truncated) : '\'' . $truncated . '\'', ENT_NOQUOTES, 'UTF-8'); + $text = htmlspecialchars($explorer ? $explorer->quote($truncated) : '\'' . $truncated . '\'', ENT_NOQUOTES, 'UTF-8'); return '' . $text . ''; } elseif (is_resource($param)) { @@ -157,7 +157,7 @@ public static function dumpSql(SqlLiteral $query, ?Connection $connection = null * @param ?array $onProgress * @return int count of commands */ - public static function loadFromFile(Connection $connection, string $file, ?callable $onProgress = null): int + public static function loadFromFile(Explorer $explorer, string $file, ?callable $onProgress = null): int { @set_time_limit(0); // @ function may be disabled @@ -170,7 +170,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla $count = $size = 0; $delimiter = ';'; $sql = ''; - $connection = $connection->getConnection(); // native query without logging + $connection = $explorer->getConnection(); // native query without logging while (($s = fgets($handle)) !== false) { $size += strlen($s); if (!strncasecmp($s, 'DELIMITER ', 10)) { @@ -204,7 +204,7 @@ public static function loadFromFile(Connection $connection, string $file, ?calla /** @deprecated use Nette\Bridges\DatabaseTracy\ConnectionPanel::initialize() */ public static function createDebugPanel( - Connection $connection, + Explorer $connection, bool $explain, string $name, Tracy\Bar $bar, @@ -218,7 +218,7 @@ public static function createDebugPanel( /** @deprecated use Nette\Bridges\DatabaseTracy\ConnectionPanel::initialize() */ public static function initializeTracy( - Connection $connection, + Explorer $connection, bool $addBarPanel = false, string $name = '', bool $explain = true, diff --git a/src/Database/Result.php b/src/Database/Result.php index 2a453e4e2..821116ef0 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -27,7 +27,7 @@ class Result implements \Iterator public function __construct( - private readonly Connection $connection, + private readonly Explorer $explorer, private readonly SqlLiteral $query, private readonly ?Drivers\Result $result, private float $time, @@ -36,10 +36,10 @@ public function __construct( /** @deprecated */ - public function getConnection(): Connection + public function getConnection(): Explorer { trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); - return $this->connection; + return $this->explorer; } @@ -223,8 +223,8 @@ public function fetchAll(): array private function normalizeRow(array $row): array { - $engine = $this->connection->getDatabaseEngine(); - $converter = $this->connection->getTypeConverter(); + $engine = $this->explorer->getDatabaseEngine(); + $converter = $this->explorer->getTypeConverter(); $columnsMeta = $this->meta ??= $this->getColumnsMeta(); foreach ($row as $key => $value) { $row[$key] = isset($value, $columnsMeta[$key]) diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 690b6075f..3bfa4cd34 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -59,10 +59,10 @@ class SqlPreprocessor private ?string $arrayMode; - public function __construct(Connection $connection) + public function __construct(Explorer $explorer) { - $this->connection = $connection->getConnection(); - $this->engine = $connection->getDatabaseEngine(); + $this->connection = $explorer->getConnection(); + $this->engine = $explorer->getDatabaseEngine(); } diff --git a/src/compatibility.php b/src/compatibility.php index 135582033..5d86f6f0d 100644 --- a/src/compatibility.php +++ b/src/compatibility.php @@ -26,3 +26,11 @@ class ResultSet extends Result } elseif (!class_exists(ResultSet::class)) { class_alias(Result::class, ResultSet::class); } + +if (false) { + class Connection extends Explorer + { + } +} elseif (!class_exists(Connection::class)) { + class_alias(Explorer::class, Connection::class); +} diff --git a/tests/Database.DI/DatabaseExtension.basic.phpt b/tests/Database.DI/DatabaseExtension.basic.phpt index abe51c70a..42cd6fc58 100644 --- a/tests/Database.DI/DatabaseExtension.basic.phpt +++ b/tests/Database.DI/DatabaseExtension.basic.phpt @@ -36,18 +36,12 @@ test('', function () { $container = new Container1; $container->initialize(); - $connection = $container->getService('database.default'); - Assert::type(Nette\Database\Connection::class, $connection); - - $explorer = $container->getService('database.default.explorer'); + $explorer = $container->getService('database.default'); Assert::type(Nette\Database\Explorer::class, $explorer); - Assert::same($connection, $explorer->getConnection()); - Assert::same($container->getService('database.default.context'), $explorer); - - Assert::type(Nette\Database\Structure::class, $explorer->getStructure()); - Assert::type(Nette\Database\Conventions\DiscoveredConventions::class, $explorer->getConventions()); + Assert::type(Nette\Caching\Cache::class, $explorer->getCache()); // aliases - Assert::same($connection, $container->getService('nette.database.default')); + Assert::same($explorer, $container->getService('database.default.explorer')); + Assert::same($explorer, $container->getService('nette.database.default')); Assert::same($explorer, $container->getService('nette.database.default.context')); }); diff --git a/tests/Database.DI/DatabaseExtension.multiple.phpt b/tests/Database.DI/DatabaseExtension.multiple.phpt index eccc9d3bd..e808b5337 100644 --- a/tests/Database.DI/DatabaseExtension.multiple.phpt +++ b/tests/Database.DI/DatabaseExtension.multiple.phpt @@ -41,22 +41,13 @@ test('', function () { $container = new Container1; $container->initialize(); - $connection = $container->getService('database.first'); - Assert::type(Nette\Database\Connection::class, $connection); - Assert::same($connection, $container->getByType(Nette\Database\Connection::class)); - - $explorer = $container->getService('database.first.explorer'); + $explorer = $container->getService('database.first'); Assert::type(Nette\Database\Explorer::class, $explorer); Assert::same($explorer, $container->getByType(Nette\Database\Explorer::class)); - Assert::same($connection, $explorer->getConnection()); - Assert::same($container->getService('database.first.context'), $explorer); - - Assert::type(Nette\Database\Structure::class, $explorer->getStructure()); - Assert::same($explorer->getStructure(), $container->getByType(Nette\Database\IStructure::class)); - Assert::type(Nette\Database\Conventions\DiscoveredConventions::class, $explorer->getConventions()); - Assert::same($explorer->getConventions(), $container->getByType(Nette\Database\Conventions::class)); + Assert::type(Nette\Caching\Cache::class, $explorer->getCache()); // aliases - Assert::same($connection, $container->getService('nette.database.first')); + Assert::same($explorer, $container->getService('database.first.explorer')); + Assert::same($explorer, $container->getService('nette.database.first')); Assert::same($explorer, $container->getService('nette.database.first.context')); }); diff --git a/tests/Database.Tracy/panel.html b/tests/Database.Tracy/panel.html index 39316ae03..3722349a9 100644 --- a/tests/Database.Tracy/panel.html +++ b/tests/Database.Tracy/panel.html @@ -7,20 +7,20 @@

Queries: 4, time: %a% ms, foo

%A% %A%
BEGIN TRANSACTION
- %a%Connection.php:%d% + %a%Explorer.php:%d% %A%
SELECT 1
%A%
- %a%Connection.php:%d% + %a%Explorer.php:%d% 0 %A%
COMMIT
- %a%Connection.php:%d% + %a%Explorer.php:%d% @@ -28,7 +28,7 @@

Queries: 4, time: %a% ms, foo

ERROR
SELECT
- %a%Connection.php:%d% + %a%Explorer.php:%d% diff --git a/tests/Database/Connection.exceptions.mysql.phpt b/tests/Database/Connection.exceptions.mysql.phpt index ad3f5f30a..8d8346638 100644 --- a/tests/Database/Connection.exceptions.mysql.phpt +++ b/tests/Database/Connection.exceptions.mysql.phpt @@ -11,13 +11,13 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('Exception thrown for invalid database credentials', function () { $options = Tester\Environment::loadData(); $e = Assert::exception( - fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), + fn() => (new Nette\Database\Explorer($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, '%a% Access denied for user %a%', 1045, diff --git a/tests/Database/Connection.exceptions.postgre.phpt b/tests/Database/Connection.exceptions.postgre.phpt index c5d48f22b..8fe0f5a05 100644 --- a/tests/Database/Connection.exceptions.postgre.phpt +++ b/tests/Database/Connection.exceptions.postgre.phpt @@ -11,14 +11,14 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('Exception thrown for invalid database credentials', function () { $options = Tester\Environment::loadData(); $e = Assert::exception( - fn() => (new Nette\Database\Connection($options['dsn'], 'unknown', 'unknown'))->connect(), + fn() => (new Nette\Database\Explorer($options['dsn'], 'unknown', 'unknown'))->connect(), Nette\Database\ConnectionException::class, null, 7, diff --git a/tests/Database/Connection.exceptions.sqlite.phpt b/tests/Database/Connection.exceptions.sqlite.phpt index b6fa35da9..12d41bbc5 100644 --- a/tests/Database/Connection.exceptions.sqlite.phpt +++ b/tests/Database/Connection.exceptions.sqlite.phpt @@ -11,13 +11,13 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('Exception thrown for unable to open database file', function () { $e = Assert::exception( - fn() => (new Nette\Database\Connection('sqlite:.'))->connect(), + fn() => (new Nette\Database\Explorer('sqlite:.'))->connect(), Nette\Database\ConnectionException::class, 'SQLSTATE[HY000] [14] unable to open database file', 14, diff --git a/tests/Database/Connection.fetch.phpt b/tests/Database/Connection.fetch.phpt index e4e09421f..0d1bffaf6 100644 --- a/tests/Database/Connection.fetch.phpt +++ b/tests/Database/Connection.fetch.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Connection.getInsertId().mysql.phpt b/tests/Database/Connection.getInsertId().mysql.phpt index 1dee7e83d..ffd7a71ff 100644 --- a/tests/Database/Connection.getInsertId().mysql.phpt +++ b/tests/Database/Connection.getInsertId().mysql.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $connection->query(' CREATE TEMPORARY TABLE noprimarykey ( diff --git a/tests/Database/Connection.getInsertId().postgre.phpt b/tests/Database/Connection.getInsertId().postgre.phpt index 60dc58b73..41e2f10d5 100644 --- a/tests/Database/Connection.getInsertId().postgre.phpt +++ b/tests/Database/Connection.getInsertId().postgre.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $connection->query(' CREATE TEMPORARY TABLE "primarykey" ( diff --git a/tests/Database/Connection.getInsertId().sqlite.phpt b/tests/Database/Connection.getInsertId().sqlite.phpt index fcfac3213..131431fca 100644 --- a/tests/Database/Connection.getInsertId().sqlite.phpt +++ b/tests/Database/Connection.getInsertId().sqlite.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $connection->query(' CREATE TABLE [noprimarykey] ( diff --git a/tests/Database/Connection.getInsertId().sqlsrv.phpt b/tests/Database/Connection.getInsertId().sqlsrv.phpt index c54444189..627fa45d0 100644 --- a/tests/Database/Connection.getInsertId().sqlsrv.phpt +++ b/tests/Database/Connection.getInsertId().sqlsrv.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $connection->query("IF OBJECT_ID('noprimarykey', 'U') IS NOT NULL DROP TABLE noprimarykey"); $connection->query(' diff --git a/tests/Database/Connection.preprocess.phpt b/tests/Database/Connection.preprocess.phpt index 5f8cd5547..1a59e3f80 100644 --- a/tests/Database/Connection.preprocess.phpt +++ b/tests/Database/Connection.preprocess.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Assert::same(['SELECT name FROM author', []], $connection->preprocess('SELECT name FROM author')); diff --git a/tests/Database/Connection.query.phpt b/tests/Database/Connection.query.phpt index 7fba6e7ff..7e4633b61 100644 --- a/tests/Database/Connection.query.phpt +++ b/tests/Database/Connection.query.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Connection.transaction.phpt b/tests/Database/Connection.transaction.phpt index 91d6f6fdc..26a3fc9bc 100644 --- a/tests/Database/Connection.transaction.phpt +++ b/tests/Database/Connection.transaction.phpt @@ -7,12 +7,12 @@ declare(strict_types=1); -use Nette\Database\Connection; +use Nette\Database\Explorer; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); @@ -27,7 +27,7 @@ test('', function () use ($connection) { test('', function () use ($connection) { Assert::exception( - fn() => $connection->transaction(function (Connection $connection) { + fn() => $connection->transaction(function (Explorer $connection) { $connection->query('DELETE FROM book'); throw new Exception('my exception'); }), @@ -52,13 +52,13 @@ test('nested transaction() call fail', function () use ($connection) { $base = (int) $connection->query('SELECT COUNT(*) AS cnt FROM author')->fetchField(); Assert::exception( - fn() => $connection->transaction(function (Connection $connection) { + fn() => $connection->transaction(function (Explorer $connection) { $connection->query('INSERT INTO author', [ 'name' => 'A', 'web' => '', ]); - $connection->transaction(function (Connection $connection2) { + $connection->transaction(function (Explorer $connection2) { $connection2->query('INSERT INTO author', [ 'name' => 'B', 'web' => '', @@ -77,7 +77,7 @@ test('nested transaction() call fail', function () use ($connection) { test('nested transaction() call success', function () use ($connection) { $base = (int) $connection->query('SELECT COUNT(*) AS cnt FROM author')->fetchField(); - $connection->transaction(function (Connection $connection) { + $connection->transaction(function (Explorer $connection) { $connection->query('INSERT INTO author', [ 'name' => 'A', 'web' => '', @@ -97,18 +97,18 @@ test('beginTransaction(), commit() & rollBack() calls are forbidden in transacti Assert::exception( fn() => $connection->transaction(fn() => $connection->beginTransaction()), LogicException::class, - Connection::class . '::beginTransaction() call is forbidden inside a transaction() callback', + Explorer::class . '::beginTransaction() call is forbidden inside a transaction() callback', ); Assert::exception( fn() => $connection->transaction(fn() => $connection->commit()), LogicException::class, - Connection::class . '::commit() call is forbidden inside a transaction() callback', + Explorer::class . '::commit() call is forbidden inside a transaction() callback', ); Assert::exception( fn() => $connection->transaction(fn() => $connection->rollBack()), LogicException::class, - Connection::class . '::rollBack() call is forbidden inside a transaction() callback', + Explorer::class . '::rollBack() call is forbidden inside a transaction() callback', ); }); diff --git a/tests/Database/Engine.postgre.10.phpt b/tests/Database/Engine.postgre.10.phpt index 6d4519adc..c6e64379e 100644 --- a/tests/Database/Engine.postgre.10.phpt +++ b/tests/Database/Engine.postgre.10.phpt @@ -12,7 +12,7 @@ use Tester\Environment; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $ver = $connection->query('SHOW server_version')->fetchField(); if (version_compare($ver, '10') < 0) { diff --git a/tests/Database/Engine.postgre.phpt b/tests/Database/Engine.postgre.phpt index 0084766ce..89fc5dd90 100644 --- a/tests/Database/Engine.postgre.phpt +++ b/tests/Database/Engine.postgre.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); function names($columns): array diff --git a/tests/Database/Engine.reflection.phpt b/tests/Database/Engine.reflection.phpt index a5b39538a..43d0449ed 100644 --- a/tests/Database/Engine.reflection.phpt +++ b/tests/Database/Engine.reflection.phpt @@ -13,12 +13,11 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/files/{$driverName}-nette_test1.sql"); -$engine = $connection->getDatabaseEngine(); +$engine = $explorer->getDatabaseEngine(); $tables = $engine->getTables(); $tables = array_filter($tables, fn($t) => in_array($t['name'], ['author', 'book', 'book_tag', 'tag'], true)); usort($tables, fn($a, $b) => strcmp($a['name'], $b['name'])); @@ -98,7 +97,7 @@ $expectedColumns = [ switch ($driverName) { case 'mysql': - $version = $connection->getServerVersion(); + $version = $explorer->getServerVersion(); if (version_compare($version, '8.0', '>=')) { $expectedColumns[0]['size'] = null; } diff --git a/tests/Database/Explorer.fetch.phpt b/tests/Database/Explorer.fetch.phpt index 8b07a8e1b..0a6fe9a8b 100644 --- a/tests/Database/Explorer.fetch.phpt +++ b/tests/Database/Explorer.fetch.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('fetch', function () use ($explorer) { diff --git a/tests/Database/Explorer.query.phpt b/tests/Database/Explorer.query.phpt index bf4f9342c..e36c76148 100644 --- a/tests/Database/Explorer.query.phpt +++ b/tests/Database/Explorer.query.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer.transaction.phpt b/tests/Database/Explorer.transaction.phpt index adbf31b1d..7dddcc567 100644 --- a/tests/Database/Explorer.transaction.phpt +++ b/tests/Database/Explorer.transaction.phpt @@ -13,9 +13,8 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/ActiveRow.__toString().phpt b/tests/Database/Explorer/ActiveRow.__toString().phpt index f3a29d9d7..ce25195b6 100644 --- a/tests/Database/Explorer/ActiveRow.__toString().phpt +++ b/tests/Database/Explorer/ActiveRow.__toString().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.aggregation.phpt b/tests/Database/Explorer/Explorer.aggregation.phpt index e04fea142..79d31bdee 100644 --- a/tests/Database/Explorer/Explorer.aggregation.phpt +++ b/tests/Database/Explorer/Explorer.aggregation.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.backjoin.phpt b/tests/Database/Explorer/Explorer.backjoin.phpt index 73f982dd0..7fdcf7f61 100644 --- a/tests/Database/Explorer/Explorer.backjoin.phpt +++ b/tests/Database/Explorer/Explorer.backjoin.phpt @@ -13,10 +13,9 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$engine = $connection->getDatabaseEngine(); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +$engine = $explorer->getDatabaseEngine(); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.basic.camelCase.phpt b/tests/Database/Explorer/Explorer.basic.camelCase.phpt index 6fe3432fc..4f3a072b6 100644 --- a/tests/Database/Explorer/Explorer.basic.camelCase.phpt +++ b/tests/Database/Explorer/Explorer.basic.camelCase.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test2.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test2.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.basic.phpt b/tests/Database/Explorer/Explorer.basic.phpt index 4a1d833be..c109645fc 100644 --- a/tests/Database/Explorer/Explorer.basic.phpt +++ b/tests/Database/Explorer/Explorer.basic.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.cache.observer.phpt b/tests/Database/Explorer/Explorer.cache.observer.phpt index 278972883..931a926a8 100644 --- a/tests/Database/Explorer/Explorer.cache.observer.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer.phpt @@ -13,21 +13,24 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $cache = Mockery::mock(Nette\Caching\Cache::class); +$cache->shouldReceive('load')->withAnyArgs()->once()->andReturn([]); $cache->shouldReceive('load')->withAnyArgs()->once()->andReturn(['id' => true]); $cache->shouldReceive('load')->withAnyArgs()->times(4)->andReturn(['id' => true, 'author_id' => true]); +$cache->shouldReceive('save')->with('structure', Mockery::any()); $cache->shouldReceive('save')->with(Mockery::any(), ['id' => true, 'author_id' => true, 'title' => true]); - -$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cache); +$explorer->setCache($cache); $queries = 0; -$connection->onQuery[] = function ($dao, Result $result) use (&$queries) { - if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQuery()->getSql())) { +$explorer->onQuery[] = function ($explorer, $result) use (&$queries) { + if ( + $result instanceof Result + && !preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQuery()->getSql()) + ) { $queries++; } }; diff --git a/tests/Database/Explorer/Explorer.cache.observer2.phpt b/tests/Database/Explorer/Explorer.cache.observer2.phpt index e81555b38..0e7ce2ea1 100644 --- a/tests/Database/Explorer/Explorer.cache.observer2.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer2.phpt @@ -14,9 +14,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); class CacheMock extends Cache @@ -32,7 +31,7 @@ class CacheMock extends Cache } $cache = new CacheMock(new MemoryStorage); -$explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(), $explorer->getConventions(), $cache); +$explorer->setCache($cache); for ($i = 0; $i < 2; ++$i) { $authors = $explorer->table('author'); @@ -53,4 +52,4 @@ for ($i = 0; $i < 2; ++$i) { } Assert::same(reformat('SELECT [id], [name] FROM [author]'), $sql); -Assert::same(2, $cache->writes); +Assert::same(3, $cache->writes); // Structure + 2x Selection diff --git a/tests/Database/Explorer/Explorer.cache.phpt b/tests/Database/Explorer/Explorer.cache.phpt index 20e94bad9..9ec981e47 100644 --- a/tests/Database/Explorer/Explorer.cache.phpt +++ b/tests/Database/Explorer/Explorer.cache.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('Testing Selection caching', function () use ($explorer) { @@ -198,7 +197,7 @@ test('Test saving the union of needed cols, the second call is not subset', func test('Test multiple use of same selection', function () use ($explorer) { $sql = []; - $explorer->getConnection()->onQuery[] = function ($_, $result) use (&$sql) { + $explorer->onQuery[] = function ($_, $result) use (&$sql) { $sql[] = $result->getQueryString(); }; diff --git a/tests/Database/Explorer/Explorer.cache.rows.phpt b/tests/Database/Explorer/Explorer.cache.rows.phpt index 78761af63..19ba71027 100644 --- a/tests/Database/Explorer/Explorer.cache.rows.phpt +++ b/tests/Database/Explorer/Explorer.cache.rows.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $selections = []; diff --git a/tests/Database/Explorer/Explorer.cache2.phpt b/tests/Database/Explorer/Explorer.cache2.phpt index c174b7779..5f6202526 100644 --- a/tests/Database/Explorer/Explorer.cache2.phpt +++ b/tests/Database/Explorer/Explorer.cache2.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $res = []; diff --git a/tests/Database/Explorer/Explorer.columnRefetch.phpt b/tests/Database/Explorer/Explorer.columnRefetch.phpt index 4ff46a398..e49d7917e 100644 --- a/tests/Database/Explorer/Explorer.columnRefetch.phpt +++ b/tests/Database/Explorer/Explorer.columnRefetch.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $books = $explorer->table('book')->order('id DESC')->limit(2); diff --git a/tests/Database/Explorer/Explorer.discoveredReflection.phpt b/tests/Database/Explorer/Explorer.discoveredReflection.phpt index eed582dab..f427a8e5c 100644 --- a/tests/Database/Explorer/Explorer.discoveredReflection.phpt +++ b/tests/Database/Explorer/Explorer.discoveredReflection.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { @@ -90,10 +89,10 @@ test('', function () use ($explorer) { }); -test('', function () use ($connection, $explorer, $driverName) { +test('', function () use ($explorer, $driverName) { if ( $driverName === 'mysql' && - ($lowerCase = $connection->query('SHOW VARIABLES LIKE "lower_case_table_names"')->fetch()) && + ($lowerCase = $explorer->query('SHOW VARIABLES LIKE "lower_case_table_names"')->fetch()) && $lowerCase->Value != 0 ) { // tests case-insensitive reflection diff --git a/tests/Database/Explorer/Explorer.join-condition.phpt b/tests/Database/Explorer/Explorer.join-condition.phpt index 1e92b43e6..9b22801ac 100644 --- a/tests/Database/Explorer/Explorer.join-condition.phpt +++ b/tests/Database/Explorer/Explorer.join-condition.phpt @@ -13,10 +13,10 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$engine = $connection->getDatabaseEngine(); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +$engine = $explorer->getDatabaseEngine(); + test('', function () use ($explorer, $engine) { $schema = $engine->isSupported(Engine::SupportSchema) ? '[public].' diff --git a/tests/Database/Explorer/Explorer.join.phpt b/tests/Database/Explorer/Explorer.join.phpt index d0638f2b1..39f63fa64 100644 --- a/tests/Database/Explorer/Explorer.join.phpt +++ b/tests/Database/Explorer/Explorer.join.phpt @@ -13,10 +13,9 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$engine = $connection->getDatabaseEngine(); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +$engine = $explorer->getDatabaseEngine(); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.limit.phpt b/tests/Database/Explorer/Explorer.limit.phpt index ccf1bec6a..95b733982 100644 --- a/tests/Database/Explorer/Explorer.limit.phpt +++ b/tests/Database/Explorer/Explorer.limit.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); Assert::same( diff --git a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt index c8ef3a7b2..a803010e6 100644 --- a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt +++ b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt @@ -12,11 +12,10 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); -$version2008 = $connection->getServerVersion() < 11; +$version2008 = $explorer->getServerVersion() < 11; Assert::same( $version2008 diff --git a/tests/Database/Explorer/Explorer.multi-primary-key.phpt b/tests/Database/Explorer/Explorer.multi-primary-key.phpt index d914c028a..decc738f8 100644 --- a/tests/Database/Explorer/Explorer.multi-primary-key.phpt +++ b/tests/Database/Explorer/Explorer.multi-primary-key.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.placeholders.phpt b/tests/Database/Explorer/Explorer.placeholders.phpt index 52ce83415..858c6dd55 100644 --- a/tests/Database/Explorer/Explorer.placeholders.phpt +++ b/tests/Database/Explorer/Explorer.placeholders.phpt @@ -13,9 +13,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('Leave literals lower-cased, also not-delimiting them is tested.', function () use ($explorer, $driverName) { diff --git a/tests/Database/Explorer/Explorer.ref().phpt b/tests/Database/Explorer/Explorer.ref().phpt index a8a017087..e2cfdd36a 100644 --- a/tests/Database/Explorer/Explorer.ref().phpt +++ b/tests/Database/Explorer/Explorer.ref().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); Assert::same('Jakub Vrana', $explorer->table('book')->get(1)->ref('author')->name); @@ -36,10 +35,10 @@ test('', function () use ($explorer) { Assert::null($explorer->table('book')->get(2)->ref('author', 'translator_id')); }); -test('', function () use ($explorer, $connection) { +test('', function () use ($explorer) { $counter = 0; - $connection->onQuery[] = function ($connection, $result) use (&$counter) { + $explorer->onQuery[] = function ($explorer, $result) use (&$counter) { $counter++; }; diff --git a/tests/Database/Explorer/Explorer.related().caching.phpt b/tests/Database/Explorer/Explorer.related().caching.phpt index d6995923d..f1322ca3b 100644 --- a/tests/Database/Explorer/Explorer.related().caching.phpt +++ b/tests/Database/Explorer/Explorer.related().caching.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.related().phpt b/tests/Database/Explorer/Explorer.related().phpt index 84455a402..daf9c3462 100644 --- a/tests/Database/Explorer/Explorer.related().phpt +++ b/tests/Database/Explorer/Explorer.related().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.self-reference.phpt b/tests/Database/Explorer/Explorer.self-reference.phpt index 345043024..6d13a2710 100644 --- a/tests/Database/Explorer/Explorer.self-reference.phpt +++ b/tests/Database/Explorer/Explorer.self-reference.phpt @@ -12,15 +12,14 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $explorer->query('UPDATE book SET next_volume = 3 WHERE id IN (2,4)'); -test('', function () use ($connection, $explorer) { +test('', function () use ($explorer) { $book = $explorer->table('book')->get(4); Assert::same('Nette', $book->volume->title); Assert::same('Nette', $book->ref('book', 'next_volume')->title); diff --git a/tests/Database/Explorer/Explorer.subquery.phpt b/tests/Database/Explorer/Explorer.subquery.phpt index 314188f0d..744a5ecc6 100644 --- a/tests/Database/Explorer/Explorer.subquery.phpt +++ b/tests/Database/Explorer/Explorer.subquery.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Explorer.update().phpt b/tests/Database/Explorer/Explorer.update().phpt index 008bae316..0c966779f 100644 --- a/tests/Database/Explorer/Explorer.update().phpt +++ b/tests/Database/Explorer/Explorer.update().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $author = $explorer->table('author')->get(12); // SELECT * FROM `author` WHERE (`id` = ?) diff --git a/tests/Database/Explorer/GroupedSelection.insert().phpt b/tests/Database/Explorer/GroupedSelection.insert().phpt index 60c239637..b24d7b307 100644 --- a/tests/Database/Explorer/GroupedSelection.insert().phpt +++ b/tests/Database/Explorer/GroupedSelection.insert().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.delete().phpt b/tests/Database/Explorer/Selection.delete().phpt index f5292bb95..f3ba7f959 100644 --- a/tests/Database/Explorer/Selection.delete().phpt +++ b/tests/Database/Explorer/Selection.delete().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.fetch().phpt b/tests/Database/Explorer/Selection.fetch().phpt index ec71f3887..594debe55 100644 --- a/tests/Database/Explorer/Selection.fetch().phpt +++ b/tests/Database/Explorer/Selection.fetch().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.fetchAssoc().phpt b/tests/Database/Explorer/Selection.fetchAssoc().phpt index 6280b9cb6..63e66b7b9 100644 --- a/tests/Database/Explorer/Selection.fetchAssoc().phpt +++ b/tests/Database/Explorer/Selection.fetchAssoc().phpt @@ -13,9 +13,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.fetchField().phpt b/tests/Database/Explorer/Selection.fetchField().phpt index 5eb29cc3b..ff35ca756 100644 --- a/tests/Database/Explorer/Selection.fetchField().phpt +++ b/tests/Database/Explorer/Selection.fetchField().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.fetchPairs().phpt b/tests/Database/Explorer/Selection.fetchPairs().phpt index 89fc97028..832ed2e3f 100644 --- a/tests/Database/Explorer/Selection.fetchPairs().phpt +++ b/tests/Database/Explorer/Selection.fetchPairs().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.get().phpt b/tests/Database/Explorer/Selection.get().phpt index 138afd97e..b153cc479 100644 --- a/tests/Database/Explorer/Selection.get().phpt +++ b/tests/Database/Explorer/Selection.get().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.group().phpt b/tests/Database/Explorer/Selection.group().phpt index ec51d7da7..68cb0ef2b 100644 --- a/tests/Database/Explorer/Selection.group().phpt +++ b/tests/Database/Explorer/Selection.group().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.insert().multi.phpt b/tests/Database/Explorer/Selection.insert().multi.phpt index 99dbcdcb7..0a032e008 100644 --- a/tests/Database/Explorer/Selection.insert().multi.phpt +++ b/tests/Database/Explorer/Selection.insert().multi.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.insert().phpt b/tests/Database/Explorer/Selection.insert().phpt index 69e219c90..4f5599278 100644 --- a/tests/Database/Explorer/Selection.insert().phpt +++ b/tests/Database/Explorer/Selection.insert().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $book = $explorer->table('author')->insert([ diff --git a/tests/Database/Explorer/Selection.insert().primaryKeys.phpt b/tests/Database/Explorer/Selection.insert().primaryKeys.phpt index 885a89e37..0562d6af7 100644 --- a/tests/Database/Explorer/Selection.insert().primaryKeys.phpt +++ b/tests/Database/Explorer/Selection.insert().primaryKeys.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test4.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test4.sql"); test('Insert into table with simple primary index (autoincrement)', function () use ($explorer) { $simplePkAutoincrementResult = $explorer->table('simple_pk_autoincrement')->insert([ diff --git a/tests/Database/Explorer/Selection.order().phpt b/tests/Database/Explorer/Selection.order().phpt index f264baa84..5633bbfb7 100644 --- a/tests/Database/Explorer/Selection.order().phpt +++ b/tests/Database/Explorer/Selection.order().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/Selection.page().phpt b/tests/Database/Explorer/Selection.page().phpt index 614b84b02..b4cce1ccb 100644 --- a/tests/Database/Explorer/Selection.page().phpt +++ b/tests/Database/Explorer/Selection.page().phpt @@ -12,13 +12,12 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -if ($driverName === 'sqlsrv' && $connection->getServerVersion() < 11) { +if ($driverName === 'sqlsrv' && $explorer->getServerVersion() < 11) { Tester\Environment::skip('Offset is supported since SQL Server 2012'); } -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); //public function page($page, $itemsPerPage, &$numOfPages = null) diff --git a/tests/Database/Explorer/Selection.whereOr().phpt b/tests/Database/Explorer/Selection.whereOr().phpt index 9cc8b1058..2279db58a 100644 --- a/tests/Database/Explorer/Selection.whereOr().phpt +++ b/tests/Database/Explorer/Selection.whereOr().phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('without question mark', function () use ($explorer) { $count = $explorer->table('book')->whereOr([ diff --git a/tests/Database/Explorer/SqlBuilder.addAlias().phpt b/tests/Database/Explorer/SqlBuilder.addAlias().phpt index 3b253b6da..adb9a41d3 100644 --- a/tests/Database/Explorer/SqlBuilder.addAlias().phpt +++ b/tests/Database/Explorer/SqlBuilder.addAlias().phpt @@ -14,9 +14,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); class SqlBuilderMock extends SqlBuilder { @@ -32,7 +31,7 @@ class SqlBuilderMock extends SqlBuilder } } -$engine = $connection->getDatabaseEngine(); +$engine = $explorer->getDatabaseEngine(); test('test duplicated table names throw exception', function () use ($explorer, $engine) { diff --git a/tests/Database/Explorer/SqlBuilder.addWhere().phpt b/tests/Database/Explorer/SqlBuilder.addWhere().phpt index 9c3541bef..92b43343e 100644 --- a/tests/Database/Explorer/SqlBuilder.addWhere().phpt +++ b/tests/Database/Explorer/SqlBuilder.addWhere().phpt @@ -15,9 +15,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); test('test paramateres with null', function () use ($explorer) { @@ -243,7 +242,7 @@ Assert::exception(function () use ($explorer) { }, Nette\InvalidArgumentException::class, 'Column operator does not accept array argument.'); -test('', function () use ($driverName, $explorer, $connection) { +test('', function () use ($driverName, $explorer) { $structure = $explorer->getStructure(); switch ($driverName) { case 'mysql': diff --git a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt index 17e6909e7..b1c6f9f5f 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt @@ -14,9 +14,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); class SqlBuilderMock extends SqlBuilder { @@ -44,7 +43,7 @@ class SqlBuilderMock extends SqlBuilder } } -$engine = $connection->getDatabaseEngine(); +$engine = $explorer->getDatabaseEngine(); test('test circular reference', function () use ($explorer) { $sqlBuilder = new SqlBuilderMock('author', $explorer); diff --git a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt index c77b37f44..3fabed91b 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt @@ -15,9 +15,8 @@ use Tester\Assert; require __DIR__ . '/../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test2.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test2.sql"); class SqlBuilderMock extends SqlBuilder @@ -37,7 +36,7 @@ class SqlBuilderMock extends SqlBuilder $structure = $explorer->getStructure(); $conventions = new DiscoveredConventions($structure); $sqlBuilder = new SqlBuilderMock('nUsers', $explorer); -$engine = $connection->getDatabaseEngine(); +$engine = $explorer->getDatabaseEngine(); $joins = []; @@ -46,7 +45,7 @@ $sqlBuilder->parseJoins($joins, $query); $join = $sqlBuilder->buildQueryJoins($joins); Assert::same('WHERE priorit.id IS NULL', $query); -$tables = $connection->getDatabaseEngine()->getTables(); +$tables = $explorer->getDatabaseEngine()->getTables(); if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_ntopics', 'nusers_ntopics_alt'], true)) { if ($engine->isSupported(Engine::SupportSchema)) { Assert::same( @@ -75,7 +74,7 @@ if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_n } -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $structure->rebuild(); $sqlBuilder = new SqlBuilderMock('author', $explorer); diff --git a/tests/Database/Explorer/bugs/ActiveRow.__isset().phpt b/tests/Database/Explorer/bugs/ActiveRow.__isset().phpt index bac634f6a..a26087efd 100644 --- a/tests/Database/Explorer/bugs/ActiveRow.__isset().phpt +++ b/tests/Database/Explorer/bugs/ActiveRow.__isset().phpt @@ -11,9 +11,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/bugs/Selection.emptyResultSet.phpt b/tests/Database/Explorer/bugs/Selection.emptyResultSet.phpt index e0b42ed08..ff3974cee 100644 --- a/tests/Database/Explorer/bugs/Selection.emptyResultSet.phpt +++ b/tests/Database/Explorer/bugs/Selection.emptyResultSet.phpt @@ -11,9 +11,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/bugs/Selection.getReferencingTable.pkTypes.phpt b/tests/Database/Explorer/bugs/Selection.getReferencingTable.pkTypes.phpt index cc4621b7b..fe38a5be9 100644 --- a/tests/Database/Explorer/bugs/Selection.getReferencingTable.pkTypes.phpt +++ b/tests/Database/Explorer/bugs/Selection.getReferencingTable.pkTypes.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test5.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test5.sql"); test('referencing table with integer primary key', function () use ($explorer) { diff --git a/tests/Database/Explorer/bugs/bug1356.phpt b/tests/Database/Explorer/bugs/bug1356.phpt index 9e70d15cd..9e26a2eda 100644 --- a/tests/Database/Explorer/bugs/bug1356.phpt +++ b/tests/Database/Explorer/bugs/bug1356.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); $books = $explorer->table('book')->limit(1); @@ -31,7 +30,7 @@ foreach ($books as $book) { } Assert::same(reformat([ - 'sqlsrv' => $connection->getServerVersion() < 11 + 'sqlsrv' => $explorer->getServerVersion() < 11 ? 'SELECT TOP 1 * FROM [book] ORDER BY [book].[id]' : 'SELECT * FROM [book] ORDER BY [book].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY', 'SELECT * FROM [book] ORDER BY [book].[id] LIMIT 1', diff --git a/tests/Database/Explorer/bugs/bug170.phpt b/tests/Database/Explorer/bugs/bug170.phpt index 53c020d17..098400e78 100644 --- a/tests/Database/Explorer/bugs/bug170.phpt +++ b/tests/Database/Explorer/bugs/bug170.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-bug170.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-bug170.sql"); Assert::noError(function () use ($explorer) { // this bug is about picking the right foreign key to specified table regardless FKs definition order diff --git a/tests/Database/Explorer/bugs/bug187.phpt b/tests/Database/Explorer/bugs/bug187.phpt index a5210b55a..e1a44fd41 100644 --- a/tests/Database/Explorer/bugs/bug187.phpt +++ b/tests/Database/Explorer/bugs/bug187.phpt @@ -13,9 +13,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-bug187.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-bug187.sql"); foreach ([true, false] as $published) { $where = $published diff --git a/tests/Database/Explorer/bugs/bug216.phpt b/tests/Database/Explorer/bugs/bug216.phpt index 23db5de1e..8f085c919 100644 --- a/tests/Database/Explorer/bugs/bug216.phpt +++ b/tests/Database/Explorer/bugs/bug216.phpt @@ -12,9 +12,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); $book = $explorer->table('author')->insert([ 'name' => $explorer->literal('LOWER(?)', 'Eddard Stark'), diff --git a/tests/Database/Explorer/bugs/deleteCacheBug.phpt b/tests/Database/Explorer/bugs/deleteCacheBug.phpt index fd1912a48..227f9c4a2 100644 --- a/tests/Database/Explorer/bugs/deleteCacheBug.phpt +++ b/tests/Database/Explorer/bugs/deleteCacheBug.phpt @@ -11,9 +11,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { for ($i = 0; $i < 2; $i++) { diff --git a/tests/Database/Explorer/bugs/query.count.phpt b/tests/Database/Explorer/bugs/query.count.phpt index 52dd15ce5..d26c4339b 100644 --- a/tests/Database/Explorer/bugs/query.count.phpt +++ b/tests/Database/Explorer/bugs/query.count.phpt @@ -11,20 +11,19 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); // add additional tags (not relevant to other tests) $explorer->query("INSERT INTO book_tag_alt (book_id, tag_id, state) VALUES (1, 24, 'private');"); $explorer->query("INSERT INTO book_tag_alt (book_id, tag_id, state) VALUES (2, 24, 'private');"); $explorer->query("INSERT INTO book_tag_alt (book_id, tag_id, state) VALUES (2, 22, 'private');"); -test('', function () use ($connection, $explorer) { +test('', function () use ($explorer) { $explorer->table('author')->get(11); // have to build cache first $count = 0; - $connection->onQuery[] = function () use (&$count) { + $explorer->onQuery[] = function () use (&$count) { $count++; }; diff --git a/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt b/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt index 0ff25ccdf..2f3ca74c2 100644 --- a/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt +++ b/tests/Database/Explorer/bugs/staticReflection.undeclaredColumn.phpt @@ -12,13 +12,10 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -$conventions = new Nette\Database\Conventions\StaticConventions; -$cache = new Nette\Caching\Cache(new Nette\Caching\Storages\MemoryStorage); -$explorer = new Nette\Database\Explorer($explorer->getConnection(), $explorer->getStructure(), $conventions, $cache); +$explorer->setConventions(new Nette\Database\Conventions\StaticConventions); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); test('', function () use ($explorer) { diff --git a/tests/Database/Explorer/bugs/view.bug.phpt b/tests/Database/Explorer/bugs/view.bug.phpt index 0837c9d93..32cd165b4 100644 --- a/tests/Database/Explorer/bugs/view.bug.phpt +++ b/tests/Database/Explorer/bugs/view.bug.phpt @@ -11,9 +11,8 @@ use Tester\Assert; require __DIR__ . '/../../../bootstrap.php'; $explorer = connectToDB(); -$connection = $explorer->getConnection(); -Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); +Nette\Database\Helpers::loadFromFile($explorer, __DIR__ . "/../../files/{$driverName}-nette_test1.sql"); $explorer->query('CREATE VIEW books_view AS SELECT * FROM book'); @@ -22,8 +21,8 @@ test('', function () use ($explorer) { Assert::same(1, $selection->count()); }); -test('', function () use ($connection) { - $engine = $connection->getDatabaseEngine(); +test('', function () use ($explorer) { + $engine = $explorer->getDatabaseEngine(); $columns = $engine->getColumns('books_view'); $columnsNames = array_map(fn($item) => $item['name'], $columns); Assert::same(['id', 'author_id', 'translator_id', 'title', 'next_volume'], $columnsNames); diff --git a/tests/Database/Helpers.dumpSql.phpt b/tests/Database/Helpers.dumpSql.phpt index e080bfa90..360961b2d 100644 --- a/tests/Database/Helpers.dumpSql.phpt +++ b/tests/Database/Helpers.dumpSql.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Helpers.loadFromFile.phpt b/tests/Database/Helpers.loadFromFile.phpt index 1f6c616cc..d0cc8ba43 100644 --- a/tests/Database/Helpers.loadFromFile.phpt +++ b/tests/Database/Helpers.loadFromFile.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-delimiter.sql'); $arr = $connection->query('SELECT name, id FROM author ORDER BY id')->fetchAll(); diff --git a/tests/Database/Reflection.columns.mysql.phpt b/tests/Database/Reflection.columns.mysql.phpt index 9f969eced..07ab45e8b 100644 --- a/tests/Database/Reflection.columns.mysql.phpt +++ b/tests/Database/Reflection.columns.mysql.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); diff --git a/tests/Database/Reflection.columns.postgre.phpt b/tests/Database/Reflection.columns.postgre.phpt index 49d5ca894..81c219838 100644 --- a/tests/Database/Reflection.columns.postgre.phpt +++ b/tests/Database/Reflection.columns.postgre.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/pgsql-nette_test3.sql'); diff --git a/tests/Database/Reflection.columns.sqlite.phpt b/tests/Database/Reflection.columns.sqlite.phpt index bd8b6ecb6..c2b7e8855 100644 --- a/tests/Database/Reflection.columns.sqlite.phpt +++ b/tests/Database/Reflection.columns.sqlite.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); diff --git a/tests/Database/Reflection.columns.sqlsrv.phpt b/tests/Database/Reflection.columns.sqlsrv.phpt index 246007031..1d7ce8952 100644 --- a/tests/Database/Reflection.columns.sqlsrv.phpt +++ b/tests/Database/Reflection.columns.sqlsrv.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index d73dc3580..9afcd4116 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetch().phpt b/tests/Database/Result.fetch().phpt index 36d5adb2a..a62214044 100644 --- a/tests/Database/Result.fetch().phpt +++ b/tests/Database/Result.fetch().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetchAll().phpt b/tests/Database/Result.fetchAll().phpt index 6ae7375ab..0609c94cf 100644 --- a/tests/Database/Result.fetchAll().phpt +++ b/tests/Database/Result.fetchAll().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetchAssoc().phpt b/tests/Database/Result.fetchAssoc().phpt index f7b60babe..2b33f8287 100644 --- a/tests/Database/Result.fetchAssoc().phpt +++ b/tests/Database/Result.fetchAssoc().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetchField().phpt b/tests/Database/Result.fetchField().phpt index 83e8473e8..f54d3b6cb 100644 --- a/tests/Database/Result.fetchField().phpt +++ b/tests/Database/Result.fetchField().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetchList().phpt b/tests/Database/Result.fetchList().phpt index e60176a71..3885b942b 100644 --- a/tests/Database/Result.fetchList().phpt +++ b/tests/Database/Result.fetchList().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/Result.fetchPairs().phpt b/tests/Database/Result.fetchPairs().phpt index 601bb155e..6927dfb8a 100644 --- a/tests/Database/Result.fetchPairs().phpt +++ b/tests/Database/Result.fetchPairs().phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql"); diff --git a/tests/Database/ResultSet.normalizeRow.mysql.phpt b/tests/Database/ResultSet.normalizeRow.mysql.phpt index b3c6388b4..e078dfbf6 100644 --- a/tests/Database/ResultSet.normalizeRow.mysql.phpt +++ b/tests/Database/ResultSet.normalizeRow.mysql.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); diff --git a/tests/Database/ResultSet.normalizeRow.postgre.phpt b/tests/Database/ResultSet.normalizeRow.postgre.phpt index fd24a2315..eac4f0e84 100644 --- a/tests/Database/ResultSet.normalizeRow.postgre.phpt +++ b/tests/Database/ResultSet.normalizeRow.postgre.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/pgsql-nette_test3.sql'); diff --git a/tests/Database/ResultSet.normalizeRow.sqlite.phpt b/tests/Database/ResultSet.normalizeRow.sqlite.phpt index 347cf977b..5e92fcd3a 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlite.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlite.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); diff --git a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt index 555e2131b..4ff795da7 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); diff --git a/tests/Database/Row.phpt b/tests/Database/Row.phpt index ea63dbe3b..c254282e8 100644 --- a/tests/Database/Row.phpt +++ b/tests/Database/Row.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); test('numeric field', function () use ($connection) { $row = $connection->fetch("SELECT 123 AS {$connection->getDatabaseEngine()->delimite('123')}, NULL as nullcol"); diff --git a/tests/Database/SqlPreprocessor.enum.phpt b/tests/Database/SqlPreprocessor.enum.phpt index 91c787cff..0dc4cc250 100644 --- a/tests/Database/SqlPreprocessor.enum.phpt +++ b/tests/Database/SqlPreprocessor.enum.phpt @@ -11,7 +11,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); enum EnumInt: int { diff --git a/tests/Database/SqlPreprocessor.phpt b/tests/Database/SqlPreprocessor.phpt index f51d5ef02..ee8daccc2 100644 --- a/tests/Database/SqlPreprocessor.phpt +++ b/tests/Database/SqlPreprocessor.phpt @@ -12,7 +12,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -$connection = connectToDB()->getConnection(); +$connection = connectToDB(); $preprocessor = new Nette\Database\SqlPreprocessor($connection); test('basic', function () use ($preprocessor) { diff --git a/tests/Database/connection.disconnect.phpt b/tests/Database/connection.disconnect.phpt index b1c3b25f0..b2b06ed8e 100644 --- a/tests/Database/connection.disconnect.phpt +++ b/tests/Database/connection.disconnect.phpt @@ -16,7 +16,7 @@ test('connect & disconnect', function () { $options = Tester\Environment::loadData() + ['username' => null, 'password' => null]; $connections = 1; - $connection = new Nette\Database\Connection($options['dsn'], $options['username'], $options['password']); + $connection = new Nette\Database\Explorer($options['dsn'], $options['username'], $options['password']); try { $connection->connect(); } catch (Nette\Database\DriverException $e) { diff --git a/tests/Database/connection.options.mysql.phpt b/tests/Database/connection.options.mysql.phpt index 9040f52fb..9e20c290d 100644 --- a/tests/Database/connection.options.mysql.phpt +++ b/tests/Database/connection.options.mysql.phpt @@ -13,13 +13,13 @@ require __DIR__ . '/../bootstrap.php'; test('default charset', function () { - $connection = connectToDB(['charset' => null])->getConnection(); + $connection = connectToDB(['charset' => null]); $row = $connection->fetch("SHOW VARIABLES LIKE 'character_set_client'"); Assert::same('utf8mb4', $row->Value); }); test('custom charset', function () { - $connection = connectToDB(['charset' => 'latin2'])->getConnection(); + $connection = connectToDB(['charset' => 'latin2']); $row = $connection->fetch("SHOW VARIABLES LIKE 'character_set_client'"); Assert::same('latin2', $row->Value); }); @@ -27,28 +27,28 @@ test('custom charset', function () { test('custom sqlmode', function () { $desiredMode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'; - $connection = connectToDB(['sqlmode' => $desiredMode])->getConnection(); + $connection = connectToDB(['sqlmode' => $desiredMode]); $field = $connection->fetchField('SELECT @@sql_mode'); Assert::same($desiredMode, $field); }); test('default convertBoolean', function () { - $connection = connectToDB(['convertBoolean' => null])->getConnection(); + $connection = connectToDB(['convertBoolean' => null]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(true, $row->bool); }); test('convertBoolean = true', function () { - $connection = connectToDB(['convertBoolean' => true])->getConnection(); + $connection = connectToDB(['convertBoolean' => true]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(true, $row->bool); }); test('convertBoolean = false', function () { - $connection = connectToDB(['convertBoolean' => false])->getConnection(); + $connection = connectToDB(['convertBoolean' => false]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(1, $row->bool); @@ -56,45 +56,45 @@ test('convertBoolean = false', function () { test('default newDateTime', function () { - $connection = connectToDB(['newDateTime' => null])->getConnection(); + $connection = connectToDB(['newDateTime' => null]); $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); test('newDateTime = false', function () { - $connection = connectToDB(['newDateTime' => false])->getConnection(); + $connection = connectToDB(['newDateTime' => false]); $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Utils\DateTime::class, $field); }); test('newDateTime = true', function () { - $connection = connectToDB(['newDateTime' => true])->getConnection(); + $connection = connectToDB(['newDateTime' => true]); $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); test('default convertDateTime', function () { - $connection = connectToDB(['convertDateTime' => null])->getConnection(); + $connection = connectToDB(['convertDateTime' => null]); $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); test('convertDateTime = false', function () { - $connection = connectToDB(['convertDateTime' => false])->getConnection(); + $connection = connectToDB(['convertDateTime' => false]); $field = $connection->fetchField('SELECT NOW()'); Assert::type('string', $field); }); test('convertDateTime = true', function () { - $connection = connectToDB(['convertDateTime' => true])->getConnection(); + $connection = connectToDB(['convertDateTime' => true]); $field = $connection->fetchField('SELECT NOW()'); Assert::type(Nette\Database\DateTime::class, $field); }); test('default convertDecimal', function () { - $connection = connectToDB(['convertDecimal' => null])->getConnection(); + $connection = connectToDB(['convertDecimal' => null]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(1, $row->decimal); @@ -105,7 +105,7 @@ test('default convertDecimal', function () { }); test('convertDecimal = false', function () { - $connection = connectToDB(['convertDecimal' => false])->getConnection(); + $connection = connectToDB(['convertDecimal' => false]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same('1', $row->decimal); @@ -116,7 +116,7 @@ test('convertDecimal = false', function () { }); test('convertDecimal = true', function () { - $connection = connectToDB(['convertDecimal' => true])->getConnection(); + $connection = connectToDB(['convertDecimal' => true]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/mysql-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(1, $row->decimal); diff --git a/tests/Database/connection.options.sqlite.phpt b/tests/Database/connection.options.sqlite.phpt index ce3967361..352803206 100644 --- a/tests/Database/connection.options.sqlite.phpt +++ b/tests/Database/connection.options.sqlite.phpt @@ -13,20 +13,20 @@ require __DIR__ . '/../bootstrap.php'; test('formatDateTime', function () { - $connection = connectToDB(['formatDateTime' => 'U'])->getConnection(); + $connection = connectToDB(['formatDateTime' => 'U']); $engine = $connection->getDatabaseEngine(); Assert::same('254358000', $engine->formatDateTime(new DateTime('1978-01-23 00:00:00'))); }); test('formatDateTime', function () { - $connection = connectToDB(['formatDateTime' => 'Y-m-d'])->getConnection(); + $connection = connectToDB(['formatDateTime' => 'Y-m-d']); $engine = $connection->getDatabaseEngine(); Assert::same('1978-01-23', $engine->formatDateTime(new DateTime('1978-01-23 00:00:00'))); }); test('default convertDateTime', function () { - $connection = connectToDB(['convertDateTime' => null])->getConnection(); + $connection = connectToDB(['convertDateTime' => null]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::type(Nette\Database\DateTime::class, $row->date); @@ -34,7 +34,7 @@ test('default convertDateTime', function () { }); test('convertDateTime = false', function () { - $connection = connectToDB(['convertDateTime' => false])->getConnection(); + $connection = connectToDB(['convertDateTime' => false]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::type('int', $row->date); @@ -42,7 +42,7 @@ test('convertDateTime = false', function () { }); test('convertDateTime = true', function () { - $connection = connectToDB(['convertDateTime' => true])->getConnection(); + $connection = connectToDB(['convertDateTime' => true]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlite-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::type(Nette\Database\DateTime::class, $row->date); diff --git a/tests/Database/connection.options.sqlsrv.phpt b/tests/Database/connection.options.sqlsrv.phpt index b23e8dd1f..39369c69a 100644 --- a/tests/Database/connection.options.sqlsrv.phpt +++ b/tests/Database/connection.options.sqlsrv.phpt @@ -13,7 +13,7 @@ require __DIR__ . '/../bootstrap.php'; test('default convertDecimal', function () { - $connection = connectToDB(['convertDecimal' => null])->getConnection(); + $connection = connectToDB(['convertDecimal' => null]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(1, $row->decimal); @@ -22,7 +22,7 @@ test('default convertDecimal', function () { }); test('convertDecimal = true', function () { - $connection = connectToDB(['convertDecimal' => true])->getConnection(); + $connection = connectToDB(['convertDecimal' => true]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same(1, $row->decimal); @@ -31,7 +31,7 @@ test('convertDecimal = true', function () { }); test('convertDecimal = false', function () { - $connection = connectToDB(['convertDecimal' => false])->getConnection(); + $connection = connectToDB(['convertDecimal' => false]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::same('1', $row->decimal); @@ -41,21 +41,21 @@ test('convertDecimal = false', function () { test('default convertBoolean', function () { - $connection = connectToDB(['convertBoolean' => null])->getConnection(); + $connection = connectToDB(['convertBoolean' => null]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::equal(true, $row->bit); }); test('convertBoolean = true', function () { - $connection = connectToDB(['convertBoolean' => true])->getConnection(); + $connection = connectToDB(['convertBoolean' => true]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::equal(true, $row->bit); }); test('convertBoolean = false', function () { - $connection = connectToDB(['convertBoolean' => false])->getConnection(); + $connection = connectToDB(['convertBoolean' => false]); Nette\Database\Helpers::loadFromFile($connection, __DIR__ . '/files/sqlsrv-nette_test3.sql'); $row = $connection->fetch('SELECT * FROM types'); Assert::equal(1, $row->bit); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3edeba25a..81f9667d9 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -34,20 +34,16 @@ function connectToDB(array $options = []): Nette\Database\Explorer Tester\Environment::lock($args['dsn'], getTempDir()); } - $connection = new Nette\Database\Connection($args['dsn'], $args['username'], $args['password'], $args['options']); + $explorer = new Nette\Database\Explorer($args['dsn'], $args['username'], $args['password'], $args['options']); try { - $connection->connect(); + $explorer->connect(); } catch (Nette\Database\ConnectionException $e) { Tester\Environment::skip("Connection to '$args[dsn]' failed. Reason: " . $e->getMessage()); } - $driverName = $connection->getConnection()->getNativeConnection()->getAttribute(PDO::ATTR_DRIVER_NAME); - - $cacheMemoryStorage = new Nette\Caching\Cache(new Nette\Caching\Storages\MemoryStorage); - $structure = new Nette\Database\Structure($connection->getDatabaseEngine(), $cacheMemoryStorage); - $conventions = new Nette\Database\Conventions\DiscoveredConventions($structure); - $explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $cacheMemoryStorage); + $driverName = $explorer->getConnection()->getNativeConnection()->getAttribute(PDO::ATTR_DRIVER_NAME); + $explorer->setCache(new Nette\Caching\Cache(new Nette\Caching\Storages\MemoryStorage)); echo "Driver: $driverName\n"; $GLOBALS['driverName'] = $driverName; From 33708a6577ccb713214d2b921a4b2e79c90b31e8 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 2 Sep 2024 23:13:59 +0200 Subject: [PATCH 46/53] removed IStructure (BC break) --- .../Conventions/DiscoveredConventions.php | 4 +- src/Database/Explorer.php | 4 +- src/Database/IStructure.php | 78 ------------------- src/Database/Structure.php | 3 +- src/Database/Table/SqlBuilder.php | 4 +- ...edConventions.getBelongsToReference().phpt | 14 ++-- ...eredConventions.getHasManyReference().phpt | 14 ++-- .../DiscoveredConventions.getPrimary().phpt | 2 +- 8 files changed, 23 insertions(+), 100 deletions(-) delete mode 100644 src/Database/IStructure.php diff --git a/src/Database/Conventions/DiscoveredConventions.php b/src/Database/Conventions/DiscoveredConventions.php index 272b942ac..930a889b4 100644 --- a/src/Database/Conventions/DiscoveredConventions.php +++ b/src/Database/Conventions/DiscoveredConventions.php @@ -10,7 +10,7 @@ namespace Nette\Database\Conventions; use Nette\Database\Conventions; -use Nette\Database\IStructure; +use Nette\Database\Structure; /** @@ -19,7 +19,7 @@ class DiscoveredConventions implements Conventions { public function __construct( - protected readonly IStructure $structure, + protected readonly Structure $structure, ) { } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index bb2c6fb27..4e6df5f07 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -45,7 +45,7 @@ class Explorer private int $transactionDepth = 0; private ?Cache $cache = null; private ?Conventions $conventions = null; - private ?IStructure $structure = null; + private ?Structure $structure = null; public function __construct( @@ -435,7 +435,7 @@ public function getConventions(): Conventions /** @internal */ - public function getStructure(): IStructure + public function getStructure(): Structure { return $this->structure ??= new Structure($this->getDatabaseEngine(), $this->getCache()); } diff --git a/src/Database/IStructure.php b/src/Database/IStructure.php deleted file mode 100644 index 8c09e7497..000000000 --- a/src/Database/IStructure.php +++ /dev/null @@ -1,78 +0,0 @@ -shouldReceive('getBelongsToReference')->with('books')->andReturn([ 'author_id' => 'authors', 'translator_id' => 'authors', @@ -26,7 +26,7 @@ test('basic test', function () { }); test('basic test', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getBelongsToReference')->with('public.books')->andReturn([ 'author_id' => 'public.authors', 'translator_id' => 'public.authors', @@ -39,7 +39,7 @@ test('basic test', function () { }); test('tests order of table columns with foreign keys', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getBelongsToReference')->with('books')->andReturn([ 'translator_id' => 'authors', 'author_id' => 'authors', @@ -52,7 +52,7 @@ test('tests order of table columns with foreign keys', function () { test('tests case insensivity', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getBelongsToReference')->with('books')->andReturn([ 'author_id' => 'authors', 'translator_id' => 'authors', @@ -65,7 +65,7 @@ test('tests case insensivity', function () { test('tests case insensivity and prefixes', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getBelongsToReference')->with('nBooks')->andReturn([ 'authorId' => 'nAuthors', 'translatorId' => 'nAuthors', @@ -79,7 +79,7 @@ test('tests case insensivity and prefixes', function () { test('tests rebuilt', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('isRebuilt')->andReturn(false); $structure->shouldReceive('rebuild'); $structure->shouldReceive('getBelongsToReference')->andReturn([])->once(); @@ -96,7 +96,7 @@ test('tests rebuilt', function () { test('tests already rebuilt structure', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('isRebuilt')->andReturn(true); $structure->shouldReceive('getBelongsToReference')->with('books')->andReturn([])->once(); diff --git a/tests/Database/Conventions/DiscoveredConventions.getHasManyReference().phpt b/tests/Database/Conventions/DiscoveredConventions.getHasManyReference().phpt index a7dc0255d..ed14a18ab 100644 --- a/tests/Database/Conventions/DiscoveredConventions.getHasManyReference().phpt +++ b/tests/Database/Conventions/DiscoveredConventions.getHasManyReference().phpt @@ -13,7 +13,7 @@ require __DIR__ . '/../../bootstrap.php'; test('basic test singular', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getHasManyReference')->with('author')->andReturn([ 'book' => ['author_id', 'translator_id'], 'book_topics' => ['author_id'], @@ -43,7 +43,7 @@ test('basic test singular', function () { test('basic test singular with schema', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getHasManyReference')->with('public.author')->andReturn([ 'public.book' => ['author_id', 'translator_id'], 'public.book_topics' => ['author_id'], @@ -88,7 +88,7 @@ test('basic test singular with schema', function () { test('basic test plural', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getHasManyReference')->with('authors')->andReturn([ 'books' => ['author_id', 'translator_id'], ])->once(); @@ -112,7 +112,7 @@ test('basic test plural', function () { test('tests column match with source table', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getHasManyReference')->with('author')->andReturn([ 'book' => ['author_id', 'tran_id'], ])->once(); @@ -139,7 +139,7 @@ test('tests column match with source table', function () { test('tests case insensivity and prefixes', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getHasManyReference')->with('nAuthors')->andReturn([ 'nBooks' => ['authorId', 'translatorId'], ])->once(); @@ -151,7 +151,7 @@ test('tests case insensivity and prefixes', function () { test('tests rebuilt', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('isRebuilt')->andReturn(false); $structure->shouldReceive('rebuild'); $structure->shouldReceive('getHasManyReference')->with('author')->andReturn([])->once(); @@ -165,7 +165,7 @@ test('tests rebuilt', function () { test('tests already rebuilt structure', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('isRebuilt')->andReturn(true); $structure->shouldReceive('getHasManyReference')->with('author')->andReturn([])->once(); diff --git a/tests/Database/Conventions/DiscoveredConventions.getPrimary().phpt b/tests/Database/Conventions/DiscoveredConventions.getPrimary().phpt index 196c4e0e6..f68b38f5c 100644 --- a/tests/Database/Conventions/DiscoveredConventions.getPrimary().phpt +++ b/tests/Database/Conventions/DiscoveredConventions.getPrimary().phpt @@ -13,7 +13,7 @@ require __DIR__ . '/../../bootstrap.php'; test('', function () { - $structure = Mockery::mock(Nette\Database\IStructure::class); + $structure = Mockery::mock(Nette\Database\Structure::class); $structure->shouldReceive('getPrimaryKey')->with('books_x_tags')->andReturn(['book_id', 'tag_id']); $conventions = new DiscoveredConventions($structure); From 24167bce1c33d75f2d14c904a15fb3b26ca8d3ff Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 23:09:29 +0200 Subject: [PATCH 47/53] Engine::applyLimit() returns string (BC break) --- src/Database/Drivers/Engine.php | 2 +- src/Database/Drivers/Engines/MSSQLEngine.php | 4 +- src/Database/Drivers/Engines/MySQLEngine.php | 4 +- src/Database/Drivers/Engines/ODBCEngine.php | 4 +- src/Database/Drivers/Engines/OracleEngine.php | 4 +- .../Drivers/Engines/PostgreSQLEngine.php | 4 +- .../Drivers/Engines/SQLServerEngine.php | 4 +- src/Database/Drivers/Engines/SQLiteEngine.php | 4 +- src/Database/Table/SqlBuilder.php | 6 +- .../Engines/MSSQLEngine.applyLimit.phpt | 109 ++++++++++-------- .../Engines/MySQLEngine.applyLimit.phpt | 65 ++++++----- .../Engines/ODBCEngine.applyLimit.phpt | 109 ++++++++++-------- .../Engines/OracleEngine.applyLimit.phpt | 65 ++++++----- .../Engines/PostgreSQLEngine.applyLimit.phpt | 65 ++++++----- .../Engines/SQLServerEngine.applyLimit.phpt | 65 ++++++----- .../Engines/SQLiteEngine.applyLimit.phpt | 65 ++++++----- 16 files changed, 325 insertions(+), 254 deletions(-) diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index 320a5872d..13ca8c7f8 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -50,7 +50,7 @@ function formatDateTime(\DateTimeInterface $value): string; function formatDateInterval(\DateInterval $value): string; /** Applies LIMIT and OFFSET clauses to an SQL query. */ - function applyLimit(string &$sql, ?int $limit, ?int $offset): void; + function applyLimit(string $sql, ?int $limit, ?int $offset): string; /********************* reflection ****************d*g**/ diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index 63c6e9f21..05a40d360 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -60,7 +60,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($offset) { throw new Nette\NotSupportedException('Offset is not supported by this database.'); @@ -74,6 +74,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void throw new Nette\InvalidArgumentException('SQL query must begin with SELECT, UPDATE or DELETE command.'); } } + + return $sql; } diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index 1e23a98b4..dba747352 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -71,7 +71,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); @@ -81,6 +81,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void $sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') . ($offset ? ' OFFSET ' . $offset : ''); } + + return $sql; } diff --git a/src/Database/Drivers/Engines/ODBCEngine.php b/src/Database/Drivers/Engines/ODBCEngine.php index 869338ea4..78aa1f612 100644 --- a/src/Database/Drivers/Engines/ODBCEngine.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -52,7 +52,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($offset) { throw new Nette\NotSupportedException('Offset is not supported by this database.'); @@ -66,6 +66,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void throw new Nette\InvalidArgumentException('SQL query must begin with SELECT, UPDATE or DELETE command.'); } } + + return $sql; } diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index c895d5293..73f8cc2ad 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -69,7 +69,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); @@ -83,6 +83,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void } elseif ($limit !== null) { $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit; } + + return $sql; } diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index 8f7869b74..ec78c1f5c 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -67,7 +67,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); @@ -80,6 +80,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void if ($offset) { $sql .= ' OFFSET ' . $offset; } + + return $sql; } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index da04de289..c33f681cc 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -61,7 +61,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); @@ -71,6 +71,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void $sql .= ' OFFSET ' . (int) $offset . ' ROWS ' . 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY'; } + + return $sql; } diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index c9245bab7..77f5f1e6b 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -88,7 +88,7 @@ public function formatDateInterval(\DateInterval $value): string } - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + public function applyLimit(string $sql, ?int $limit, ?int $offset): string { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); @@ -97,6 +97,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void $sql .= ' LIMIT ' . ($limit ?? '-1') . ($offset ? ' OFFSET ' . $offset : ''); } + + return $sql; } diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 6040fa63c..262b0c427 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -85,7 +85,7 @@ public function buildUpdateQuery(): string } if ($this->limit !== null || $this->offset) { - $this->engine->applyLimit($query, $this->limit, $this->offset); + $query = $this->engine->applyLimit($query, $this->limit, $this->offset); } return $query; @@ -96,7 +96,7 @@ public function buildDeleteQuery(): string { $query = "DELETE FROM {$this->delimitedTable}" . $this->tryDelimite($this->buildConditions()); if ($this->limit !== null || $this->offset) { - $this->engine->applyLimit($query, $this->limit, $this->offset); + $query = $this->engine->applyLimit($query, $this->limit, $this->offset); } return $query; @@ -183,7 +183,7 @@ public function buildSelectQuery(?array $columns = null): string $queryJoins = $this->buildQueryJoins($joins, $finalJoinConditions); $query = "{$querySelect} FROM {$this->delimitedTable}{$queryJoins}{$queryCondition}{$queryEnd}"; - $this->engine->applyLimit($query, $this->limit, $this->offset); + $query = $this->engine->applyLimit($query, $this->limit, $this->offset); return $this->tryDelimite($query); } diff --git a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt index 5c1766d31..35cfb9159 100644 --- a/tests/Database/Engines/MSSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MSSQLEngine.applyLimit.phpt @@ -9,52 +9,63 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\MSSQLEngine($connection); -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, 10, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, 0, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT TOP 10 1 FROM t', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT TOP 10 1 FROM t', $query); - -$query = ' select distinct 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same(' select distinct TOP 10 1 FROM t', $query); - -$query = 'UPDATE t SET'; -$engine->applyLimit($query, 10, null); -Assert::same('UPDATE TOP 10 t SET', $query); - -$query = 'DELETE FROM t SET'; -$engine->applyLimit($query, 10, null); -Assert::same('DELETE TOP 10 FROM t SET', $query); - -Assert::exception(function () use ($engine) { - $query = 'SET FROM t'; - $engine->applyLimit($query, 10, null); -}, Nette\InvalidArgumentException::class, 'SQL query must begin with SELECT, UPDATE or DELETE command.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', 10, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', 0, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT TOP 10 1 FROM t', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT TOP 10 1 FROM t', +); + +Assert::same( + $engine->applyLimit(' select distinct 1 FROM t', 10, null), + ' select distinct TOP 10 1 FROM t', +); + +Assert::same( + $engine->applyLimit('UPDATE t SET', 10, null), + 'UPDATE TOP 10 t SET', +); + +Assert::same( + $engine->applyLimit('DELETE FROM t SET', 10, null), + 'DELETE TOP 10 FROM t SET', +); + +Assert::exception( + fn() => $engine->applyLimit('SET FROM t', 10, null), + Nette\InvalidArgumentException::class, + 'SQL query must begin with SELECT, UPDATE or DELETE command.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); diff --git a/tests/Database/Engines/MySQLEngine.applyLimit.phpt b/tests/Database/Engines/MySQLEngine.applyLimit.phpt index d07234d28..59f67b995 100644 --- a/tests/Database/Engines/MySQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/MySQLEngine.applyLimit.phpt @@ -9,32 +9,39 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\MySQLEngine($connection); -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 20); -Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 0, 20); -Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, null, 20); -Assert::same('SELECT 1 FROM t LIMIT 18446744073709551615 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 20), + 'SELECT 1 FROM t LIMIT 10 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 0, 20), + 'SELECT 1 FROM t LIMIT 0 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', null, 20), + 'SELECT 1 FROM t LIMIT 18446744073709551615 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); diff --git a/tests/Database/Engines/ODBCEngine.applyLimit.phpt b/tests/Database/Engines/ODBCEngine.applyLimit.phpt index 2d3c22902..ea38594c2 100644 --- a/tests/Database/Engines/ODBCEngine.applyLimit.phpt +++ b/tests/Database/Engines/ODBCEngine.applyLimit.phpt @@ -9,52 +9,63 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\ODBCEngine($connection); -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, 10, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, 0, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT TOP 10 1 FROM t', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, 20); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT TOP 10 1 FROM t', $query); - -$query = ' select distinct 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same(' select distinct TOP 10 1 FROM t', $query); - -$query = 'UPDATE t SET'; -$engine->applyLimit($query, 10, null); -Assert::same('UPDATE TOP 10 t SET', $query); - -$query = 'DELETE FROM t SET'; -$engine->applyLimit($query, 10, null); -Assert::same('DELETE TOP 10 FROM t SET', $query); - -Assert::exception(function () use ($engine) { - $query = 'SET FROM t'; - $engine->applyLimit($query, 10, null); -}, Nette\InvalidArgumentException::class, 'SQL query must begin with SELECT, UPDATE or DELETE command.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\NotSupportedException::class, 'Offset is not supported by this database.'); +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', 10, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', 0, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT TOP 10 1 FROM t', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, 20), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT TOP 10 1 FROM t', +); + +Assert::same( + $engine->applyLimit(' select distinct 1 FROM t', 10, null), + ' select distinct TOP 10 1 FROM t', +); + +Assert::same( + $engine->applyLimit('UPDATE t SET', 10, null), + 'UPDATE TOP 10 t SET', +); + +Assert::same( + $engine->applyLimit('DELETE FROM t SET', 10, null), + 'DELETE TOP 10 FROM t SET', +); + +Assert::exception( + fn() => $engine->applyLimit('SET FROM t', 10, null), + Nette\InvalidArgumentException::class, + 'SQL query must begin with SELECT, UPDATE or DELETE command.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\NotSupportedException::class, + 'Offset is not supported by this database.', +); diff --git a/tests/Database/Engines/OracleEngine.applyLimit.phpt b/tests/Database/Engines/OracleEngine.applyLimit.phpt index 370f0615f..a2ed1d618 100644 --- a/tests/Database/Engines/OracleEngine.applyLimit.phpt +++ b/tests/Database/Engines/OracleEngine.applyLimit.phpt @@ -9,32 +9,39 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\OracleEngine($connection); -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 20); -Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 30) WHERE "__rnum" > 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 0, 20); -Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 20) WHERE "__rnum" > 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, null, 20); -Assert::same('SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t ) WHERE "__rnum" > 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 20), + 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 30) WHERE "__rnum" > 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 0, 20), + 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t WHERE ROWNUM <= 20) WHERE "__rnum" > 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', null, 20), + 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (SELECT 1 FROM t) t ) WHERE "__rnum" > 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT * FROM (SELECT 1 FROM t) WHERE ROWNUM <= 10', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); diff --git a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt index 59a867319..b77321e76 100644 --- a/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt +++ b/tests/Database/Engines/PostgreSQLEngine.applyLimit.phpt @@ -9,32 +9,39 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\PostgreSQLEngine($connection); -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 20); -Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 0, 20); -Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, null, 20); -Assert::same('SELECT 1 FROM t OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 20), + 'SELECT 1 FROM t LIMIT 10 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 0, 20), + 'SELECT 1 FROM t LIMIT 0 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', null, 20), + 'SELECT 1 FROM t OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); diff --git a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt index 917bbc325..0bdc50957 100644 --- a/tests/Database/Engines/SQLServerEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLServerEngine.applyLimit.phpt @@ -9,32 +9,39 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\SQLServerEngine($connection); -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 20); -Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 0, 20); -Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, null, 20); -Assert::same('SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 20), + 'SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 0, 20), + 'SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', null, 20), + 'SELECT 1 FROM t OFFSET 20 ROWS FETCH NEXT 0 ROWS ONLY', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT 1 FROM t OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); diff --git a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt index 280298ee4..4258393e7 100644 --- a/tests/Database/Engines/SQLiteEngine.applyLimit.phpt +++ b/tests/Database/Engines/SQLiteEngine.applyLimit.phpt @@ -9,32 +9,39 @@ require __DIR__ . '/../../bootstrap.php'; $connection = Mockery::mock(Nette\Database\Drivers\Connection::class); $engine = new Nette\Database\Drivers\Engines\SQLiteEngine($connection); -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 20); -Assert::same('SELECT 1 FROM t LIMIT 10 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 0, 20); -Assert::same('SELECT 1 FROM t LIMIT 0 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, 0); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, null, 20); -Assert::same('SELECT 1 FROM t LIMIT -1 OFFSET 20', $query); - -$query = 'SELECT 1 FROM t'; -$engine->applyLimit($query, 10, null); -Assert::same('SELECT 1 FROM t LIMIT 10', $query); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, -1, null); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); - -Assert::exception(function () use ($engine) { - $query = 'SELECT 1 FROM t'; - $engine->applyLimit($query, null, -1); -}, Nette\InvalidArgumentException::class, 'Negative offset or limit.'); +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 20), + 'SELECT 1 FROM t LIMIT 10 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 0, 20), + 'SELECT 1 FROM t LIMIT 0 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, 0), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', null, 20), + 'SELECT 1 FROM t LIMIT -1 OFFSET 20', +); + +Assert::same( + $engine->applyLimit('SELECT 1 FROM t', 10, null), + 'SELECT 1 FROM t LIMIT 10', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', -1, null), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); + +Assert::exception( + fn() => $engine->applyLimit('SELECT 1 FROM t', null, -1), + Nette\InvalidArgumentException::class, + 'Negative offset or limit.', +); From 1ef0bd4372d5734d0e4f6cf6c1f008a27246a570 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 12:01:43 +0200 Subject: [PATCH 48/53] Engine::delimite() -> delimit() (BC break) --- src/Database/Drivers/Engine.php | 2 +- src/Database/Drivers/Engines/MSSQLEngine.php | 2 +- src/Database/Drivers/Engines/MySQLEngine.php | 6 ++-- src/Database/Drivers/Engines/ODBCEngine.php | 2 +- src/Database/Drivers/Engines/OracleEngine.php | 2 +- .../Drivers/Engines/PostgreSQLEngine.php | 12 ++++---- .../Drivers/Engines/SQLServerEngine.php | 2 +- src/Database/Drivers/Engines/SQLiteEngine.php | 10 +++---- src/Database/SqlPreprocessor.php | 2 +- src/Database/Table/Selection.php | 2 +- src/Database/Table/SqlBuilder.php | 12 ++++---- .../Explorer/SqlBuilder.tryDelimit().phpt | 29 +++++++++++++++++++ .../Explorer/SqlBuilder.tryDelimite().phpt | 29 ------------------- tests/Database/Row.phpt | 2 +- 14 files changed, 57 insertions(+), 57 deletions(-) create mode 100644 tests/Database/Explorer/SqlBuilder.tryDelimit().phpt delete mode 100644 tests/Database/Explorer/SqlBuilder.tryDelimite().phpt diff --git a/src/Database/Drivers/Engine.php b/src/Database/Drivers/Engine.php index 13ca8c7f8..fbe8aa6e1 100644 --- a/src/Database/Drivers/Engine.php +++ b/src/Database/Drivers/Engine.php @@ -41,7 +41,7 @@ function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixe /********************* SQL utilities ****************d*g**/ /** Escapes an identifier for use in an SQL statement. */ - function delimite(string $name): string; + function delimit(string $name): string; /** Formats a date-time value for use in an SQL statement. */ function formatDateTime(\DateTimeInterface $value): string; diff --git a/src/Database/Drivers/Engines/MSSQLEngine.php b/src/Database/Drivers/Engines/MSSQLEngine.php index 05a40d360..afbdca0c6 100644 --- a/src/Database/Drivers/Engines/MSSQLEngine.php +++ b/src/Database/Drivers/Engines/MSSQLEngine.php @@ -41,7 +41,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx return '[' . str_replace(['[', ']'], ['[[', ']]'], $name) . ']'; diff --git a/src/Database/Drivers/Engines/MySQLEngine.php b/src/Database/Drivers/Engines/MySQLEngine.php index dba747352..40c0f0dc4 100644 --- a/src/Database/Drivers/Engines/MySQLEngine.php +++ b/src/Database/Drivers/Engines/MySQLEngine.php @@ -52,7 +52,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html return '`' . str_replace('`', '``', $name) . '`'; @@ -108,7 +108,7 @@ public function getTables(): array public function getColumns(string $table): array { $columns = []; - $rows = $this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table)); + $rows = $this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimit($table)); while ($row = $rows->fetch()) { $row = array_change_key_case($row); $typeInfo = Nette\Database\Helpers::parseColumnType($row['type']); @@ -133,7 +133,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - $rows = $this->connection->query('SHOW INDEX FROM ' . $this->delimite($table)); + $rows = $this->connection->query('SHOW INDEX FROM ' . $this->delimit($table)); while ($row = $rows->fetch()) { $id = $row['Key_name']; $indexes[$id]['name'] = $id; diff --git a/src/Database/Drivers/Engines/ODBCEngine.php b/src/Database/Drivers/Engines/ODBCEngine.php index 78aa1f612..f6842367a 100644 --- a/src/Database/Drivers/Engines/ODBCEngine.php +++ b/src/Database/Drivers/Engines/ODBCEngine.php @@ -34,7 +34,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { return '[' . str_replace(['[', ']'], ['[[', ']]'], $name) . ']'; } diff --git a/src/Database/Drivers/Engines/OracleEngine.php b/src/Database/Drivers/Engines/OracleEngine.php index 73f8cc2ad..ead792035 100644 --- a/src/Database/Drivers/Engines/OracleEngine.php +++ b/src/Database/Drivers/Engines/OracleEngine.php @@ -50,7 +50,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm return '"' . str_replace('"', '""', $name) . '"'; diff --git a/src/Database/Drivers/Engines/PostgreSQLEngine.php b/src/Database/Drivers/Engines/PostgreSQLEngine.php index ec78c1f5c..b297025ec 100644 --- a/src/Database/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Database/Drivers/Engines/PostgreSQLEngine.php @@ -48,7 +48,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS return '"' . str_replace('"', '""', $name) . '"'; @@ -152,7 +152,7 @@ public function getColumns(string $table): array AND NOT a.attisdropped ORDER BY a.attnum - X, [$this->delimiteFQN($table)]); + X, [$this->delimitFQN($table)]); while ($row = $rows->fetch()) { $column = $row; @@ -183,7 +183,7 @@ public function getIndexes(string $table): array WHERE c1.relkind IN ('r', 'p') AND c1.oid = ?::regclass - X, [$this->delimiteFQN($table)]); + X, [$this->delimitFQN($table)]); while ($row = $rows->fetch()) { $id = $row['name']; @@ -218,7 +218,7 @@ public function getForeignKeys(string $table): array co.contype = 'f' AND cl.oid = ?::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) - X, [$this->delimiteFQN($table)]); + X, [$this->delimitFQN($table)]); while ($row = $rows->fetch()) { $id = $row['name']; @@ -243,8 +243,8 @@ public function convertToPhp(mixed $value, array $meta, TypeConverter $converter /** * Converts: schema.name => "schema"."name" */ - private function delimiteFQN(string $name): string + private function delimitFQN(string $name): string { - return implode('.', array_map([$this, 'delimite'], explode('.', $name))); + return implode('.', array_map([$this, 'delimit'], explode('.', $name))); } } diff --git a/src/Database/Drivers/Engines/SQLServerEngine.php b/src/Database/Drivers/Engines/SQLServerEngine.php index c33f681cc..89f3489d2 100644 --- a/src/Database/Drivers/Engines/SQLServerEngine.php +++ b/src/Database/Drivers/Engines/SQLServerEngine.php @@ -41,7 +41,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { /** @see https://msdn.microsoft.com/en-us/library/ms176027.aspx */ return '[' . str_replace(']', ']]', $name) . ']'; diff --git a/src/Database/Drivers/Engines/SQLiteEngine.php b/src/Database/Drivers/Engines/SQLiteEngine.php index 77f5f1e6b..52e582a46 100644 --- a/src/Database/Drivers/Engines/SQLiteEngine.php +++ b/src/Database/Drivers/Engines/SQLiteEngine.php @@ -70,7 +70,7 @@ public function classifyException(Nette\Database\DriverException $e): ?string /********************* SQL ****************d*g**/ - public function delimite(string $name): string + public function delimit(string $name): string { return '[' . strtr($name, '[]', ' ') . ']'; } @@ -143,7 +143,7 @@ public function getColumns(string $table): array X, [$table, $table])->fetch(); $columns = []; - $rows = $this->connection->query("PRAGMA table_info({$this->delimite($table)})"); + $rows = $this->connection->query("PRAGMA table_info({$this->delimit($table)})"); while ($row = $rows->fetch()) { $column = $row['name']; $pattern = "/(\"$column\"|`$column`|\\[$column\\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui"; @@ -169,7 +169,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - $rows = $this->connection->query("PRAGMA index_list({$this->delimite($table)})"); + $rows = $this->connection->query("PRAGMA index_list({$this->delimit($table)})"); while ($row = $rows->fetch()) { $id = $row['name']; $indexes[$id]['name'] = $id; @@ -178,7 +178,7 @@ public function getIndexes(string $table): array } foreach ($indexes as $index => $values) { - $res = $this->connection->query("PRAGMA index_info({$this->delimite($index)})"); + $res = $this->connection->query("PRAGMA index_info({$this->delimit($index)})"); while ($row = $res->fetch()) { $indexes[$index]['columns'][] = $row['name']; } @@ -216,7 +216,7 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { $keys = []; - $rows = $this->connection->query("PRAGMA foreign_key_list({$this->delimite($table)})"); + $rows = $this->connection->query("PRAGMA foreign_key_list({$this->delimit($table)})"); while ($row = $rows->fetch()) { $id = $row['id']; $keys[$id]['name'] = $id; diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 3bfa4cd34..f9fb1d0ac 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -320,6 +320,6 @@ private function formatValue(mixed $value, ?string $mode = null): string private function delimite(string $name): string { - return implode('.', array_map($this->engine->delimite(...), explode('.', $name))); + return implode('.', array_map($this->engine->delimit(...), explode('.', $name))); } } diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index bf217e887..c8b00abb6 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -790,7 +790,7 @@ public function insert(iterable $data): ActiveRow|array|int|bool // First check sequence if (!empty($primarySequenceName) && $primaryAutoincrementKey) { - $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getDatabaseEngine()->delimite($primarySequenceName)); + $primaryKey[$primaryAutoincrementKey] = $this->explorer->getInsertId($this->explorer->getDatabaseEngine()->delimit($primarySequenceName)); // Autoincrement primary without sequence } elseif ($primaryAutoincrementKey) { diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 262b0c427..29c0182b6 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -59,7 +59,7 @@ public function __construct(string $tableName, Explorer $explorer) $this->conventions = $explorer->getConventions(); $this->structure = $explorer->getStructure(); $tableNameParts = explode('.', $tableName); - $this->delimitedTable = implode('.', array_map($this->engine->delimite(...), $tableNameParts)); + $this->delimitedTable = implode('.', array_map($this->engine->delimit(...), $tableNameParts)); $this->checkUniqueTableName(end($tableNameParts), $tableName); } @@ -78,7 +78,7 @@ public function buildInsertQuery(): string public function buildUpdateQuery(): string { - $query = "UPDATE {$this->delimitedTable} SET ?set" . $this->tryDelimite($this->buildConditions()); + $query = "UPDATE {$this->delimitedTable} SET ?set" . $this->tryDelimit($this->buildConditions()); if ($this->order !== []) { $query .= ' ORDER BY ' . implode(', ', $this->order); @@ -94,7 +94,7 @@ public function buildUpdateQuery(): string public function buildDeleteQuery(): string { - $query = "DELETE FROM {$this->delimitedTable}" . $this->tryDelimite($this->buildConditions()); + $query = "DELETE FROM {$this->delimitedTable}" . $this->tryDelimit($this->buildConditions()); if ($this->limit !== null || $this->offset) { $query = $this->engine->applyLimit($query, $this->limit, $this->offset); } @@ -185,7 +185,7 @@ public function buildSelectQuery(?array $columns = null): string $query = $this->engine->applyLimit($query, $this->limit, $this->offset); - return $this->tryDelimite($query); + return $this->tryDelimit($query); } @@ -789,13 +789,13 @@ protected function buildQueryEnd(): string } - protected function tryDelimite(string $s): string + protected function tryDelimit(string $s): string { return preg_replace_callback( '#(?<=[^\w`"\[?:]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#Di', fn(array $m): string => strtoupper($m[0]) === $m[0] ? $m[0] - : $this->engine->delimite($m[0]), + : $this->engine->delimit($m[0]), $s, ); } diff --git a/tests/Database/Explorer/SqlBuilder.tryDelimit().phpt b/tests/Database/Explorer/SqlBuilder.tryDelimit().phpt new file mode 100644 index 000000000..a2930edf2 --- /dev/null +++ b/tests/Database/Explorer/SqlBuilder.tryDelimit().phpt @@ -0,0 +1,29 @@ +getMethod('tryDelimit'); +$tryDelimit->setAccessible(true); + +Assert::same(reformat('[hello]'), $tryDelimit->invoke($sqlBuilder, 'hello')); +Assert::same(reformat(' [hello] '), $tryDelimit->invoke($sqlBuilder, ' hello ')); +Assert::same(reformat('HELLO'), $tryDelimit->invoke($sqlBuilder, 'HELLO')); +Assert::same(reformat('[HellO]'), $tryDelimit->invoke($sqlBuilder, 'HellO')); +Assert::same(reformat('[hello].[world]'), $tryDelimit->invoke($sqlBuilder, 'hello.world')); +Assert::same(reformat('[hello] [world]'), $tryDelimit->invoke($sqlBuilder, 'hello world')); +Assert::same(reformat('HELLO([world])'), $tryDelimit->invoke($sqlBuilder, 'HELLO(world)')); +Assert::same(reformat('hello([world])'), $tryDelimit->invoke($sqlBuilder, 'hello(world)')); +Assert::same('[hello]', $tryDelimit->invoke($sqlBuilder, '[hello]')); +Assert::same(reformat('::int'), $tryDelimit->invoke($sqlBuilder, '::int')); diff --git a/tests/Database/Explorer/SqlBuilder.tryDelimite().phpt b/tests/Database/Explorer/SqlBuilder.tryDelimite().phpt deleted file mode 100644 index 5aa8aaf31..000000000 --- a/tests/Database/Explorer/SqlBuilder.tryDelimite().phpt +++ /dev/null @@ -1,29 +0,0 @@ -getMethod('tryDelimite'); -$tryDelimite->setAccessible(true); - -Assert::same(reformat('[hello]'), $tryDelimite->invoke($sqlBuilder, 'hello')); -Assert::same(reformat(' [hello] '), $tryDelimite->invoke($sqlBuilder, ' hello ')); -Assert::same(reformat('HELLO'), $tryDelimite->invoke($sqlBuilder, 'HELLO')); -Assert::same(reformat('[HellO]'), $tryDelimite->invoke($sqlBuilder, 'HellO')); -Assert::same(reformat('[hello].[world]'), $tryDelimite->invoke($sqlBuilder, 'hello.world')); -Assert::same(reformat('[hello] [world]'), $tryDelimite->invoke($sqlBuilder, 'hello world')); -Assert::same(reformat('HELLO([world])'), $tryDelimite->invoke($sqlBuilder, 'HELLO(world)')); -Assert::same(reformat('hello([world])'), $tryDelimite->invoke($sqlBuilder, 'hello(world)')); -Assert::same('[hello]', $tryDelimite->invoke($sqlBuilder, '[hello]')); -Assert::same(reformat('::int'), $tryDelimite->invoke($sqlBuilder, '::int')); diff --git a/tests/Database/Row.phpt b/tests/Database/Row.phpt index c254282e8..04631da86 100644 --- a/tests/Database/Row.phpt +++ b/tests/Database/Row.phpt @@ -14,7 +14,7 @@ require __DIR__ . '/../bootstrap.php'; $connection = connectToDB(); test('numeric field', function () use ($connection) { - $row = $connection->fetch("SELECT 123 AS {$connection->getDatabaseEngine()->delimite('123')}, NULL as nullcol"); + $row = $connection->fetch("SELECT 123 AS {$connection->getDatabaseEngine()->delimit('123')}, NULL as nullcol"); Assert::same(123, $row->{123}); Assert::same(123, $row->{'123'}); Assert::true(isset($row->{123})); From b7aeb45946cdfbb404c527987dc6a5e90744ce32 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 5 Sep 2024 04:18:54 +0200 Subject: [PATCH 49/53] Selection: uses yield for iteration --- src/Database/Table/Selection.php | 48 +++++++------------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index c8b00abb6..ba6479e3e 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -17,10 +17,10 @@ * Filtered table representation. * Selection is based on the great library NotORM http://www.notorm.com written by Jakub Vrana. * @template T of ActiveRow - * @implements \Iterator + * @implements \IteratorAggregate * @implements \ArrayAccess */ -class Selection implements \Iterator, \ArrayAccess, \Countable +class Selection implements \IteratorAggregate, \ArrayAccess, \Countable { protected readonly Explorer $explorer; protected readonly ?Nette\Caching\Cache $cache; @@ -58,9 +58,6 @@ class Selection implements \Iterator, \ArrayAccess, \Countable /** should instance observe accessed columns caching */ protected ?self $observeCache = null; - /** of primary key values */ - protected array $keys = []; - /** * Creates filtered table representation. @@ -948,43 +945,18 @@ public function getReferencingTable( } - /********************* interface Iterator ****************d*g**/ + /********************* interface IteratorAggregate ****************d*g**/ - public function rewind(): void + /** @return \Generator */ + public function getIterator(): \Generator { $this->execute(); - $this->keys = array_keys($this->data); - reset($this->keys); - } - - - /** @return T|false */ - public function current(): ActiveRow|false - { - return ($key = current($this->keys)) !== false - ? $this->data[$key] - : false; - } - - - public function key(): string|int - { - return current($this->keys); - } - - - public function next(): void - { - do { - next($this->keys); - } while (($key = current($this->keys)) !== false && !isset($this->data[$key])); - } - - - public function valid(): bool - { - return current($this->keys) !== false; + foreach ($this->data as $key => $value) { + if (isset($this->data[$key])) { // may be unset by offsetUnset + yield $key => $value; + } + } } From 2e2aa3bccb3253a7e581a808af03cd2ab2989ba9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 5 Sep 2024 04:30:56 +0200 Subject: [PATCH 50/53] Result: uses yield for iteration --- src/Database/Result.php | 55 +++++++++++------------------------------ 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/src/Database/Result.php b/src/Database/Result.php index 821116ef0..3de8d3ff2 100644 --- a/src/Database/Result.php +++ b/src/Database/Result.php @@ -16,10 +16,9 @@ /** * Represents a result set. */ -class Result implements \Iterator +class Result implements \IteratorAggregate { - private Row|false|null $lastRow = null; - private int $lastRowKey = -1; + private bool $fetched = false; /** @var Row[] */ private array $rows; @@ -93,42 +92,20 @@ public function dump(): void } - /********************* interface Iterator ****************d*g**/ + /********************* interface IteratorAggregate ****************d*g**/ - public function rewind(): void + /** @return \Generator */ + public function getIterator(): \Generator { - if ($this->lastRow === false) { + if ($this->fetched) { throw new Nette\InvalidStateException(self::class . ' implements only one way iterator.'); } - } - - - public function current(): Row|false|null - { - return $this->lastRow; - } - - - public function key(): int - { - return $this->lastRowKey; - } - - public function next(): void - { - $this->lastRow = false; - } - - - public function valid(): bool - { - if ($this->lastRow) { - return true; + $counter = 0; + while (($row = $this->fetch()) !== null) { + yield $counter++ => $row; } - - return $this->fetch() !== null; } @@ -146,13 +123,15 @@ public function fetchAssoc(?string $path = null): ?array $data = $this->result?->fetch(); if ($data === null) { + $this->fetched = true; return null; - } elseif ($this->lastRow === null && count($data) !== $this->result->getColumnCount()) { + } elseif (!$this->fetched && count($data) !== $this->result->getColumnCount()) { $duplicates = array_filter(array_count_values(array_column($this->result->getColumnsInfo(), 'name')), fn($val) => $val > 1); trigger_error("Found duplicate columns in database result set: '" . implode("', '", array_keys($duplicates)) . "'."); } + $this->fetched = true; return $this->normalizeRow($data); } @@ -163,12 +142,7 @@ public function fetchAssoc(?string $path = null): ?array public function fetch(): ?Row { $data = $this->fetchAssoc(); - if ($data === null) { - return null; - } - - $this->lastRowKey++; - return $this->lastRow = Arrays::toObject($data, new Row); + return $data === null ? null : Arrays::toObject($data, new Row); } @@ -216,8 +190,7 @@ public function fetchPairs(string|int|\Closure|null $keyOrCallback = null, strin */ public function fetchAll(): array { - $this->rows ??= iterator_to_array($this); - return $this->rows; + return $this->rows ??= iterator_to_array($this); } From 4b581253f26a8b381aa1ba7aab83d840c09192bb Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 Sep 2024 01:30:26 +0200 Subject: [PATCH 51/53] added Explorer::createFromDsn() & createFromParameters() --- src/Database/Explorer.php | 67 +++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 4e6df5f07..e500345ea 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -48,29 +48,68 @@ class Explorer private ?Structure $structure = null; - public function __construct( + public static function createFromParameters( + #[\SensitiveParameter] + ...$params, + ): self + { + $params = count($params) === 1 && is_array($params[0] ?? null) ? $params[0] : $params; + + if ($class = $params['driverClass'] ?? null) { + if (!is_subclass_of($class, Drivers\Driver::class)) { + throw new \LogicException("Driver class '$class' is not subclass of " . Drivers\Driver::class); + } + unset($params['driverClass']); + + } elseif ($driver = $params['driver'] ?? null) { + $class = self::Drivers[$driver] ?? throw new \LogicException("Unknown driver '$driver'."); + unset($params['driver']); + + } elseif ($dsn = $params['dsn'] ?? null) { + $driver = explode(':', $dsn)[0]; + $class = self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'."); + + } else { + throw new \LogicException("Missing options 'driver', 'driverClass' or 'dsn'."); + } + + $args = array_diff_key($params, array_flip(self::TypeConverterOptions)); + $explorer = new self(new $class(...$args)); + array_map(fn($opt) => isset($params[$opt]) && ($explorer->typeConverter->$opt = (bool) $params[$opt]), self::TypeConverterOptions); + return $explorer; + } + + + public static function createFromDsn( string $dsn, ?string $username = null, #[\SensitiveParameter] ?string $password = null, array $options = [], - ) { - $driver = explode(':', $dsn)[0]; - $class = empty($options['driverClass']) - ? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'.")) - : $options['driverClass']; - $args = compact('dsn', 'username', 'password', 'options'); - unset($options['lazy'], $options['driverClass']); + ): self + { + $params = compact('dsn', 'username', 'password', 'options'); foreach ($options as $key => $value) { if (!is_int($key) && $value !== null) { - $args[$key] = $value; - unset($args['options'][$key]); + $params[$key] = $value; + unset($params['options'][$key]); } } - $args = array_diff_key($args, array_flip(self::TypeConverterOptions)); - $this->driver = new $class(...$args); - $this->typeConverter = new TypeConverter; - array_map(fn($opt) => isset($options[$opt]) && ($this->typeConverter->$opt = (bool) $options[$opt]), self::TypeConverterOptions); + unset($params['lazy']); + return self::createFromParameters($params); + } + + + public function __construct( + Drivers\Driver|string $driver, + ) { + if (is_string($driver)) { // back compatibility with version 3.x + $explorer = self::createFromDsn(...func_get_args()); + [$this->driver, $this->typeConverter] = [$explorer->driver, $explorer->typeConverter]; + } else { + $this->driver = $driver; + $this->typeConverter = new TypeConverter; + } } From a1798c0b572c0e03ac41603bb68ea3fafafc0a85 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 14 Aug 2024 17:16:15 +0200 Subject: [PATCH 52/53] wip php 8.4 test --- tests/bootstrap.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 81f9667d9..73bb24e86 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,6 +16,10 @@ Tester\Environment::setupFunctions(); date_default_timezone_set('Europe/Prague'); +if (PHP_VERSION_ID >= 80400) { + error_reporting(E_ALL & ~E_DEPRECATED); +} + function getTempDir(): string { From 1769e30b8833584f818739a033386aa91acc70b8 Mon Sep 17 00:00:00 2001 From: website21cz <62235288+website21cz@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:30:06 +0200 Subject: [PATCH 53/53] Remove importants from database panel css to allow overwrite it in custom css --- .../templates/ConnectionPanel.panel.phtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml index a7f035eb0..b2d015084 100644 --- a/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml +++ b/src/Bridges/DatabaseTracy/templates/ConnectionPanel.panel.phtml @@ -8,12 +8,12 @@ use Tracy\Helpers; ?>

Queries: