From 18626f89cd2c393d849660a810d7316f68a5b3ce Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 15:28:10 +0100 Subject: [PATCH 01/35] opened 4.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6c2a56fd7..acf9e944d 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "4.0-dev" } } } From 751e9d1dc96000359258b4e6057606869699ea81 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 15:49:32 +0100 Subject: [PATCH 02/35] requires PHP 8.0 --- .github/workflows/coding-style.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- .github/workflows/tests.yml | 2 +- composer.json | 2 +- readme.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index 79c9babf1..875287f8f 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: - php-version: 7.2 + php-version: 8.0 coverage: none - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 18e8ae5be..ba9869082 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 coverage: none - run: composer install --no-progress --prefer-dist diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a2d9b0e86..888bec203 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['8.0', '8.1', '8.2'] fail-fast: false diff --git a/composer.json b/composer.json index acf9e944d..0aeffcbbf 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": ">=7.2 <8.3", + "php": ">=8.0 <8.3", "ext-pdo": "*", "nette/caching": "^3.0", "nette/utils": "^3.2.1 || ~4.0.0" diff --git a/readme.md b/readme.md index 011991390..3e8e36168 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ The recommended way to install is via Composer: composer require nette/database ``` -It requires PHP version 7.2 and supports PHP up to 8.2. +It requires PHP version 8.0 and supports PHP up to 8.2. Usage From decde4f3ea1eec6f90a03f28e2510bf6014828fe Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 23 Apr 2021 17:15:38 +0200 Subject: [PATCH 03/35] composer: updated dependencies --- composer.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 0aeffcbbf..37797628d 100644 --- a/composer.json +++ b/composer.json @@ -17,18 +17,15 @@ "require": { "php": ">=8.0 <8.3", "ext-pdo": "*", - "nette/caching": "^3.0", - "nette/utils": "^3.2.1 || ~4.0.0" + "nette/caching": "^3.1 || ^4.0", + "nette/utils": "^4.0" }, "require-dev": { "nette/tester": "^2.4", - "nette/di": "^v3.0", - "mockery/mockery": "^1.3.4", - "tracy/tracy": "^2.4", - "phpstan/phpstan-nette": "^0.12" - }, - "conflict": { - "nette/di": "<3.0-stable" + "nette/di": "^3.1 || ^4.0", + "mockery/mockery": "^1.4.3", + "tracy/tracy": "^2.8", + "phpstan/phpstan-nette": "^1.0" }, "autoload": { "classmap": ["src/"] From a11b550e48dc02f7fdd87faf47770923b4686d0e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 14:05:00 +0100 Subject: [PATCH 04/35] coding style --- src/Bridges/DatabaseDI/DatabaseExtension.php | 12 +++++------ src/Bridges/DatabaseTracy/ConnectionPanel.php | 6 +++--- src/Database/Connection.php | 2 +- src/Database/Explorer.php | 6 ++---- src/Database/Helpers.php | 4 ++-- src/Database/ResultSet.php | 2 +- src/Database/SqlPreprocessor.php | 6 +++--- src/Database/Structure.php | 12 +++-------- src/Database/Table/GroupedSelection.php | 2 +- src/Database/Table/Selection.php | 4 ++-- src/Database/Table/SqlBuilder.php | 20 ++++++++----------- .../Database/Explorer/Explorer.backjoin.phpt | 4 ++-- tests/Database/Explorer/Explorer.basic.phpt | 2 +- .../Explorer/Explorer.join-condition.phpt | 10 +++++----- tests/Database/Explorer/Explorer.join.phpt | 10 +++++----- tests/Database/Explorer/Explorer.limit.phpt | 14 ++++++------- .../Explorer/Explorer.limit.sqlsrv.phpt | 12 +++++------ .../Database/Explorer/Selection.insert().phpt | 2 +- .../Explorer/SqlBuilder.addAlias().phpt | 6 +++--- .../Explorer/SqlBuilder.addWhere().phpt | 6 +++--- .../SqlBuilder.parseJoinConditions().phpt | 4 ++-- .../Explorer/SqlBuilder.parseJoins().phpt | 12 +++++------ tests/Database/Explorer/bugs/bug49.phpt | 2 +- tests/Database/Explorer/bugs/view.bug.phpt | 4 +--- tests/Database/Helpers.dumpSql.phpt | 12 +++++------ tests/Database/Reflection.phpt | 6 +++--- tests/Database/Reflection.postgre.10.phpt | 14 ++++++------- .../Database/ResultSet.customNormalizer.phpt | 2 +- .../Database/ResultSet.parameters.mysql.phpt | 2 +- .../ResultSet.parameters.postgre.phpt | 2 +- tests/Database/Structure.phpt | 4 ++-- 31 files changed, 94 insertions(+), 112 deletions(-) diff --git a/src/Bridges/DatabaseDI/DatabaseExtension.php b/src/Bridges/DatabaseDI/DatabaseExtension.php index 6815b4b67..16179c9da 100644 --- a/src/Bridges/DatabaseDI/DatabaseExtension.php +++ b/src/Bridges/DatabaseDI/DatabaseExtension.php @@ -42,12 +42,10 @@ public function getConfigSchema(): Nette\Schema\Schema 'reflection' => Expect::string(), // BC 'conventions' => Expect::string('discovered'), // Nette\Database\Conventions\DiscoveredConventions 'autowired' => Expect::bool(), - ]) - )->before(function ($val) { - return is_array(reset($val)) || reset($val) === null + ]), + )->before(fn($val) => is_array(reset($val)) || reset($val) === null ? $val - : ['default' => $val]; - }); + : ['default' => $val]); } @@ -55,7 +53,7 @@ public function loadConfiguration() { $autowired = true; foreach ($this->config as $name => $config) { - $config->autowired = $config->autowired ?? $autowired; + $config->autowired ??= $autowired; $autowired = false; $this->setupDatabase($config, $name); } @@ -71,7 +69,7 @@ public function beforeCompile() $connection = $builder->getDefinition($this->prefix("$name.connection")); $connection->addSetup( [Nette\Bridges\DatabaseTracy\ConnectionPanel::class, 'initialize'], - [$connection, $this->debugMode, $name, !empty($config->explain)] + [$connection, $this->debugMode, $name, !empty($config->explain)], ); } } diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index 152fb2f95..ccc7300d2 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -56,17 +56,17 @@ public static function initialize( string $name = '', bool $explain = true, ?Tracy\Bar $bar = null, - ?Tracy\BlueScreen $blueScreen = null + ?Tracy\BlueScreen $blueScreen = null, ): ?self { - $blueScreen = $blueScreen ?? Tracy\Debugger::getBlueScreen(); + $blueScreen ??= Tracy\Debugger::getBlueScreen(); $blueScreen->addPanel([self::class, 'renderException']); if ($addBarPanel) { $panel = new self($connection, $blueScreen); $panel->explain = $explain; $panel->name = $name; - $bar = $bar ?? Tracy\Debugger::getBar(); + $bar ??= Tracy\Debugger::getBar(); $bar->addPanel($panel); } diff --git a/src/Database/Connection.php b/src/Database/Connection.php index aec7d909f..225c7b491 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -59,7 +59,7 @@ public function __construct( ?string $user = null, #[\SensitiveParameter] ?string $password = null, - ?array $options = null + ?array $options = null, ) { $this->params = [$dsn, $user, $password]; $this->options = (array) $options; diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 0f8bdb530..dd5dba0e6 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -37,7 +37,7 @@ public function __construct( Connection $connection, Structure $structure, ?Conventions $conventions = null, - ?Nette\Caching\IStorage $cacheStorage = null + ?Nette\Caching\IStorage $cacheStorage = null, ) { $this->connection = $connection; $this->structure = $structure; @@ -69,9 +69,7 @@ public function rollBack(): void */ public function transaction(callable $callback) { - return $this->connection->transaction(function () use ($callback) { - return $callback($this); - }); + return $this->connection->transaction(fn() => $callback($this)); } diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 510280cd1..aa7d0cbfd 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -299,7 +299,7 @@ public static function createDebugPanel( bool $explain, string $name, Tracy\Bar $bar, - Tracy\BlueScreen $blueScreen + Tracy\BlueScreen $blueScreen, ): ?ConnectionPanel { return ConnectionPanel::initialize($connection, true, $name, $explain, $bar, $blueScreen); @@ -313,7 +313,7 @@ public static function initializeTracy( string $name = '', bool $explain = true, ?Tracy\Bar $bar = null, - ?Tracy\BlueScreen $blueScreen = null + ?Tracy\BlueScreen $blueScreen = null, ): ?ConnectionPanel { return ConnectionPanel::initialize($connection, $addBarPanel, $name, $explain, $bar, $blueScreen); diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 55b9845b8..8cae996ca 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -55,7 +55,7 @@ public function __construct( Connection $connection, string $queryString, array $params, - ?callable $normalizer = null + ?callable $normalizer = null, ) { $time = microtime(true); $this->connection = $connection; diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index f5cb36227..efbba9e89 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -113,7 +113,7 @@ public function process(array $params, bool $useParams = false): array |/\*.*?\*/ |--[^\n]* ~Dsix', - \Closure::fromCallable([$this, 'callback']) + \Closure::fromCallable([$this, 'callback']), ); } else { throw new Nette\InvalidArgumentException('There are more parameters than placeholders.'); @@ -225,7 +225,7 @@ private function formatValue($value, ?string $mode = null): string if (!is_array($value[0]) && !$value[0] instanceof Row) { throw new Nette\InvalidArgumentException( 'Automaticaly detected multi-insert, but values aren\'t array. If you need try to change mode like "?[' - . implode('|', self::Modes) . ']". Mode "' . $mode . '" was used.' + . implode('|', self::Modes) . ']". Mode "' . $mode . '" was used.', ); } @@ -324,7 +324,7 @@ private function formatValue($value, ?string $mode = null): string throw new Nette\InvalidArgumentException("Unknown placeholder ?$mode."); } else { - throw new Nette\InvalidArgumentException('Unexpected type of parameter: ' . (is_object($value) ? get_class($value) : gettype($value))); + throw new Nette\InvalidArgumentException('Unexpected type of parameter: ' . (is_object($value) ? $value::class : gettype($value))); } } diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 395c00f53..ec9bacacb 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -207,9 +207,7 @@ protected function loadStructure(): array if (isset($structure['hasMany'])) { foreach ($structure['hasMany'] as &$table) { - uksort($table, function ($a, $b): int { - return strlen($a) <=> strlen($b); - }); + uksort($table, fn($a, $b): int => strlen($a) <=> strlen($b)); } } @@ -250,9 +248,7 @@ protected function analyzeForeignKeys(array &$structure, string $table): void $tmp++; } - usort($foreignKeys, function ($a, $b) use ($fksColumnsCounts): int { - return $fksColumnsCounts[$b['name']] <=> $fksColumnsCounts[$a['name']]; - }); + usort($foreignKeys, fn($a, $b): int => $fksColumnsCounts[$b['name']] <=> $fksColumnsCounts[$a['name']]); foreach ($foreignKeys as $row) { $structure['belongsTo'][$lowerTable][$row['local']] = $row['table']; @@ -260,9 +256,7 @@ protected function analyzeForeignKeys(array &$structure, string $table): void } if (isset($structure['belongsTo'][$lowerTable])) { - uksort($structure['belongsTo'][$lowerTable], function ($a, $b): int { - return strlen($a) <=> strlen($b); - }); + uksort($structure['belongsTo'][$lowerTable], fn($a, $b): int => strlen($a) <=> strlen($b)); } } diff --git a/src/Database/Table/GroupedSelection.php b/src/Database/Table/GroupedSelection.php index 85b41b89a..94a394c0f 100644 --- a/src/Database/Table/GroupedSelection.php +++ b/src/Database/Table/GroupedSelection.php @@ -42,7 +42,7 @@ public function __construct( string $tableName, string $column, Selection $refTable, - ?Nette\Caching\IStorage $cacheStorage = null + ?Nette\Caching\IStorage $cacheStorage = null, ) { $this->refTable = $refTable; $this->column = $column; diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 9ecacb098..01ca307ce 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -90,7 +90,7 @@ public function __construct( Explorer $explorer, Conventions $conventions, string $tableName, - ?Nette\Caching\IStorage $cacheStorage = null + ?Nette\Caching\IStorage $cacheStorage = null, ) { $this->explorer = $this->context = $explorer; $this->conventions = $conventions; @@ -906,7 +906,7 @@ public function update(iterable $data): int return $this->explorer->query( $this->sqlBuilder->buildUpdateQuery(), - ...array_merge([$data], $this->sqlBuilder->getParameters()) + ...array_merge([$data], $this->sqlBuilder->getParameters()), )->getRowCount(); } diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 06155aaa6..4c90f8093 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -185,8 +185,8 @@ public function buildSelectQuery(?array $columns = null): string { if (!$this->order && ($this->limit !== null || $this->offset)) { $this->order = array_map( - function ($col) { return "$this->tableName.$col"; }, - (array) $this->conventions->getPrimary($this->tableName) + fn($col) => "$this->tableName.$col", + (array) $this->conventions->getPrimary($this->tableName), ); } @@ -242,7 +242,7 @@ public function getParameters(): array $this->parameters['where'], $this->parameters['group'], $this->parameters['having'], - $this->parameters['order'] + $this->parameters['order'], ); } @@ -590,7 +590,7 @@ protected function parseJoinConditions(&$joins, $joinConditions): array protected function getSortedJoins(string $table, &$leftJoinDependency, &$tableJoins, &$finalJoins): void { if (isset($this->expandingJoins[$table])) { - $path = implode("' => '", array_map(function (string $value): string { return $this->reservedTableNames[$value]; }, array_merge(array_keys($this->expandingJoins), [$table]))); + $path = implode("' => '", array_map(fn(string $value): string => $this->reservedTableNames[$value], array_merge(array_keys($this->expandingJoins), [$table]))); throw new Nette\InvalidArgumentException("Circular reference detected at left join conditions (tables '$path')."); } @@ -820,11 +820,9 @@ protected function buildQueryEnd(): string protected function tryDelimite(string $s): string { - return preg_replace_callback('#(?<=[^\w`"\[?:]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#Di', function (array $m): string { - return strtoupper($m[0]) === $m[0] + return preg_replace_callback('#(?<=[^\w`"\[?:]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#Di', fn(array $m): string => strtoupper($m[0]) === $m[0] ? $m[0] - : $this->driver->delimite($m[0]); - }, $s); + : $this->driver->delimite($m[0]), $s); } @@ -832,7 +830,7 @@ protected function addConditionComposition( array $columns, array $parameters, array &$conditions, - array &$conditionsParameters + array &$conditionsParameters, ): bool { if ($this->driver->isSupported(Driver::SUPPORT_MULTI_COLUMN_AS_OR_COND)) { @@ -866,9 +864,7 @@ private function getConditionHash($condition, array $parameters): string private function getCachedTableList(): array { if (!$this->cacheTableList) { - $this->cacheTableList = array_flip(array_map(function (array $pair): string { - return $pair['fullName'] ?? $pair['name']; - }, $this->structure->getTables())); + $this->cacheTableList = array_flip(array_map(fn(array $pair): string => $pair['fullName'] ?? $pair['name'], $this->structure->getTables())); } return $this->cacheTableList; diff --git a/tests/Database/Explorer/Explorer.backjoin.phpt b/tests/Database/Explorer/Explorer.backjoin.phpt index a56a8d89f..9b070e76a 100644 --- a/tests/Database/Explorer/Explorer.backjoin.phpt +++ b/tests/Database/Explorer/Explorer.backjoin.phpt @@ -42,12 +42,12 @@ test('', function () use ($explorer, $driver) { if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { 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() + $authorsSelection->getSql(), ); } else { Assert::same( reformat('SELECT [author].* FROM [author] LEFT JOIN [book] ON [author].[id] = [book].[author_id] WHERE ([book].[translator_id] IS NOT NULL) AND ([author].[id] = ?)'), - $authorsSelection->getSql() + $authorsSelection->getSql(), ); } diff --git a/tests/Database/Explorer/Explorer.basic.phpt b/tests/Database/Explorer/Explorer.basic.phpt index ddad637ed..3a56f50b3 100644 --- a/tests/Database/Explorer/Explorer.basic.phpt +++ b/tests/Database/Explorer/Explorer.basic.phpt @@ -78,7 +78,7 @@ test('', function () use ($connection, $structure) { $explorer = new Nette\Database\Explorer( $connection, $structure, - new Nette\Database\Conventions\DiscoveredConventions($structure) + new Nette\Database\Conventions\DiscoveredConventions($structure), ); $book = $explorer->table('book')->get(1); diff --git a/tests/Database/Explorer/Explorer.join-condition.phpt b/tests/Database/Explorer/Explorer.join-condition.phpt index c999edce0..4c0b152dd 100644 --- a/tests/Database/Explorer/Explorer.join-condition.phpt +++ b/tests/Database/Explorer/Explorer.join-condition.phpt @@ -22,7 +22,7 @@ test('', function () use ($explorer, $driver) { Assert::same(reformat( 'SELECT [book].* FROM [book] ' . - "LEFT JOIN {$schema}[author] [translator] ON [book].[translator_id] = [translator].[id] AND ([translator].[name] = ?)" + "LEFT JOIN {$schema}[author] [translator] ON [book].[translator_id] = [translator].[id] AND ([translator].[name] = ?)", ), $sql); }); @@ -44,9 +44,9 @@ test('', function () use ($explorer, $driver) { 'LEFT JOIN [public].[author] [next_volume_author] ON [book_ref].[author_id] = [next_volume_author].[id] ' . 'LEFT JOIN [public].[author] [author] ON [book].[author_id] = [author].[id] AND ([author].[born] < [next_volume_author].[born]) ' . 'WHERE ([tag].[name] = ?) ' . - 'GROUP BY [tag].[name]' + 'GROUP BY [tag].[name]', ), - $sql + $sql, ); } else { Assert::same( @@ -58,9 +58,9 @@ test('', function () use ($explorer, $driver) { 'LEFT JOIN [author] [next_volume_author] ON [book_ref].[author_id] = [next_volume_author].[id] ' . 'LEFT JOIN [author] ON [book].[author_id] = [author].[id] AND ([author].[born] < [next_volume_author].[born]) ' . 'WHERE ([tag].[name] = ?) ' . - 'GROUP BY [tag].[name]' + 'GROUP BY [tag].[name]', ), - $sql + $sql, ); } }); diff --git a/tests/Database/Explorer/Explorer.join.phpt b/tests/Database/Explorer/Explorer.join.phpt index 62ddc1967..68f06e41d 100644 --- a/tests/Database/Explorer/Explorer.join.phpt +++ b/tests/Database/Explorer/Explorer.join.phpt @@ -37,12 +37,12 @@ test('', function () use ($explorer, $driver) { if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { Assert::same( reformat('SELECT [tag].* FROM [book_tag] LEFT JOIN [public].[tag] [tag] ON [book_tag].[tag_id] = [tag].[id] WHERE ([book_id] = ?)'), - $joinSql + $joinSql, ); } else { Assert::same( reformat('SELECT [tag].* FROM [book_tag] LEFT JOIN [tag] ON [book_tag].[tag_id] = [tag].[id] WHERE ([book_id] = ?)'), - $joinSql + $joinSql, ); } }); @@ -54,12 +54,12 @@ test('', function () use ($explorer, $driver) { if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { 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 + $joinSql, ); } else { Assert::same( reformat('SELECT [Tag].[id] FROM [book_tag] LEFT JOIN [tag] [Tag] ON [book_tag].[tag_id] = [Tag].[id] WHERE ([book_id] = ?)'), - $joinSql + $joinSql, ); } }); @@ -88,7 +88,7 @@ test('', function () use ($connection, $structure) { $explorer = new Nette\Database\Explorer( $connection, $structure, - new Nette\Database\Conventions\DiscoveredConventions($structure) + new Nette\Database\Conventions\DiscoveredConventions($structure), ); $books = $explorer->table('book')->select('book.*, author.name, translator.name'); diff --git a/tests/Database/Explorer/Explorer.limit.phpt b/tests/Database/Explorer/Explorer.limit.phpt index a407d5bc0..859a5bd59 100644 --- a/tests/Database/Explorer/Explorer.limit.phpt +++ b/tests/Database/Explorer/Explorer.limit.phpt @@ -16,38 +16,38 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 2'), - $explorer->table('author')->limit(2)->getSql() + $explorer->table('author')->limit(2)->getSql(), ); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 2 OFFSET 10'), - $explorer->table('author')->limit(2, 10)->getSql() + $explorer->table('author')->limit(2, 10)->getSql(), ); Assert::same( reformat('SELECT * FROM [author] ORDER BY [name] LIMIT 2'), - $explorer->table('author')->order('name')->limit(2)->getSql() + $explorer->table('author')->order('name')->limit(2)->getSql(), ); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 10'), - $explorer->table('author')->page(1, 10)->getSql() + $explorer->table('author')->page(1, 10)->getSql(), ); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 0'), - $explorer->table('author')->page(0, 10, $count)->getSql() + $explorer->table('author')->page(0, 10, $count)->getSql(), ); Assert::same(1, $count); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 10 OFFSET 10'), - $explorer->table('author')->page(2, 10, $count)->getSql() + $explorer->table('author')->page(2, 10, $count)->getSql(), ); Assert::same(1, $count); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] LIMIT 2 OFFSET 2'), - $explorer->table('author')->page(2, 2, $count)->getSql() + $explorer->table('author')->page(2, 2, $count)->getSql(), ); Assert::same(2, $count); diff --git a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt index 29e47556f..ca894fa82 100644 --- a/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt +++ b/tests/Database/Explorer/Explorer.limit.sqlsrv.phpt @@ -19,28 +19,28 @@ Assert::same( $version2008 ? 'SELECT TOP 2 * FROM [author] ORDER BY [author].[id]' : 'SELECT * FROM [author] ORDER BY [author].[id] OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY', - $explorer->table('author')->limit(2)->getSql() + $explorer->table('author')->limit(2)->getSql(), ); Assert::same( $version2008 ? 'SELECT TOP 2 * FROM [author] ORDER BY [name]' : 'SELECT * FROM [author] ORDER BY [name] OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY', - $explorer->table('author')->order('name')->limit(2)->getSql() + $explorer->table('author')->order('name')->limit(2)->getSql(), ); Assert::same( $version2008 ? 'SELECT TOP 10 * FROM [author] ORDER BY [author].[id]' : 'SELECT * FROM [author] ORDER BY [author].[id] OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', - $explorer->table('author')->page(1, 10)->getSql() + $explorer->table('author')->page(1, 10)->getSql(), ); Assert::same( $version2008 ? 'SELECT TOP 0 * FROM [author] ORDER BY [author].[id]' : 'SELECT * FROM [author] ORDER BY [author].[id] OFFSET 0 ROWS FETCH NEXT 0 ROWS ONLY', - $explorer->table('author')->page(0, 10)->getSql() + $explorer->table('author')->page(0, 10)->getSql(), ); if ($version2008) { @@ -55,13 +55,13 @@ if ($version2008) { } else { Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY'), - $explorer->table('author')->page(2, 10, $count)->getSql() + $explorer->table('author')->page(2, 10, $count)->getSql(), ); Assert::same(1, $count); Assert::same( reformat('SELECT * FROM [author] ORDER BY [author].[id] OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY'), - $explorer->table('author')->page(2, 2, $count)->getSql() + $explorer->table('author')->page(2, 2, $count)->getSql(), ); Assert::same(2, $count); } diff --git a/tests/Database/Explorer/Selection.insert().phpt b/tests/Database/Explorer/Selection.insert().phpt index e3eb97902..a303a6685 100644 --- a/tests/Database/Explorer/Selection.insert().phpt +++ b/tests/Database/Explorer/Selection.insert().phpt @@ -79,7 +79,7 @@ if ($driverName !== 'sqlsrv') { $explorer = new Nette\Database\Explorer( $connection, $structure, - new Nette\Database\Conventions\DiscoveredConventions($structure) + new Nette\Database\Conventions\DiscoveredConventions($structure), ); $inserted = $explorer->table('note')->insert([ diff --git a/tests/Database/Explorer/SqlBuilder.addAlias().phpt b/tests/Database/Explorer/SqlBuilder.addAlias().phpt index a5bf3378d..d25e6a56e 100644 --- a/tests/Database/Explorer/SqlBuilder.addAlias().phpt +++ b/tests/Database/Explorer/SqlBuilder.addAlias().phpt @@ -76,7 +76,7 @@ test('test same table chain with another alias', function () use ($explorer, $dr Assert::same( 'LEFT JOIN book translated_book ON author.id = translated_book.translator_id ' . 'LEFT JOIN book translated_book2 ON author.id = translated_book2.translator_id', - trim($join) + trim($join), ); }); @@ -95,14 +95,14 @@ test('test nested alias', function () use ($explorer, $driver) { 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', - trim($join) + trim($join), ); } else { Assert::same( 'LEFT JOIN book translated_book ON author.id = translated_book.translator_id ' . 'LEFT JOIN book next ON translated_book.next_volume = next.id', - trim($join) + trim($join), ); } }); diff --git a/tests/Database/Explorer/SqlBuilder.addWhere().phpt b/tests/Database/Explorer/SqlBuilder.addWhere().phpt index b75a42443..3c250b163 100644 --- a/tests/Database/Explorer/SqlBuilder.addWhere().phpt +++ b/tests/Database/Explorer/SqlBuilder.addWhere().phpt @@ -203,7 +203,7 @@ test('tests operator suffix', function () use ($explorer) { test('', function () use ($explorer) { $books = $explorer->table('book')->where( 'id', - $explorer->table('book_tag')->select('book_id')->where('tag_id', 21) + $explorer->table('book_tag')->select('book_id')->where('tag_id', 21), ); Assert::same(3, $books->count()); }); @@ -212,7 +212,7 @@ test('', function () use ($explorer) { Assert::exception(function () use ($explorer) { $explorer->table('book')->where( 'id', - $explorer->table('book_tag')->where('tag_id', 21) + $explorer->table('book_tag')->where('tag_id', 21), ); }, Nette\InvalidArgumentException::class, 'Selection argument must have defined a select column.'); @@ -272,7 +272,7 @@ test('', function () use ($driverName, $explorer, $connection, $structure) { $e = Assert::exception(function () use ($dao) { $books = $dao->table('book')->where( 'id', - $dao->table('book_tag')->where('tag_id', 21) + $dao->table('book_tag')->where('tag_id', 21), ); $books->fetch(); }, Nette\InvalidArgumentException::class, 'Selection argument must have defined a select column.'); diff --git a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt index 7f16686b2..2a8a58905 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt @@ -78,13 +78,13 @@ test('', function () use ($explorer, $driver) { 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 = ?)', - trim($join) + trim($join), ); } else { Assert::same( 'LEFT JOIN book ON author.id = book.translator_id AND (book.id > ?) ' . 'LEFT JOIN book_tag_alt ON book.id = book_tag_alt.book_id AND (book_tag_alt.state = ?)', - trim($join) + trim($join), ); } diff --git a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt index edf39933a..8a75cb465 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt @@ -49,7 +49,7 @@ if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_n 'LEFT JOIN public.nUsers_nTopics nusers_ntopics ON nUsers.nUserId = nusers_ntopics.nUserId ' . 'LEFT JOIN public.nTopics topic ON nusers_ntopics.nTopicId = topic.nTopicId ' . 'LEFT JOIN public.nPriorities priorit ON topic.nPriorityId = priorit.nPriorityId', - trim($join) + trim($join), ); } else { @@ -57,7 +57,7 @@ if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_n 'LEFT JOIN nUsers_nTopics nusers_ntopics ON nUsers.nUserId = nusers_ntopics.nUserId ' . 'LEFT JOIN nTopics topic ON nusers_ntopics.nTopicId = topic.nTopicId ' . 'LEFT JOIN nPriorities priorit ON topic.nPriorityId = priorit.nPriorityId', - trim($join) + trim($join), ); } @@ -66,7 +66,7 @@ if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_n 'LEFT JOIN nusers_ntopics ON nUsers.nUserId = nusers_ntopics.nUserId ' . 'LEFT JOIN ntopics topic ON nusers_ntopics.nTopicId = topic.nTopicId ' . 'LEFT JOIN npriorities priorit ON topic.nPriorityId = priorit.nPriorityId', - trim($join) + trim($join), ); } @@ -83,7 +83,7 @@ $join = $sqlBuilder->buildQueryJoins($joins); Assert::same('WHERE book.next_volume IS NULL', $query); Assert::same( 'LEFT JOIN book ON author.id = book.translator_id', - trim($join) + trim($join), ); @@ -101,12 +101,12 @@ if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { 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', - trim($join) + trim($join), ); } else { Assert::same( 'LEFT JOIN book book_ref ON book.id = book_ref.next_volume ' . 'LEFT JOIN book book_ref_ref ON book_ref.id = book_ref_ref.next_volume', - trim($join) + trim($join), ); } diff --git a/tests/Database/Explorer/bugs/bug49.phpt b/tests/Database/Explorer/bugs/bug49.phpt index 428af14a5..2c2706529 100644 --- a/tests/Database/Explorer/bugs/bug49.phpt +++ b/tests/Database/Explorer/bugs/bug49.phpt @@ -17,5 +17,5 @@ $explorer->query('CREATE TABLE `TABLE 30` (id int)'); Assert::same( reformat('SELECT * FROM `TABLE 30`'), - $explorer->table('TABLE 30')->getSql() + $explorer->table('TABLE 30')->getSql(), ); diff --git a/tests/Database/Explorer/bugs/view.bug.phpt b/tests/Database/Explorer/bugs/view.bug.phpt index 89907b349..18f18259d 100644 --- a/tests/Database/Explorer/bugs/view.bug.phpt +++ b/tests/Database/Explorer/bugs/view.bug.phpt @@ -22,9 +22,7 @@ test('', function () use ($explorer) { test('', function () use ($connection) { $driver = $connection->getDriver(); $columns = $driver->getColumns('books_view'); - $columnsNames = array_map(function ($item) { - return $item['name']; - }, $columns); + $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 631f9caa7..c5b410613 100644 --- a/tests/Database/Helpers.dumpSql.phpt +++ b/tests/Database/Helpers.dumpSql.phpt @@ -17,35 +17,35 @@ 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('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('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('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('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('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), ); }); @@ -58,6 +58,6 @@ 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('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection), ); }); diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index e5d5073f1..2a85dde3a 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -17,8 +17,8 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName $driver = $connection->getDriver(); $tables = $driver->getTables(); -$tables = array_filter($tables, function ($t) { return in_array($t['name'], ['author', 'book', 'book_tag', 'tag'], true); }); -usort($tables, function ($a, $b) { return strcmp($a['name'], $b['name']); }); +$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::SUPPORT_SCHEMA)) { Assert::same( @@ -28,7 +28,7 @@ if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { ['name' => 'book_tag', 'view' => false, 'fullName' => 'public.book_tag'], ['name' => 'tag', 'view' => false, 'fullName' => 'public.tag'], ], - $tables + $tables, ); } else { Assert::same([ diff --git a/tests/Database/Reflection.postgre.10.phpt b/tests/Database/Reflection.postgre.10.phpt index 38f58327a..b2b0cd3f9 100644 --- a/tests/Database/Reflection.postgre.10.phpt +++ b/tests/Database/Reflection.postgre.10.phpt @@ -21,13 +21,11 @@ if (version_compare($ver, '10') < 0) { function shortInfo(array $columns): array { - return array_map(function (array $col): array { - return [ - 'name' => $col['name'], - 'autoincrement' => $col['autoincrement'], - 'sequence' => $col['vendor']['sequence'], - ]; - }, $columns); + return array_map(fn(array $col): array => [ + 'name' => $col['name'], + 'autoincrement' => $col['autoincrement'], + 'sequence' => $col['vendor']['sequence'], + ], $columns); } @@ -123,7 +121,7 @@ test('Materialized view columns', function () use ($connection) { Assert::same( ['name', 'id'], - array_column($driver->getColumns('source_mt'), 'name') + array_column($driver->getColumns('source_mt'), 'name'), ); }); diff --git a/tests/Database/ResultSet.customNormalizer.phpt b/tests/Database/ResultSet.customNormalizer.phpt index 25cf4a3b6..d139e022e 100644 --- a/tests/Database/ResultSet.customNormalizer.phpt +++ b/tests/Database/ResultSet.customNormalizer.phpt @@ -24,7 +24,7 @@ test('disabled normalization', function () use ($connection) { 'id' => $asInt ? 11 : '11', 'name' => 'Jakub Vrana', 'web' => 'http://www.vrana.cz/', - 'born' => $driverName === 'sqlite' ? ($asInt ? 1642892400 : '1642892400') : '2022-01-23', + 'born' => $driverName === 'sqlite' ? ($asInt ? 1_642_892_400 : '1642892400') : '2022-01-23', ], (array) $res->fetch()); }); diff --git a/tests/Database/ResultSet.parameters.mysql.phpt b/tests/Database/ResultSet.parameters.mysql.phpt index 99498ae7a..9f0bfd464 100644 --- a/tests/Database/ResultSet.parameters.mysql.phpt +++ b/tests/Database/ResultSet.parameters.mysql.phpt @@ -15,5 +15,5 @@ $res = $connection->fetch('SELECT ? AS c1, ? AS c2, ? AS c3, ? as c4', fopen(__F Assert::same( ['c1' => file_get_contents(__FILE__), 'c2' => 1, 'c3' => null, 'c4' => 123], - (array) $res + (array) $res, ); diff --git a/tests/Database/ResultSet.parameters.postgre.phpt b/tests/Database/ResultSet.parameters.postgre.phpt index 327207c48..03b8a5958 100644 --- a/tests/Database/ResultSet.parameters.postgre.phpt +++ b/tests/Database/ResultSet.parameters.postgre.phpt @@ -15,5 +15,5 @@ $res = $connection->fetch('SELECT ?::bool AS c1, ? AS c2, ?::int AS c3', true, n Assert::same( ['c1' => true, 'c2' => null, 'c3' => 123], - (array) $res + (array) $res, ); diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index 852dfb5db..0a6bbf38b 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -155,7 +155,7 @@ class StructureTestCase extends TestCase Assert::same( ['author_id', 'translator_id'], - $this->structure->getHasManyReference('authors', 'books') + $this->structure->getHasManyReference('authors', 'books'), ); } @@ -176,7 +176,7 @@ class StructureTestCase extends TestCase Assert::same( ['Books', 'book_id'], - $this->structure->getBelongsToReference('books_x_tags', 'book_id') + $this->structure->getBelongsToReference('books_x_tags', 'book_id'), ); Assert::null($this->structure->getBelongsToReference('books_x_tags', 'non_exist')); From 52557bcacca432d09b32fe7a8ce38d27e14dec39 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 7 May 2022 23:48:25 +0200 Subject: [PATCH 05/35] unified php.ini --- tests/php-win.ini | 9 --------- tests/php.ini | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 tests/php-win.ini create mode 100644 tests/php.ini diff --git a/tests/php-win.ini b/tests/php-win.ini deleted file mode 100644 index 7d024ac2e..000000000 --- a/tests/php-win.ini +++ /dev/null @@ -1,9 +0,0 @@ -[PHP] -extension_dir = "./ext" -extension=php_pdo_mysql.dll -extension=php_pdo_sqlite.dll -extension=php_pdo_pgsql.dll -extension=php_pdo_sqlsrv_ts.dll - -[Zend] -;zend_extension="./ext/php_xdebug-2.0.5-5.3-vc6.dll" diff --git a/tests/php.ini b/tests/php.ini new file mode 100644 index 000000000..0fcf36853 --- /dev/null +++ b/tests/php.ini @@ -0,0 +1,5 @@ +[PHP] +extension=pdo_mysql +extension=pdo_sqlite +extension=pdo_pgsql +extension=pdo_sqlsrv_ts From d55111661a68de6dbba3f4a0707e77e089b5da6c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 7 Mar 2022 14:40:35 +0100 Subject: [PATCH 06/35] internal constants are PascalCase --- src/Database/Driver.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/SqlPreprocessor.php | 2 +- src/Database/Structure.php | 2 +- src/Database/Table/SqlBuilder.php | 10 +++++----- tests/Database/Explorer/Explorer.backjoin.phpt | 2 +- tests/Database/Explorer/Explorer.join-condition.phpt | 4 ++-- tests/Database/Explorer/Explorer.join.phpt | 4 ++-- tests/Database/Explorer/SqlBuilder.addAlias().phpt | 6 +++--- tests/Database/Explorer/SqlBuilder.addWhere().phpt | 2 +- .../Explorer/SqlBuilder.parseJoinConditions().phpt | 2 +- tests/Database/Explorer/SqlBuilder.parseJoins().phpt | 6 +++--- tests/Database/Reflection.phpt | 2 +- 19 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 227c7ff62..6dbc14856 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -16,12 +16,12 @@ interface Driver { 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'; + SupportSequence = 'sequence', + SupportSelectUngroupedColumns = 'ungrouped_cols', + SupportMultiInsertAsSelect = 'insert_as_select', + SupportMultiColumnAsOrCond = 'multi_column_as_or', + SupportSubselect = 'subselect', + SupportSchema = 'schema'; /** * Initializes connection. diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 9e21e1964..d3ef00f53 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -218,6 +218,6 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_SUBSELECT; + return $item === self::SupportSubselect; } } diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 84269d989..32d113a74 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -208,6 +208,6 @@ public function isSupported(string $item): bool // - http://bugs.mysql.com/bug.php?id=31188 // - http://bugs.mysql.com/bug.php?id=35819 // and more. - return $item === self::SUPPORT_SELECT_UNGROUPED_COLUMNS || $item === self::SUPPORT_MULTI_COLUMN_AS_OR_COND; + return $item === self::SupportSelectUngroupedColumns || $item === self::SupportMultiColumnAsOrCond; } } diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index e18308d02..e79f6d7a9 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -141,6 +141,6 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_SEQUENCE || $item === self::SUPPORT_SUBSELECT; + return $item === self::SupportSequence || $item === self::SupportSubselect; } } diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index c80a80764..4150c20c4 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -110,6 +110,6 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_SUBSELECT; + return $item === self::SupportSubselect; } } diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index e16871555..5bf2e36f7 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -235,7 +235,7 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_SEQUENCE || $item === self::SUPPORT_SUBSELECT || $item === self::SUPPORT_SCHEMA; + return $item === self::SupportSequence || $item === self::SupportSubselect || $item === self::SupportSchema; } diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 1c4d0e2e2..88c9451c6 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -239,6 +239,6 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_MULTI_INSERT_AS_SELECT || $item === self::SUPPORT_SUBSELECT || $item === self::SUPPORT_MULTI_COLUMN_AS_OR_COND; + 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 63b5c4bde..6fde3b6be 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -243,6 +243,6 @@ public function getColumnTypes(\PDOStatement $statement): array public function isSupported(string $item): bool { - return $item === self::SUPPORT_SUBSELECT; + return $item === self::SupportSubselect; } } diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index efbba9e89..c3e9b9e03 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -242,7 +242,7 @@ private function formatValue($value, ?string $mode = null): string $vx[] = implode(', ', $vx2); } - $select = $this->driver->isSupported(Driver::SUPPORT_MULTI_INSERT_AS_SELECT); + $select = $this->driver->isSupported(Driver::SupportMultiInsertAsSelect); return '(' . implode(', ', $kx) . ($select ? ') SELECT ' : ') VALUES (') . implode($select ? ' UNION ALL SELECT ' : '), (', $vx) . ($select ? '' : ')'); } diff --git a/src/Database/Structure.php b/src/Database/Structure.php index ec9bacacb..714462348 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -101,7 +101,7 @@ public function getPrimaryKeySequence(string $table): ?string $this->needStructure(); $table = $this->resolveFQTableName($table); - if (!$this->connection->getDriver()->isSupported(Driver::SUPPORT_SEQUENCE)) { + if (!$this->connection->getDriver()->isSupported(Driver::SupportSequence)) { return null; } diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 4c90f8093..275ebe13d 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -160,7 +160,7 @@ public function getSelectQueryHash(?array $columns = null): string $parts[] = $this->select; } elseif ($columns) { $parts[] = [$this->delimitedTable, $columns]; - } elseif ($this->group && !$this->driver->isSupported(Driver::SUPPORT_SELECT_UNGROUPED_COLUMNS)) { + } elseif ($this->group && !$this->driver->isSupported(Driver::SupportSelectUngroupedColumns)) { $parts[] = [$this->group]; } else { $parts[] = "{$this->delimitedTable}.*"; @@ -212,7 +212,7 @@ public function buildSelectQuery(?array $columns = null): string $querySelect = $this->buildSelect($cols); - } elseif ($this->group && !$this->driver->isSupported(Driver::SUPPORT_SELECT_UNGROUPED_COLUMNS)) { + } elseif ($this->group && !$this->driver->isSupported(Driver::SupportSelectUngroupedColumns)) { $querySelect = $this->buildSelect([$this->group]); $this->parseJoins($joins, $querySelect); @@ -380,7 +380,7 @@ protected function addCondition($condition, array $params, array &$conditions, a } } - if ($this->driver->isSupported(Driver::SUPPORT_SUBSELECT)) { + if ($this->driver->isSupported(Driver::SupportSubselect)) { $arg = null; $subSelectPlaceholderCount = substr_count($clone->getSql(), '?'); $replace = $match[2][0] . '(' . $clone->getSql() . (!$subSelectPlaceholderCount && count($clone->getSqlBuilder()->getParameters()) === 1 ? ' ?' : '') . ')'; @@ -663,7 +663,7 @@ public function parseJoinsCb(&$joins, $match): string $parentAlias = preg_replace('#^(.*\.)?(.*)$#', '$2', $this->tableName); // join schema keyMatch and table keyMatch to schema.table keyMatch - if ($this->driver->isSupported(Driver::SUPPORT_SCHEMA) && count($keyMatches) > 1) { + if ($this->driver->isSupported(Driver::SupportSchema) && count($keyMatches) > 1) { $tables = $this->getCachedTableList(); if ( !isset($tables[$keyMatches[0]['key']]) @@ -833,7 +833,7 @@ protected function addConditionComposition( array &$conditionsParameters, ): bool { - if ($this->driver->isSupported(Driver::SUPPORT_MULTI_COLUMN_AS_OR_COND)) { + if ($this->driver->isSupported(Driver::SupportMultiColumnAsOrCond)) { $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/Explorer/Explorer.backjoin.phpt b/tests/Database/Explorer/Explorer.backjoin.phpt index 9b070e76a..212234a79 100644 --- a/tests/Database/Explorer/Explorer.backjoin.phpt +++ b/tests/Database/Explorer/Explorer.backjoin.phpt @@ -39,7 +39,7 @@ test('', function () use ($explorer) { test('', function () use ($explorer, $driver) { $authorsSelection = $explorer->table('author')->where(':book.translator_id IS NOT NULL')->wherePrimary(12); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 4c0b152dd..a21d6838c 100644 --- a/tests/Database/Explorer/Explorer.join-condition.phpt +++ b/tests/Database/Explorer/Explorer.join-condition.phpt @@ -15,7 +15,7 @@ require __DIR__ . '/../connect.inc.php'; // create $connection Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverName}-nette_test1.sql"); $driver = $connection->getDriver(); test('', function () use ($explorer, $driver) { - $schema = $driver->isSupported(Driver::SUPPORT_SCHEMA) + $schema = $driver->isSupported(Driver::SupportSchema) ? '[public].' : ''; $sql = $explorer->table('book')->joinWhere('translator', 'translator.name', 'Geek')->select('book.*')->getSql(); @@ -34,7 +34,7 @@ test('', function () use ($explorer, $driver) { ->where('tag.name', 'PHP') ->group('tag.name') ->getSql(); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 68f06e41d..2fb9f32de 100644 --- a/tests/Database/Explorer/Explorer.join.phpt +++ b/tests/Database/Explorer/Explorer.join.phpt @@ -34,7 +34,7 @@ test('', function () use ($explorer) { test('', function () use ($explorer, $driver) { $joinSql = $explorer->table('book_tag')->where('book_id', 1)->select('tag.*')->getSql(); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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,7 +51,7 @@ test('', function () use ($explorer, $driver) { test('', function () use ($explorer, $driver) { $joinSql = $explorer->table('book_tag')->where('book_id', 1)->select('Tag.id')->getSql(); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 d25e6a56e..8094bda29 100644 --- a/tests/Database/Explorer/SqlBuilder.addAlias().phpt +++ b/tests/Database/Explorer/SqlBuilder.addAlias().phpt @@ -33,7 +33,7 @@ $driver = $connection->getDriver(); test('test duplicated table names throw exception', function () use ($explorer, $driver) { - $authorTable = ($driver->isSupported(Driver::SUPPORT_SCHEMA) ? 'public.' : '') . 'author'; + $authorTable = ($driver->isSupported(Driver::SupportSchema) ? 'public.' : '') . 'author'; $sqlBuilder = new SqlBuilderMock($authorTable, $explorer); $sqlBuilder->addAlias(':book(translator)', 'book1'); $sqlBuilder->addAlias(':book:book_tag', 'book2'); @@ -82,7 +82,7 @@ test('test same table chain with another alias', function () use ($explorer, $dr test('test nested alias', function () use ($explorer, $driver) { - $sqlBuilder = $driver->isSupported(Driver::SUPPORT_SCHEMA) + $sqlBuilder = $driver->isSupported(Driver::SupportSchema) ? new SqlBuilderMock('public.author', $explorer) : new SqlBuilderMock('author', $explorer); $sqlBuilder->addAlias(':book(translator)', 'translated_book'); @@ -91,7 +91,7 @@ test('test nested alias', function () use ($explorer, $driver) { $joins = []; $sqlBuilder->parseJoins($joins, $query); $join = $sqlBuilder->buildQueryJoins($joins); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 3c250b163..66048910d 100644 --- a/tests/Database/Explorer/SqlBuilder.addWhere().phpt +++ b/tests/Database/Explorer/SqlBuilder.addWhere().phpt @@ -80,7 +80,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::SUPPORT_SCHEMA); + $schemaSupported = $explorer->getConnection()->getDriver()->isSupported(Driver::SupportSchema); Assert::equal(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 2a8a58905..19c73e6cc 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoinConditions().phpt @@ -74,7 +74,7 @@ test('', function () use ($explorer, $driver) { $leftJoinConditions = $sqlBuilder->parseJoinConditions($joins, $sqlBuilder->buildJoinConditions()); $join = $sqlBuilder->buildQueryJoins($joins, $leftJoinConditions); - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 8a75cb465..d437e231b 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt @@ -44,7 +44,7 @@ Assert::same('WHERE priorit.id IS NULL', $query); $tables = $connection->getDriver()->getTables(); if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_ntopics', 'nusers_ntopics_alt'], true)) { - if ($driver->isSupported(Driver::SUPPORT_SCHEMA)) { + if ($driver->isSupported(Driver::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 ' . @@ -87,7 +87,7 @@ Assert::same( ); -$sqlBuilder = $driver->isSupported(Driver::SUPPORT_SCHEMA) +$sqlBuilder = $driver->isSupported(Driver::SupportSchema) ? new SqlBuilderMock('public.book', $explorer) : new SqlBuilderMock('book', $explorer); @@ -97,7 +97,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::SUPPORT_SCHEMA)) { +if ($driver->isSupported(Driver::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/Reflection.phpt b/tests/Database/Reflection.phpt index 2a85dde3a..5b60e3bc4 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -20,7 +20,7 @@ $tables = $driver->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::SUPPORT_SCHEMA)) { +if ($driver->isSupported(Driver::SupportSchema)) { Assert::same( [ ['name' => 'author', 'view' => false, 'fullName' => 'public.author'], From 46efc03337c6aceec61bc27d5845c2507fedddd8 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 18:33:16 +0100 Subject: [PATCH 07/35] removed support for PHP 7 --- src/Database/ResultSet.php | 2 +- src/Database/Table/ActiveRow.php | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 8cae996ca..8e9767d87 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -67,7 +67,7 @@ public function __construct( if (substr($queryString, 0, 2) === '::') { $connection->getPdo()->{substr($queryString, 2)}(); } elseif ($queryString !== null) { - $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; + $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL, ]; $this->pdoStatement = $connection->getPdo()->prepare($queryString); foreach ($params as $key => $value) { $type = gettype($value); diff --git a/src/Database/Table/ActiveRow.php b/src/Database/Table/ActiveRow.php index a44bc36af..4a8354317 100644 --- a/src/Database/Table/ActiveRow.php +++ b/src/Database/Table/ActiveRow.php @@ -55,16 +55,7 @@ public function getTable(): Selection public function __toString() { - try { - return (string) $this->getPrimary(); - } catch (\Throwable $e) { - if (func_num_args() || PHP_VERSION_ID >= 70400) { - throw $e; - } - - trigger_error('Exception in ' . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR); - return ''; - } + return (string) $this->getPrimary(); } From b00a2dcb596cb8ea780d71e7144466bc238f22d5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 19:11:28 +0100 Subject: [PATCH 08/35] added property typehints --- src/Bridges/DatabaseDI/DatabaseExtension.php | 3 +- src/Bridges/DatabaseTracy/ConnectionPanel.php | 27 +++---- src/Database/Connection.php | 31 +++----- .../Conventions/DiscoveredConventions.php | 3 +- .../Conventions/StaticConventions.php | 9 +-- src/Database/DriverException.php | 6 +- src/Database/Drivers/MsSqlDriver.php | 3 +- src/Database/Drivers/MySqlDriver.php | 3 +- src/Database/Drivers/OciDriver.php | 7 +- src/Database/Drivers/PgSqlDriver.php | 3 +- src/Database/Drivers/SqliteDriver.php | 7 +- src/Database/Drivers/SqlsrvDriver.php | 6 +- src/Database/Explorer.php | 12 +-- src/Database/Helpers.php | 7 +- src/Database/ResultSet.php | 37 +++------ src/Database/SqlLiteral.php | 6 +- src/Database/SqlPreprocessor.php | 24 +++--- src/Database/Structure.php | 14 ++-- src/Database/Table/ActiveRow.php | 9 +-- src/Database/Table/GroupedSelection.php | 16 ++-- src/Database/Table/Selection.php | 75 ++++++++----------- src/Database/Table/SqlBuilder.php | 72 ++++++++---------- .../Explorer/Explorer.cache.observer2.phpt | 2 +- tests/Database/Structure.phpt | 14 ++-- tests/Database/Structure.schemas.phpt | 14 ++-- 25 files changed, 157 insertions(+), 253 deletions(-) diff --git a/src/Bridges/DatabaseDI/DatabaseExtension.php b/src/Bridges/DatabaseDI/DatabaseExtension.php index 16179c9da..d33ecff29 100644 --- a/src/Bridges/DatabaseDI/DatabaseExtension.php +++ b/src/Bridges/DatabaseDI/DatabaseExtension.php @@ -19,8 +19,7 @@ */ class DatabaseExtension extends Nette\DI\CompilerExtension { - /** @var bool */ - private $debugMode; + private bool $debugMode; public function __construct(bool $debugMode = false) diff --git a/src/Bridges/DatabaseTracy/ConnectionPanel.php b/src/Bridges/DatabaseTracy/ConnectionPanel.php index ccc7300d2..f9e470e35 100644 --- a/src/Bridges/DatabaseTracy/ConnectionPanel.php +++ b/src/Bridges/DatabaseTracy/ConnectionPanel.php @@ -22,32 +22,23 @@ class ConnectionPanel implements Tracy\IBarPanel { use Nette\SmartObject; - /** @var int */ - public $maxQueries = 100; + public int $maxQueries = 100; - /** @var string */ - public $name; + public string $name; - /** @var bool|string explain queries? */ - public $explain = true; + public bool|string $explain = true; - /** @var bool */ - public $disabled = false; + public bool $disabled = false; - /** @var float */ - public $performanceScale = 0.25; + public float $performanceScale = 0.25; - /** @var float logged time */ - private $totalTime = 0; + private float $totalTime = 0; - /** @var int */ - private $count = 0; + private int $count = 0; - /** @var array */ - private $queries = []; + private array $queries = []; - /** @var Tracy\BlueScreen */ - private $blueScreen; + private Tracy\BlueScreen $blueScreen; public static function initialize( diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 225c7b491..c5d728700 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -23,34 +23,21 @@ class Connection use Nette\SmartObject; /** @var array Occurs after connection is established */ - public $onConnect = []; + public array $onConnect = []; /** @var array Occurs after query is executed */ - public $onQuery = []; + public array $onQuery = []; - /** @var array */ - private $params; - - /** @var array */ - private $options; - - /** @var Driver */ - private $driver; - - /** @var SqlPreprocessor */ - private $preprocessor; - - /** @var PDO|null */ - private $pdo; + private array $params; + private array $options; + private Driver $driver; + private SqlPreprocessor $preprocessor; + private ?PDO $pdo = null; /** @var callable(array, ResultSet): array */ private $rowNormalizer = [Helpers::class, 'normalizeRow']; - - /** @var string|null */ - private $sql; - - /** @var int */ - private $transactionDepth = 0; + private ?string $sql = null; + private int $transactionDepth = 0; public function __construct( diff --git a/src/Database/Conventions/DiscoveredConventions.php b/src/Database/Conventions/DiscoveredConventions.php index 933731db9..77947bd05 100644 --- a/src/Database/Conventions/DiscoveredConventions.php +++ b/src/Database/Conventions/DiscoveredConventions.php @@ -18,8 +18,7 @@ */ class DiscoveredConventions implements Conventions { - /** @var IStructure */ - protected $structure; + protected IStructure $structure; public function __construct(IStructure $structure) diff --git a/src/Database/Conventions/StaticConventions.php b/src/Database/Conventions/StaticConventions.php index 2581039b6..0a562ed06 100644 --- a/src/Database/Conventions/StaticConventions.php +++ b/src/Database/Conventions/StaticConventions.php @@ -20,14 +20,11 @@ class StaticConventions implements Conventions { use Nette\SmartObject; - /** @var string */ - protected $primary; + protected string $primary; - /** @var string */ - protected $foreign; + protected string $foreign; - /** @var string */ - protected $table; + protected string $table; /** diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index 82cbfa5ea..be2a52ce3 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -15,11 +15,9 @@ */ class DriverException extends \PDOException { - /** @var string */ - public $queryString; + public ?string $queryString = null; - /** @var array */ - public $params; + public ?array $params = null; /** diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index d3ef00f53..08fa7650f 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -19,8 +19,7 @@ class MsSqlDriver implements Nette\Database\Driver { use Nette\SmartObject; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; public function initialize(Nette\Database\Connection $connection, array $options): void diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 32d113a74..1eaf43b44 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -24,8 +24,7 @@ class MySqlDriver implements Nette\Database\Driver ERROR_DUPLICATE_ENTRY = 1062, ERROR_DATA_TRUNCATED = 1265; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; /** diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index e79f6d7a9..72cf42235 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -19,11 +19,10 @@ class OciDriver implements Nette\Database\Driver { use Nette\SmartObject; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; - /** @var string Datetime format */ - private $fmtDateTime; + /** Datetime format */ + private string $fmtDateTime; 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 5bf2e36f7..5c6b09d98 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -19,8 +19,7 @@ class PgSqlDriver implements Nette\Database\Driver { use Nette\SmartObject; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; public function initialize(Nette\Database\Connection $connection, array $options): void diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 88c9451c6..b2942533e 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -19,11 +19,10 @@ class SqliteDriver implements Nette\Database\Driver { use Nette\SmartObject; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; - /** @var string Datetime format */ - private $fmtDateTime; + /** Datetime format */ + private string $fmtDateTime; public function initialize(Nette\Database\Connection $connection, array $options): void diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 6fde3b6be..0edc7a809 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -19,11 +19,9 @@ class SqlsrvDriver implements Nette\Database\Driver { use Nette\SmartObject; - /** @var Nette\Database\Connection */ - private $connection; + private Nette\Database\Connection $connection; - /** @var string */ - private $version; + private string $version; public function initialize(Nette\Database\Connection $connection, array $options): void diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index dd5dba0e6..08807a52d 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -20,17 +20,13 @@ class Explorer { use Nette\SmartObject; - /** @var Connection */ - private $connection; + private Connection $connection; - /** @var IStructure */ - private $structure; + private IStructure $structure; - /** @var Conventions */ - private $conventions; + private Conventions $conventions; - /** @var Nette\Caching\IStorage */ - private $cacheStorage; + private ?Nette\Caching\IStorage $cacheStorage; public function __construct( diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index aa7d0cbfd..74ffb1577 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -21,11 +21,10 @@ class Helpers { use Nette\StaticClass; - /** @var int maximum SQL length */ - public static $maxLength = 100; + /** maximum SQL length */ + public static int $maxLength = 100; - /** @var array */ - public static $typePatterns = [ + public static array $typePatterns = [ '^_' => IStructure::FIELD_TEXT, // PostgreSQL arrays '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => IStructure::FIELD_INTEGER, '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|REAL|DOUBLE( PRECISION)?|FLOAT\d*|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_FLOAT, diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 8e9767d87..265a7e814 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -20,35 +20,22 @@ class ResultSet implements \Iterator, IRowContainer { use Nette\SmartObject; - /** @var Connection */ - private $connection; - - /** @var \PDOStatement|null */ - private $pdoStatement; + private Connection $connection; + private ?\PDOStatement $pdoStatement; /** @var callable(array, ResultSet): array */ private $normalizer; - /** @var Row|false|null */ - private $lastRow; - - /** @var int */ - private $lastRowKey = -1; + private Row|false|null $lastRow = null; + private int $lastRowKey = -1; /** @var Row[] */ - private $rows; - - /** @var float */ - private $time; - - /** @var string */ - private $queryString; - - /** @var array */ - private $params; + private array $rows; - /** @var array */ - private $types; + private float $time; + private string $queryString; + private array $params; + private array $types; public function __construct( @@ -130,7 +117,7 @@ public function getRowCount(): ?int public function getColumnTypes(): array { - if ($this->types === null) { + if (!isset($this->types)) { $this->types = $this->connection->getDriver()->getColumnTypes($this->pdoStatement); } @@ -216,7 +203,7 @@ public function fetch(): ?Row $this->pdoStatement->closeCursor(); return null; - } elseif ($this->lastRow === null && count($data) !== $this->pdoStatement->columnCount()) { + } elseif (!isset($this->lastRow) && count($data) !== $this->pdoStatement->columnCount()) { $duplicates = Helpers::findDuplicates($this->pdoStatement); trigger_error("Found duplicate columns in database result set: $duplicates.", E_USER_NOTICE); } @@ -275,7 +262,7 @@ public function fetchPairs($key = null, $value = null): array */ public function fetchAll(): array { - if ($this->rows === null) { + if (!isset($this->rows)) { $this->rows = iterator_to_array($this); } diff --git a/src/Database/SqlLiteral.php b/src/Database/SqlLiteral.php index 46ec925cd..8e2e010a9 100644 --- a/src/Database/SqlLiteral.php +++ b/src/Database/SqlLiteral.php @@ -19,11 +19,9 @@ class SqlLiteral { use Nette\SmartObject; - /** @var string */ - private $value; + private string $value; - /** @var array */ - private $parameters; + private array $parameters; public function __construct(string $value, array $parameters = []) diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index c3e9b9e03..f8a7a1cc4 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -50,26 +50,22 @@ class SqlPreprocessor 'EXPLAIN' => 1, ]; - /** @var Connection */ - private $connection; + private Connection $connection; - /** @var Driver */ - private $driver; + private Driver $driver; - /** @var array of input parameters */ - private $params; + /** input parameters */ + private array $params; - /** @var array of parameters to be processed by PDO */ - private $remaining; + /** parameters to be processed by PDO */ + private array $remaining; - /** @var int */ - private $counter; + private int $counter; - /** @var bool */ - private $useParams; + private bool $useParams; - /** @var string|null values|set|and|order|items */ - private $arrayMode; + /** values|set|and|order|items */ + private ?string $arrayMode; public function __construct(Connection $connection) diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 714462348..49d3aeea5 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -19,17 +19,13 @@ class Structure implements IStructure { use Nette\SmartObject; - /** @var Connection */ - protected $connection; + protected Connection $connection; - /** @var Nette\Caching\Cache */ - protected $cache; + protected Nette\Caching\Cache $cache; - /** @var array */ - protected $structure; + protected array $structure; - /** @var bool */ - protected $isRebuilt = false; + protected bool $isRebuilt = false; public function __construct(Connection $connection, Nette\Caching\IStorage $cacheStorage) @@ -174,7 +170,7 @@ public function isRebuilt(): bool protected function needStructure(): void { - if ($this->structure !== null) { + if (isset($this->structure)) { return; } diff --git a/src/Database/Table/ActiveRow.php b/src/Database/Table/ActiveRow.php index 4a8354317..d3b627439 100644 --- a/src/Database/Table/ActiveRow.php +++ b/src/Database/Table/ActiveRow.php @@ -18,14 +18,11 @@ */ class ActiveRow implements \IteratorAggregate, IRow { - /** @var Selection */ - private $table; + private Selection $table; - /** @var array of row data */ - private $data; + private array $data; - /** @var bool */ - private $dataRefreshed = false; + private bool $dataRefreshed = false; public function __construct(array $data, Selection $table) diff --git a/src/Database/Table/GroupedSelection.php b/src/Database/Table/GroupedSelection.php index 94a394c0f..84a09f18c 100644 --- a/src/Database/Table/GroupedSelection.php +++ b/src/Database/Table/GroupedSelection.php @@ -20,17 +20,17 @@ */ class GroupedSelection extends Selection { - /** @var Selection referenced table */ - protected $refTable; + /** referenced table */ + protected Selection $refTable; - /** @var mixed current assigned referencing array */ - protected $refCacheCurrent; + /** current assigned referencing array */ + protected mixed $refCacheCurrent; - /** @var string grouping column name */ - protected $column; + /** grouping column name */ + protected string $column; - /** @var int primary key */ - protected $active; + /** primary key */ + protected int|string $active; /** diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 01ca307ce..414ac67ac 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -22,65 +22,52 @@ class Selection implements \Iterator, IRowContainer, \ArrayAccess, \Countable { use Nette\SmartObject; - /** @var Explorer */ - protected $explorer; + protected Explorer $explorer; - /** @var Explorer back compatibility */ - protected $context; + /** back compatibility */ + protected Explorer $context; + protected Conventions $conventions; + protected ?Nette\Caching\Cache $cache; + protected SqlBuilder $sqlBuilder; - /** @var Conventions */ - protected $conventions; - - /** @var Nette\Caching\Cache */ - protected $cache; - - /** @var SqlBuilder */ - protected $sqlBuilder; - - /** @var string table name */ - protected $name; + /** table name */ + protected string $name; /** @var string|string[]|null primary key field name */ - protected $primary; - - /** @var string|bool primary column sequence name, false for autodetection */ - protected $primarySequence = false; - - /** @var ActiveRow[] data read from database in [primary key => ActiveRow] format */ - protected $rows; + protected string|array|null $primary; - /** @var ActiveRow[] modifiable data in [primary key => ActiveRow] format */ - protected $data; + /** primary column sequence name, false for autodetection */ + protected string|bool|null $primarySequence = false; - /** @var bool */ - protected $dataRefreshed = false; + /** @var ActiveRow[]|null data read from database in [primary key => ActiveRow] format */ + protected ?array $rows = null; - /** @var mixed cache array of Selection and GroupedSelection prototypes */ - protected $globalRefCache; + /** @var ActiveRow[]|null modifiable data in [primary key => ActiveRow] format */ + protected ?array $data = null; - /** @var mixed */ - protected $refCache; + protected bool $dataRefreshed = false; - /** @var string|null */ - protected $generalCacheKey; + /** cache array of Selection and GroupedSelection prototypes */ + protected mixed $globalRefCache; - /** @var string|null */ - protected $specificCacheKey; + protected mixed $refCache; + protected ?string $generalCacheKey = null; + protected ?string $specificCacheKey = null; - /** @var array of [conditions => [key => ActiveRow]]; used by GroupedSelection */ - protected $aggregation = []; + /** of [conditions => [key => ActiveRow]]; used by GroupedSelection */ + protected array $aggregation = []; - /** @var array|false|null of touched columns */ - protected $accessedColumns; + /** touched columns */ + protected array|false|null $accessedColumns = null; - /** @var array|false|null of earlier touched columns */ - protected $previousAccessedColumns; + /** earlier touched columns */ + protected array|false|null $previousAccessedColumns = null; - /** @var self|null should instance observe accessed columns caching */ - protected $observeCache; + /** should instance observe accessed columns caching */ + protected ?self $observeCache = null; - /** @var array of primary key values */ - protected $keys = []; + /** of primary key values */ + protected array $keys = []; /** diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 275ebe13d..67ff0b4f5 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -25,29 +25,24 @@ class SqlBuilder { use Nette\SmartObject; - /** @var string */ - protected $tableName; + protected string $tableName; - /** @var Conventions */ - protected $conventions; + protected Conventions $conventions; - /** @var string delimited table name */ - protected $delimitedTable; + protected string $delimitedTable; - /** @var array of column to select */ - protected $select = []; + /** column to select */ + protected array $select = []; - /** @var array of where conditions */ - protected $where = []; + protected array $where = []; - /** @var array of array of join conditions */ - protected $joinCondition = []; + protected array $joinCondition = []; - /** @var array of where conditions for caching */ - protected $conditions = []; + /** where conditions for caching */ + protected array $conditions = []; - /** @var array of parameters passed to where conditions */ - protected $parameters = [ + /** parameters passed to where conditions */ + protected array $parameters = [ 'select' => [], 'joinCondition' => [], 'where' => [], @@ -56,41 +51,38 @@ class SqlBuilder 'order' => [], ]; - /** @var array or columns to order by */ - protected $order = []; + /** columns to order by */ + protected array $order = []; - /** @var int number of rows to fetch */ - protected $limit; + /** number of rows to fetch */ + protected ?int $limit = null; - /** @var int first row to fetch */ - protected $offset; + /** first row to fetch */ + protected ?int $offset = null; - /** @var string columns to grouping */ - protected $group = ''; + /** columns to grouping */ + protected string $group = ''; - /** @var string grouping condition */ - protected $having = ''; + /** grouping condition */ + protected string $having = ''; - /** @var array of reserved table names associated with chain */ - protected $reservedTableNames = []; + /** reserved table names associated with chain */ + protected array $reservedTableNames = []; - /** @var array of table aliases */ - protected $aliases = []; + /** table aliases */ + protected array $aliases = []; - /** @var string currently parsing alias for joins */ - protected $currentAlias; + /** currently parsing alias for joins */ + protected string $currentAlias = ''; - /** @var Driver */ - private $driver; + private Driver $driver; - /** @var IStructure */ - private $structure; + private IStructure $structure; - /** @var array */ - private $cacheTableList; + private array $cacheTableList = []; - /** @var array of expanding joins */ - private $expandingJoins = []; + /** expanding joins */ + private array $expandingJoins = []; public function __construct(string $tableName, Explorer $explorer) diff --git a/tests/Database/Explorer/Explorer.cache.observer2.phpt b/tests/Database/Explorer/Explorer.cache.observer2.phpt index 42bf9f30e..010a22306 100644 --- a/tests/Database/Explorer/Explorer.cache.observer2.phpt +++ b/tests/Database/Explorer/Explorer.cache.observer2.phpt @@ -17,7 +17,7 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/../files/{$driverN class CacheMock extends MemoryStorage { - public $writes = 0; + public int $writes = 0; public function write(string $key, $data, array $dependencies): void diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index 0a6bbf38b..6c70d22ea 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -18,7 +18,7 @@ class StructureMock extends Structure { protected function needStructure(): void { - if (!$this->structure) { + if (!isset($this->structure)) { $this->structure = $this->loadStructure(); } } @@ -30,17 +30,13 @@ class StructureMock extends Structure */ class StructureTestCase extends TestCase { - /** @var MockInterface */ - private $connection; + private MockInterface $connection; - /** @var MockInterface */ - private $driver; + private MockInterface $driver; - /** @var MockInterface */ - private $storage; + private MockInterface $storage; - /** @var Structure */ - private $structure; + private Structure $structure; protected function setUp() diff --git a/tests/Database/Structure.schemas.phpt b/tests/Database/Structure.schemas.phpt index e840c1747..6152b2c95 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -18,7 +18,7 @@ class StructureMock extends Structure { protected function needStructure(): void { - if (!$this->structure) { + if (!isset($this->structure)) { $this->structure = $this->loadStructure(); } } @@ -30,17 +30,13 @@ class StructureMock extends Structure */ class StructureSchemasTestCase extends TestCase { - /** @var MockInterface */ - private $connection; + private MockInterface $connection; - /** @var MockInterface */ - private $driver; + private MockInterface $driver; - /** @var MockInterface */ - private $storage; + private MockInterface $storage; - /** @var Structure */ - private $structure; + private Structure $structure; protected function setUp() From 51f004db1eeedded6cf47fe42ebbb72a5325e062 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Dec 2021 18:45:32 +0100 Subject: [PATCH 09/35] added PHP 8 typehints --- src/Database/Connection.php | 8 +- src/Database/Conventions.php | 3 +- .../Conventions/DiscoveredConventions.php | 2 +- src/Database/Driver.php | 1 - src/Database/DriverException.php | 10 +-- src/Database/Explorer.php | 8 +- src/Database/IStructure.php | 2 +- src/Database/ResultSet.php | 12 +-- src/Database/Row.php | 4 +- src/Database/Structure.php | 2 +- src/Database/Table/ActiveRow.php | 8 +- src/Database/Table/GroupedSelection.php | 20 ++--- src/Database/Table/Selection.php | 88 +++++++------------ 13 files changed, 54 insertions(+), 114 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index c5d728700..1a8cae1a9 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -179,10 +179,7 @@ public function rollBack(): void } - /** - * @return mixed - */ - public function transaction(callable $callback) + public function transaction(callable $callback): mixed { if ($this->transactionDepth === 0) { $this->beginTransaction(); @@ -270,9 +267,8 @@ public function fetch(string $sql, ...$params): ?Row /** * Shortcut for query()->fetchField() * @param literal-string $sql - * @return mixed */ - public function fetchField(string $sql, ...$params) + public function fetchField(string $sql, ...$params): mixed { return $this->query($sql, ...$params)->fetchField(); } diff --git a/src/Database/Conventions.php b/src/Database/Conventions.php index 19ab567af..3e7a68710 100644 --- a/src/Database/Conventions.php +++ b/src/Database/Conventions.php @@ -16,9 +16,8 @@ interface Conventions { /** * Returns primary key for table. - * @return string|string[]|null */ - function getPrimary(string $table); + function getPrimary(string $table): string|array|null; /** * Returns referenced table & referenced column. diff --git a/src/Database/Conventions/DiscoveredConventions.php b/src/Database/Conventions/DiscoveredConventions.php index 77947bd05..6780b8f2e 100644 --- a/src/Database/Conventions/DiscoveredConventions.php +++ b/src/Database/Conventions/DiscoveredConventions.php @@ -27,7 +27,7 @@ public function __construct(IStructure $structure) } - public function getPrimary(string $table) + public function getPrimary(string $table): string|array|null { return $this->structure->getPrimaryKey($table); } diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 6dbc14856..56b38688f 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -55,7 +55,6 @@ function formatLike(string $value, int $pos): string; /** * Injects LIMIT/OFFSET to the SQL query. - * @param string $sql query that will be modified. */ function applyLimit(string &$sql, ?int $limit, ?int $offset): void; diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index be2a52ce3..008ce0bb1 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -20,10 +20,7 @@ class DriverException extends \PDOException public ?array $params = null; - /** - * @return static - */ - public static function from(\PDOException $src) + public static function from(\PDOException $src): static { $e = new static($src->message, 0, $src); $e->file = $src->file; @@ -42,10 +39,7 @@ public static function from(\PDOException $src) } - /** - * @return int|string|null Driver-specific error code - */ - public function getDriverCode() + public function getDriverCode(): int|string|null { return $this->errorInfo[1] ?? null; } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 08807a52d..53fac3f09 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -60,10 +60,7 @@ public function rollBack(): void } - /** - * @return mixed - */ - public function transaction(callable $callback) + public function transaction(callable $callback): mixed { return $this->connection->transaction(fn() => $callback($this)); } @@ -132,9 +129,8 @@ public function fetch(string $sql, ...$params): ?Row /** * Shortcut for query()->fetchField() * @param literal-string $sql - * @return mixed */ - public function fetchField(string $sql, ...$params) + public function fetchField(string $sql, ...$params): mixed { return $this->connection->query($sql, ...$params)->fetchField(); } diff --git a/src/Database/IStructure.php b/src/Database/IStructure.php index 8aa1b38e7..d08db66b0 100644 --- a/src/Database/IStructure.php +++ b/src/Database/IStructure.php @@ -41,7 +41,7 @@ function getColumns(string $table): array; * Returns table primary key. * @return string|string[]|null */ - function getPrimaryKey(string $table); + function getPrimaryKey(string $table): string|array|null; /** * Returns autoincrement primary key name. diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 265a7e814..302c95225 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -163,15 +163,13 @@ public function rewind(): void } - #[\ReturnTypeWillChange] - public function current() + public function current(): mixed { return $this->lastRow; } - #[\ReturnTypeWillChange] - public function key() + public function key(): mixed { return $this->lastRowKey; } @@ -222,9 +220,8 @@ public function fetch(): ?Row /** * Fetches single field. - * @return mixed */ - public function fetchField($column = 0) + public function fetchField(int $column = 0): mixed { if (func_num_args()) { trigger_error(__METHOD__ . '() argument is deprecated.', E_USER_DEPRECATED); @@ -250,7 +247,7 @@ public function fetchFields(): ?array * @param string|int $key column name used for an array key or null for numeric index * @param string|int $value column name used for an array value or null for the whole row */ - public function fetchPairs($key = null, $value = null): array + public function fetchPairs(string|int|null $key = null, string|int|null $value = null): array { return Helpers::toPairs($this->fetchAll(), $key, $value); } @@ -272,7 +269,6 @@ public function fetchAll(): array /** * Fetches all rows and returns associative tree. - * @param string $path associative descriptor */ public function fetchAssoc(string $path): array { diff --git a/src/Database/Row.php b/src/Database/Row.php index b57b728db..1d7187d8e 100644 --- a/src/Database/Row.php +++ b/src/Database/Row.php @@ -33,10 +33,8 @@ public function __isset($key) /** * Returns a item. * @param string|int $key key or index - * @return mixed */ - #[\ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet($key): mixed { if (is_int($key)) { $arr = array_slice((array) $this, $key, 1); diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 49d3aeea5..7460daa40 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -54,7 +54,7 @@ public function getColumns(string $table): array /** * @return string|string[]|null */ - public function getPrimaryKey(string $table) + public function getPrimaryKey(string $table): string|array|null { $this->needStructure(); $table = $this->resolveFQTableName($table); diff --git a/src/Database/Table/ActiveRow.php b/src/Database/Table/ActiveRow.php index d3b627439..8e0517951 100644 --- a/src/Database/Table/ActiveRow.php +++ b/src/Database/Table/ActiveRow.php @@ -67,7 +67,7 @@ public function toArray(): array * Returns primary key value. * @return mixed possible int, string, array, object (Nette\Utils\DateTime) */ - public function getPrimary(bool $throw = true) + public function getPrimary(bool $throw = true): mixed { $primary = $this->table->getPrimary($throw); if ($primary === null) { @@ -219,10 +219,8 @@ public function offsetSet($column, $value): void /** * Returns value of column. * @param string $column - * @return mixed */ - #[\ReturnTypeWillChange] - public function offsetGet($column) + public function offsetGet($column): mixed { return $this->__get($column); } @@ -258,7 +256,7 @@ public function __set($column, $value) * @return ActiveRow|mixed * @throws Nette\MemberAccessException */ - public function &__get(string $key) + public function &__get(string $key): mixed { if ($this->accessColumn($key)) { return $this->data[$key]; diff --git a/src/Database/Table/GroupedSelection.php b/src/Database/Table/GroupedSelection.php index 84a09f18c..fbc3a31f8 100644 --- a/src/Database/Table/GroupedSelection.php +++ b/src/Database/Table/GroupedSelection.php @@ -54,19 +54,15 @@ public function __construct( * Sets active group. * @internal * @param int|string $active primary key of grouped rows - * @return static */ - public function setActive($active) + public function setActive(int|string $active): static { $this->active = $active; return $this; } - /** - * @return static - */ - public function select($columns, ...$params) + public function select($columns, ...$params): static { if (!$this->sqlBuilder->getSelect()) { $this->sqlBuilder->addSelect("$this->name.$this->column"); @@ -76,10 +72,7 @@ public function select($columns, ...$params) } - /** - * @return static - */ - public function order(string $columns, ...$params) + public function order(string $columns, ...$params): static { if (!$this->sqlBuilder->getOrder()) { // improve index utilization @@ -93,10 +86,7 @@ public function order(string $columns, ...$params) /********************* aggregations ****************d*g**/ - /** - * @return mixed - */ - public function aggregation(string $function, ?string $groupFunction = null) + public function aggregation(string $function, ?string $groupFunction = null): mixed { $aggregation = &$this->getRefTable($refPath)->aggregation[$refPath . $function . $this->sqlBuilder->getSelectQueryHash($this->getPreviousAccessedColumns())]; @@ -241,7 +231,7 @@ protected function emptyResultSet(bool $saveCache = true, bool $deleteRererenced /********************* manipulation ****************d*g**/ - public function insert(iterable $data) + public function insert(iterable $data): ActiveRow|array|int|bool { if ($data instanceof \Traversable && !$data instanceof Selection) { $data = iterator_to_array($data); diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 414ac67ac..bedb3368a 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -113,7 +113,7 @@ public function getName(): string /** * @return string|string[]|null */ - public function getPrimary(bool $throw = true) + public function getPrimary(bool $throw = true): string|array|null { if ($this->primary === null && $throw) { throw new \LogicException("Table '{$this->name}' does not have a primary key."); @@ -133,10 +133,7 @@ public function getPrimarySequence(): ?string } - /** - * @return static - */ - public function setPrimarySequence(string $sequence) + public function setPrimarySequence(string $sequence): static { $this->primarySequence = $sequence; return $this; @@ -152,9 +149,8 @@ public function getSql(): string /** * Loads cache of previous accessed columns and returns it. * @internal - * @return array|bool */ - public function getPreviousAccessedColumns() + public function getPreviousAccessedColumns(): array|bool { if ($this->cache && $this->previousAccessedColumns === null) { $this->accessedColumns = $this->previousAccessedColumns = $this->cache->load($this->getGeneralCacheKey()); @@ -181,9 +177,8 @@ public function getSqlBuilder(): SqlBuilder /** * Returns row specified by primary key. - * @param mixed $key primary key */ - public function get($key): ?ActiveRow + public function get(mixed $key): ?ActiveRow { $clone = clone $this; return $clone->wherePrimary($key)->fetch(); @@ -204,10 +199,9 @@ public function fetch(): ?ActiveRow /** * Fetches single field. - * @return mixed * @deprecated */ - public function fetchField(?string $column = null) + public function fetchField(?string $column = null): mixed { if ($column) { $this->select($column); @@ -227,7 +221,7 @@ public function fetchField(?string $column = null) * @param string|int $key column name used for an array key or null for numeric index * @param string|int $value column name used for an array value or null for the whole row */ - public function fetchPairs($key = null, $value = null): array + public function fetchPairs(string|int|null $key = null, string|int|null $value = null): array { return Nette\Database\Helpers::toPairs($this->fetchAll(), $key, $value); } @@ -245,7 +239,6 @@ public function fetchAll(): array /** * Fetches all rows and returns associative tree. - * @param string $path associative descriptor */ public function fetchAssoc(string $path): array { @@ -260,9 +253,8 @@ public function fetchAssoc(string $path): array /** * Adds select clause, more calls appends to the end. * @param string|string[] $columns for example "column, MD5(column) AS column_md5" - * @return static */ - public function select($columns, ...$params) + public function select($columns, ...$params): static { $this->emptyResultSet(); $this->sqlBuilder->addSelect($columns, ...$params); @@ -272,10 +264,8 @@ public function select($columns, ...$params) /** * Adds condition for primary key. - * @param mixed $key - * @return static */ - public function wherePrimary($key) + public function wherePrimary(mixed $key): static { if (is_array($this->primary) && Nette\Utils\Arrays::isList($key)) { if (isset($key[0]) && is_array($key[0])) { @@ -298,9 +288,8 @@ public function wherePrimary($key) /** * Adds where condition, more calls appends with AND. * @param string|array $condition possibly containing ? - * @return static */ - public function where($condition, ...$params) + public function where(string|array $condition, ...$params): static { $this->condition($condition, $params); return $this; @@ -311,9 +300,8 @@ public function where($condition, ...$params) * Adds ON condition when joining specified table, more calls appends with AND. * @param string $tableChain table chain or table alias for which you need additional left join condition * @param string $condition possibly containing ? - * @return static */ - public function joinWhere(string $tableChain, string $condition, ...$params) + public function joinWhere(string $tableChain, string $condition, ...$params): static { $this->condition($condition, $params, $tableChain); return $this; @@ -347,10 +335,9 @@ protected function condition($condition, array $params, $tableChain = null): voi * Adds where condition using the OR operator between parameters. * More calls appends with AND. * @param array $parameters ['column1' => 1, 'column2 > ?' => 2, 'full condition'] - * @return static * @throws Nette\InvalidArgumentException */ - public function whereOr(array $parameters) + public function whereOr(array $parameters): static { if (count($parameters) < 2) { return $this->where($parameters); @@ -383,9 +370,8 @@ public function whereOr(array $parameters) /** * Adds order clause, more calls appends to the end. * @param string $columns for example 'column1, column2 DESC' - * @return static */ - public function order(string $columns, ...$params) + public function order(string $columns, ...$params): static { $this->emptyResultSet(); $this->sqlBuilder->addOrder($columns, ...$params); @@ -395,9 +381,8 @@ public function order(string $columns, ...$params) /** * Sets limit clause, more calls rewrite old values. - * @return static */ - public function limit(?int $limit, ?int $offset = null) + public function limit(?int $limit, ?int $offset = null): static { $this->emptyResultSet(); $this->sqlBuilder->setLimit($limit, $offset); @@ -407,9 +392,8 @@ public function limit(?int $limit, ?int $offset = null) /** * Sets offset using page number, more calls rewrite old values. - * @return static */ - public function page(int $page, int $itemsPerPage, &$numOfPages = null) + public function page(int $page, int $itemsPerPage, &$numOfPages = null): static { if (func_num_args() > 2) { $numOfPages = (int) ceil($this->count('*') / $itemsPerPage); @@ -425,9 +409,8 @@ public function page(int $page, int $itemsPerPage, &$numOfPages = null) /** * Sets group clause, more calls rewrite old value. - * @return static */ - public function group(string $columns, ...$params) + public function group(string $columns, ...$params): static { $this->emptyResultSet(); $this->sqlBuilder->setGroup($columns, ...$params); @@ -437,9 +420,8 @@ public function group(string $columns, ...$params) /** * Sets having clause, more calls rewrite old value. - * @return static */ - public function having(string $having, ...$params) + public function having(string $having, ...$params): static { $this->emptyResultSet(); $this->sqlBuilder->setHaving($having, ...$params); @@ -449,9 +431,8 @@ public function having(string $having, ...$params) /** * Aliases table. Example ':book:book_tag.tag', 'tg' - * @return static */ - public function alias(string $tableChain, string $alias) + public function alias(string $tableChain, string $alias): static { $this->sqlBuilder->addAlias($tableChain, $alias); return $this; @@ -464,9 +445,8 @@ public function alias(string $tableChain, string $alias) /** * Executes aggregation function. * @param string $function select call in "FUNCTION(column)" format - * @return mixed */ - public function aggregation(string $function, ?string $groupFunction = null) + public function aggregation(string $function, ?string $groupFunction = null): mixed { $selection = $this->createSelectionInstance(); $selection->getSqlBuilder()->importConditions($this->getSqlBuilder()); @@ -500,9 +480,8 @@ public function count(?string $column = null): int /** * Returns minimum value from a column. - * @return mixed */ - public function min(string $column) + public function min(string $column): mixed { return $this->aggregation("MIN($column)", 'MIN'); } @@ -510,9 +489,8 @@ public function min(string $column) /** * Returns maximum value from a column. - * @return mixed */ - public function max(string $column) + public function max(string $column): mixed { return $this->aggregation("MAX($column)", 'MAX'); } @@ -520,9 +498,8 @@ public function max(string $column) /** * Returns sum of values in a column. - * @return mixed */ - public function sum(string $column) + public function sum(string $column): mixed { return $this->aggregation("SUM($column)", 'SUM'); } @@ -648,9 +625,8 @@ protected function saveCacheState(): void /** * Returns Selection parent for caching. - * @return static */ - protected function getRefTable(&$refPath) + protected function getRefTable(&$refPath): self { return $this; } @@ -797,7 +773,7 @@ public function getDataRefreshed(): bool * @param array|\Traversable|Selection $data [$column => $value]|\Traversable|Selection for INSERT ... SELECT * @return ActiveRow|int|bool Returns ActiveRow or number of affected rows for Selection or table without primary key */ - public function insert(iterable $data) + public function insert(iterable $data): ActiveRow|array|int|bool { //should be called before query for not to spoil PDO::lastInsertId $primarySequenceName = $this->getPrimarySequence(); @@ -915,7 +891,7 @@ public function delete(): int * Returns referenced row. * @return ActiveRow|false|null null if the row does not exist, false if the relationship does not exist */ - public function getReferencedTable(ActiveRow $row, ?string $table, ?string $column = null) + public function getReferencedTable(ActiveRow $row, ?string $table, ?string $column = null): ActiveRow|false|null { if (!$column) { $belongsTo = $this->conventions->getBelongsToReference($this->name, $table); @@ -963,7 +939,11 @@ public function getReferencedTable(ActiveRow $row, ?string $table, ?string $colu * Returns referencing rows. * @param int|string $active primary key */ - public function getReferencingTable(string $table, ?string $column = null, $active = null): ?GroupedSelection + public function getReferencingTable( + string $table, + ?string $column = null, + int|string|null $active = null, + ): ?GroupedSelection { if (strpos($table, '.') !== false) { [$table, $column] = explode('.', $table); @@ -999,9 +979,7 @@ public function rewind(): void } - /** @return ActiveRow|false */ - #[\ReturnTypeWillChange] - public function current() + public function current(): ActiveRow|false { return ($key = current($this->keys)) !== false ? $this->data[$key] @@ -1009,11 +987,7 @@ public function current() } - /** - * @return string|int row ID - */ - #[\ReturnTypeWillChange] - public function key() + public function key(): string|int { return current($this->keys); } From 5eb8f29d62964a12de0b188c08b108c5f6a08c73 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Dec 2021 18:45:51 +0100 Subject: [PATCH 10/35] uses new Nette interfaces --- src/Database/Explorer.php | 4 ++-- src/Database/Structure.php | 2 +- src/Database/Table/GroupedSelection.php | 2 +- src/Database/Table/Selection.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index 53fac3f09..83f7ca7f1 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -26,14 +26,14 @@ class Explorer private Conventions $conventions; - private ?Nette\Caching\IStorage $cacheStorage; + private ?Nette\Caching\Storage $cacheStorage; public function __construct( Connection $connection, Structure $structure, ?Conventions $conventions = null, - ?Nette\Caching\IStorage $cacheStorage = null, + ?Nette\Caching\Storage $cacheStorage = null, ) { $this->connection = $connection; $this->structure = $structure; diff --git a/src/Database/Structure.php b/src/Database/Structure.php index 7460daa40..ebee29227 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -28,7 +28,7 @@ class Structure implements IStructure protected bool $isRebuilt = false; - public function __construct(Connection $connection, Nette\Caching\IStorage $cacheStorage) + public function __construct(Connection $connection, Nette\Caching\Storage $cacheStorage) { $this->connection = $connection; $this->cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Database.Structure.' . md5($this->connection->getDsn())); diff --git a/src/Database/Table/GroupedSelection.php b/src/Database/Table/GroupedSelection.php index fbc3a31f8..0f621bdcd 100644 --- a/src/Database/Table/GroupedSelection.php +++ b/src/Database/Table/GroupedSelection.php @@ -42,7 +42,7 @@ public function __construct( string $tableName, string $column, Selection $refTable, - ?Nette\Caching\IStorage $cacheStorage = null, + ?Nette\Caching\Storage $cacheStorage = null, ) { $this->refTable = $refTable; $this->column = $column; diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index bedb3368a..4a42c3c7f 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -77,7 +77,7 @@ public function __construct( Explorer $explorer, Conventions $conventions, string $tableName, - ?Nette\Caching\IStorage $cacheStorage = null, + ?Nette\Caching\Storage $cacheStorage = null, ) { $this->explorer = $this->context = $explorer; $this->conventions = $conventions; From 00a49e3af66f53c4e09710aa4c314aa311a5b7ea Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 2 Mar 2021 14:54:52 +0100 Subject: [PATCH 11/35] removed deprecated stuff --- src/Database/ResultSet.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 302c95225..b3e8662d2 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -221,14 +221,10 @@ public function fetch(): ?Row /** * Fetches single field. */ - public function fetchField(int $column = 0): mixed + public function fetchField(): mixed { - if (func_num_args()) { - trigger_error(__METHOD__ . '() argument is deprecated.', E_USER_DEPRECATED); - } - $row = $this->fetch(); - return $row ? $row[$column] : null; + return $row ? $row[0] : null; } From 56786bee479d6eff991b5b5ecdf0bee10b812b5a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 14:05:47 +0100 Subject: [PATCH 12/35] deprecated methods trigger notices --- src/Database/Connection.php | 2 ++ src/Database/Explorer.php | 1 + src/Database/Helpers.php | 2 ++ src/Database/ResultSet.php | 2 +- tests/Database/Connection.query.phpt | 2 +- tests/Database/Explorer.query.phpt | 2 +- 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 1a8cae1a9..089b1581e 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -116,6 +116,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(string $sql, ...$params): ResultSet /** @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 83f7ca7f1..a9c337257 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -85,6 +85,7 @@ public function query(string $sql, ...$params): ResultSet /** @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 74ffb1577..61478beed 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -301,6 +301,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); } @@ -315,6 +316,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 b3e8662d2..65b79eaa2 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -78,7 +78,7 @@ public function __construct( /** @deprecated */ public function getConnection(): Connection { - return $this->connection; + throw new Nette\DeprecatedException(__METHOD__ . '() is deprecated.'); } diff --git a/tests/Database/Connection.query.phpt b/tests/Database/Connection.query.phpt index b7fcfa8d8..d8c530aec 100644 --- a/tests/Database/Connection.query.phpt +++ b/tests/Database/Connection.query.phpt @@ -31,7 +31,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 ef984fb8a..dd1e3a7a5 100644 --- a/tests/Database/Explorer.query.phpt +++ b/tests/Database/Explorer.query.phpt @@ -30,7 +30,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 0a07e24930162f33c3bbeb0e61a73d35a6ede15f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 20 Jan 2022 19:24:38 +0100 Subject: [PATCH 13/35] Helpers::normalizeRow() moved to RowNormalizer --- src/Database/Connection.php | 3 +- src/Database/Helpers.php | 39 ++-------------------- src/Database/RowNormalizer.php | 61 ++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 src/Database/RowNormalizer.php diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 089b1581e..d736a07bc 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -35,7 +35,7 @@ class Connection private ?PDO $pdo = null; /** @var callable(array, ResultSet): array */ - private $rowNormalizer = [Helpers::class, 'normalizeRow']; + private $rowNormalizer; private ?string $sql = null; private int $transactionDepth = 0; @@ -50,6 +50,7 @@ public function __construct( ) { $this->params = [$dsn, $user, $password]; $this->options = (array) $options; + $this->rowNormalizer = new RowNormalizer; if (empty($options['lazy'])) { $this->connect(); diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 61478beed..87c6a931e 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -200,45 +200,10 @@ public static function detectType(string $type): string } - /** @internal */ + /** @deprecated use RowNormalizer::normalizeRow() */ public static function normalizeRow(array $row, ResultSet $resultSet): array { - foreach ($resultSet->getColumnTypes() as $key => $type) { - $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) { - if (is_string($value) && ($pos = strpos($value, '.')) !== false) { - $value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.'); - } - - $row[$key] = (float) $value; - - } elseif ($type === IStructure::FIELD_BOOL) { - $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; - - } elseif ( - $type === IStructure::FIELD_DATETIME - || $type === IStructure::FIELD_DATE - || $type === IStructure::FIELD_TIME - ) { - $row[$key] = new Nette\Utils\DateTime($value); - - } 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] = Nette\Utils\DateTime::from($value); - } - } - - return $row; + return (new RowNormalizer)($row, $resultSet); } diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php new file mode 100644 index 000000000..59ebe7159 --- /dev/null +++ b/src/Database/RowNormalizer.php @@ -0,0 +1,61 @@ +getColumnTypes() as $key => $type) { + $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) { + if (is_string($value) && ($pos = strpos($value, '.')) !== false) { + $value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.'); + } + + $row[$key] = (float) $value; + + } elseif ($type === IStructure::FIELD_BOOL) { + $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; + + } elseif ( + $type === IStructure::FIELD_DATETIME + || $type === IStructure::FIELD_DATE + || $type === IStructure::FIELD_TIME + ) { + $row[$key] = new Nette\Utils\DateTime($value); + + } 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] = Nette\Utils\DateTime::from($value); + } + } + + return $row; + } +} From 12af7c891921952bf2ff857e9ab132df068d51fc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 20 Jan 2022 19:24:55 +0100 Subject: [PATCH 14/35] RowNormalizer: refactoring --- src/Database/RowNormalizer.php | 58 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php index 59ebe7159..42c6ab020 100644 --- a/src/Database/RowNormalizer.php +++ b/src/Database/RowNormalizer.php @@ -22,40 +22,50 @@ final class RowNormalizer public function __invoke(array $row, ResultSet $resultSet): array { foreach ($resultSet->getColumnTypes() as $key => $type) { - $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; + $row[$key] = $this->normalizeField($row[$key], $type); + } + + return $row; + } + + + public function normalizeField(mixed $value, string $type): mixed + { + if ($value === null || $value === false) { + return $value; + } + + switch ($type) { + case IStructure::FIELD_INTEGER: + return is_float($tmp = $value * 1) ? $value : $tmp; - } elseif ($type === IStructure::FIELD_FLOAT) { + case IStructure::FIELD_FLOAT: if (is_string($value) && ($pos = strpos($value, '.')) !== false) { $value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.'); } - $row[$key] = (float) $value; + return (float) $value; - } elseif ($type === IStructure::FIELD_BOOL) { - $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; + case IStructure::FIELD_BOOL: + return $value && $value !== 'f' && $value !== 'F'; - } elseif ( - $type === IStructure::FIELD_DATETIME - || $type === IStructure::FIELD_DATE - || $type === IStructure::FIELD_TIME - ) { - $row[$key] = new Nette\Utils\DateTime($value); + case IStructure::FIELD_DATETIME: + case IStructure::FIELD_DATE: + case IStructure::FIELD_TIME: + return new Nette\Utils\DateTime($value); - } elseif ($type === IStructure::FIELD_TIME_INTERVAL) { + case 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]; + $di = new \DateInterval("PT$m[2]H$m[3]M$m[4]S"); + $di->f = isset($m[5]) ? (float) $m[5] : 0.0; + $di->invert = (int) (bool) $m[1]; + return $di; - } elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) { - $row[$key] = Nette\Utils\DateTime::from($value); - } - } + case IStructure::FIELD_UNIX_TIMESTAMP: + return Nette\Utils\DateTime::from($value); - return $row; + default: + return $value; + } } } From fcba1ac1257a91b96632362d37358adef08348d1 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 8 Mar 2021 16:04:54 +0100 Subject: [PATCH 15/35] returns date-time as immutable object Nette\Database\DateTime (BC break) [Closes #270] --- src/Database/DateTime.php | 49 +++++++++++++++++++ src/Database/RowNormalizer.php | 4 +- src/Database/Table/ActiveRow.php | 2 +- tests/Database/DateTime.phpt | 23 +++++++++ .../Database/Explorer/Selection.insert().phpt | 2 +- tests/Database/Explorer/bugs/bug216.phpt | 2 +- .../ResultSet.normalizeRow.mysql.phpt | 2 +- .../ResultSet.normalizeRow.postgre.phpt | 2 +- .../ResultSet.normalizeRow.sqlite.phpt | 2 +- .../ResultSet.normalizeRow.sqlsrv.phpt | 6 +-- 10 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 src/Database/DateTime.php create mode 100644 tests/Database/DateTime.phpt diff --git a/src/Database/DateTime.php b/src/Database/DateTime.php new file mode 100644 index 000000000..bfa90e726 --- /dev/null +++ b/src/Database/DateTime.php @@ -0,0 +1,49 @@ +setTimezone(new \DateTimeZone(date_default_timezone_get())) + ->format('Y-m-d H:i:s.u'); + } + parent::__construct($time); + } + + + /** + * Returns JSON representation in ISO 8601 (used by JavaScript). + */ + public function jsonSerialize(): string + { + return $this->format('c'); + } + + + /** + * Returns the date and time in the format 'Y-m-d H:i:s'. + */ + public function __toString(): string + { + return $this->format('Y-m-d H:i:s'); + } +} diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php index 42c6ab020..f160b6399 100644 --- a/src/Database/RowNormalizer.php +++ b/src/Database/RowNormalizer.php @@ -52,7 +52,7 @@ public function normalizeField(mixed $value, string $type): mixed case IStructure::FIELD_DATETIME: case IStructure::FIELD_DATE: case IStructure::FIELD_TIME: - return new Nette\Utils\DateTime($value); + return new DateTime($value); case IStructure::FIELD_TIME_INTERVAL: preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m); @@ -62,7 +62,7 @@ public function normalizeField(mixed $value, string $type): mixed return $di; case IStructure::FIELD_UNIX_TIMESTAMP: - return Nette\Utils\DateTime::from($value); + return new DateTime($value); default: return $value; diff --git a/src/Database/Table/ActiveRow.php b/src/Database/Table/ActiveRow.php index 8e0517951..7c3ec200a 100644 --- a/src/Database/Table/ActiveRow.php +++ b/src/Database/Table/ActiveRow.php @@ -65,7 +65,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/DateTime.phpt b/tests/Database/DateTime.phpt new file mode 100644 index 000000000..8c3c5503e --- /dev/null +++ b/tests/Database/DateTime.phpt @@ -0,0 +1,23 @@ +getTimestamp()); + +Assert::same(is_int(2_544_000_000) ? 2_544_000_000 : '2544000000', (new DateTime(2_544_000_000))->getTimestamp()); // 64 bit + +// to string +Assert::same('1978-01-23 11:40:00', (string) new DateTime('1978-01-23 11:40')); + +// JSON +Assert::same('"1978-01-23T11:40:00+01:00"', json_encode(new DateTime('1978-01-23 11:40'))); diff --git a/tests/Database/Explorer/Selection.insert().phpt b/tests/Database/Explorer/Selection.insert().phpt index a303a6685..2507f2564 100644 --- a/tests/Database/Explorer/Selection.insert().phpt +++ b/tests/Database/Explorer/Selection.insert().phpt @@ -22,7 +22,7 @@ $book = $explorer->table('author')->insert([ // id = 14 Assert::equal('eddard stark', $book->name); -Assert::equal(new Nette\Utils\DateTime('2011-11-11'), $book->born); +Assert::equal(new Nette\Database\DateTime('2011-11-11'), $book->born); $books = $explorer->table('book'); diff --git a/tests/Database/Explorer/bugs/bug216.phpt b/tests/Database/Explorer/bugs/bug216.phpt index fe775ef36..1e13a0593 100644 --- a/tests/Database/Explorer/bugs/bug216.phpt +++ b/tests/Database/Explorer/bugs/bug216.phpt @@ -43,4 +43,4 @@ $book = $explorer->table('author')->insert([ Assert::type(Nette\Database\Table\ActiveRow::class, $book); Assert::equal('eddard stark', $book->name); -Assert::equal(new Nette\Utils\DateTime('2011-11-11'), $book->born); +Assert::equal(new Nette\Database\DateTime('2011-11-11'), $book->born); diff --git a/tests/Database/ResultSet.normalizeRow.mysql.phpt b/tests/Database/ResultSet.normalizeRow.mysql.phpt index 35a02a313..3db654228 100644 --- a/tests/Database/ResultSet.normalizeRow.mysql.phpt +++ b/tests/Database/ResultSet.normalizeRow.mysql.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Utils\DateTime; +use Nette\Database\DateTime; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection diff --git a/tests/Database/ResultSet.normalizeRow.postgre.phpt b/tests/Database/ResultSet.normalizeRow.postgre.phpt index 2266c2469..30a58776e 100644 --- a/tests/Database/ResultSet.normalizeRow.postgre.phpt +++ b/tests/Database/ResultSet.normalizeRow.postgre.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Utils\DateTime; +use Nette\Database\DateTime; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection diff --git a/tests/Database/ResultSet.normalizeRow.sqlite.phpt b/tests/Database/ResultSet.normalizeRow.sqlite.phpt index 58a897812..916ef9f87 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlite.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlite.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Utils\DateTime; +use Nette\Database\DateTime; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection diff --git a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt index e93ef75e1..19ed59e46 100644 --- a/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt +++ b/tests/Database/ResultSet.normalizeRow.sqlsrv.phpt @@ -7,7 +7,7 @@ declare(strict_types=1); -use Nette\Utils\DateTime; +use Nette\Database\DateTime; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection @@ -131,13 +131,13 @@ function isTimestamp($str) $row = (array) $connection->query('SELECT [datetimeoffset], CAST([sql_variant] AS int) AS [sql_variant], [timestamp] FROM types2 WHERE id = 1')->fetch(); -Assert::type('DateTime', $row['datetimeoffset']); +Assert::type(DateTime::class, $row['datetimeoffset']); Assert::same($row['datetimeoffset']->format('Y-m-d H:i:s P'), '2012-10-13 10:10:10 +02:00'); Assert::same($row['sql_variant'], 123456); Assert::true(isTimestamp($row['timestamp'])); $row = (array) $connection->query('SELECT [datetimeoffset], CAST([sql_variant] AS varchar) AS [sql_variant], [timestamp] FROM types2 WHERE id = 2')->fetch(); -Assert::type('DateTime', $row['datetimeoffset']); +Assert::type(DateTime::class, $row['datetimeoffset']); Assert::same($row['datetimeoffset']->format('Y-m-d H:i:s P'), '0001-01-01 00:00:00 +00:00'); Assert::same($row['sql_variant'], 'abcd'); Assert::true(isTimestamp($row['timestamp'])); From b23a0723d592274279763857db1153a6187240b1 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 13:22:21 +0100 Subject: [PATCH 16/35] normalizeRow coverts zero-date 0000-00-00 to NULL (BC break) --- src/Database/RowNormalizer.php | 4 +++- tests/Database/ResultSet.normalizeRow.mysql.phpt | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php index f160b6399..37708bf90 100644 --- a/src/Database/RowNormalizer.php +++ b/src/Database/RowNormalizer.php @@ -52,7 +52,9 @@ public function normalizeField(mixed $value, string $type): mixed case IStructure::FIELD_DATETIME: case IStructure::FIELD_DATE: case IStructure::FIELD_TIME: - return new DateTime($value); + return $value && !str_starts_with((string) $value, '0000-00') + ? new DateTime($value) + : null; case IStructure::FIELD_TIME_INTERVAL: preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m); diff --git a/tests/Database/ResultSet.normalizeRow.mysql.phpt b/tests/Database/ResultSet.normalizeRow.mysql.phpt index 3db654228..c69f5f415 100644 --- a/tests/Database/ResultSet.normalizeRow.mysql.phpt +++ b/tests/Database/ResultSet.normalizeRow.mysql.phpt @@ -62,10 +62,10 @@ Assert::equal([ 'decimal2' => 0.5, 'float' => 0.5, 'double' => 0.5, - 'date' => new DateTime('0000-00-00 00:00:00'), + 'date' => null, 'time' => new DateInterval('P0D'), - 'datetime' => new DateTime('0000-00-00 00:00:00'), - 'timestamp' => new DateTime('0000-00-00 00:00:00'), + 'datetime' => null, + 'timestamp' => null, 'year' => 2000, 'char' => '', 'varchar' => '', From 0fa68787c551070d62ed8702d9450ba5910124fc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 15:32:56 +0100 Subject: [PATCH 17/35] Numeric/decimal data type is FIELD_FIXED (BC break) --- src/Database/Helpers.php | 3 ++- src/Database/IStructure.php | 1 + src/Database/RowNormalizer.php | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 87c6a931e..09d558467 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -27,7 +27,8 @@ class Helpers public static array $typePatterns = [ '^_' => IStructure::FIELD_TEXT, // PostgreSQL arrays '(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => IStructure::FIELD_INTEGER, - '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|REAL|DOUBLE( PRECISION)?|FLOAT\d*|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_FLOAT, + '(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => IStructure::FIELD_FIXED, + 'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => IStructure::FIELD_FLOAT, 'BOOL(EAN)?' => IStructure::FIELD_BOOL, 'TIME' => IStructure::FIELD_TIME, 'DATE' => IStructure::FIELD_DATE, diff --git a/src/Database/IStructure.php b/src/Database/IStructure.php index d08db66b0..deaff9c7b 100644 --- a/src/Database/IStructure.php +++ b/src/Database/IStructure.php @@ -21,6 +21,7 @@ interface IStructure FIELD_BOOL = 'bool', FIELD_INTEGER = 'int', FIELD_FLOAT = 'float', + FIELD_FIXED = 'fixed', FIELD_DATE = 'date', FIELD_TIME = 'time', FIELD_DATETIME = 'datetime', diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php index 37708bf90..8175e2753 100644 --- a/src/Database/RowNormalizer.php +++ b/src/Database/RowNormalizer.php @@ -40,6 +40,7 @@ public function normalizeField(mixed $value, string $type): mixed return is_float($tmp = $value * 1) ? $value : $tmp; case IStructure::FIELD_FLOAT: + case IStructure::FIELD_FIXED: if (is_string($value) && ($pos = strpos($value, '.')) !== false) { $value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.'); } From 3b3fe8dabea37c0fe3a4ec6cb51eaa45f284dce0 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 15:47:36 +0100 Subject: [PATCH 18/35] RowNormalizer: added configuring methods [Closes #257] --- src/Database/RowNormalizer.php | 31 ++++++++++++++++++- .../Database/ResultSet.customNormalizer.phpt | 14 +++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Database/RowNormalizer.php b/src/Database/RowNormalizer.php index 8175e2753..55b1cb1ca 100644 --- a/src/Database/RowNormalizer.php +++ b/src/Database/RowNormalizer.php @@ -19,10 +19,39 @@ final class RowNormalizer { use Nette\SmartObject; + private $skipped = []; + + + public function skipNumeric(): static + { + $this->skipped[IStructure::FIELD_FIXED] = true; + return $this; + } + + + public function skipDateTime(): static + { + $this->skipped[IStructure::FIELD_DATETIME] = true; + $this->skipped[IStructure::FIELD_DATE] = true; + $this->skipped[IStructure::FIELD_TIME] = true; + $this->skipped[IStructure::FIELD_UNIX_TIMESTAMP] = true; + return $this; + } + + + public function skipInterval(): static + { + $this->skipped[IStructure::FIELD_TIME_INTERVAL] = true; + return $this; + } + + public function __invoke(array $row, ResultSet $resultSet): array { foreach ($resultSet->getColumnTypes() as $key => $type) { - $row[$key] = $this->normalizeField($row[$key], $type); + if (!isset($this->skipped[$type])) { + $row[$key] = $this->normalizeField($row[$key], $type); + } } return $row; diff --git a/tests/Database/ResultSet.customNormalizer.phpt b/tests/Database/ResultSet.customNormalizer.phpt index d139e022e..7c85d01fa 100644 --- a/tests/Database/ResultSet.customNormalizer.phpt +++ b/tests/Database/ResultSet.customNormalizer.phpt @@ -29,6 +29,20 @@ test('disabled normalization', function () use ($connection) { }); +test('configured RowNormalizer', function () use ($connection) { + $driverName = $GLOBALS['driverName']; + + $connection->setRowNormalizer((new Nette\Database\RowNormalizer)->skipDateTime()); + $res = $connection->query('SELECT * FROM author'); + Assert::same([ + 'id' => 11, + 'name' => 'Jakub Vrana', + 'web' => 'http://www.vrana.cz/', + 'born' => $driverName === 'sqlite' ? (PHP_VERSION_ID >= 80100 ? 1_642_892_400 : '1642892400') : '2022-01-23', + ], (array) $res->fetch()); +}); + + test('custom normalization', function () use ($connection) { $driverName = $GLOBALS['driverName']; From a94ac6e1411914b59f9818376f5ee3ef24d061ac Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 8 Mar 2021 16:28:16 +0100 Subject: [PATCH 19/35] DateTime::toString() returns microseconds (BC break) --- src/Database/DateTime.php | 5 +++-- tests/Database/DateTime.phpt | 4 ++-- tests/Database/Explorer/Selection.fetchPairs().phpt | 4 ++-- tests/Database/ResultSet.fetchAssoc().phpt | 4 ++-- tests/Database/ResultSet.fetchPairs().phpt | 8 ++++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Database/DateTime.php b/src/Database/DateTime.php index bfa90e726..f3c60bbdc 100644 --- a/src/Database/DateTime.php +++ b/src/Database/DateTime.php @@ -26,6 +26,7 @@ public function __construct(string|int $time = 'now') ->setTimezone(new \DateTimeZone(date_default_timezone_get())) ->format('Y-m-d H:i:s.u'); } + parent::__construct($time); } @@ -40,10 +41,10 @@ public function jsonSerialize(): string /** - * Returns the date and time in the format 'Y-m-d H:i:s'. + * Returns the date and time in the format 'Y-m-d H:i:s.u'. */ public function __toString(): string { - return $this->format('Y-m-d H:i:s'); + return $this->format('Y-m-d H:i:s.u'); } } diff --git a/tests/Database/DateTime.phpt b/tests/Database/DateTime.phpt index 8c3c5503e..90c7e2c2e 100644 --- a/tests/Database/DateTime.phpt +++ b/tests/Database/DateTime.phpt @@ -11,13 +11,13 @@ require __DIR__ . '/../bootstrap.php'; date_default_timezone_set('Europe/Prague'); // timestamp -Assert::same('1978-01-23 11:40:00', (string) new DateTime(254_400_000)); +Assert::same('1978-01-23 11:40:00.000000', (string) new DateTime(254_400_000)); Assert::same(254_400_000, (new DateTime(254_400_000))->getTimestamp()); Assert::same(is_int(2_544_000_000) ? 2_544_000_000 : '2544000000', (new DateTime(2_544_000_000))->getTimestamp()); // 64 bit // to string -Assert::same('1978-01-23 11:40:00', (string) new DateTime('1978-01-23 11:40')); +Assert::same('1978-01-23 11:40:00.000000', (string) new DateTime('1978-01-23 11:40')); // JSON Assert::same('"1978-01-23T11:40:00+01:00"', json_encode(new DateTime('1978-01-23 11:40'))); diff --git a/tests/Database/Explorer/Selection.fetchPairs().phpt b/tests/Database/Explorer/Selection.fetchPairs().phpt index 684c43cac..5eb1a9a50 100644 --- a/tests/Database/Explorer/Selection.fetchPairs().phpt +++ b/tests/Database/Explorer/Selection.fetchPairs().phpt @@ -41,7 +41,7 @@ test('', function () use ($explorer) { $explorer->table('author')->get(12)->update(['born' => new DateTime('2002-02-02')]); $list = $explorer->table('author')->where('born IS NOT NULL')->order('born')->fetchPairs('born', 'name'); Assert::same([ - '2002-02-02 00:00:00' => 'David Grudl', - '2002-02-20 00:00:00' => 'Jakub Vrana', + '2002-02-02 00:00:00.000000' => 'David Grudl', + '2002-02-20 00:00:00.000000' => 'Jakub Vrana', ], $list); }); diff --git a/tests/Database/ResultSet.fetchAssoc().phpt b/tests/Database/ResultSet.fetchAssoc().phpt index 92bd13fec..cd8f0fc5b 100644 --- a/tests/Database/ResultSet.fetchAssoc().phpt +++ b/tests/Database/ResultSet.fetchAssoc().phpt @@ -51,7 +51,7 @@ test('', function () use ($connection) { $pairs = $connection->query('UPDATE author SET born = ? WHERE id = 12', new DateTime('2002-02-02')); $pairs = $connection->query('SELECT * FROM author WHERE born IS NOT NULL ORDER BY born')->fetchAssoc('born=name'); Assert::same([ - '2002-02-02 00:00:00' => 'David Grudl', - '2002-02-20 00:00:00' => 'Jakub Vrana', + '2002-02-02 00:00:00.000000' => 'David Grudl', + '2002-02-20 00:00:00.000000' => 'Jakub Vrana', ], $pairs); }); diff --git a/tests/Database/ResultSet.fetchPairs().phpt b/tests/Database/ResultSet.fetchPairs().phpt index 58d6696c1..48acb6af9 100644 --- a/tests/Database/ResultSet.fetchPairs().phpt +++ b/tests/Database/ResultSet.fetchPairs().phpt @@ -70,8 +70,8 @@ test('', function () use ($connection) { $pairs = $connection->query('UPDATE author SET born = ? WHERE id = 12', new DateTime('2002-02-02')); $pairs = $connection->query('SELECT * FROM author WHERE born IS NOT NULL ORDER BY born')->fetchPairs('born', 'name'); Assert::same([ - '2002-02-02 00:00:00' => 'David Grudl', - '2002-02-20 00:00:00' => 'Jakub Vrana', + '2002-02-02 00:00:00.000000' => 'David Grudl', + '2002-02-20 00:00:00.000000' => 'Jakub Vrana', ], $pairs); }); @@ -125,8 +125,8 @@ $pairs = $connection->query('UPDATE author SET born = ? WHERE id = 11', new Date $pairs = $connection->query('UPDATE author SET born = ? WHERE id = 12', new DateTime('2002-02-02')); $pairs = $connection->query('SELECT * FROM author WHERE born IS NOT NULL ORDER BY born')->fetchPairs('born', 'name'); Assert::same([ - '2002-02-02 00:00:00' => 'David Grudl', - '2002-02-20 00:00:00' => 'Jakub Vrana', + '2002-02-02 00:00:00.000000' => 'David Grudl', + '2002-02-20 00:00:00.000000' => 'Jakub Vrana', ], $pairs); From c871fcf1558d76babdba46792dbf36fa9ed2df09 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 11 Mar 2021 21:52:35 +0100 Subject: [PATCH 20/35] removed community health files --- .github/ISSUE_TEMPLATE/Bug_report.md | 19 ------------- .github/ISSUE_TEMPLATE/Feature_request.md | 9 ------ .github/ISSUE_TEMPLATE/Support_question.md | 12 -------- .github/ISSUE_TEMPLATE/Support_us.md | 21 -------------- .github/funding.yml | 2 -- .github/pull_request_template.md | 11 -------- contributing.md | 33 ---------------------- 7 files changed, 107 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/Support_question.md delete mode 100644 .github/ISSUE_TEMPLATE/Support_us.md delete mode 100644 .github/funding.yml delete mode 100644 .github/pull_request_template.md delete mode 100644 contributing.md diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index a4cd12634..000000000 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: "🐛 Bug Report" -about: "If something isn't working as expected 🤔" - ---- - -Version: ?.?.? - -### Bug Description -... A clear and concise description of what the bug is. A good bug report shouldn't leave others needing to chase you up for more information. - -### Steps To Reproduce -... If possible a minimal demo of the problem ... - -### Expected Behavior -... A clear and concise description of what you expected to happen. - -### Possible Solution -... Only if you have suggestions on a fix for the bug diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index d2e219489..000000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: "🚀 Feature Request" -about: "I have a suggestion (and may want to implement it) 🙂" - ---- - -- Is your feature request related to a problem? Please describe. -- Explain your intentions. -- It's up to you to make a strong case to convince the project's developers of the merits of this feature. diff --git a/.github/ISSUE_TEMPLATE/Support_question.md b/.github/ISSUE_TEMPLATE/Support_question.md deleted file mode 100644 index 75c48b6ed..000000000 --- a/.github/ISSUE_TEMPLATE/Support_question.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: "🤗 Support Question" -about: "If you have a question 💬, please check out our forum!" - ---- - ---------------^ Click "Preview" for a nicer view! -We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks! 😁. - -* Nette Forum: https://forum.nette.org -* Nette Gitter: https://gitter.im/nette/nette -* Slack (czech): https://pehapkari.slack.com/messages/C2R30BLKA diff --git a/.github/ISSUE_TEMPLATE/Support_us.md b/.github/ISSUE_TEMPLATE/Support_us.md deleted file mode 100644 index 92d8a4c3a..000000000 --- a/.github/ISSUE_TEMPLATE/Support_us.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: "❤️ Support us" -about: "If you would like to support our efforts in maintaining this project 🙌" - ---- - ---------------^ Click "Preview" for a nicer view! - -> https://nette.org/donate - -Help support Nette! - -We develop Nette Framework for more than 14 years. In order to make your life more comfortable. Nette cares about the safety of your sites. Nette saves you time. And gives job opportunities. - -Nette earns you money. And is absolutely free. - -To ensure future development and improving the documentation, we need your donation. - -Whether you are chief of IT company which benefits from Nette, or developer who goes for advice on our forum, if you like Nette, [please make a donation now](https://nette.org/donate). - -Thank you! diff --git a/.github/funding.yml b/.github/funding.yml deleted file mode 100644 index 25adc9520..000000000 --- a/.github/funding.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: dg -custom: "https://nette.org/donate" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index f8aa3f408..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,11 +0,0 @@ -- bug fix / new feature? -- BC break? yes/no -- doc PR: nette/docs#??? - - diff --git a/contributing.md b/contributing.md deleted file mode 100644 index 184152c02..000000000 --- a/contributing.md +++ /dev/null @@ -1,33 +0,0 @@ -How to contribute & use the issue tracker -========================================= - -Nette welcomes your contributions. There are several ways to help out: - -* Create an issue on GitHub, if you have found a bug -* Write test cases for open bug issues -* Write fixes for open bug/feature issues, preferably with test cases included -* Contribute to the [documentation](https://nette.org/en/writing) - -Issues ------- - -Please **do not use the issue tracker to ask questions**. We will be happy to help you -on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette). - -A good bug report shouldn't leave others needing to chase you up for more -information. Please try to be as detailed as possible in your report. - -**Feature requests** are welcome. But take a moment to find out whether your idea -fits with the scope and aims of the project. It's up to *you* to make a strong -case to convince the project's developers of the merits of this feature. - -Contributing ------------- - -If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing). - -The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them. - -Please do not fix whitespace, format code, or make a purely cosmetic patch. - -Thanks! :heart: From 7f27c5363a7a3c3df9999060e2f11823a378208f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 28 Jul 2021 12:48:44 +0200 Subject: [PATCH 21/35] used native PHP 8 features --- src/Database/Drivers/PgSqlDriver.php | 2 +- src/Database/Drivers/SqliteDriver.php | 14 +++++++------- src/Database/ResultSet.php | 2 +- src/Database/SqlPreprocessor.php | 10 +++++----- src/Database/Table/Selection.php | 4 ++-- src/Database/Table/SqlBuilder.php | 8 ++++---- tests/Database/Explorer/bugs/bug216.phpt | 2 +- tests/Database/connect.inc.php | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 5c6b09d98..bf7173441 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -31,7 +31,7 @@ public function initialize(Nette\Database\Connection $connection, array $options public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[0] ?? null; - if ($code === '0A000' && strpos($e->getMessage(), 'truncate') !== false) { + if ($code === '0A000' && str_contains($e->getMessage(), 'truncate')) { return Nette\Database\ForeignKeyConstraintViolationException::from($e); } elseif ($code === '23502') { diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index b2942533e..75eac127e 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -40,21 +40,21 @@ public function convertException(\PDOException $e): Nette\Database\DriverExcepti return Nette\Database\DriverException::from($e); } elseif ( - strpos($msg, 'must be unique') !== false - || strpos($msg, 'is not unique') !== false - || strpos($msg, 'UNIQUE constraint failed') !== false + str_contains($msg, 'must be unique') + || str_contains($msg, 'is not unique') + || str_contains($msg, 'UNIQUE constraint failed') ) { return Nette\Database\UniqueConstraintViolationException::from($e); } elseif ( - strpos($msg, 'may not be null') !== false - || strpos($msg, 'NOT NULL constraint failed') !== false + str_contains($msg, 'may not be null') + || str_contains($msg, 'NOT NULL constraint failed') ) { return Nette\Database\NotNullConstraintViolationException::from($e); } elseif ( - strpos($msg, 'foreign key constraint failed') !== false - || strpos($msg, 'FOREIGN KEY constraint failed') !== false + str_contains($msg, 'foreign key constraint failed') + || str_contains($msg, 'FOREIGN KEY constraint failed') ) { return Nette\Database\ForeignKeyConstraintViolationException::from($e); diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index 65b79eaa2..ef8e5ba3d 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -51,7 +51,7 @@ public function __construct( $this->normalizer = $normalizer; try { - if (substr($queryString, 0, 2) === '::') { + if (str_starts_with($queryString, '::')) { $connection->getPdo()->{substr($queryString, 2)}(); } elseif ($queryString !== null) { $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL, ]; diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index f8a7a1cc4..40829faca 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -193,13 +193,13 @@ private function formatValue($value, ?string $mode = null): string $this->remaining[] = $value->value; return '?'; - } elseif (is_object($value) && method_exists($value, '__toString')) { + } elseif ($value instanceof \Stringable) { $this->remaining[] = (string) $value; return '?'; } } elseif ($mode === 'name') { if (!is_string($value)) { - $type = gettype($value); + $type = get_debug_type($value); throw new Nette\InvalidArgumentException("Placeholder ?$mode expects string, $type given."); } @@ -254,7 +254,7 @@ private function formatValue($value, ?string $mode = null): string foreach ($value as $k => $v) { if (is_int($k)) { // value, value, ... $vx[] = $this->formatValue($v); - } elseif (substr($k, -1) === '=') { // key+=value, key-=value, ... + } elseif (str_ends_with($k, '=')) { // key+=value, key-=value, ... $k2 = $this->delimite(substr($k, 0, -2)); $vx[] = $k2 . '=' . $k2 . ' ' . substr($k, -2, 1) . ' ' . $this->formatValue($v); } else { // key=value, key=value, ... @@ -313,14 +313,14 @@ private function formatValue($value, ?string $mode = null): string throw new Nette\InvalidArgumentException("Unknown placeholder ?$mode."); } } elseif (in_array($mode, self::Modes, true)) { - $type = gettype($value); + $type = get_debug_type($value); throw new Nette\InvalidArgumentException("Placeholder ?$mode expects array or Traversable object, $type given."); } elseif ($mode && $mode !== self::ModeAuto) { throw new Nette\InvalidArgumentException("Unknown placeholder ?$mode."); } else { - throw new Nette\InvalidArgumentException('Unexpected type of parameter: ' . (is_object($value) ? $value::class : gettype($value))); + throw new Nette\InvalidArgumentException('Unexpected type of parameter: ' . get_debug_type($value)); } } diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 4a42c3c7f..8abdfe321 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -348,7 +348,7 @@ public function whereOr(array $parameters): static foreach ($parameters as $key => $val) { if (is_int($key)) { // whereOr(['full condition']) $columns[] = $val; - } elseif (strpos($key, '?') === false) { // whereOr(['column1' => 1]) + } elseif (!str_contains($key, '?')) { // whereOr(['column1' => 1]) $columns[] = $key . ' ?'; $values[] = $val; } else { // whereOr(['column1 > ?' => 1]) @@ -945,7 +945,7 @@ public function getReferencingTable( int|string|null $active = null, ): ?GroupedSelection { - if (strpos($table, '.') !== false) { + if (str_contains($table, '.')) { [$table, $column] = explode('.', $table); } elseif (!$column) { $hasMany = $this->conventions->getHasManyReference($this->name, $table); diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 67ff0b4f5..87698c4bd 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -389,10 +389,10 @@ protected function addCondition($condition, array $params, array &$conditions, a if ($arg !== null) { if (!$arg) { - $hasBrackets = strpos($condition, '(') !== false; + $hasBrackets = str_contains($condition, '('); $hasOperators = preg_match('#AND|OR#', $condition); - $hasNot = strpos($condition, 'NOT') !== false; - $hasPrefixNot = strpos($match[2][0], 'NOT') !== false; + $hasNot = str_contains($condition, 'NOT'); + $hasPrefixNot = str_contains($match[2][0], 'NOT'); if (!$hasBrackets && ($hasOperators || ($hasNot && !$hasPrefixNot))) { throw new Nette\InvalidArgumentException('Possible SQL query corruption. Add parentheses around operators.'); } @@ -842,7 +842,7 @@ private function getConditionHash($condition, array $parameters): string $parameter = $this->getConditionHash($parameter->getSql(), $parameter->getSqlBuilder()->getParameters()); } elseif ($parameter instanceof SqlLiteral) { $parameter = $this->getConditionHash($parameter->__toString(), $parameter->getParameters()); - } elseif (is_object($parameter) && method_exists($parameter, '__toString')) { + } elseif ($parameter instanceof \Stringable) { $parameter = $parameter->__toString(); } elseif (is_array($parameter) || $parameter instanceof \ArrayAccess) { $parameter = $this->getConditionHash($key, $parameter); diff --git a/tests/Database/Explorer/bugs/bug216.phpt b/tests/Database/Explorer/bugs/bug216.phpt index 1e13a0593..bd43dd239 100644 --- a/tests/Database/Explorer/bugs/bug216.phpt +++ b/tests/Database/Explorer/bugs/bug216.phpt @@ -20,7 +20,7 @@ try { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } -if (strpos($options['dsn'], 'sqlite::memory:') === false) { +if (!str_contains($options['dsn'], 'sqlite::memory:')) { Tester\Environment::lock($options['dsn'], getTempDir()); } diff --git a/tests/Database/connect.inc.php b/tests/Database/connect.inc.php index eaa773522..64020ee84 100644 --- a/tests/Database/connect.inc.php +++ b/tests/Database/connect.inc.php @@ -17,7 +17,7 @@ Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } -if (strpos($options['dsn'], 'sqlite::memory:') === false) { +if (!str_contains($options['dsn'], 'sqlite::memory:')) { Tester\Environment::lock($options['dsn'], getTempDir()); } From ad4b9787d9b9784832697df32af1f7cb9d83ea43 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Sep 2021 22:08:58 +0200 Subject: [PATCH 22/35] introduced PdoDriver, descendant of all PDO-based drivers --- src/Database/Driver.php | 2 +- src/Database/Drivers/MsSqlDriver.php | 4 +--- src/Database/Drivers/MySqlDriver.php | 4 +--- src/Database/Drivers/OciDriver.php | 4 +--- src/Database/Drivers/OdbcDriver.php | 4 +--- src/Database/Drivers/PdoDriver.php | 21 +++++++++++++++++++++ src/Database/Drivers/PgSqlDriver.php | 4 +--- src/Database/Drivers/SqliteDriver.php | 4 +--- src/Database/Drivers/SqlsrvDriver.php | 4 +--- 9 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 src/Database/Drivers/PdoDriver.php diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 56b38688f..28c84ef90 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -11,7 +11,7 @@ /** - * Supplemental PDO database driver. + * Supplemental database driver. */ interface Driver { diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 08fa7650f..2e1ea787d 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -15,10 +15,8 @@ /** * Supplemental MS SQL database driver. */ -class MsSqlDriver implements Nette\Database\Driver +class MsSqlDriver extends PdoDriver { - use Nette\SmartObject; - private Nette\Database\Connection $connection; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 1eaf43b44..8764b1a37 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -15,10 +15,8 @@ /** * Supplemental MySQL database driver. */ -class MySqlDriver implements Nette\Database\Driver +class MySqlDriver extends PdoDriver { - use Nette\SmartObject; - public const ERROR_ACCESS_DENIED = 1045, ERROR_DUPLICATE_ENTRY = 1062, diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index 72cf42235..5f278c3dd 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -15,10 +15,8 @@ /** * Supplemental Oracle database driver. */ -class OciDriver implements Nette\Database\Driver +class OciDriver extends PdoDriver { - use Nette\SmartObject; - private Nette\Database\Connection $connection; /** Datetime format */ diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index 4150c20c4..ea06bef57 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -15,10 +15,8 @@ /** * Supplemental ODBC database driver. */ -class OdbcDriver implements Nette\Database\Driver +class OdbcDriver extends PdoDriver { - use Nette\SmartObject; - public function initialize(Nette\Database\Connection $connection, array $options): void { } diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php new file mode 100644 index 000000000..167276a25 --- /dev/null +++ b/src/Database/Drivers/PdoDriver.php @@ -0,0 +1,21 @@ + Date: Mon, 8 Nov 2021 22:51:07 +0100 Subject: [PATCH 23/35] database connection moved to PdoDriver::connect() --- src/Database/Connection.php | 26 ++++++++++++-------------- src/Database/Driver.php | 3 ++- src/Database/Drivers/MsSqlDriver.php | 9 --------- src/Database/Drivers/MySqlDriver.php | 18 +++++++++++------- src/Database/Drivers/OciDriver.php | 12 ++++++++---- src/Database/Drivers/OdbcDriver.php | 5 ----- src/Database/Drivers/PdoDriver.php | 27 +++++++++++++++++++++++++++ src/Database/Drivers/PgSqlDriver.php | 9 --------- src/Database/Drivers/SqliteDriver.php | 12 ++++++++---- src/Database/Drivers/SqlsrvDriver.php | 14 +++++++++----- 10 files changed, 77 insertions(+), 58 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index d736a07bc..1be911d67 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -30,9 +30,8 @@ class Connection private array $params; private array $options; - private Driver $driver; + private ?Driver $driver = null; private SqlPreprocessor $preprocessor; - private ?PDO $pdo = null; /** @var callable(array, ResultSet): array */ private $rowNormalizer; @@ -60,23 +59,21 @@ public function __construct( public function connect(): void { - if ($this->pdo) { + if ($this->driver) { return; } - try { - $this->pdo = new PDO($this->params[0], $this->params[1], $this->params[2], $this->options); - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch (PDOException $e) { - throw ConnectionException::from($e); - } - + $dsn = explode(':', $this->params[0])[0]; $class = empty($this->options['driverClass']) - ? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver' + ? 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $dsn)) . 'Driver' : $this->options['driverClass']; + if (!class_exists($class)) { + throw new ConnectionException("Invalid data source '$dsn'."); + } + $this->driver = new $class; + $this->driver->connect($this->params[0], $this->params[1], $this->params[2], $this->options); $this->preprocessor = new SqlPreprocessor($this); - $this->driver->initialize($this, $this->options); Arrays::invoke($this->onConnect, $this); } @@ -90,7 +87,7 @@ public function reconnect(): void public function disconnect(): void { - $this->pdo = null; + $this->driver = null; } @@ -100,10 +97,11 @@ public function getDsn(): string } + /** deprecated use getDriver()->getPdo() */ public function getPdo(): PDO { $this->connect(); - return $this->pdo; + return $this->driver->getPdo(); } diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 28c84ef90..2d7a6991d 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -25,8 +25,9 @@ interface Driver /** * Initializes connection. + * @throws ConnectionException */ - function initialize(Connection $connection, array $options): void; + function connect(string $dsn, ?string $user = null, ?string $password = null, ?array $options = null): void; /** * Converts PDOException to DriverException or its descendant. diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 2e1ea787d..a58055a23 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -17,15 +17,6 @@ */ class MsSqlDriver extends PdoDriver { - private Nette\Database\Connection $connection; - - - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; - } - - public function convertException(\PDOException $e): Nette\Database\DriverException { return Nette\Database\DriverException::from($e); diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 8764b1a37..7fe115d07 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -22,25 +22,29 @@ class MySqlDriver extends PdoDriver ERROR_DUPLICATE_ENTRY = 1062, ERROR_DATA_TRUNCATED = 1265; - private Nette\Database\Connection $connection; - /** * Driver options: * - charset => character encoding to set (default is utf8 or utf8mb4 since MySQL 5.5.3) * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html */ - public function initialize(Nette\Database\Connection $connection, array $options): void + public function connect( + string $dsn, + ?string $user = null, + #[\SensitiveParameter] + ?string $password = null, + ?array $options = null, + ): void { - $this->connection = $connection; + parent::connect($dsn, $user, $password, $options); $charset = $options['charset'] - ?? (version_compare($connection->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION), '5.5.3', '>=') ? 'utf8mb4' : 'utf8'); + ?? (version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '5.5.3', '>=') ? 'utf8mb4' : 'utf8'); if ($charset) { - $connection->query('SET NAMES ?', $charset); + $this->pdo->query('SET NAMES ' . $this->pdo->quote($charset)); } if (isset($options['sqlmode'])) { - $connection->query('SET sql_mode=?', $options['sqlmode']); + $this->pdo->query('SET sql_mode=' . $this->pdo->quote($options['sqlmode'])); } } diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index 5f278c3dd..bbed1c760 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -17,15 +17,19 @@ */ class OciDriver extends PdoDriver { - private Nette\Database\Connection $connection; - /** Datetime format */ private string $fmtDateTime; - public function initialize(Nette\Database\Connection $connection, array $options): void + public function connect( + string $dsn, + ?string $user = null, + #[\SensitiveParameter] + ?string $password = null, + ?array $options = null, + ): void { - $this->connection = $connection; + parent::connect($dsn, $user, $password, $options); $this->fmtDateTime = $options['formatDateTime'] ?? 'U'; } diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index ea06bef57..651caa52c 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -17,11 +17,6 @@ */ class OdbcDriver extends PdoDriver { - public function initialize(Nette\Database\Connection $connection, array $options): void - { - } - - public function convertException(\PDOException $e): Nette\Database\DriverException { return Nette\Database\DriverException::from($e); diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php index 167276a25..4ab63a2ee 100644 --- a/src/Database/Drivers/PdoDriver.php +++ b/src/Database/Drivers/PdoDriver.php @@ -10,6 +10,8 @@ namespace Nette\Database\Drivers; use Nette; +use PDO; +use PDOException; /** @@ -18,4 +20,29 @@ abstract class PdoDriver implements Nette\Database\Driver { use Nette\SmartObject; + + protected ?PDO $pdo = null; + + + public function connect( + string $dsn, + ?string $user = null, + #[\SensitiveParameter] + ?string $password = null, + ?array $options = null, + ): void + { + try { + $this->pdo = new PDO($dsn, $user, $password, $options); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException $e) { + throw Nette\Database\ConnectionException::from($e); + } + } + + + public function getPdo(): ?PDO + { + return $this->pdo; + } } diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 6463231ea..1aee932a7 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -17,15 +17,6 @@ */ class PgSqlDriver extends PdoDriver { - private Nette\Database\Connection $connection; - - - public function initialize(Nette\Database\Connection $connection, array $options): void - { - $this->connection = $connection; - } - - public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[0] ?? null; diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 726255eb1..647105530 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -17,15 +17,19 @@ */ class SqliteDriver extends PdoDriver { - private Nette\Database\Connection $connection; - /** Datetime format */ private string $fmtDateTime; - public function initialize(Nette\Database\Connection $connection, array $options): void + public function connect( + string $dsn, + ?string $user = null, + #[\SensitiveParameter] + ?string $password = null, + ?array $options = null, + ): void { - $this->connection = $connection; + parent::connect($dsn, $user, $password, $options); $this->fmtDateTime = $options['formatDateTime'] ?? 'U'; } diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 973848fb3..00d1ae468 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -17,15 +17,19 @@ */ class SqlsrvDriver extends PdoDriver { - private Nette\Database\Connection $connection; - private string $version; - public function initialize(Nette\Database\Connection $connection, array $options): void + public function connect( + string $dsn, + ?string $user = null, + #[\SensitiveParameter] + ?string $password = null, + ?array $options = null, + ): void { - $this->connection = $connection; - $this->version = $connection->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + parent::connect($dsn, $user, $password, $options); + $this->version = $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION); } From e81994ee9810800d5f3d480179f920337e1a6c96 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 21 Sep 2021 01:37:59 +0200 Subject: [PATCH 24/35] drivers uses PDO instead of Connection --- src/Database/Drivers/MsSqlDriver.php | 18 +++++++-------- src/Database/Drivers/MySqlDriver.php | 16 +++++++------- src/Database/Drivers/OciDriver.php | 2 +- src/Database/Drivers/PgSqlDriver.php | 32 +++++++++++---------------- src/Database/Drivers/SqliteDriver.php | 27 +++++++++++----------- src/Database/Drivers/SqlsrvDriver.php | 25 ++++++++++----------- 6 files changed, 56 insertions(+), 64 deletions(-) diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index a58055a23..e00ba7849 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -75,7 +75,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->connection->query('SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES') as $row) { + foreach ($this->pdo->query('SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES') as $row) { $tables[] = [ 'name' => $row['TABLE_SCHEMA'] . '.' . $row['TABLE_NAME'], 'view' => ($row['TABLE_TYPE'] ?? null) === 'VIEW', @@ -103,10 +103,10 @@ public function getColumns(string $table): array FROM INFORMATION_SCHEMA.COLUMNS WHERE - TABLE_SCHEMA = {$this->connection->quote($table_schema)} - AND TABLE_NAME = {$this->connection->quote($table_name)}"; + TABLE_SCHEMA = {$this->pdo->quote($table_schema)} + AND TABLE_NAME = {$this->pdo->quote($table_name)}"; - foreach ($this->connection->query($query) as $row) { + foreach ($this->pdo->query($query, \PDO::FETCH_ASSOC) as $row) { $columns[] = [ 'name' => $row['COLUMN_NAME'], 'table' => $table, @@ -117,7 +117,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, ]; } @@ -143,11 +143,11 @@ public function getIndexes(string $table): array INNER JOIN sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id INNER JOIN sys.tables t ON ind.object_id = t.object_id WHERE - t.name = {$this->connection->quote($table_name)} + t.name = {$this->pdo->quote($table_name)} ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id"; - foreach ($this->connection->query($query) as $row) { + foreach ($this->pdo->query($query) as $row) { $id = $row['name_index']; $indexes[$id]['name'] = $id; $indexes[$id]['unique'] = $row['is_unique'] !== 'False'; @@ -185,9 +185,9 @@ public function getForeignKeys(string $table): array INNER JOIN sys.columns col2 ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id WHERE - tab1.name = {$this->connection->quote($table_name)}"; + tab1.name = {$this->pdo->quote($table_name)}"; - foreach ($this->connection->query($query) as $id => $row) { + foreach ($this->pdo->query($query) as $id => $row) { $keys[$id]['name'] = $row['fk_name']; $keys[$id]['local'] = $row['column']; $keys[$id]['table'] = $table_schema . '.' . $row['referenced_table']; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 7fe115d07..a6344bec8 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -95,7 +95,7 @@ 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), '%_'); + $value = addcslashes(substr($this->pdo->quote($value), 1, -1), '%_'); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); } @@ -119,7 +119,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->connection->query('SHOW FULL TABLES') as $row) { + foreach ($this->pdo->query('SHOW FULL TABLES') as $row) { $tables[] = [ 'name' => $row[0], 'view' => ($row[1] ?? null) === 'VIEW', @@ -133,8 +133,8 @@ public function getTables(): array public function getColumns(string $table): array { $columns = []; - foreach ($this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table)) as $row) { - $row = array_change_key_case((array) $row, CASE_LOWER); + foreach ($this->pdo->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table), \PDO::FETCH_ASSOC) as $row) { + $row = array_change_key_case($row, CASE_LOWER); $type = explode('(', $row['type']); $columns[] = [ 'name' => $row['field'], @@ -145,7 +145,7 @@ public function getColumns(string $table): array 'default' => $row['default'], 'autoincrement' => $row['extra'] === 'auto_increment', 'primary' => $row['key'] === 'PRI', - 'vendor' => (array) $row, + 'vendor' => $row, ]; } @@ -156,7 +156,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - foreach ($this->connection->query('SHOW INDEX FROM ' . $this->delimite($table)) as $row) { + foreach ($this->pdo->query('SHOW INDEX FROM ' . $this->delimite($table)) as $row) { $id = $row['Key_name']; $indexes[$id]['name'] = $id; $indexes[$id]['unique'] = !$row['Non_unique']; @@ -172,9 +172,9 @@ public function getForeignKeys(string $table): array { $keys = []; $query = 'SELECT CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE ' - . 'WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ' . $this->connection->quote($table); + . 'WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ' . $this->pdo->quote($table); - foreach ($this->connection->query($query) as $id => $row) { + foreach ($this->pdo->query($query) as $id => $row) { $keys[$id]['name'] = $row['CONSTRAINT_NAME']; $keys[$id]['local'] = $row['COLUMN_NAME']; $keys[$id]['table'] = $row['REFERENCED_TABLE_NAME']; diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index bbed1c760..47e77ff30 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -103,7 +103,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->connection->query('SELECT * FROM cat') as $row) { + foreach ($this->pdo->query('SELECT * FROM cat') as $row) { if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { $tables[] = [ 'name' => $row[0], diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 1aee932a7..0fd665832 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -65,8 +65,8 @@ 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); + $bs = substr($this->pdo->quote('\\'), 1, -1); // standard_conforming_strings = on/off + $value = substr($this->pdo->quote($value), 1, -1); $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); } @@ -93,8 +93,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - $tables = []; - foreach ($this->connection->query(" + return $this->pdo->query(" SELECT DISTINCT ON (c.relname) c.relname::varchar AS name, c.relkind IN ('v', 'm') AS view, @@ -107,18 +106,14 @@ public function getTables(): array AND n.nspname = ANY (pg_catalog.current_schemas(FALSE)) ORDER BY c.relname - ") as $row) { - $tables[] = (array) $row; - } - - return $tables; + ")->fetchAll(\PDO::FETCH_ASSOC); } public function getColumns(string $table): array { $columns = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT a.attname::varchar AS name, c.relname::varchar AS table, @@ -139,13 +134,12 @@ public function getColumns(string $table): array LEFT JOIN pg_catalog.pg_constraint AS co ON co.connamespace = c.relnamespace AND contype = 'p' AND co.conrelid = c.oid AND a.attnum = ANY(co.conkey) WHERE c.relkind IN ('r', 'v', 'm', 'p') - AND c.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass + AND c.oid = {$this->pdo->quote($this->delimiteFQN($table))}::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum - ") as $row) { - $column = (array) $row; + ", \PDO::FETCH_ASSOC) as $column) { $column['vendor'] = $column; unset($column['sequence']); @@ -159,7 +153,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT c2.relname::varchar AS name, i.indisunique AS unique, @@ -172,7 +166,7 @@ public function getIndexes(string $table): array LEFT JOIN pg_catalog.pg_attribute AS a ON c1.oid = a.attrelid AND a.attnum = ANY(i.indkey) WHERE c1.relkind IN ('r', 'p') - AND c1.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass + AND c1.oid = {$this->pdo->quote($this->delimiteFQN($table))}::regclass ") as $row) { $id = $row['name']; $indexes[$id]['name'] = $id; @@ -187,8 +181,8 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { - /* Does't work with multicolumn foreign keys */ - return $this->connection->query(" + /* Doesn't work with multi-column foreign keys */ + return $this->pdo->query(" SELECT co.conname::varchar AS name, al.attname::varchar AS local, @@ -203,9 +197,9 @@ public function getForeignKeys(string $table): array JOIN pg_catalog.pg_attribute AS af ON af.attrelid = cf.oid AND af.attnum = co.confkey[1] WHERE co.contype = 'f' - AND cl.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass + AND cl.oid = {$this->pdo->quote($this->delimiteFQN($table))}::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) - ")->fetchAll(); + ")->fetchAll(\PDO::FETCH_ASSOC); } diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 647105530..899e5171d 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -89,7 +89,7 @@ public function formatDateInterval(\DateInterval $value): string public function formatLike(string $value, int $pos): string { - $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\'); + $value = addcslashes(substr($this->pdo->quote($value), 1, -1), '%_\\'); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; } @@ -112,15 +112,15 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' UNION ALL SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' ORDER BY name ") as $row) { $tables[] = [ - 'name' => $row->name, - 'view' => (bool) $row->view, + 'name' => $row['name'], + 'view' => (bool) $row['view'], ]; } @@ -130,14 +130,14 @@ public function getTables(): array public function getColumns(string $table): array { - $meta = $this->connection->query(" - SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + $meta = $this->pdo->query(" + SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->pdo->quote($table)} UNION ALL - SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->pdo->quote($table)} ")->fetch(); $columns = []; - foreach ($this->connection->query("PRAGMA table_info({$this->delimite($table)})") as $row) { + foreach ($this->pdo->query("PRAGMA table_info({$this->delimite($table)})", \PDO::FETCH_ASSOC) as $row) { $column = $row['name']; $pattern = "/(\"$column\"|`$column`|\\[$column\\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui"; $type = explode('(', $row['type']); @@ -146,11 +146,11 @@ public function getColumns(string $table): array 'table' => $table, 'nativetype' => strtoupper($type[0]), 'size' => isset($type[1]) ? (int) $type[1] : null, - 'nullable' => $row['notnull'] === 0, + 'nullable' => !$row['notnull'], 'default' => $row['dflt_value'], 'autoincrement' => $meta && preg_match($pattern, (string) $meta['sql']), 'primary' => $row['pk'] > 0, - 'vendor' => (array) $row, + 'vendor' => $row, ]; } @@ -161,7 +161,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - foreach ($this->connection->query("PRAGMA index_list({$this->delimite($table)})") as $row) { + foreach ($this->pdo->query("PRAGMA index_list({$this->delimite($table)})") as $row) { $id = $row['name']; $indexes[$id]['name'] = $id; $indexes[$id]['unique'] = (bool) $row['unique']; @@ -169,8 +169,7 @@ public function getIndexes(string $table): array } foreach ($indexes as $index => $values) { - $res = $this->connection->query("PRAGMA index_info({$this->delimite($index)})"); - while ($row = $res->fetch()) { + foreach ($this->pdo->query("PRAGMA index_info({$this->delimite($index)})") as $row) { $indexes[$index]['columns'][] = $row['name']; } } @@ -207,7 +206,7 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { $keys = []; - foreach ($this->connection->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { + foreach ($this->pdo->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { $id = $row['id']; $keys[$id]['name'] = $id; $keys[$id]['local'] = $row['from']; diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 00d1ae468..d17f93499 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -99,7 +99,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT name, CASE type @@ -112,8 +112,8 @@ public function getTables(): array type IN ('U', 'V') ") as $row) { $tables[] = [ - 'name' => $row->name, - 'view' => (bool) $row->view, + 'name' => $row['name'], + 'view' => (bool) $row['view'], ]; } @@ -124,7 +124,7 @@ public function getTables(): array public function getColumns(string $table): array { $columns = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT c.name AS name, o.name AS [table], @@ -145,9 +145,8 @@ public function getColumns(string $table): array LEFT JOIN sys.index_columns i ON k.parent_object_id = i.object_id AND i.index_id = k.unique_index_id AND i.column_id = c.column_id WHERE o.type IN ('U', 'V') - AND o.name = {$this->connection->quote($table)} - ") as $row) { - $row = (array) $row; + AND o.name = {$this->pdo->quote($table)} + ", \PDO::FETCH_ASSOC) as $row) { $row['vendor'] = $row; $row['nullable'] = (bool) $row['nullable']; $row['autoincrement'] = (bool) $row['autoincrement']; @@ -163,7 +162,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT i.name AS name, CASE WHEN i.is_unique = 1 OR i.is_unique_constraint = 1 @@ -178,7 +177,7 @@ public function getIndexes(string $table): array JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id JOIN sys.tables t ON i.object_id = t.object_id WHERE - t.name = {$this->connection->quote($table)} + t.name = {$this->pdo->quote($table)} ORDER BY i.index_id, ic.index_column_id @@ -198,7 +197,7 @@ public function getForeignKeys(string $table): array { // Does't work with multicolumn foreign keys $keys = []; - foreach ($this->connection->query(" + foreach ($this->pdo->query(" SELECT fk.name AS name, cl.name AS local, @@ -212,9 +211,9 @@ public function getForeignKeys(string $table): array JOIN sys.tables tf ON fkc.referenced_object_id = tf.object_id JOIN sys.columns cf ON fkc.referenced_object_id = cf.object_id AND fkc.referenced_column_id = cf.column_id WHERE - tl.name = {$this->connection->quote($table)} - ") as $row) { - $keys[$row->name] = (array) $row; + tl.name = {$this->pdo->quote($table)} + ", \PDO::FETCH_ASSOC) as $row) { + $keys[$row['name']] = $row; } return array_values($keys); From 624bae690e83362e87373372a5192b70cf30d1d8 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Dec 2021 05:22:13 +0100 Subject: [PATCH 25/35] used heredoc --- src/Database/Drivers/MsSqlDriver.php | 15 +++++++++------ src/Database/Drivers/MySqlDriver.php | 11 +++++++---- src/Database/Drivers/PgSqlDriver.php | 20 ++++++++++---------- src/Database/Drivers/SqliteDriver.php | 24 ++++++++++++++++-------- src/Database/Drivers/SqlsrvDriver.php | 16 ++++++++-------- src/Database/SqlPreprocessor.php | 22 ++++++++++++---------- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index e00ba7849..d24db5d27 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -91,7 +91,7 @@ public function getColumns(string $table): array [$table_schema, $table_name] = explode('.', $table); $columns = []; - $query = " + $query = <<pdo->quote($table_schema)} - AND TABLE_NAME = {$this->pdo->quote($table_name)}"; + AND TABLE_NAME = {$this->pdo->quote($table_name)} + X; foreach ($this->pdo->query($query, \PDO::FETCH_ASSOC) as $row) { $columns[] = [ @@ -130,7 +131,7 @@ public function getIndexes(string $table): array [, $table_name] = explode('.', $table); $indexes = []; - $query = " + $query = <<pdo->quote($table_name)} ORDER BY - t.name, ind.name, ind.index_id, ic.index_column_id"; + t.name, ind.name, ind.index_id, ic.index_column_id + X; foreach ($this->pdo->query($query) as $row) { $id = $row['name_index']; @@ -164,7 +166,7 @@ public function getForeignKeys(string $table): array [$table_schema, $table_name] = explode('.', $table); $keys = []; - $query = " + $query = <<pdo->quote($table_name)}"; + tab1.name = {$this->pdo->quote($table_name)} + X; foreach ($this->pdo->query($query) as $id => $row) { $keys[$id]['name'] = $row['fk_name']; diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index a6344bec8..eeb9c4d0d 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -171,10 +171,13 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { $keys = []; - $query = 'SELECT CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE ' - . 'WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ' . $this->pdo->quote($table); - - foreach ($this->pdo->query($query) as $id => $row) { + foreach ($this->pdo->query(<<pdo->quote($table)} + X) as $id => $row) { $keys[$id]['name'] = $row['CONSTRAINT_NAME']; $keys[$id]['local'] = $row['COLUMN_NAME']; $keys[$id]['table'] = $row['REFERENCED_TABLE_NAME']; diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index 0fd665832..b0daf9e37 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -93,11 +93,11 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - return $this->pdo->query(" + return $this->pdo->query(<<<'X' SELECT DISTINCT ON (c.relname) c.relname::varchar AS name, c.relkind IN ('v', 'm') AS view, - n.nspname::varchar || '.' || c.relname::varchar AS \"fullName\" + n.nspname::varchar || '.' || c.relname::varchar AS "fullName" FROM pg_catalog.pg_class AS c JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace @@ -106,14 +106,14 @@ public function getTables(): array AND n.nspname = ANY (pg_catalog.current_schemas(FALSE)) ORDER BY c.relname - ")->fetchAll(\PDO::FETCH_ASSOC); + X)->fetchAll(\PDO::FETCH_ASSOC); } public function getColumns(string $table): array { $columns = []; - foreach ($this->pdo->query(" + foreach ($this->pdo->query(<<pdo->query(" + foreach ($this->pdo->query(<<pdo->quote($this->delimiteFQN($table))}::regclass - ") as $row) { + X) as $row) { $id = $row['name']; $indexes[$id]['name'] = $id; $indexes[$id]['unique'] = $row['unique']; @@ -182,7 +182,7 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { /* Doesn't work with multi-column foreign keys */ - return $this->pdo->query(" + return $this->pdo->query(<<pdo->quote($this->delimiteFQN($table))}::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) - ")->fetchAll(\PDO::FETCH_ASSOC); + X)->fetchAll(\PDO::FETCH_ASSOC); } diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 899e5171d..fb9eb7d0b 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -112,12 +112,16 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->pdo->query(" - SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' + foreach ($this->pdo->query(<<<'X' + SELECT name, type = 'view' as view + FROM sqlite_master + WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' UNION ALL - SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' + SELECT name, type = 'view' as view + FROM sqlite_temp_master + WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' ORDER BY name - ") as $row) { + X) as $row) { $tables[] = [ 'name' => $row['name'], 'view' => (bool) $row['view'], @@ -130,11 +134,15 @@ public function getTables(): array public function getColumns(string $table): array { - $meta = $this->pdo->query(" - SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->pdo->quote($table)} + $meta = $this->pdo->query(<<pdo->quote($table)} UNION ALL - SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->pdo->quote($table)} - ")->fetch(); + SELECT sql + FROM sqlite_temp_master + WHERE type = 'table' AND name = {$this->pdo->quote($table)} + X)->fetch(); $columns = []; foreach ($this->pdo->query("PRAGMA table_info({$this->delimite($table)})", \PDO::FETCH_ASSOC) as $row) { diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index d17f93499..951699a53 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -99,7 +99,7 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { $tables = []; - foreach ($this->pdo->query(" + foreach ($this->pdo->query(<<<'X' SELECT name, CASE type @@ -110,7 +110,7 @@ public function getTables(): array sys.objects WHERE type IN ('U', 'V') - ") as $row) { + X) as $row) { $tables[] = [ 'name' => $row['name'], 'view' => (bool) $row['view'], @@ -124,7 +124,7 @@ public function getTables(): array public function getColumns(string $table): array { $columns = []; - foreach ($this->pdo->query(" + foreach ($this->pdo->query(<<pdo->quote($table)} - ", \PDO::FETCH_ASSOC) as $row) { + X, \PDO::FETCH_ASSOC) as $row) { $row['vendor'] = $row; $row['nullable'] = (bool) $row['nullable']; $row['autoincrement'] = (bool) $row['autoincrement']; @@ -162,7 +162,7 @@ public function getColumns(string $table): array public function getIndexes(string $table): array { $indexes = []; - foreach ($this->pdo->query(" + foreach ($this->pdo->query(<<pdo->query(" + foreach ($this->pdo->query(<<pdo->quote($table)} - ", \PDO::FETCH_ASSOC) as $row) { + X, \PDO::FETCH_ASSOC) as $row) { $keys[$row['name']] = $row; } diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 40829faca..1829d8123 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -99,16 +99,18 @@ public function process(array $params, bool $useParams = false): array $this->arrayMode = null; $res[] = Nette\Utils\Strings::replace( $param, - '~ - \'[^\']*+\' - |"[^"]*+" - |\?[a-z]* - |^\s*+(?:\(?\s*SELECT|INSERT|UPDATE|DELETE|REPLACE|EXPLAIN)\b - |\b(?:SET|WHERE|HAVING|ORDER\ BY|GROUP\ BY|KEY\ UPDATE)(?=\s*$|\s*\?) - |\bIN\s+(?:\?|\(\?\)) - |/\*.*?\*/ - |--[^\n]* - ~Dsix', + <<<'X' + ~ + '[^']*+' + |"[^"]*+" + |\?[a-z]* + |^\s*+(?:\(?\s*SELECT|INSERT|UPDATE|DELETE|REPLACE|EXPLAIN)\b + |\b(?:SET|WHERE|HAVING|ORDER\ BY|GROUP\ BY|KEY\ UPDATE)(?=\s*$|\s*\?) + |\bIN\s+(?:\?|\(\?\)) + |/\*.*?\*/ + |--[^\n]* + ~Dsix + X, \Closure::fromCallable([$this, 'callback']), ); } else { From c5a758d7dd918cd8b5c26b5291098506f4d5829f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 2 Jun 2022 13:40:50 +0200 Subject: [PATCH 26/35] some others methods moved to PdoDriver --- src/Database/Connection.php | 23 +++------ src/Database/Driver.php | 20 +++++++- src/Database/Drivers/PdoDriver.php | 76 ++++++++++++++++++++++++++++++ src/Database/ResultSet.php | 24 ++-------- 4 files changed, 107 insertions(+), 36 deletions(-) diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 1be911d67..83c7792a3 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -11,8 +11,6 @@ use Nette; use Nette\Utils\Arrays; -use PDO; -use PDOException; /** @@ -98,7 +96,7 @@ public function getDsn(): string /** deprecated use getDriver()->getPdo() */ - public function getPdo(): PDO + public function getPdo(): \PDO { $this->connect(); return $this->driver->getPdo(); @@ -130,22 +128,15 @@ public function setRowNormalizer(?callable $normalizer): self public function getInsertId(?string $sequence = null): string { - try { - $res = $this->getPdo()->lastInsertId($sequence); - return $res === false ? '0' : $res; - } catch (PDOException $e) { - throw $this->driver->convertException($e); - } + $this->connect(); + return $this->driver->getInsertId($sequence); } - 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); - } + $this->connect(); + return $this->driver->quote($string); } @@ -215,7 +206,7 @@ public function query(string $sql, ...$params): ResultSet [$this->sql, $params] = $this->preprocess($sql, ...$params); try { $result = new ResultSet($this, $this->sql, $params, $this->rowNormalizer); - } catch (PDOException $e) { + } catch (\PDOException $e) { Arrays::invoke($this->onQuery, $this, $e); throw $e; } diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 2d7a6991d..566f17c5f 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -34,8 +34,26 @@ function connect(string $dsn, ?string $user = null, ?string $password = null, ?a */ function convertException(\PDOException $e): DriverException; + function query(string $queryString, array $params); + + function beginTransaction(): void; + + function commit(): void; + + function rollBack(): void; + + /** + * Returns the ID of the last inserted row or sequence value. + */ + function getInsertId(?string $sequence = null): string; + + /** + * Delimits string for use in SQL statement. + */ + function quote(string $string): string; + /** - * Delimites identifier for use in a SQL statement. + * Delimits identifier for use in SQL statement. */ function delimite(string $name): string; diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php index 4ab63a2ee..6953a9dee 100644 --- a/src/Database/Drivers/PdoDriver.php +++ b/src/Database/Drivers/PdoDriver.php @@ -10,6 +10,7 @@ namespace Nette\Database\Drivers; use Nette; +use Nette\Database\DriverException; use PDO; use PDOException; @@ -45,4 +46,79 @@ public function getPdo(): ?PDO { return $this->pdo; } + + + public function query(string $queryString, array $params) + { + try { + $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; + + $statement = $this->pdo->prepare($queryString); + foreach ($params as $key => $value) { + $type = gettype($value); + $statement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[$type] ?? PDO::PARAM_STR); + } + + $statement->setFetchMode(PDO::FETCH_ASSOC); + @$statement->execute(); // @ PHP generates warning when ATTR_ERRMODE = ERRMODE_EXCEPTION bug #73878 + return $statement; + + } catch (PDOException $e) { + $e = $this->convertException($e); + $e->queryString = $queryString; + $e->params = $params; + throw $e; + } + } + + + public function beginTransaction(): void + { + try { + $this->pdo->beginTransaction(); + } catch (PDOException $e) { + throw $this->convertException($e); + } + } + + + public function commit(): void + { + try { + $this->pdo->commit(); + } catch (PDOException $e) { + throw $this->convertException($e); + } + } + + + public function rollBack(): void + { + try { + $this->pdo->rollBack(); + } catch (PDOException $e) { + throw $this->convertException($e); + } + } + + + public function getInsertId(?string $sequence = null): string + { + try { + $res = $this->pdo->lastInsertId($sequence); + return $res === false ? '0' : $res; + } catch (PDOException $e) { + throw $this->convertException($e); + } + } + + + public function quote(string $string, int $type = PDO::PARAM_STR): string + { + try { + return $this->pdo->quote($string, $type); + } catch (PDOException $e) { + throw DriverException::from($e); + } + } } diff --git a/src/Database/ResultSet.php b/src/Database/ResultSet.php index ef8e5ba3d..85d7ea7a7 100644 --- a/src/Database/ResultSet.php +++ b/src/Database/ResultSet.php @@ -50,25 +50,11 @@ public function __construct( $this->params = $params; $this->normalizer = $normalizer; - try { - if (str_starts_with($queryString, '::')) { - $connection->getPdo()->{substr($queryString, 2)}(); - } elseif ($queryString !== null) { - $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL, ]; - $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(); // @ PHP generates warning when ATTR_ERRMODE = ERRMODE_EXCEPTION bug #73878 - } - } catch (\PDOException $e) { - $e = $connection->getDriver()->convertException($e); - $e->queryString = $queryString; - $e->params = $params; - throw $e; + $driver = $connection->getDriver(); + if (str_starts_with($queryString, '::')) { + $driver->{substr($queryString, 2)}(); + } elseif ($queryString !== null) { + $this->pdoStatement = $driver->query($queryString, $params); } $this->time = microtime(true) - $time; From 95681b9f207d3ec085167a8106a8bb62707f1863 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 21 Sep 2021 01:09:28 +0200 Subject: [PATCH 27/35] added ResultDriver --- src/Database/Connection.php | 2 +- src/Database/Driver.php | 7 +-- src/Database/Drivers/PdoDriver.php | 4 +- src/Database/Drivers/PdoResultDriver.php | 74 ++++++++++++++++++++++++ src/Database/Helpers.php | 6 +- src/Database/ResultDriver.php | 42 ++++++++++++++ src/Database/ResultSet.php | 29 ++++++---- src/Database/Table/Selection.php | 4 +- 8 files changed, 143 insertions(+), 25 deletions(-) create mode 100644 src/Database/Drivers/PdoResultDriver.php create mode 100644 src/Database/ResultDriver.php diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 83c7792a3..e69257269 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -206,7 +206,7 @@ public function query(string $sql, ...$params): ResultSet [$this->sql, $params] = $this->preprocess($sql, ...$params); try { $result = new ResultSet($this, $this->sql, $params, $this->rowNormalizer); - } catch (\PDOException $e) { + } catch (DriverException $e) { Arrays::invoke($this->onQuery, $this, $e); throw $e; } diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 566f17c5f..483de58fc 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -34,7 +34,7 @@ function connect(string $dsn, ?string $user = null, ?string $password = null, ?a */ function convertException(\PDOException $e): DriverException; - function query(string $queryString, array $params); + function query(string $queryString, array $params): ResultDriver; function beginTransaction(): void; @@ -102,11 +102,6 @@ function getIndexes(string $table): array; */ function getForeignKeys(string $table): array; - /** - * Returns associative array of detected types (IStructure::FIELD_*) in result set. - */ - function getColumnTypes(\PDOStatement $statement): array; - /** * Cheks if driver supports specific property * @param string $item self::SUPPORT_* property diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php index 6953a9dee..15da64ef8 100644 --- a/src/Database/Drivers/PdoDriver.php +++ b/src/Database/Drivers/PdoDriver.php @@ -48,7 +48,7 @@ public function getPdo(): ?PDO } - public function query(string $queryString, array $params) + public function query(string $queryString, array $params): PdoResultDriver { try { $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL]; @@ -61,7 +61,7 @@ public function query(string $queryString, array $params) $statement->setFetchMode(PDO::FETCH_ASSOC); @$statement->execute(); // @ PHP generates warning when ATTR_ERRMODE = ERRMODE_EXCEPTION bug #73878 - return $statement; + return new PdoResultDriver($statement, $this); } catch (PDOException $e) { $e = $this->convertException($e); diff --git a/src/Database/Drivers/PdoResultDriver.php b/src/Database/Drivers/PdoResultDriver.php new file mode 100644 index 000000000..27d348340 --- /dev/null +++ b/src/Database/Drivers/PdoResultDriver.php @@ -0,0 +1,74 @@ +result = $result; + $this->driver = $driver; + } + + + public function fetch(): ?array + { + $data = $this->result->fetch(); + if (!$data) { + $this->result->closeCursor(); + return null; + } + + return $data; + } + + + public function getColumnCount(): int + { + return $this->result->columnCount(); + } + + + public function getRowCount(): int + { + return $this->result->rowCount(); + } + + + public function getColumnTypes(): array + { + return $this->driver->getColumnTypes($this->result); + } + + + public function getColumnMeta(int $col): array + { + return $this->result->getColumnMeta($col); + } + + + public function getPdoStatement(): \PDOStatement + { + return $this->result; + } +} diff --git a/src/Database/Helpers.php b/src/Database/Helpers.php index 09d558467..e2dbcedc1 100644 --- a/src/Database/Helpers.php +++ b/src/Database/Helpers.php @@ -326,11 +326,11 @@ public static function toPairs(array $rows, $key = null, $value = null): array /** * Finds duplicate columns in select statement */ - public static function findDuplicates(\PDOStatement $statement): string + public static function findDuplicates(ResultDriver $result): string { $cols = []; - for ($i = 0; $i < $statement->columnCount(); $i++) { - $meta = $statement->getColumnMeta($i); + for ($i = 0; $i < $result->getColumnCount(); $i++) { + $meta = $result->getColumnMeta($i); $cols[$meta['name']][] = $meta['table'] ?? ''; } diff --git a/src/Database/ResultDriver.php b/src/Database/ResultDriver.php new file mode 100644 index 000000000..dc19e8bf6 --- /dev/null +++ b/src/Database/ResultDriver.php @@ -0,0 +1,42 @@ +{substr($queryString, 2)}(); } elseif ($queryString !== null) { - $this->pdoStatement = $driver->query($queryString, $params); + $this->result = $driver->query($queryString, $params); } $this->time = microtime(true) - $time; @@ -73,7 +72,7 @@ public function getConnection(): Connection */ public function getPdoStatement(): ?\PDOStatement { - return $this->pdoStatement; + return $this->result->getPDOStatement(); } @@ -91,20 +90,20 @@ 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(); } public function getColumnTypes(): array { if (!isset($this->types)) { - $this->types = $this->connection->getDriver()->getColumnTypes($this->pdoStatement); + $this->types = $this->result->getColumnTypes(); } return $this->types; @@ -182,13 +181,12 @@ public function valid(): bool */ public function fetch(): ?Row { - $data = $this->pdoStatement ? $this->pdoStatement->fetch() : null; - if (!$data) { - $this->pdoStatement->closeCursor(); + $data = $this->result?->fetch(); + if ($data === null) { return null; - } elseif (!isset($this->lastRow) && count($data) !== $this->pdoStatement->columnCount()) { - $duplicates = Helpers::findDuplicates($this->pdoStatement); + } elseif (!isset($this->lastRow) && count($data) !== $this->result->getColumnCount()) { + $duplicates = Helpers::findDuplicates($this->result); trigger_error("Found duplicate columns in database result set: $duplicates.", E_USER_NOTICE); } @@ -204,6 +202,13 @@ public function fetch(): ?Row } + /** @internal */ + public function fetchArray(): ?array + { + return $this->result?->fetch(); + } + + /** * Fetches single field. */ diff --git a/src/Database/Table/Selection.php b/src/Database/Table/Selection.php index 8abdfe321..aa3c7b28c 100644 --- a/src/Database/Table/Selection.php +++ b/src/Database/Table/Selection.php @@ -535,11 +535,13 @@ protected function execute(): void $this->rows = []; $usedPrimary = true; - foreach ($result->getPdoStatement() as $key => $row) { + $key = 0; + while ($row = $result->fetchArray()) { $row = $this->createRow($result->normalizeRow($row)); $primary = $row->getSignature(false); $usedPrimary = $usedPrimary && $primary !== ''; $this->rows[$usedPrimary ? $primary : $key] = $row; + $key++; } $this->data = $this->rows; From cf01b127717b312faebfb0c2c45d48f70aa0a6d0 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 21 Sep 2021 00:08:03 +0200 Subject: [PATCH 28/35] exceptions are converted in PdoDriver --- src/Database/Driver.php | 5 --- src/Database/DriverException.php | 35 ++++++++++--------- src/Database/Drivers/MsSqlDriver.php | 9 ----- src/Database/Drivers/MySqlDriver.php | 12 +++---- src/Database/Drivers/OciDriver.php | 10 +++--- src/Database/Drivers/OdbcDriver.php | 9 ----- src/Database/Drivers/PdoDriver.php | 28 +++++++++++++-- src/Database/Drivers/PgSqlDriver.php | 14 ++++---- src/Database/Drivers/SqliteDriver.php | 12 +++---- src/Database/Drivers/SqlsrvDriver.php | 6 ---- tests/Database/Connection.lazy.phpt | 2 +- .../Database/Explorer/Selection.insert().phpt | 2 +- tests/Database/Explorer/bugs/bug216.phpt | 2 +- tests/Database/connect.inc.php | 2 +- 14 files changed, 72 insertions(+), 76 deletions(-) diff --git a/src/Database/Driver.php b/src/Database/Driver.php index 483de58fc..a6f03ff59 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -29,11 +29,6 @@ interface Driver */ function connect(string $dsn, ?string $user = null, ?string $password = null, ?array $options = null): void; - /** - * Converts PDOException to DriverException or its descendant. - */ - function convertException(\PDOException $e): DriverException; - function query(string $queryString, array $params): ResultDriver; function beginTransaction(): void; diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index 008ce0bb1..d7f6aed51 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -13,41 +13,42 @@ /** * 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; + private int|string|null $driverCode = null; + private string|null $sqlState = null; - public static function from(\PDOException $src): static + public function __construct(string $message = '', $code = 0, ?\Throwable $previous = null) { - $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; + parent::__construct($message, 0, $previous); + $this->code = $code; + if ($previous) { + $this->file = $previous->file; + $this->line = $previous->line; } + } - return $e; + + /** @internal */ + public function setDriverCode(string $state, int|string $code): void + { + $this->sqlState = $state; + $this->driverCode = $code; } public function getDriverCode(): int|string|null { - return $this->errorInfo[1] ?? null; + return $this->driverCode; } public function getSqlState(): ?string { - return $this->errorInfo[0] ?? null; + return $this->sqlState; } diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index d24db5d27..91d5eb490 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -17,15 +17,6 @@ */ class MsSqlDriver extends PdoDriver { - public function convertException(\PDOException $e): Nette\Database\DriverException - { - return Nette\Database\DriverException::from($e); - } - - - /********************* SQL ****************d*g**/ - - public function delimite(string $name): string { // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index eeb9c4d0d..5a71239a6 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -49,23 +49,23 @@ public function connect( } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function detectExceptionClass(\PDOException $e): ?string { $code = $e->errorInfo[1] ?? null; if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); + return Nette\Database\ForeignKeyConstraintViolationException::class; } elseif (in_array($code, [1062, 1557, 1569, 1586], true)) { - return Nette\Database\UniqueConstraintViolationException::from($e); + return Nette\Database\UniqueConstraintViolationException::class; } elseif ($code >= 2001 && $code <= 2028) { - return Nette\Database\ConnectionException::from($e); + return Nette\Database\ConnectionException::class; } elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], true)) { - return Nette\Database\NotNullConstraintViolationException::from($e); + return Nette\Database\NotNullConstraintViolationException::class; } else { - return Nette\Database\DriverException::from($e); + return null; } } diff --git a/src/Database/Drivers/OciDriver.php b/src/Database/Drivers/OciDriver.php index 47e77ff30..4160eae41 100644 --- a/src/Database/Drivers/OciDriver.php +++ b/src/Database/Drivers/OciDriver.php @@ -34,20 +34,20 @@ public function connect( } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function detectExceptionClass(\PDOException $e): ?string { $code = $e->errorInfo[1] ?? null; if (in_array($code, [1, 2299, 38911], true)) { - return Nette\Database\UniqueConstraintViolationException::from($e); + return Nette\Database\UniqueConstraintViolationException::class; } elseif (in_array($code, [1400], true)) { - return Nette\Database\NotNullConstraintViolationException::from($e); + return Nette\Database\NotNullConstraintViolationException::class; } elseif (in_array($code, [2266, 2291, 2292], true)) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); + return Nette\Database\ForeignKeyConstraintViolationException::class; } else { - return Nette\Database\DriverException::from($e); + return null; } } diff --git a/src/Database/Drivers/OdbcDriver.php b/src/Database/Drivers/OdbcDriver.php index 651caa52c..050dffe44 100644 --- a/src/Database/Drivers/OdbcDriver.php +++ b/src/Database/Drivers/OdbcDriver.php @@ -17,15 +17,6 @@ */ class OdbcDriver extends PdoDriver { - public function convertException(\PDOException $e): Nette\Database\DriverException - { - return Nette\Database\DriverException::from($e); - } - - - /********************* SQL ****************d*g**/ - - public function delimite(string $name): string { return '[' . str_replace(['[', ']'], ['[[', ']]'], $name) . ']'; diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php index 15da64ef8..f8e3c7a9a 100644 --- a/src/Database/Drivers/PdoDriver.php +++ b/src/Database/Drivers/PdoDriver.php @@ -37,7 +37,7 @@ public function connect( $this->pdo = new PDO($dsn, $user, $password, $options); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { - throw Nette\Database\ConnectionException::from($e); + throw $this->convertException($e, Nette\Database\ConnectionException::class); } } @@ -118,7 +118,31 @@ public function quote(string $string, int $type = PDO::PARAM_STR): string try { return $this->pdo->quote($string, $type); } catch (PDOException $e) { - throw DriverException::from($e); + throw $this->convertException($e); } } + + + public function convertException(\PDOException $src, ?string $class = null): DriverException + { + if ($src->errorInfo) { + [$sqlState, $driverCode] = $src->errorInfo; + } elseif (preg_match('#SQLSTATE\[(.*?)\] \[(.*?)\] (.*)#A', $src->getMessage(), $m)) { + [, $sqlState, $driverCode] = $m; + } + + $class = $this->detectExceptionClass($src) ?? $class ?? DriverException::class; + $e = new $class($src->getMessage(), $sqlState ?? $src->getCode(), $src); + if (isset($sqlState)) { + $e->setDriverCode($sqlState, (int) $driverCode); + } + + return $e; + } + + + public function detectExceptionClass(\PDOException $e): ?string + { + return null; + } } diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index b0daf9e37..c0afa582f 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -17,26 +17,26 @@ */ class PgSqlDriver extends PdoDriver { - public function convertException(\PDOException $e): Nette\Database\DriverException + public function detectExceptionClass(\PDOException $e): ?string { $code = $e->errorInfo[0] ?? null; if ($code === '0A000' && str_contains($e->getMessage(), 'truncate')) { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); + return Nette\Database\ForeignKeyConstraintViolationException::class; } elseif ($code === '23502') { - return Nette\Database\NotNullConstraintViolationException::from($e); + return Nette\Database\NotNullConstraintViolationException::class; } elseif ($code === '23503') { - return Nette\Database\ForeignKeyConstraintViolationException::from($e); + return Nette\Database\ForeignKeyConstraintViolationException::class; } elseif ($code === '23505') { - return Nette\Database\UniqueConstraintViolationException::from($e); + return Nette\Database\UniqueConstraintViolationException::class; } elseif ($code === '08006') { - return Nette\Database\ConnectionException::from($e); + return Nette\Database\ConnectionException::class; } else { - return Nette\Database\DriverException::from($e); + return null; } } diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index fb9eb7d0b..dd5fda86e 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -34,34 +34,34 @@ public function connect( } - public function convertException(\PDOException $e): Nette\Database\DriverException + public function detectExceptionClass(\PDOException $e): ?string { $code = $e->errorInfo[1] ?? null; $msg = $e->getMessage(); if ($code !== 19) { - return Nette\Database\DriverException::from($e); + return null; } elseif ( str_contains($msg, 'must be unique') || str_contains($msg, 'is not unique') || str_contains($msg, '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') ) { - 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') ) { - 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/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 951699a53..bbda633bc 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -33,12 +33,6 @@ public function connect( } - public function convertException(\PDOException $e): Nette\Database\DriverException - { - return Nette\Database\DriverException::from($e); - } - - /********************* SQL ****************d*g**/ diff --git a/tests/Database/Connection.lazy.phpt b/tests/Database/Connection.lazy.phpt index c7f9d7483..9114e8b73 100644 --- a/tests/Database/Connection.lazy.phpt +++ b/tests/Database/Connection.lazy.phpt @@ -44,7 +44,7 @@ test('connect & disconnect', function () { try { $connection = new Nette\Database\Connection($options['dsn'], $options['user'], $options['password']); - } catch (PDOException $e) { + } catch (Nette\Database\ConnectionException $e) { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } diff --git a/tests/Database/Explorer/Selection.insert().phpt b/tests/Database/Explorer/Selection.insert().phpt index 2507f2564..ac79ef56b 100644 --- a/tests/Database/Explorer/Selection.insert().phpt +++ b/tests/Database/Explorer/Selection.insert().phpt @@ -47,7 +47,7 @@ if ($driverName !== 'sqlsrv') { 'name' => 'Jon Snow', 'web' => 'http://example.com', ]); - }, PDOException::class); + }, Nette\Database\DriverException::class); } diff --git a/tests/Database/Explorer/bugs/bug216.phpt b/tests/Database/Explorer/bugs/bug216.phpt index bd43dd239..03ae60b2a 100644 --- a/tests/Database/Explorer/bugs/bug216.phpt +++ b/tests/Database/Explorer/bugs/bug216.phpt @@ -16,7 +16,7 @@ $options = Tester\Environment::loadData() + ['user' => null, 'password' => null] try { $connection = new Nette\Database\Connection($options['dsn'], $options['user'], $options['password']); -} catch (PDOException $e) { +} catch (Nette\Database\ConnectionException $e) { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } diff --git a/tests/Database/connect.inc.php b/tests/Database/connect.inc.php index 64020ee84..6defd138f 100644 --- a/tests/Database/connect.inc.php +++ b/tests/Database/connect.inc.php @@ -13,7 +13,7 @@ try { $connection = new Nette\Database\Connection($options['dsn'], $options['user'], $options['password']); -} catch (PDOException $e) { +} catch (Nette\Database\ConnectionException $e) { Tester\Environment::skip("Connection to '$options[dsn]' failed. Reason: " . $e->getMessage()); } From 46aa9ee543c2ac39b44a899d0bf883365723e510 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 21 Sep 2021 01:06:36 +0200 Subject: [PATCH 29/35] added Nette\Database\QueryException --- src/Database/DriverException.php | 14 ----------- src/Database/Drivers/PdoDriver.php | 5 ++-- src/Database/QueryException.php | 37 ++++++++++++++++++++++++++++++ src/Database/exceptions.php | 2 +- 4 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 src/Database/QueryException.php diff --git a/src/Database/DriverException.php b/src/Database/DriverException.php index d7f6aed51..af9cee5b8 100644 --- a/src/Database/DriverException.php +++ b/src/Database/DriverException.php @@ -15,8 +15,6 @@ */ class DriverException extends \Exception { - public ?string $queryString = null; - public ?array $params = null; private int|string|null $driverCode = null; private string|null $sqlState = null; @@ -50,16 +48,4 @@ public function getSqlState(): ?string { return $this->sqlState; } - - - public function getQueryString(): ?string - { - return $this->queryString; - } - - - public function getParameters(): ?array - { - return $this->params; - } } diff --git a/src/Database/Drivers/PdoDriver.php b/src/Database/Drivers/PdoDriver.php index f8e3c7a9a..eb0c8e301 100644 --- a/src/Database/Drivers/PdoDriver.php +++ b/src/Database/Drivers/PdoDriver.php @@ -64,9 +64,8 @@ public function query(string $queryString, array $params): PdoResultDriver return new PdoResultDriver($statement, $this); } catch (PDOException $e) { - $e = $this->convertException($e); - $e->queryString = $queryString; - $e->params = $params; + $e = $this->convertException($e, Nette\Database\QueryException::class); + $e->setQueryInfo($queryString, $params); throw $e; } } diff --git a/src/Database/QueryException.php b/src/Database/QueryException.php new file mode 100644 index 000000000..3ab5ce014 --- /dev/null +++ b/src/Database/QueryException.php @@ -0,0 +1,37 @@ +queryString = $queryString; + $this->params = $params; + } + + + public function getQueryString(): string + { + return $this->queryString; + } + + + public function getParameters(): array + { + return $this->params; + } +} diff --git a/src/Database/exceptions.php b/src/Database/exceptions.php index c968ef2b7..d6e1bfd77 100644 --- a/src/Database/exceptions.php +++ b/src/Database/exceptions.php @@ -21,7 +21,7 @@ class ConnectionException extends DriverException /** * Base class for all constraint violation related exceptions. */ -class ConstraintViolationException extends DriverException +class ConstraintViolationException extends QueryException { } From 652309a307963946e93ad1e5a9f2c4a02e4578ea Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 20 Jan 2022 01:15:08 +0100 Subject: [PATCH 30/35] Driver::getColumns() removed case convert for 'nativetype' and keys in 'vendor' (BC break) --- src/Database/Drivers/MsSqlDriver.php | 2 +- src/Database/Drivers/MySqlDriver.php | 15 +++++++-------- src/Database/Drivers/PgSqlDriver.php | 2 +- src/Database/Drivers/SqlsrvDriver.php | 2 +- tests/Database/Reflection.phpt | 11 ++++++----- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 91d5eb490..50b9b2610 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -102,7 +102,7 @@ public function getColumns(string $table): array $columns[] = [ 'name' => $row['COLUMN_NAME'], 'table' => $table, - 'nativetype' => strtoupper($row['DATA_TYPE']), + 'nativetype' => $row['DATA_TYPE'], 'size' => $row['CHARACTER_MAXIMUM_LENGTH'] ?? ($row['NUMERIC_PRECISION'] ?? null), 'unsigned' => false, 'nullable' => $row['IS_NULLABLE'] === 'YES', diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 5a71239a6..569d1826a 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -134,17 +134,16 @@ public function getColumns(string $table): array { $columns = []; foreach ($this->pdo->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table), \PDO::FETCH_ASSOC) as $row) { - $row = array_change_key_case($row, CASE_LOWER); - $type = explode('(', $row['type']); + $type = explode('(', $row['Type']); $columns[] = [ - 'name' => $row['field'], + 'name' => $row['Field'], 'table' => $table, - 'nativetype' => strtoupper($type[0]), + 'nativetype' => $type[0], 'size' => isset($type[1]) ? (int) $type[1] : null, - 'nullable' => $row['null'] === 'YES', - 'default' => $row['default'], - 'autoincrement' => $row['extra'] === 'auto_increment', - 'primary' => $row['key'] === 'PRI', + 'nullable' => $row['Null'] === 'YES', + 'default' => $row['Default'], + '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 c0afa582f..afd2e17c0 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -117,7 +117,7 @@ public function getColumns(string $table): array SELECT a.attname::varchar AS name, c.relname::varchar AS table, - upper(t.typname) AS nativetype, + t.typname AS nativetype, CASE WHEN a.atttypmod = -1 THEN NULL ELSE a.atttypmod -4 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, diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index bbda633bc..2477dd5ad 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -122,7 +122,7 @@ public function getColumns(string $table): array SELECT c.name AS name, o.name AS [table], - UPPER(t.name) AS nativetype, + t.name AS nativetype, NULL AS size, c.is_nullable AS nullable, OBJECT_DEFINITION(c.default_object_id) AS [default], diff --git a/tests/Database/Reflection.phpt b/tests/Database/Reflection.phpt index 5b60e3bc4..78c6d4623 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -50,7 +50,7 @@ $expectedColumns = [ [ 'name' => 'id', 'table' => 'author', - 'nativetype' => 'INT', + 'nativetype' => 'int', 'size' => 11, 'nullable' => false, 'default' => null, @@ -60,7 +60,7 @@ $expectedColumns = [ [ 'name' => 'name', 'table' => 'author', - 'nativetype' => 'VARCHAR', + 'nativetype' => 'varchar', 'size' => 30, 'nullable' => false, 'default' => null, @@ -70,7 +70,7 @@ $expectedColumns = [ [ 'name' => 'web', 'table' => 'author', - 'nativetype' => 'VARCHAR', + 'nativetype' => 'varchar', 'size' => 100, 'nullable' => false, 'default' => null, @@ -80,7 +80,7 @@ $expectedColumns = [ [ 'name' => 'born', 'table' => 'author', - 'nativetype' => 'DATE', + 'nativetype' => 'date', 'size' => null, 'nullable' => true, 'default' => null, @@ -97,7 +97,7 @@ switch ($driverName) { } break; case 'pgsql': - $expectedColumns[0]['nativetype'] = 'INT4'; + $expectedColumns[0]['nativetype'] = 'int4'; $expectedColumns[0]['default'] = "nextval('author_id_seq'::regclass)"; $expectedColumns[0]['size'] = null; break; @@ -108,6 +108,7 @@ switch ($driverName) { $expectedColumns[1]['size'] = null; $expectedColumns[2]['nativetype'] = 'TEXT'; $expectedColumns[2]['size'] = null; + $expectedColumns[3]['nativetype'] = 'DATE'; break; case 'sqlsrv': $expectedColumns[0]['size'] = null; From 88bbccfb387d70ab31ee5ed9aebe1acc72849140 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 19 Jan 2022 12:42:09 +0100 Subject: [PATCH 31/35] drivers: getForeignKeys() works with multi-column foreign keys --- src/Database/Drivers/MsSqlDriver.php | 9 +++++---- src/Database/Drivers/MySqlDriver.php | 9 +++++---- src/Database/Drivers/PgSqlDriver.php | 13 +++++++++++-- src/Database/Drivers/SqliteDriver.php | 7 +++++-- src/Database/Drivers/SqlsrvDriver.php | 6 +++++- src/Database/Structure.php | 12 +++--------- tests/Database/Reflection.postgre.phpt | 4 ++-- tests/Database/Structure.phpt | 8 ++++---- tests/Database/Structure.schemas.phpt | 4 ++-- 9 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 50b9b2610..538503237 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -181,11 +181,12 @@ public function getForeignKeys(string $table): array tab1.name = {$this->pdo->quote($table_name)} X; - foreach ($this->pdo->query($query) as $id => $row) { - $keys[$id]['name'] = $row['fk_name']; - $keys[$id]['local'] = $row['column']; + foreach ($this->pdo->query($query) as $row) { + $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/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 569d1826a..34127aaae 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -176,11 +176,12 @@ public function getForeignKeys(string $table): array WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = {$this->pdo->quote($table)} - X) as $id => $row) { - $keys[$id]['name'] = $row['CONSTRAINT_NAME']; - $keys[$id]['local'] = $row['COLUMN_NAME']; + X) as $row) { + $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/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index afd2e17c0..c4c66fd13 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -182,7 +182,8 @@ public function getIndexes(string $table): array public function getForeignKeys(string $table): array { /* Doesn't work with multi-column foreign keys */ - return $this->pdo->query(<<pdo->query(<<pdo->quote($this->delimiteFQN($table))}::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) - X)->fetchAll(\PDO::FETCH_ASSOC); + X) as $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/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index dd5fda86e..58c581bfc 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -217,9 +217,12 @@ public function getForeignKeys(string $table): array foreach ($this->pdo->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { $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/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 2477dd5ad..77fad9b13 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -207,7 +207,11 @@ public function getForeignKeys(string $table): array WHERE tl.name = {$this->pdo->quote($table)} X, \PDO::FETCH_ASSOC) as $row) { - $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['column']; } return array_values($keys); diff --git a/src/Database/Structure.php b/src/Database/Structure.php index ebee29227..f34eddcb0 100644 --- a/src/Database/Structure.php +++ b/src/Database/Structure.php @@ -238,17 +238,11 @@ protected function analyzeForeignKeys(array &$structure, string $table): void $foreignKeys = $this->connection->getDriver()->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/Reflection.postgre.phpt b/tests/Database/Reflection.postgre.phpt index c5460661d..ab58272e5 100644 --- a/tests/Database/Reflection.postgre.phpt +++ b/tests/Database/Reflection.postgre.phpt @@ -63,9 +63,9 @@ test('Tables in schema', function () use ($connection) { $foreign = $driver->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 6c70d22ea..4a2c8d00c 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -78,13 +78,13 @@ class StructureTestCase extends TestCase $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([ - ['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->driver->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]); $this->driver->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 6152b2c95..5c9cabe46 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -64,8 +64,8 @@ class StructureSchemasTestCase extends TestCase $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([ - ['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 2b9ea0e37178b2f59920a5253657fb71f045236f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 20 Jan 2022 12:23:28 +0100 Subject: [PATCH 32/35] added entites for Table, Column, Index, ForeignKey (BC break) --- src/Database/Driver.php | 8 +- src/Database/Drivers/MsSqlDriver.php | 46 +++---- src/Database/Drivers/MySqlDriver.php | 45 +++---- src/Database/Drivers/PgSqlDriver.php | 35 ++--- src/Database/Drivers/SqliteDriver.php | 59 ++++---- src/Database/Drivers/SqlsrvDriver.php | 35 +++-- src/Database/Reflection/Column.php | 34 +++++ src/Database/Reflection/ForeignKey.php | 29 ++++ src/Database/Reflection/Index.php | 29 ++++ src/Database/Reflection/Table.php | 28 ++++ src/Database/Structure.php | 51 +++---- src/Database/Table/SqlBuilder.php | 2 +- .../Explorer/SqlBuilder.parseJoins().phpt | 2 +- tests/Database/Explorer/bugs/view.bug.phpt | 2 +- tests/Database/Reflection.phpt | 127 +++++++++--------- tests/Database/Reflection.postgre.10.phpt | 35 ++--- tests/Database/Reflection.postgre.phpt | 13 +- tests/Database/Structure.phpt | 61 +++++---- tests/Database/Structure.schemas.phpt | 19 +-- 19 files changed, 389 insertions(+), 271 deletions(-) create mode 100644 src/Database/Reflection/Column.php create mode 100644 src/Database/Reflection/ForeignKey.php create mode 100644 src/Database/Reflection/Index.php create mode 100644 src/Database/Reflection/Table.php diff --git a/src/Database/Driver.php b/src/Database/Driver.php index a6f03ff59..b1d742afd 100644 --- a/src/Database/Driver.php +++ b/src/Database/Driver.php @@ -75,25 +75,25 @@ function applyLimit(string &$sql, ?int $limit, ?int $offset): void; /********************* reflection ****************d*g**/ /** - * Returns list of tables as tuples [(string) name, (bool) view, [(string) fullName]] + * @return Reflection\Table[] */ function getTables(): array; /** * Returns metadata for all columns in a table. - * As tuples [(string) name, (string) table, (string) nativetype, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (bool) primary, (array) vendor]] + * @return Reflection\Column[] */ function getColumns(string $table): array; /** * Returns metadata for all indexes in a table. - * As tuples [(string) name, (string[]) columns, (bool) unique, (bool) primary] + * @return Reflection\Index[] */ function getIndexes(string $table): array; /** * Returns metadata for all foreign keys in a table. - * As tuples [(string) name, (string) local, (string) table, (string) foreign] + * @return Reflection\ForeignKey[] */ function getForeignKeys(string $table): array; diff --git a/src/Database/Drivers/MsSqlDriver.php b/src/Database/Drivers/MsSqlDriver.php index 538503237..13142cf8d 100644 --- a/src/Database/Drivers/MsSqlDriver.php +++ b/src/Database/Drivers/MsSqlDriver.php @@ -65,15 +65,10 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - $tables = []; - foreach ($this->pdo->query('SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES') as $row) { - $tables[] = [ - 'name' => $row['TABLE_SCHEMA'] . '.' . $row['TABLE_NAME'], - 'view' => ($row['TABLE_TYPE'] ?? null) === 'VIEW', - ]; - } - - return $tables; + return $this->pdo->query('SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES')->fetchAll( + \PDO::FETCH_FUNC, + fn($schema, $name, $type) => new Nette\Database\Reflection\Table($schema . '.' . $name, $type === 'VIEW'), + ); } @@ -99,18 +94,17 @@ public function getColumns(string $table): array X; foreach ($this->pdo->query($query, \PDO::FETCH_ASSOC) as $row) { - $columns[] = [ - 'name' => $row['COLUMN_NAME'], - 'table' => $table, - 'nativetype' => $row['DATA_TYPE'], - 'size' => $row['CHARACTER_MAXIMUM_LENGTH'] ?? ($row['NUMERIC_PRECISION'] ?? null), - 'unsigned' => false, - 'nullable' => $row['IS_NULLABLE'] === 'YES', - 'default' => $row['COLUMN_DEFAULT'], - 'autoincrement' => $row['DOMAIN_NAME'] === 'COUNTER', - 'primary' => $row['COLUMN_NAME'] === 'ID', - 'vendor' => $row, - ]; + $columns[] = new Nette\Database\Reflection\Column( + name: $row['COLUMN_NAME'], + table: $table, + nativeType: $row['DATA_TYPE'], + size: $row['CHARACTER_MAXIMUM_LENGTH'] ?? $row['NUMERIC_PRECISION'] ?? null, + nullable: $row['IS_NULLABLE'] === 'YES', + default: $row['COLUMN_DEFAULT'], + autoIncrement: $row['DOMAIN_NAME'] === 'COUNTER', + primary: $row['COLUMN_NAME'] === 'ID', + vendor: $row, + ); } return $columns; @@ -148,7 +142,7 @@ public function getIndexes(string $table): array $indexes[$id]['columns'][$row['id_column'] - 1] = $row['name_column']; } - return array_values($indexes); + return array_map(fn($data) => new Nette\Database\Reflection\Index(...$data), array_values($indexes)); } @@ -184,12 +178,12 @@ public function getForeignKeys(string $table): array foreach ($this->pdo->query($query) as $row) { $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]['columns'][] = $row['column']; + $keys[$id]['targetTable'] = $table_schema . '.' . $row['referenced_table']; + $keys[$id]['targetColumns'][] = $row['referenced_column']; } - return array_values($keys); + return array_map(fn($data) => new Nette\Database\Reflection\ForeignKey(...$data), array_values($keys)); } diff --git a/src/Database/Drivers/MySqlDriver.php b/src/Database/Drivers/MySqlDriver.php index 34127aaae..2615b24bc 100644 --- a/src/Database/Drivers/MySqlDriver.php +++ b/src/Database/Drivers/MySqlDriver.php @@ -118,15 +118,10 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - $tables = []; - foreach ($this->pdo->query('SHOW FULL TABLES') as $row) { - $tables[] = [ - 'name' => $row[0], - 'view' => ($row[1] ?? null) === 'VIEW', - ]; - } - - return $tables; + return $this->pdo->query('SHOW FULL TABLES')->fetchAll( + \PDO::FETCH_FUNC, + fn($name, $type) => new Nette\Database\Reflection\Table($name, $type === 'VIEW'), + ); } @@ -135,17 +130,17 @@ public function getColumns(string $table): array $columns = []; foreach ($this->pdo->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table), \PDO::FETCH_ASSOC) as $row) { $type = explode('(', $row['Type']); - $columns[] = [ - 'name' => $row['Field'], - 'table' => $table, - 'nativetype' => $type[0], - 'size' => isset($type[1]) ? (int) $type[1] : null, - 'nullable' => $row['Null'] === 'YES', - 'default' => $row['Default'], - 'autoincrement' => $row['Extra'] === 'auto_increment', - 'primary' => $row['Key'] === 'PRI', - 'vendor' => $row, - ]; + $columns[] = new Nette\Database\Reflection\Column( + name: $row['Field'], + table: $table, + nativeType: $type[0], + size: isset($type[1]) ? (int) $type[1] : null, + nullable: $row['Null'] === 'YES', + default: $row['Default'], + autoIncrement: $row['Extra'] === 'auto_increment', + primary: $row['Key'] === 'PRI', + vendor: $row, + ); } return $columns; @@ -163,7 +158,7 @@ public function getIndexes(string $table): array $indexes[$id]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name']; } - return array_values($indexes); + return array_map(fn($data) => new Nette\Database\Reflection\Index(...$data), array_values($indexes)); } @@ -179,12 +174,12 @@ public function getForeignKeys(string $table): array X) as $row) { $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]['columns'][] = $row['COLUMN_NAME']; + $keys[$id]['targetTable'] = $row['REFERENCED_TABLE_NAME']; + $keys[$id]['targetColumns'][] = $row['REFERENCED_COLUMN_NAME']; } - return array_values($keys); + return array_map(fn($data) => new Nette\Database\Reflection\ForeignKey(...$data), array_values($keys)); } diff --git a/src/Database/Drivers/PgSqlDriver.php b/src/Database/Drivers/PgSqlDriver.php index c4c66fd13..79596f066 100644 --- a/src/Database/Drivers/PgSqlDriver.php +++ b/src/Database/Drivers/PgSqlDriver.php @@ -95,9 +95,9 @@ public function getTables(): array { return $this->pdo->query(<<<'X' SELECT DISTINCT ON (c.relname) - c.relname::varchar AS name, - c.relkind IN ('v', 'm') AS view, - n.nspname::varchar || '.' || c.relname::varchar AS "fullName" + c.relname::varchar, + c.relkind IN ('v', 'm'), + n.nspname::varchar || '.' || c.relname::varchar FROM pg_catalog.pg_class AS c JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace @@ -106,7 +106,10 @@ public function getTables(): array AND n.nspname = ANY (pg_catalog.current_schemas(FALSE)) ORDER BY c.relname - X)->fetchAll(\PDO::FETCH_ASSOC); + X)->fetchAll( + \PDO::FETCH_FUNC, + fn($name, $view, $full) => new Nette\Database\Reflection\Table($name, $view, $full), + ); } @@ -117,11 +120,11 @@ public function getColumns(string $table): array SELECT a.attname::varchar AS name, c.relname::varchar AS table, - t.typname AS nativetype, + t.typname AS "nativeType", CASE WHEN a.atttypmod = -1 THEN NULL ELSE a.atttypmod -4 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 @@ -139,11 +142,11 @@ public function getColumns(string $table): array AND NOT a.attisdropped ORDER BY a.attnum - X, \PDO::FETCH_ASSOC) as $column) { - $column['vendor'] = $column; - unset($column['sequence']); - - $columns[] = $column; + X, \PDO::FETCH_ASSOC) as $row + ) { + $row['vendor'] = $row; + unset($row['sequence']); + $columns[] = new Nette\Database\Reflection\Column(...$row); } return $columns; @@ -175,7 +178,7 @@ public function getIndexes(string $table): array $indexes[$id]['columns'][] = $row['column']; } - return array_values($indexes); + return array_map(fn($data) => new Nette\Database\Reflection\Index(...$data), array_values($indexes)); } @@ -203,12 +206,12 @@ public function getForeignKeys(string $table): array X) as $row) { $id = $row['name']; $keys[$id]['name'] = $id; - $keys[$id]['local'][] = $row['local']; - $keys[$id]['table'] = $row['table']; - $keys[$id]['foreign'][] = $row['foreign']; + $keys[$id]['columns'][] = $row['local']; + $keys[$id]['targetTable'] = $row['table']; + $keys[$id]['targetColumns'][] = $row['foreign']; } - return array_values($keys); + return array_map(fn($data) => new Nette\Database\Reflection\ForeignKey(...$data), array_values($keys)); } diff --git a/src/Database/Drivers/SqliteDriver.php b/src/Database/Drivers/SqliteDriver.php index 58c581bfc..7469b3c47 100644 --- a/src/Database/Drivers/SqliteDriver.php +++ b/src/Database/Drivers/SqliteDriver.php @@ -111,9 +111,8 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - $tables = []; - foreach ($this->pdo->query(<<<'X' - SELECT name, type = 'view' as view + return $this->pdo->query(<<<'X' + SELECT name, type = 'view' FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' UNION ALL @@ -121,14 +120,10 @@ public function getTables(): array FROM sqlite_temp_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' ORDER BY name - X) as $row) { - $tables[] = [ - 'name' => $row['name'], - 'view' => (bool) $row['view'], - ]; - } - - return $tables; + X)->fetchAll( + \PDO::FETCH_FUNC, + fn($name, $view) => new Nette\Database\Reflection\Table($name, (bool) $view), + ); } @@ -149,17 +144,17 @@ public function getColumns(string $table): array $column = $row['name']; $pattern = "/(\"$column\"|`$column`|\\[$column\\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui"; $type = explode('(', $row['type']); - $columns[] = [ - 'name' => $column, - 'table' => $table, - 'nativetype' => strtoupper($type[0]), - 'size' => isset($type[1]) ? (int) $type[1] : null, - 'nullable' => !$row['notnull'], - 'default' => $row['dflt_value'], - 'autoincrement' => $meta && preg_match($pattern, (string) $meta['sql']), - 'primary' => $row['pk'] > 0, - 'vendor' => $row, - ]; + $columns[] = new Nette\Database\Reflection\Column( + name: $column, + table: $table, + nativeType: $type[0], + size: isset($type[1]) ? (int) $type[1] : null, + nullable: !$row['notnull'], + default: $row['dflt_value'], + autoIncrement: $meta && preg_match($pattern, (string) $meta['sql']), + primary: $row['pk'] > 0, + vendor: $row, + ); } return $columns; @@ -186,8 +181,8 @@ public function getIndexes(string $table): array foreach ($indexes as $index => $values) { $column = $indexes[$index]['columns'][0]; foreach ($columns as $info) { - if ($column === $info['name']) { - $indexes[$index]['primary'] = (bool) $info['primary']; + if ($column === $info->name) { + $indexes[$index]['primary'] = $info->primary; break; } } @@ -207,7 +202,7 @@ public function getIndexes(string $table): array } } - return array_values($indexes); + return array_map(fn($data) => new Nette\Database\Reflection\Index(...$data), array_values($indexes)); } @@ -216,16 +211,16 @@ public function getForeignKeys(string $table): array $keys = []; foreach ($this->pdo->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { $id = $row['id']; - $keys[$id]['name'] = $id; - $keys[$id]['local'][] = $row['from']; - $keys[$id]['table'] = $row['table']; - $keys[$id]['foreign'][] = $row['to']; - if ($keys[$id]['foreign'][0] == null) { - $keys[$id]['foreign'] = []; + $keys[$id]['name'] = (string) $id; + $keys[$id]['columns'][] = $row['from']; + $keys[$id]['targetTable'] = $row['table']; + $keys[$id]['targetColumns'][] = $row['to']; + if ($keys[$id]['targetColumns'][0] == null) { + $keys[$id]['targetColumns'] = []; } } - return array_values($keys); + return array_map(fn($data) => new Nette\Database\Reflection\ForeignKey(...$data), array_values($keys)); } diff --git a/src/Database/Drivers/SqlsrvDriver.php b/src/Database/Drivers/SqlsrvDriver.php index 77fad9b13..19bc68c45 100644 --- a/src/Database/Drivers/SqlsrvDriver.php +++ b/src/Database/Drivers/SqlsrvDriver.php @@ -92,26 +92,21 @@ public function applyLimit(string &$sql, ?int $limit, ?int $offset): void public function getTables(): array { - $tables = []; - foreach ($this->pdo->query(<<<'X' + return $this->pdo->query(<<<'X' SELECT name, CASE type WHEN 'U' THEN 0 WHEN 'V' THEN 1 - END AS [view] + END FROM sys.objects WHERE type IN ('U', 'V') - X) as $row) { - $tables[] = [ - 'name' => $row['name'], - 'view' => (bool) $row['view'], - ]; - } - - return $tables; + X)->fetchAll( + \PDO::FETCH_FUNC, + fn($name, $view) => new Nette\Database\Reflection\Table($name, (bool) $view), + ); } @@ -122,11 +117,11 @@ public function getColumns(string $table): array SELECT c.name AS name, o.name AS [table], - t.name AS nativetype, + t.name AS nativeType, NULL 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 @@ -143,10 +138,10 @@ public function getColumns(string $table): array X, \PDO::FETCH_ASSOC) as $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; + $columns[] = new Nette\Database\Reflection\Column(...$row); } return $columns; @@ -183,7 +178,7 @@ public function getIndexes(string $table): array $indexes[$id]['columns'][] = $row['column']; } - return array_values($indexes); + return array_map(fn($data) => new Nette\Database\Reflection\Index(...$data), array_values($indexes)); } @@ -209,12 +204,12 @@ public function getForeignKeys(string $table): array X, \PDO::FETCH_ASSOC) as $row) { $id = $row['name']; $keys[$id]['name'] = $id; - $keys[$id]['local'][] = $row['local']; - $keys[$id]['table'] = $row['table']; - $keys[$id]['foreign'][] = $row['column']; + $keys[$id]['columns'][] = $row['local']; + $keys[$id]['targetTable'] = $row['table']; + $keys[$id]['targetColumns'][] = $row['column']; } - return array_values($keys); + return array_map(fn($data) => new Nette\Database\Reflection\ForeignKey(...$data), array_values($keys)); } diff --git a/src/Database/Reflection/Column.php b/src/Database/Reflection/Column.php new file mode 100644 index 000000000..c2607ebbf --- /dev/null +++ b/src/Database/Reflection/Column.php @@ -0,0 +1,34 @@ +connection = $connection; - $this->cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Database.Structure.' . md5($this->connection->getDsn())); + $this->cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Database.Structure4.' . md5($this->connection->getDsn())); } + /** @return Reflection\Table[] */ public function getTables(): array { $this->needStructure(); @@ -42,6 +43,7 @@ public function getTables(): array } + /** @return Reflection\Column[] */ public function getColumns(string $table): array { $this->needStructure(); @@ -73,8 +75,8 @@ public function getPrimaryAutoincrementKey(string $table): ?string if (is_array($primaryKey)) { $keys = array_flip($primaryKey); foreach ($this->getColumns($table) as $column) { - if (isset($keys[$column['name']]) && $column['autoincrement']) { - return $column['name']; + if (isset($keys[$column->name]) && $column->autoIncrement) { + return $column->name; } } @@ -83,8 +85,8 @@ public function getPrimaryAutoincrementKey(string $table): ?string // Search for autoincrement key from simple primary key foreach ($this->getColumns($table) as $column) { - if ($column['name'] === $primaryKey) { - return $column['autoincrement'] ? $column['name'] : null; + if ($column->name === $primaryKey) { + return $column->autoIncrement ? $column->name : null; } } @@ -108,8 +110,8 @@ public function getPrimaryKeySequence(string $table): ?string // Search for sequence from simple primary key foreach ($this->structure['columns'][$table] as $columnMeta) { - if ($columnMeta['name'] === $autoincrementPrimaryKeyName) { - return $columnMeta['vendor']['sequence'] ?? null; + if ($columnMeta->name === $autoincrementPrimaryKeyName) { + return $columnMeta->vendor['sequence'] ?? null; } } @@ -185,25 +187,25 @@ protected function loadStructure(): array $structure = []; $structure['tables'] = $driver->getTables(); - foreach ($structure['tables'] as $tablePair) { - if (isset($tablePair['fullName'])) { - $table = $tablePair['fullName']; - $structure['aliases'][strtolower($tablePair['name'])] = strtolower($table); + foreach ($structure['tables'] as $table) { + if (isset($table->fullName)) { + $tableName = $table->fullName; + $structure['aliases'][strtolower($table->name)] = strtolower($tableName); } else { - $table = $tablePair['name']; + $tableName = $table->name; } - $structure['columns'][strtolower($table)] = $columns = $driver->getColumns($table); + $structure['columns'][strtolower($tableName)] = $columns = $driver->getColumns($tableName); - if (!$tablePair['view']) { - $structure['primary'][strtolower($table)] = $this->analyzePrimaryKey($columns); - $this->analyzeForeignKeys($structure, $table); + if (!$table->view) { + $structure['primary'][strtolower($tableName)] = $this->analyzePrimaryKey($columns); + $this->analyzeForeignKeys($structure, $tableName); } } if (isset($structure['hasMany'])) { - foreach ($structure['hasMany'] as &$table) { - uksort($table, fn($a, $b): int => strlen($a) <=> strlen($b)); + foreach ($structure['hasMany'] as &$tableName) { + uksort($tableName, fn($a, $b): int => strlen($a) <=> strlen($b)); } } @@ -213,12 +215,13 @@ protected function loadStructure(): array } + /** @param Reflection\Column[] $columns */ protected function analyzePrimaryKey(array $columns) { $primary = []; foreach ($columns as $column) { - if ($column['primary']) { - $primary[] = $column['name']; + if ($column->primary) { + $primary[] = $column->name; } } @@ -238,11 +241,11 @@ protected function analyzeForeignKeys(array &$structure, string $table): void $foreignKeys = $this->connection->getDriver()->getForeignKeys($table); - usort($foreignKeys, fn($a, $b): int => count($b['local']) <=> count($a['local'])); + usort($foreignKeys, fn($a, $b): int => count($b->columns) <=> count($a->columns)); - foreach ($foreignKeys as $row) { - $structure['belongsTo'][$lowerTable][$row['local'][0]] = $row['table']; - $structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'][0]; + foreach ($foreignKeys as $key) { + $structure['belongsTo'][$lowerTable][$key->columns[0]] = $key->targetTable; + $structure['hasMany'][strtolower($key->targetTable)][$table][] = $key->columns[0]; } if (isset($structure['belongsTo'][$lowerTable])) { diff --git a/src/Database/Table/SqlBuilder.php b/src/Database/Table/SqlBuilder.php index 87698c4bd..f80ce1b9d 100644 --- a/src/Database/Table/SqlBuilder.php +++ b/src/Database/Table/SqlBuilder.php @@ -856,7 +856,7 @@ private function getConditionHash($condition, array $parameters): string private function getCachedTableList(): array { if (!$this->cacheTableList) { - $this->cacheTableList = array_flip(array_map(fn(array $pair): string => $pair['fullName'] ?? $pair['name'], $this->structure->getTables())); + $this->cacheTableList = array_flip(array_map(fn($pair): string => $pair->fullName ?? $pair->name, $this->structure->getTables())); } return $this->cacheTableList; diff --git a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt index d437e231b..2127e1cfa 100644 --- a/tests/Database/Explorer/SqlBuilder.parseJoins().phpt +++ b/tests/Database/Explorer/SqlBuilder.parseJoins().phpt @@ -43,7 +43,7 @@ $join = $sqlBuilder->buildQueryJoins($joins); Assert::same('WHERE priorit.id IS NULL', $query); $tables = $connection->getDriver()->getTables(); -if (!in_array($tables[0]['name'], ['npriorities', 'ntopics', 'nusers', 'nusers_ntopics', 'nusers_ntopics_alt'], true)) { +if (!in_array($tables[0]->name, ['npriorities', 'ntopics', 'nusers', 'nusers_ntopics', 'nusers_ntopics_alt'], true)) { if ($driver->isSupported(Driver::SupportSchema)) { Assert::same( 'LEFT JOIN public.nUsers_nTopics nusers_ntopics ON nUsers.nUserId = nusers_ntopics.nUserId ' . diff --git a/tests/Database/Explorer/bugs/view.bug.phpt b/tests/Database/Explorer/bugs/view.bug.phpt index 18f18259d..0ce78920a 100644 --- a/tests/Database/Explorer/bugs/view.bug.phpt +++ b/tests/Database/Explorer/bugs/view.bug.phpt @@ -22,7 +22,7 @@ test('', function () use ($explorer) { test('', function () use ($connection) { $driver = $connection->getDriver(); $columns = $driver->getColumns('books_view'); - $columnsNames = array_map(fn($item) => $item['name'], $columns); + $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 78c6d4623..40ed2a61a 100644 --- a/tests/Database/Reflection.phpt +++ b/tests/Database/Reflection.phpt @@ -8,6 +8,9 @@ declare(strict_types=1); use Nette\Database\Driver; +use Nette\Database\Reflection\Column; +use Nette\Database\Reflection\Index; +use Nette\Database\Reflection\Table; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection @@ -17,74 +20,73 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName $driver = $connection->getDriver(); $tables = $driver->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'])); +$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)) { - Assert::same( + Assert::equal( [ - ['name' => 'author', 'view' => false, 'fullName' => 'public.author'], - ['name' => 'book', 'view' => false, 'fullName' => 'public.book'], - ['name' => 'book_tag', 'view' => false, 'fullName' => 'public.book_tag'], - ['name' => 'tag', 'view' => false, 'fullName' => 'public.tag'], + new Table(name: 'author', view: false, fullName: 'public.author'), + new Table(name: 'book', view: false, fullName: 'public.book'), + new Table(name: 'book_tag', view: false, fullName: 'public.book_tag'), + new Table(name: 'tag', view: false, fullName: 'public.tag'), ], $tables, ); } else { - Assert::same([ - ['name' => 'author', 'view' => false], - ['name' => 'book', 'view' => false], - ['name' => 'book_tag', 'view' => false], - ['name' => 'tag', 'view' => false], + Assert::equal([ + new Table(name: 'author', view: false), + new Table(name: 'book', view: false), + new Table(name: 'book_tag', view: false), + new Table(name: 'tag', view: false), ], $tables); } $columns = $driver->getColumns('author'); array_walk($columns, function (&$item) { - Assert::type('array', $item['vendor']); - unset($item['vendor']); + $item->vendor = []; }); $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, ], ]; @@ -97,18 +99,18 @@ switch ($driverName) { } break; case 'pgsql': - $expectedColumns[0]['nativetype'] = 'int4'; + $expectedColumns[0]['nativeType'] = 'int4'; $expectedColumns[0]['default'] = "nextval('author_id_seq'::regclass)"; $expectedColumns[0]['size'] = null; 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; - $expectedColumns[3]['nativetype'] = 'DATE'; + $expectedColumns[3]['nativeType'] = 'DATE'; break; case 'sqlsrv': $expectedColumns[0]['size'] = null; @@ -119,69 +121,70 @@ switch ($driverName) { Assert::fail("Unsupported driver $driverName"); } -Assert::same($expectedColumns, $columns); +$expectedColumns = array_map(fn($data) => new Column(...$data), $expectedColumns); +Assert::equal($expectedColumns, $columns); $indexes = $driver->getIndexes('book_tag'); switch ($driverName) { case 'pgsql': - Assert::same([ - [ - 'name' => 'book_tag_pkey', - 'unique' => true, - 'primary' => true, - 'columns' => [ + Assert::equal([ + new Index( + name: 'book_tag_pkey', + unique: true, + primary: true, + columns: [ 'book_id', 'tag_id', ], - ], + ), ], $indexes); break; case 'sqlite': - Assert::same([ - [ - 'name' => 'sqlite_autoindex_book_tag_1', - 'unique' => true, - 'primary' => true, - 'columns' => [ + Assert::equal([ + new Index( + name: 'sqlite_autoindex_book_tag_1', + unique: true, + primary: true, + columns: [ 'book_id', 'tag_id', ], - ], + ), ], $indexes); break; case 'sqlsrv': - Assert::same([ - [ - 'name' => 'PK_book_tag', - 'unique' => true, - 'primary' => true, - 'columns' => [ + Assert::equal([ + new Index( + name: 'PK_book_tag', + unique: true, + primary: true, + columns: [ 'book_id', 'tag_id', ], - ], + ), ], $indexes); break; case 'mysql': - Assert::same([ - [ - 'name' => 'PRIMARY', - 'unique' => true, - 'primary' => true, - 'columns' => [ + Assert::equal([ + new Index( + name: 'PRIMARY', + unique: true, + primary: true, + columns: [ 'book_id', 'tag_id', ], - ], - [ - 'name' => 'book_tag_tag', - 'unique' => false, - 'primary' => false, - 'columns' => [ + ), + new Index( + name: 'book_tag_tag', + unique: false, + primary: false, + columns: [ 'tag_id', ], - ], + ), ], $indexes); break; default: diff --git a/tests/Database/Reflection.postgre.10.phpt b/tests/Database/Reflection.postgre.10.phpt index b2b0cd3f9..b11263765 100644 --- a/tests/Database/Reflection.postgre.10.phpt +++ b/tests/Database/Reflection.postgre.10.phpt @@ -7,6 +7,9 @@ declare(strict_types=1); +use Nette\Database\Reflection\Column; +use Nette\Database\Reflection\Index; +use Nette\Database\Reflection\Table; use Tester\Assert; use Tester\Environment; @@ -21,10 +24,10 @@ if (version_compare($ver, '10') < 0) { function shortInfo(array $columns): array { - return array_map(fn(array $col): array => [ - 'name' => $col['name'], - 'autoincrement' => $col['autoincrement'], - 'sequence' => $col['vendor']['sequence'], + return array_map(fn(Column $col): array => [ + 'name' => $col->name, + 'autoincrement' => $col->autoIncrement, + 'sequence' => $col->vendor['sequence'], ], $columns); } @@ -114,9 +117,9 @@ test('Materialized view columns', function () use ($connection) { $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'], + Assert::equal([ + new Table(name: 'source', view: false, fullName: 'reflection_10.source'), + new Table(name: 'source_mt', view: true, fullName: 'reflection_10.source_mt'), ], $driver->getTables()); Assert::same( @@ -143,18 +146,18 @@ test('Partitioned table', function () use ($connection) { $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'], + Assert::equal([ + new Table(name: 'part_1', view: false, fullName: 'reflection_10.part_1'), + new Table(name: 'parted', view: false, fullName: 'reflection_10.parted'), ], $driver->getTables()); Assert::same(['id', 'value'], array_column($driver->getColumns('parted'), 'name')); Assert::same(['id', 'value'], array_column($driver->getColumns('part_1'), 'name')); - Assert::same([[ - 'name' => 'parted_pkey', - 'unique' => true, - 'primary' => true, - 'columns' => ['id'], - ]], $driver->getIndexes('parted')); + Assert::equal([new Index( + name: 'parted_pkey', + unique: true, + primary: true, + columns: ['id'], + )], $driver->getIndexes('parted')); }); diff --git a/tests/Database/Reflection.postgre.phpt b/tests/Database/Reflection.postgre.phpt index ab58272e5..187e916ad 100644 --- a/tests/Database/Reflection.postgre.phpt +++ b/tests/Database/Reflection.postgre.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); +use Nette\Database\Reflection\ForeignKey; use Tester\Assert; require __DIR__ . '/connect.inc.php'; // create $connection @@ -61,12 +62,12 @@ test('Tables in schema', function () use ($connection) { 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([ - 'name' => 'one_slave_fk', - 'local' => ['one_id'], - 'table' => 'one.master', - 'foreign' => ['one_id'], - ], (array) $foreign[0]); + Assert::equal(new ForeignKey( + name: 'one_slave_fk', + columns: ['one_id'], + targetTable: 'one.master', + targetColumns: ['one_id'], + ), $foreign[0]); // Limit foreign keys for current schemas only diff --git a/tests/Database/Structure.phpt b/tests/Database/Structure.phpt index 4a2c8d00c..54307ae76 100644 --- a/tests/Database/Structure.phpt +++ b/tests/Database/Structure.phpt @@ -7,6 +7,9 @@ declare(strict_types=1); use Mockery\MockInterface; +use Nette\Database\Reflection\Column; +use Nette\Database\Reflection\ForeignKey; +use Nette\Database\Reflection\Table; use Nette\Database\Structure; use Tester\Assert; use Tester\TestCase; @@ -49,42 +52,42 @@ class StructureTestCase extends TestCase $this->connection->shouldReceive('getDsn')->once()->andReturn(''); $this->connection->shouldReceive('getDriver')->once()->andReturn($this->driver); $this->driver->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], + new Table(name: 'authors', view: false), + new Table(name: 'Books', view: false), + new Table(name: 'tags', view: false), + new Table(name: 'books_x_tags', view: false), + new Table(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' => []], + new Column(name: 'id', primary: true, autoIncrement: true, vendor: ['sequence' => '"public"."authors_id_seq"']), + new Column(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' => []], + new Column(name: 'id', primary: true, autoIncrement: true, vendor: ['sequence' => '"public"."Books_id_seq"']), + new Column(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' => []], + new Column(name: 'id', primary: true, autoIncrement: false, vendor: []), + new Column(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' => []], + new Column(name: 'book_id', primary: true, autoIncrement: false, vendor: []), + new Column(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' => []], + new Column(name: 'id', primary: false, autoIncrement: false, vendor: []), + new Column(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([ - ['local' => ['author_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk1'], - ['local' => ['translator_id'], 'table' => 'authors', 'foreign' => ['id'], 'name' => 'authors_fk2'], + new ForeignKey(columns: ['author_id'], targetTable: 'authors', targetColumns: ['id'], name: 'authors_fk1'), + new ForeignKey(columns: ['translator_id'], targetTable: 'authors', targetColumns: ['id'], name: 'authors_fk2'), ]); $this->driver->shouldReceive('getForeignKeys')->with('tags')->once()->andReturn([]); $this->driver->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'], + new ForeignKey(columns: ['book_id'], targetTable: 'Books', targetColumns: ['id'], name: 'books_x_tags_fk1'), + new ForeignKey(columns: ['tag_id'], targetTable: 'tags', targetColumns: ['id'], name: 'books_x_tags_fk2'), ]); $this->structure = new StructureMock($this->connection, $this->storage); @@ -93,12 +96,12 @@ class StructureTestCase extends TestCase public function testGetTables() { - Assert::same([ - ['name' => 'authors', 'view' => false], - ['name' => 'Books', 'view' => false], - ['name' => 'tags', 'view' => false], - ['name' => 'books_x_tags', 'view' => false], - ['name' => 'books_view', 'view' => true], + Assert::equal([ + new Table(name: 'authors', view: false), + new Table(name: 'Books', view: false), + new Table(name: 'tags', view: false), + new Table(name: 'books_x_tags', view: false), + new Table(name: 'books_view', view: true), ], $this->structure->getTables()); } @@ -106,12 +109,12 @@ class StructureTestCase extends TestCase public function testGetColumns() { $columns = [ - ['name' => 'id', 'primary' => true, 'autoincrement' => false, 'vendor' => []], - ['name' => 'name', 'primary' => false, 'autoincrement' => false, 'vendor' => []], + new Column(name: 'id', primary: true, autoIncrement: false, vendor: []), + new Column(name: 'name', primary: false, autoIncrement: false, vendor: []), ]; - Assert::same($columns, $this->structure->getColumns('tags')); - Assert::same($columns, $this->structure->getColumns('Tags')); + Assert::equal($columns, $this->structure->getColumns('tags')); + Assert::equal($columns, $this->structure->getColumns('Tags')); $structure = $this->structure; Assert::exception(function () use ($structure) { diff --git a/tests/Database/Structure.schemas.phpt b/tests/Database/Structure.schemas.phpt index 5c9cabe46..077a18875 100644 --- a/tests/Database/Structure.schemas.phpt +++ b/tests/Database/Structure.schemas.phpt @@ -7,6 +7,9 @@ declare(strict_types=1); use Mockery\MockInterface; +use Nette\Database\Reflection\Column; +use Nette\Database\Reflection\ForeignKey; +use Nette\Database\Reflection\Table; use Nette\Database\Structure; use Tester\Assert; use Tester\TestCase; @@ -49,23 +52,23 @@ class StructureSchemasTestCase extends TestCase $this->connection->shouldReceive('getDsn')->once()->andReturn(''); $this->connection->shouldReceive('getDriver')->once()->andReturn($this->driver); $this->driver->shouldReceive('getTables')->once()->andReturn([ - ['name' => 'authors', 'view' => false, 'fullName' => 'authors.authors'], - ['name' => 'books', 'view' => false, 'fullName' => 'books.books'], + new Table(name: 'authors', view: false, fullName: 'authors.authors'), + new Table(name: 'books', view: false, fullName: 'books.books'), ]); $this->driver->shouldReceive('getColumns')->with('authors.authors')->once()->andReturn([ - ['name' => 'id', 'primary' => true, 'vendor' => ['sequence' => '"authors"."authors_id_seq"']], - ['name' => 'name', 'primary' => false, 'vendor' => []], + new Column(name: 'id', primary: true, vendor: ['sequence' => '"authors"."authors_id_seq"']), + new Column(name: 'name', primary: false, vendor: []), ]); $this->driver->shouldReceive('getColumns')->with('books.books')->once()->andReturn([ - ['name' => 'id', 'primary' => true, 'vendor' => ['sequence' => '"books"."books_id_seq"']], - ['name' => 'title', 'primary' => false, 'vendor' => []], + new Column(name: 'id', primary: true, vendor: ['sequence' => '"books"."books_id_seq"']), + new Column(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([ - ['local' => ['author_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk1'], - ['local' => ['translator_id'], 'table' => 'authors.authors', 'foreign' => ['id'], 'name' => 'authors_authors_fk2'], + new ForeignKey(columns: ['author_id'], targetTable: 'authors.authors', targetColumns: ['id'], name: 'authors_authors_fk1'), + new ForeignKey(columns: ['translator_id'], targetTable: 'authors.authors', targetColumns: ['id'], name: 'authors_authors_fk2'), ]); $this->structure = new StructureMock($this->connection, $this->storage); From 86605613e2f8a372c39c0b742f2834183d555acf Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 10 Jan 2023 02:21:58 +0100 Subject: [PATCH 33/35] used PhpStorm Language attribute [WIP] --- composer.json | 3 ++- src/Database/Connection.php | 13 +++++++------ src/Database/Explorer.php | 13 +++++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 37797628d..525b38ec3 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "nette/di": "^3.1 || ^4.0", "mockery/mockery": "^1.4.3", "tracy/tracy": "^2.8", - "phpstan/phpstan-nette": "^1.0" + "phpstan/phpstan-nette": "^1.0", + "jetbrains/phpstorm-attributes": "dev-master" }, "autoload": { "classmap": ["src/"] diff --git a/src/Database/Connection.php b/src/Database/Connection.php index e69257269..acfc3d66e 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -9,6 +9,7 @@ namespace Nette\Database; +use JetBrains\PhpStorm\Language; use Nette; use Nette\Utils\Arrays; @@ -201,7 +202,7 @@ public function transaction(callable $callback): mixed * Generates and executes SQL query. * @param literal-string $sql */ - public function query(string $sql, ...$params): ResultSet + public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ResultSet { [$this->sql, $params] = $this->preprocess($sql, ...$params); try { @@ -250,7 +251,7 @@ public function getLastQueryString(): ?string * Shortcut for query()->fetch() * @param literal-string $sql */ - public function fetch(string $sql, ...$params): ?Row + public function fetch(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?Row { return $this->query($sql, ...$params)->fetch(); } @@ -260,7 +261,7 @@ public function fetch(string $sql, ...$params): ?Row * Shortcut for query()->fetchField() * @param literal-string $sql */ - public function fetchField(string $sql, ...$params): mixed + public function fetchField(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): mixed { return $this->query($sql, ...$params)->fetchField(); } @@ -270,7 +271,7 @@ public function fetchField(string $sql, ...$params): mixed * Shortcut for query()->fetchFields() * @param literal-string $sql */ - public function fetchFields(string $sql, ...$params): ?array + public function fetchFields(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array { return $this->query($sql, ...$params)->fetchFields(); } @@ -280,7 +281,7 @@ public function fetchFields(string $sql, ...$params): ?array * Shortcut for query()->fetchPairs() * @param literal-string $sql */ - public function fetchPairs(string $sql, ...$params): array + public function fetchPairs(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array { return $this->query($sql, ...$params)->fetchPairs(); } @@ -290,7 +291,7 @@ public function fetchPairs(string $sql, ...$params): array * Shortcut for query()->fetchAll() * @param literal-string $sql */ - public function fetchAll(string $sql, ...$params): array + public function fetchAll(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array { return $this->query($sql, ...$params)->fetchAll(); } diff --git a/src/Database/Explorer.php b/src/Database/Explorer.php index a9c337257..fb3f332b7 100644 --- a/src/Database/Explorer.php +++ b/src/Database/Explorer.php @@ -9,6 +9,7 @@ namespace Nette\Database; +use JetBrains\PhpStorm\Language; use Nette; use Nette\Database\Conventions\StaticConventions; @@ -76,7 +77,7 @@ public function getInsertId(?string $sequence = null): string * Generates and executes SQL query. * @param literal-string $sql */ - public function query(string $sql, ...$params): ResultSet + public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ResultSet { return $this->connection->query($sql, ...$params); } @@ -121,7 +122,7 @@ public function getConventions(): Conventions * Shortcut for query()->fetch() * @param literal-string $sql */ - public function fetch(string $sql, ...$params): ?Row + public function fetch(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?Row { return $this->connection->query($sql, ...$params)->fetch(); } @@ -131,7 +132,7 @@ public function fetch(string $sql, ...$params): ?Row * Shortcut for query()->fetchField() * @param literal-string $sql */ - public function fetchField(string $sql, ...$params): mixed + public function fetchField(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): mixed { return $this->connection->query($sql, ...$params)->fetchField(); } @@ -141,7 +142,7 @@ public function fetchField(string $sql, ...$params): mixed * Shortcut for query()->fetchFields() * @param literal-string $sql */ - public function fetchFields(string $sql, ...$params): ?array + public function fetchFields(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): ?array { return $this->connection->query($sql, ...$params)->fetchFields(); } @@ -151,7 +152,7 @@ public function fetchFields(string $sql, ...$params): ?array * Shortcut for query()->fetchPairs() * @param literal-string $sql */ - public function fetchPairs(string $sql, ...$params): array + public function fetchPairs(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array { return $this->connection->query($sql, ...$params)->fetchPairs(); } @@ -161,7 +162,7 @@ public function fetchPairs(string $sql, ...$params): array * Shortcut for query()->fetchAll() * @param literal-string $sql */ - public function fetchAll(string $sql, ...$params): array + public function fetchAll(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): array { return $this->connection->query($sql, ...$params)->fetchAll(); } From 4048b01d374f169e394517f0a4628604c4fd4e4b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 5 Feb 2023 07:32:32 +0100 Subject: [PATCH 34/35] used Tester 2.5 --- composer.json | 2 +- tests/Database/Explorer/Explorer.cache.phpt | 2 +- tests/bootstrap.php | 19 +------------------ 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index 525b38ec3..448bb09fa 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "nette/utils": "^4.0" }, "require-dev": { - "nette/tester": "^2.4", + "nette/tester": "^2.5", "nette/di": "^3.1 || ^4.0", "mockery/mockery": "^1.4.3", "tracy/tracy": "^2.8", diff --git a/tests/Database/Explorer/Explorer.cache.phpt b/tests/Database/Explorer/Explorer.cache.phpt index 12c1a30a9..2e6194ef5 100644 --- a/tests/Database/Explorer/Explorer.cache.phpt +++ b/tests/Database/Explorer/Explorer.cache.phpt @@ -79,7 +79,7 @@ test('Testing GroupedSelection reinvalidation caching', function () use ($explor }); -before(function () use ($cacheMemoryStorage) { +setup(function () use ($cacheMemoryStorage) { $cacheMemoryStorage->clean([Nette\Caching\Cache::ALL => true]); }); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 0a821deca..537cfda16 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -13,7 +13,7 @@ // configure environment Tester\Environment::setup(); -date_default_timezone_set('Europe/Prague'); +Tester\Environment::setupFunctions(); function getTempDir(): string @@ -22,20 +22,3 @@ function getTempDir(): string @mkdir($dir); return $dir; } - - -function before(?Closure $function = null) -{ - static $val; - if (!func_num_args()) { - return $val ? $val() : null; - } - $val = $function; -} - - -function test(string $title, Closure $function): void -{ - before(); - $function(); -} From 88ed3df1c3e0042161c0b5b334b3d2b5b5bc388f Mon Sep 17 00:00:00 2001 From: 8ctopus Date: Mon, 3 Apr 2023 11:19:05 +0400 Subject: [PATCH 35/35] added support for SELECT BETWEEN operator [Closes #297] --- src/Database/SqlPreprocessor.php | 18 +++++++++++++++++- tests/Database/SqlPreprocessor.phpt | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index 1829d8123..c6c8af25d 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -286,7 +286,23 @@ private function formatValue($value, ?string $mode = null): string $k = $this->delimite($k); if (is_array($v)) { if ($v) { - $vx[] = $k . ' ' . ($operator ? $operator . ' ' : '') . 'IN (' . $this->formatValue(array_values($v), self::ModeList) . ')'; + switch ($operator) { + case '': + case 'IN': + $vx[] = $k . ' ' . ($operator ? $operator . ' ' : 'IN') . ' (' . $this->formatValue(array_values($v), self::ModeList) . ')'; + break; + + case 'NOT': + $vx[] = $k . ' ' . $operator . ' IN (' . $this->formatValue(array_values($v), self::ModeList) . ')'; + break; + + case 'BETWEEN': + $vx[] = $k . ' ' . $operator . $this->formatValue(array_values($v), self::ModeAnd); + break; + + default: + throw new Nette\InvalidArgumentException("unsupported operator {$operator}"); + } } elseif ($operator === 'NOT') { } else { $vx[] = '1=0'; diff --git a/tests/Database/SqlPreprocessor.phpt b/tests/Database/SqlPreprocessor.phpt index d85beebc1..b43e3a3c6 100644 --- a/tests/Database/SqlPreprocessor.phpt +++ b/tests/Database/SqlPreprocessor.phpt @@ -104,6 +104,11 @@ test('IN', function () use ($preprocessor) { Assert::same([10, 11], $params); }); +test('BETWEEN', function () use ($preprocessor) { + [$sql, $params] = $preprocessor->process(['SELECT id FROM author WHERE id BETWEEN (?, ?)', [10, 11]]); + Assert::same('SELECT id FROM author WHERE id BETWEEN (?, ?)', $sql); + Assert::same([10, 11], $params); +}); test('?name', function () use ($preprocessor) { [$sql, $params] = $preprocessor->process(['SELECT id FROM author WHERE ?name = ? OR ?name = ?', 'id', 12, 'table.number', 23]);