mirror of
https://github.com/dg/dibi.git
synced 2025-09-04 11:45:27 +02:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
32b6976209 | ||
|
f484630e56 | ||
|
611e051c02 | ||
|
7595a6d5bd | ||
|
494d7c1c21 | ||
|
658dbe388a | ||
|
c7fe0fef21 | ||
|
9151d1eb9c | ||
|
d76f40c2a4 | ||
|
befde664fe | ||
|
e1c4cbaece | ||
|
1df20ced10 | ||
|
ce1ba4668b | ||
|
0f21a6ab3d | ||
|
78f552fe8e | ||
|
97053089e0 | ||
|
2c7b35c29d | ||
|
c04d2197e3 | ||
|
d342d8d78f | ||
|
7d8c39f42a | ||
|
29b58d64dd | ||
|
0a32bb5bdf | ||
|
d707b4ba0e | ||
|
490cf143ba | ||
|
12ffa0ffd1 | ||
|
23f65ef837 |
8
.github/workflows/coding-style.yml
vendored
8
.github/workflows/coding-style.yml
vendored
@@ -7,10 +7,10 @@ jobs:
|
|||||||
name: Nette Code Checker
|
name: Nette Code Checker
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.3
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
- run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
||||||
@@ -21,10 +21,10 @@ jobs:
|
|||||||
name: Nette Coding Standard
|
name: Nette Coding Standard
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.3
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
|
- run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
|
||||||
|
4
.github/workflows/static-analysis.yml
vendored
4
.github/workflows/static-analysis.yml
vendored
@@ -10,10 +10,10 @@ jobs:
|
|||||||
name: PHPStan
|
name: PHPStan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.2
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- run: composer install --no-progress --prefer-dist
|
- run: composer install --no-progress --prefer-dist
|
||||||
|
17
.github/workflows/tests.yml
vendored
17
.github/workflows/tests.yml
vendored
@@ -3,7 +3,7 @@ name: Tests
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.10.0beta2, pdo_sqlsrv-5.10.0beta2
|
php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.12.0, pdo_sqlsrv-5.12.0
|
||||||
php-tools: "composer:v2, pecl"
|
php-tools: "composer:v2, pecl"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.0', '8.1', '8.2', '8.3']
|
php: ['8.2', '8.3', '8.4', '8.5']
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ jobs:
|
|||||||
--health-retries=5
|
--health-retries=5
|
||||||
-e MYSQL_ROOT_PASSWORD=root
|
-e MYSQL_ROOT_PASSWORD=root
|
||||||
-e MYSQL_DATABASE=dibi_test
|
-e MYSQL_DATABASE=dibi_test
|
||||||
--entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
|
||||||
|
|
||||||
postgres96:
|
postgres96:
|
||||||
image: postgres:9.6
|
image: postgres:9.6
|
||||||
@@ -83,13 +82,13 @@ jobs:
|
|||||||
- 1433:1433
|
- 1433:1433
|
||||||
options: >-
|
options: >-
|
||||||
--name=mssql
|
--name=mssql
|
||||||
--health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'"
|
--health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1' -N -C"
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
@@ -101,19 +100,19 @@ jobs:
|
|||||||
run: cp ./tests/databases.github.ini ./tests/databases.ini
|
run: cp ./tests/databases.github.ini ./tests/databases.ini
|
||||||
|
|
||||||
- name: Create MS SQL Database
|
- name: Create MS SQL Database
|
||||||
run: docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test'
|
run: docker exec -i mssql /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test' -N -C
|
||||||
|
|
||||||
- run: composer install --no-progress --prefer-dist
|
- run: composer install --no-progress --prefer-dist
|
||||||
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
|
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
|
||||||
- if: failure()
|
- if: failure()
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: output
|
name: output-${{ matrix.php }}
|
||||||
path: tests/**/output
|
path: tests/**/output
|
||||||
|
|
||||||
|
|
||||||
- name: Save Code Coverage
|
- name: Save Code Coverage
|
||||||
if: ${{ matrix.php == '8.0' }}
|
if: ${{ matrix.php == '8.2' }}
|
||||||
env:
|
env:
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
@@ -11,28 +11,32 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "8.0 - 8.3"
|
"php": "8.2 - 8.5"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"tracy/tracy": "^2.9",
|
"tracy/tracy": "^2.9",
|
||||||
"nette/tester": "^2.5",
|
"nette/tester": "^2.5",
|
||||||
"nette/di": "^3.1",
|
"nette/di": "^3.1",
|
||||||
"phpstan/phpstan": "^1.0",
|
"phpstan/phpstan-nette": "^2.0@stable",
|
||||||
"jetbrains/phpstorm-attributes": "^1.0"
|
"jetbrains/phpstorm-attributes": "^1.0"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
"dg/dibi": "*"
|
"dg/dibi": "*"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": ["src/"]
|
"classmap": ["src/"],
|
||||||
|
"psr-4": {
|
||||||
|
"Dibi\\": "src/Dibi"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"phpstan": "phpstan analyse",
|
"phpstan": "phpstan analyse",
|
||||||
"tester": "tester tests -s"
|
"tester": "tester tests -s"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "5.0-dev"
|
"dev-master": "5.1-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,9 +27,9 @@ $dibi = new Dibi\Connection([
|
|||||||
// using manual hints
|
// using manual hints
|
||||||
$res = $dibi->query('SELECT * FROM [customers]');
|
$res = $dibi->query('SELECT * FROM [customers]');
|
||||||
|
|
||||||
$res->setType('customer_id', Type::INTEGER)
|
$res->setType('customer_id', Type::Integer)
|
||||||
->setType('added', Type::DATETIME)
|
->setType('added', Type::DateTime)
|
||||||
->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
->setFormat(Type::DateTime, 'Y-m-d H:i:s');
|
||||||
|
|
||||||
|
|
||||||
Tracy\Dumper::dump($res->fetch());
|
Tracy\Dumper::dump($res->fetch());
|
||||||
|
@@ -45,7 +45,7 @@ function substFallBack($expr)
|
|||||||
|
|
||||||
|
|
||||||
// define callback
|
// define callback
|
||||||
$dibi->getSubstitutes()->setCallback('substFallBack');
|
$dibi->getSubstitutes()->setCallback(substFallBack(...));
|
||||||
|
|
||||||
// define substitutes as constants
|
// define substitutes as constants
|
||||||
define('SUBST_ACCOUNT', 'eshop_');
|
define('SUBST_ACCOUNT', 'eshop_');
|
||||||
|
@@ -34,7 +34,7 @@ Install Dibi via Composer:
|
|||||||
composer require dibi/dibi
|
composer require dibi/dibi
|
||||||
```
|
```
|
||||||
|
|
||||||
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.3.
|
The Dibi 5.1 requires PHP version 8.2 and supports PHP up to 8.5.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
@@ -608,7 +608,7 @@ $database->query("UPDATE [:blog:items] SET [text]='Hello World'");
|
|||||||
Dibi automatically detects the types of query columns and converts fields them to native PHP types. We can also specify the type manually. You can find the possible types in the `Dibi\Type` class.
|
Dibi automatically detects the types of query columns and converts fields them to native PHP types. We can also specify the type manually. You can find the possible types in the `Dibi\Type` class.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$result->setType('id', Dibi\Type::INTEGER); // id will be integer
|
$result->setType('id', Dibi\Type::Integer); // id will be integer
|
||||||
$row = $result->fetch();
|
$row = $result->fetch();
|
||||||
|
|
||||||
is_int($row->id) // true
|
is_int($row->id) // true
|
||||||
|
@@ -19,14 +19,10 @@ use Tracy;
|
|||||||
*/
|
*/
|
||||||
class DibiExtension22 extends Nette\DI\CompilerExtension
|
class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||||
{
|
{
|
||||||
private ?bool $debugMode;
|
public function __construct(
|
||||||
private ?bool $cliMode;
|
private ?bool $debugMode = null,
|
||||||
|
private ?bool $cliMode = null,
|
||||||
|
) {
|
||||||
public function __construct(?bool $debugMode = null, ?bool $cliMode = null)
|
|
||||||
{
|
|
||||||
$this->debugMode = $debugMode;
|
|
||||||
$this->cliMode = $cliMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ use Dibi;
|
|||||||
use Nette;
|
use Nette;
|
||||||
use Nette\Schema\Expect;
|
use Nette\Schema\Expect;
|
||||||
use Tracy;
|
use Tracy;
|
||||||
|
use function is_array;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,14 +21,10 @@ use Tracy;
|
|||||||
*/
|
*/
|
||||||
class DibiExtension3 extends Nette\DI\CompilerExtension
|
class DibiExtension3 extends Nette\DI\CompilerExtension
|
||||||
{
|
{
|
||||||
private ?bool $debugMode;
|
public function __construct(
|
||||||
private ?bool $cliMode;
|
private ?bool $debugMode = null,
|
||||||
|
private ?bool $cliMode = null,
|
||||||
|
) {
|
||||||
public function __construct(?bool $debugMode = null, ?bool $cliMode = null)
|
|
||||||
{
|
|
||||||
$this->debugMode = $debugMode;
|
|
||||||
$this->cliMode = $cliMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +46,7 @@ class DibiExtension3 extends Nette\DI\CompilerExtension
|
|||||||
'formatDateTime' => Expect::string(),
|
'formatDateTime' => Expect::string(),
|
||||||
'formatTimeInterval' => Expect::string(),
|
'formatTimeInterval' => Expect::string(),
|
||||||
'formatJson' => Expect::string(),
|
'formatJson' => Expect::string(),
|
||||||
]),
|
])->castTo('array'),
|
||||||
])->otherItems(Expect::type('mixed'))
|
])->otherItems(Expect::type('mixed'))
|
||||||
->castTo('array');
|
->castTo('array');
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ use Dibi;
|
|||||||
use Dibi\Event;
|
use Dibi\Event;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
use Tracy;
|
use Tracy;
|
||||||
|
use function count, is_string, strlen;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,23 +22,22 @@ use Tracy;
|
|||||||
class Panel implements Tracy\IBarPanel
|
class Panel implements Tracy\IBarPanel
|
||||||
{
|
{
|
||||||
public static int $maxLength = 1000;
|
public static int $maxLength = 1000;
|
||||||
public bool|string $explain;
|
|
||||||
public int $filter;
|
|
||||||
private array $events = [];
|
private array $events = [];
|
||||||
|
|
||||||
|
|
||||||
public function __construct(bool $explain = true, ?int $filter = null)
|
public function __construct(
|
||||||
{
|
public bool|string $explain = true,
|
||||||
$this->filter = $filter ?: Event::QUERY;
|
public int $filter = Event::QUERY,
|
||||||
$this->explain = $explain;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function register(Dibi\Connection $connection): void
|
public function register(Dibi\Connection $connection): void
|
||||||
{
|
{
|
||||||
Tracy\Debugger::getBar()->addPanel($this);
|
Tracy\Debugger::getBar()->addPanel($this);
|
||||||
Tracy\Debugger::getBlueScreen()->addPanel([self::class, 'renderException']);
|
Tracy\Debugger::getBlueScreen()->addPanel(self::renderException(...));
|
||||||
$connection->onEvent[] = [$this, 'logEvent'];
|
$connection->onEvent[] = $this->logEvent(...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -151,8 +151,8 @@ class Panel implements Tracy\IBarPanel
|
|||||||
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
|
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
|
||||||
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
|
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
|
||||||
<h1>Queries:' . "\u{a0}" . count($this->events)
|
<h1>Queries:' . "\u{a0}" . count($this->events)
|
||||||
. ($totalTime === null ? '' : ", time:\u{a0}" . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms") . ', '
|
. ($totalTime === null ? '' : ", time:\u{a0}" . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms")
|
||||||
. htmlspecialchars($this->getConnectionName($singleConnection)) . '</h1>
|
. ($singleConnection === null ? '' : ', ' . htmlspecialchars($this->getConnectionName($singleConnection))) . '</h1>
|
||||||
<div class="tracy-inner tracy-DibiProfiler">
|
<div class="tracy-inner tracy-DibiProfiler">
|
||||||
<table class="tracy-sortable">
|
<table class="tracy-sortable">
|
||||||
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
|
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
|
||||||
|
@@ -11,6 +11,8 @@ namespace Dibi;
|
|||||||
|
|
||||||
use JetBrains\PhpStorm\Language;
|
use JetBrains\PhpStorm\Language;
|
||||||
use Traversable;
|
use Traversable;
|
||||||
|
use function array_key_exists, is_array, sprintf;
|
||||||
|
use const PHP_SAPI;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,10 +76,10 @@ class Connection implements IConnection
|
|||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
|
||||||
$this->formats = [
|
$this->formats = [
|
||||||
Type::DATE => $this->config['result']['formatDate'],
|
Type::Date => $this->config['result']['formatDate'],
|
||||||
Type::DATETIME => $this->config['result']['formatDateTime'],
|
Type::DateTime => $this->config['result']['formatDateTime'],
|
||||||
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
||||||
Type::TIME_INTERVAL => $this->config['result']['formatTimeInterval'] ?? null,
|
Type::TimeInterval => $this->config['result']['formatTimeInterval'] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// profiler
|
// profiler
|
||||||
@@ -673,7 +675,7 @@ class Connection implements IConnection
|
|||||||
/**
|
/**
|
||||||
* Prevents unserialization.
|
* Prevents unserialization.
|
||||||
*/
|
*/
|
||||||
public function __wakeup()
|
public function __unserialize($_)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
||||||
}
|
}
|
||||||
@@ -682,7 +684,7 @@ class Connection implements IConnection
|
|||||||
/**
|
/**
|
||||||
* Prevents serialization.
|
* Prevents serialization.
|
||||||
*/
|
*/
|
||||||
public function __sleep()
|
public function __serialize()
|
||||||
{
|
{
|
||||||
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,16 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function func_get_args, is_array, strpbrk;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of IDataSource.
|
* Default implementation of IDataSource.
|
||||||
*/
|
*/
|
||||||
class DataSource implements IDataSource
|
class DataSource implements IDataSource
|
||||||
{
|
{
|
||||||
private Connection $connection;
|
private readonly Connection $connection;
|
||||||
private string $sql;
|
private readonly string $sql;
|
||||||
private ?Result $result = null;
|
private ?Result $result = null;
|
||||||
private ?int $count = null;
|
private ?int $count = null;
|
||||||
private ?int $totalCount = null;
|
private ?int $totalCount = null;
|
||||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
|||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +27,10 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class FirebirdDriver implements Dibi\Driver
|
class FirebirdDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
public const ERROR_EXCEPTION_THROWN = -836;
|
public const ErrorExceptionThrown = -836;
|
||||||
|
|
||||||
|
/** @deprecated use FirebirdDriver::ErrorExceptionThrown */
|
||||||
|
public const ERROR_EXCEPTION_THROWN = self::ErrorExceptionThrown;
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
@@ -17,12 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class FirebirdReflector implements Dibi\Reflector
|
class FirebirdReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
|||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,16 +19,10 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class FirebirdResult implements Dibi\ResultDriver
|
class FirebirdResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
/** @var resource */
|
public function __construct(
|
||||||
private $resultSet;
|
/** @var resource */
|
||||||
|
private $resultSet,
|
||||||
|
) {
|
||||||
/**
|
|
||||||
* @param resource $resultSet
|
|
||||||
*/
|
|
||||||
public function __construct($resultSet)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -18,12 +18,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqlReflector implements Dibi\Reflector
|
class MySqlReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function in_array;
|
||||||
|
use const MYSQLI_REPORT_OFF, MYSQLI_STORE_RESULT, MYSQLI_USE_RESULT, PREG_SET_ORDER;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,9 +34,18 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqliDriver implements Dibi\Driver
|
class MySqliDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
public const ERROR_ACCESS_DENIED = 1045;
|
public const ErrorAccessDenied = 1045;
|
||||||
public const ERROR_DUPLICATE_ENTRY = 1062;
|
public const ErrorDuplicateEntry = 1062;
|
||||||
public const ERROR_DATA_TRUNCATED = 1265;
|
public const ErrorDataTruncated = 1265;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorAccessDenied */
|
||||||
|
public const ERROR_ACCESS_DENIED = self::ErrorAccessDenied;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorDuplicateEntry */
|
||||||
|
public const ERROR_DUPLICATE_ENTRY = self::ErrorDuplicateEntry;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorDataTruncated */
|
||||||
|
public const ERROR_DATA_TRUNCATED = self::ErrorDataTruncated;
|
||||||
|
|
||||||
private \mysqli $connection;
|
private \mysqli $connection;
|
||||||
private bool $buffered = false;
|
private bool $buffered = false;
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use const MYSQLI_TYPE_LONG, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_TIME, MYSQLI_TYPE_TINY;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,14 +18,10 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqliResult implements Dibi\ResultDriver
|
class MySqliResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
private \mysqli_result $resultSet;
|
public function __construct(
|
||||||
private bool $buffered;
|
private readonly \mysqli_result $resultSet,
|
||||||
|
private readonly bool $buffered,
|
||||||
|
) {
|
||||||
public function __construct(\mysqli_result $resultSet, bool $buffered)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
$this->buffered = $buffered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +100,7 @@ class MySqliResult implements Dibi\ResultDriver
|
|||||||
'table' => $row['orgtable'],
|
'table' => $row['orgtable'],
|
||||||
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
||||||
'nativetype' => $types[$row['type']] ?? $row['type'],
|
'nativetype' => $types[$row['type']] ?? $row['type'],
|
||||||
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : null,
|
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TimeInterval : null,
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -17,12 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class NoDataResult implements Dibi\ResultDriver
|
class NoDataResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
private int $rows;
|
public function __construct(
|
||||||
|
private readonly int $rows,
|
||||||
|
) {
|
||||||
public function __construct(int $rows)
|
|
||||||
{
|
|
||||||
$this->rows = $rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,12 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OdbcReflector implements Dibi\Reflector
|
class OdbcReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,17 +18,13 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OdbcResult implements Dibi\ResultDriver
|
class OdbcResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
/** @var resource */
|
|
||||||
private $resultSet;
|
|
||||||
private int $row = 0;
|
private int $row = 0;
|
||||||
|
|
||||||
|
|
||||||
/**
|
public function __construct(
|
||||||
* @param resource $resultSet
|
/** @var resource */
|
||||||
*/
|
private $resultSet,
|
||||||
public function __construct($resultSet)
|
) {
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function in_array, is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,12 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OracleReflector implements Dibi\Reflector
|
class OracleReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,16 +18,10 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OracleResult implements Dibi\ResultDriver
|
class OracleResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
/** @var resource */
|
public function __construct(
|
||||||
private $resultSet;
|
/** @var resource */
|
||||||
|
private $resultSet,
|
||||||
|
) {
|
||||||
/**
|
|
||||||
* @param resource $resultSet
|
|
||||||
*/
|
|
||||||
public function __construct($resultSet)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +75,7 @@ class OracleResult implements Dibi\ResultDriver
|
|||||||
'name' => oci_field_name($this->resultSet, $i),
|
'name' => oci_field_name($this->resultSet, $i),
|
||||||
'table' => null,
|
'table' => null,
|
||||||
'fullname' => oci_field_name($this->resultSet, $i),
|
'fullname' => oci_field_name($this->resultSet, $i),
|
||||||
'type' => $type === 'LONG' ? Dibi\Type::TEXT : null,
|
'type' => $type === 'LONG' ? Dibi\Type::Text : null,
|
||||||
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
|
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ namespace Dibi\Drivers;
|
|||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,14 +24,12 @@ use PDO;
|
|||||||
* - password (or pass)
|
* - password (or pass)
|
||||||
* - options (array) => driver specific options {@see PDO::__construct}
|
* - options (array) => driver specific options {@see PDO::__construct}
|
||||||
* - resource (PDO) => existing connection
|
* - resource (PDO) => existing connection
|
||||||
* - version
|
|
||||||
*/
|
*/
|
||||||
class PdoDriver implements Dibi\Driver
|
class PdoDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
private ?PDO $connection;
|
private ?PDO $connection;
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
private string $driverName;
|
private string $driverName;
|
||||||
private string $serverVersion = '';
|
|
||||||
|
|
||||||
|
|
||||||
/** @throws Dibi\NotSupportedException */
|
/** @throws Dibi\NotSupportedException */
|
||||||
@@ -65,7 +64,6 @@ class PdoDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
|
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
$this->serverVersion = (string) ($config['version'] ?? @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION)); // @ - may be not supported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -176,27 +174,14 @@ class PdoDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function getReflector(): Dibi\Reflector
|
public function getReflector(): Dibi\Reflector
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => new MySqlReflector($this),
|
||||||
return new MySqlReflector($this);
|
'oci' => new OracleReflector($this),
|
||||||
|
'pgsql' => new PostgreReflector($this),
|
||||||
case 'oci':
|
'sqlite' => new SqliteReflector($this),
|
||||||
return new OracleReflector($this);
|
'mssql', 'dblib', 'sqlsrv' => new SqlsrvReflector($this),
|
||||||
|
default => throw new Dibi\NotSupportedException,
|
||||||
case 'pgsql':
|
};
|
||||||
return new PostgreReflector($this, $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION));
|
|
||||||
|
|
||||||
case 'sqlite':
|
|
||||||
return new SqliteReflector($this);
|
|
||||||
|
|
||||||
case 'mssql':
|
|
||||||
case 'dblib':
|
|
||||||
case 'sqlsrv':
|
|
||||||
return new SqlsrvReflector($this);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Dibi\NotSupportedException;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -217,44 +202,34 @@ class PdoDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function escapeText(string $value): string
|
public function escapeText(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
return match ($this->driverName) {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
'odbc' => "'" . str_replace("'", "''", $value) . "'",
|
||||||
: $this->connection->quote($value, PDO::PARAM_STR);
|
'sqlsrv' => "N'" . str_replace("'", "''", $value) . "'",
|
||||||
|
default => $this->connection->quote($value, PDO::PARAM_STR),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function escapeBinary(string $value): string
|
public function escapeBinary(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
return match ($this->driverName) {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
'odbc' => "'" . str_replace("'", "''", $value) . "'",
|
||||||
: $this->connection->quote($value, PDO::PARAM_LOB);
|
'sqlsrv' => '0x' . bin2hex($value),
|
||||||
|
default => $this->connection->quote($value, PDO::PARAM_LOB),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function escapeIdentifier(string $value): string
|
public function escapeIdentifier(string $value): string
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => '`' . str_replace('`', '``', $value) . '`',
|
||||||
return '`' . str_replace('`', '``', $value) . '`';
|
'oci', 'pgsql' => '"' . str_replace('"', '""', $value) . '"',
|
||||||
|
'sqlite' => '[' . strtr($value, '[]', ' ') . ']',
|
||||||
case 'oci':
|
'odbc', 'mssql' => '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']',
|
||||||
case 'pgsql':
|
'dblib', 'sqlsrv' => '[' . str_replace(']', ']]', $value) . ']',
|
||||||
return '"' . str_replace('"', '""', $value) . '"';
|
default => $value,
|
||||||
|
};
|
||||||
case 'sqlite':
|
|
||||||
return '[' . strtr($value, '[]', ' ') . ']';
|
|
||||||
|
|
||||||
case 'odbc':
|
|
||||||
case 'mssql':
|
|
||||||
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
|
|
||||||
|
|
||||||
case 'dblib':
|
|
||||||
case 'sqlsrv':
|
|
||||||
return '[' . str_replace(']', ']]', $value) . ']';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -276,16 +251,11 @@ class PdoDriver implements Dibi\Driver
|
|||||||
|
|
||||||
public function escapeDateTime(\DateTimeInterface $value): string
|
public function escapeDateTime(\DateTimeInterface $value): string
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'odbc':
|
'odbc' => $value->format('#m/d/Y H:i:s.u#'),
|
||||||
return $value->format('#m/d/Y H:i:s.u#');
|
'mssql', 'dblib', 'sqlsrv' => 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')',
|
||||||
case 'mssql':
|
default => $value->format("'Y-m-d H:i:s.u'"),
|
||||||
case 'dblib':
|
};
|
||||||
case 'sqlsrv':
|
|
||||||
return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
|
|
||||||
default:
|
|
||||||
return $value->format("'Y-m-d H:i:s.u'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -387,17 +357,15 @@ class PdoDriver implements Dibi\Driver
|
|||||||
case 'mssql':
|
case 'mssql':
|
||||||
case 'sqlsrv':
|
case 'sqlsrv':
|
||||||
case 'dblib':
|
case 'dblib':
|
||||||
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
|
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
if ($limit !== null) {
|
||||||
if ($limit !== null) {
|
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
||||||
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
} elseif ($offset) {
|
||||||
} elseif ($offset) {
|
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
|
||||||
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// break omitted
|
|
||||||
|
break;
|
||||||
|
|
||||||
case 'odbc':
|
case 'odbc':
|
||||||
if ($offset) {
|
if ($offset) {
|
||||||
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
||||||
|
@@ -19,14 +19,10 @@ use PDO;
|
|||||||
*/
|
*/
|
||||||
class PdoResult implements Dibi\ResultDriver
|
class PdoResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
private ?\PDOStatement $resultSet;
|
public function __construct(
|
||||||
private string $driverName;
|
private ?\PDOStatement $resultSet,
|
||||||
|
private readonly string $driverName,
|
||||||
|
) {
|
||||||
public function __construct(\PDOStatement $resultSet, string $driverName)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
$this->driverName = $driverName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -90,7 +86,7 @@ class PdoResult implements Dibi\ResultDriver
|
|||||||
'name' => $row['name'],
|
'name' => $row['name'],
|
||||||
'table' => $row['table'],
|
'table' => $row['table'],
|
||||||
'nativetype' => $row['native_type'],
|
'nativetype' => $row['native_type'],
|
||||||
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : null,
|
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TimeInterval : null,
|
||||||
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
|
@@ -12,6 +12,7 @@ namespace Dibi\Drivers;
|
|||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
use PgSql;
|
use PgSql;
|
||||||
|
use function in_array, is_array, is_resource, strlen;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,13 +24,12 @@ use PgSql;
|
|||||||
* - schema => the schema search path
|
* - schema => the schema search path
|
||||||
* - charset => character encoding to set (default is utf8)
|
* - charset => character encoding to set (default is utf8)
|
||||||
* - persistent (bool) => try to find a persistent link?
|
* - persistent (bool) => try to find a persistent link?
|
||||||
* - resource (resource) => existing connection resource
|
* - resource (PgSql\Connection) => existing connection resource
|
||||||
* - connect_type (int) => see pg_connect()
|
* - connect_type (int) => see pg_connect()
|
||||||
*/
|
*/
|
||||||
class PostgreDriver implements Dibi\Driver
|
class PostgreDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
/** @var resource|PgSql\Connection */
|
private PgSql\Connection $connection;
|
||||||
private $connection;
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_resource($this->connection) && !$this->connection instanceof PgSql\Connection) {
|
if (!$this->connection instanceof PgSql\Connection) {
|
||||||
throw new Dibi\DriverException($error ?: 'Connecting error.');
|
throw new Dibi\DriverException($error ?: 'Connecting error.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
if ($res === false) {
|
if ($res === false) {
|
||||||
throw static::createException(pg_last_error($this->connection), null, $sql);
|
throw static::createException(pg_last_error($this->connection), null, $sql);
|
||||||
|
|
||||||
} elseif (is_resource($res) || $res instanceof PgSql\Result) {
|
} elseif ($res instanceof PgSql\Result) {
|
||||||
$this->affectedRows = Helpers::false2Null(pg_affected_rows($res));
|
$this->affectedRows = Helpers::false2Null(pg_affected_rows($res));
|
||||||
if (pg_num_fields($res)) {
|
if (pg_num_fields($res)) {
|
||||||
return $this->createResultDriver($res);
|
return $this->createResultDriver($res);
|
||||||
@@ -222,13 +222,10 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the connection resource.
|
* Returns the connection resource.
|
||||||
* @return resource|null
|
|
||||||
*/
|
*/
|
||||||
public function getResource(): mixed
|
public function getResource(): PgSql\Connection
|
||||||
{
|
{
|
||||||
return is_resource($this->connection) || $this->connection instanceof PgSql\Connection
|
return $this->connection;
|
||||||
? $this->connection
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -237,15 +234,14 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function getReflector(): Dibi\Reflector
|
public function getReflector(): Dibi\Reflector
|
||||||
{
|
{
|
||||||
return new PostgreReflector($this, pg_parameter_status($this->connection, 'server_version'));
|
return new PostgreReflector($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result set driver factory.
|
* Result set driver factory.
|
||||||
* @param resource $resource
|
|
||||||
*/
|
*/
|
||||||
public function createResultDriver($resource): PostgreResult
|
public function createResultDriver(PgSql\Result $resource): PostgreResult
|
||||||
{
|
{
|
||||||
return new PostgreResult($resource);
|
return new PostgreResult($resource);
|
||||||
}
|
}
|
||||||
|
@@ -17,14 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class PostgreReflector implements Dibi\Reflector
|
class PostgreReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
private string $version;
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
|
|
||||||
public function __construct(Dibi\Driver $driver, string $version)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
$this->version = $version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -43,18 +38,15 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
FROM
|
FROM
|
||||||
information_schema.tables
|
information_schema.tables
|
||||||
WHERE
|
WHERE
|
||||||
table_schema = ANY (current_schemas(false))";
|
table_schema = ANY (current_schemas(false))
|
||||||
|
|
||||||
if ($this->version >= 9.3) {
|
UNION ALL
|
||||||
$query .= '
|
SELECT
|
||||||
UNION ALL
|
matviewname, 1
|
||||||
SELECT
|
FROM
|
||||||
matviewname, 1
|
pg_matviews
|
||||||
FROM
|
WHERE
|
||||||
pg_matviews
|
schemaname = ANY (current_schemas(false))";
|
||||||
WHERE
|
|
||||||
schemaname = ANY (current_schemas(false))';
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = $this->driver->query($query);
|
$res = $this->driver->query($query);
|
||||||
$tables = [];
|
$tables = [];
|
||||||
|
@@ -19,16 +19,9 @@ use PgSql;
|
|||||||
*/
|
*/
|
||||||
class PostgreResult implements Dibi\ResultDriver
|
class PostgreResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
/** @var resource|PgSql\Result */
|
public function __construct(
|
||||||
private $resultSet;
|
private readonly PgSql\Result $resultSet,
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource|PgSql\Result $resultSet
|
|
||||||
*/
|
|
||||||
public function __construct($resultSet)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,13 +87,10 @@ class PostgreResult implements Dibi\ResultDriver
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the result set resource.
|
* Returns the result set resource.
|
||||||
* @return resource|PgSql\Result|null
|
|
||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): PgSql\Result
|
||||||
{
|
{
|
||||||
return is_resource($this->resultSet) || $this->resultSet instanceof PgSql\Result
|
return $this->resultSet;
|
||||||
? $this->resultSet
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -56,11 +56,7 @@ class SqliteDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
|
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
|
||||||
$version = SQLite3::version();
|
$this->query('PRAGMA foreign_keys = ON');
|
||||||
$this->connection->enableExceptions(false);
|
|
||||||
if ($version['versionNumber'] >= '3006019') {
|
|
||||||
$this->query('PRAGMA foreign_keys = ON');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,12 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqliteReflector implements Dibi\Reflector
|
class SqliteReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
|||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
|
use const SQLITE3_ASSOC, SQLITE3_BLOB, SQLITE3_FLOAT, SQLITE3_INTEGER, SQLITE3_NULL, SQLITE3_NUM, SQLITE3_TEXT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,12 +19,9 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class SqliteResult implements Dibi\ResultDriver
|
class SqliteResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
private \SQLite3Result $resultSet;
|
public function __construct(
|
||||||
|
private readonly \SQLite3Result $resultSet,
|
||||||
|
) {
|
||||||
public function __construct(\SQLite3Result $resultSet)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
|||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
use Dibi\Helpers;
|
use Dibi\Helpers;
|
||||||
|
use function is_resource, sprintf;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,6 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
private string $version = '';
|
|
||||||
|
|
||||||
|
|
||||||
/** @throws Dibi\NotSupportedException */
|
/** @throws Dibi\NotSupportedException */
|
||||||
@@ -68,8 +68,6 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
|
|
||||||
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -256,13 +254,6 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
if ($limit < 0 || $offset < 0) {
|
if ($limit < 0 || $offset < 0) {
|
||||||
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
||||||
|
|
||||||
} elseif (version_compare($this->version, '11', '<')) { // 11 == SQL Server 2012
|
|
||||||
if ($offset) {
|
|
||||||
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
|
||||||
|
|
||||||
} elseif ($limit !== null) {
|
|
||||||
$sql = sprintf('SELECT TOP (%d) * FROM (%s) t', $limit, $sql);
|
|
||||||
}
|
|
||||||
} elseif ($limit !== null) {
|
} elseif ($limit !== null) {
|
||||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||||
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,12 +18,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqlsrvReflector implements Dibi\Reflector
|
class SqlsrvReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
private Dibi\Driver $driver;
|
public function __construct(
|
||||||
|
private readonly Dibi\Driver $driver,
|
||||||
|
) {
|
||||||
public function __construct(Dibi\Driver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function is_resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,16 +18,10 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqlsrvResult implements Dibi\ResultDriver
|
class SqlsrvResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
/** @var resource */
|
public function __construct(
|
||||||
private $resultSet;
|
/** @var resource */
|
||||||
|
private $resultSet,
|
||||||
|
) {
|
||||||
/**
|
|
||||||
* @param resource $resultSet
|
|
||||||
*/
|
|
||||||
public function __construct($resultSet)
|
|
||||||
{
|
|
||||||
$this->resultSet = $resultSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -9,6 +9,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function count, dirname, microtime, preg_match, str_starts_with, strtoupper, trim;
|
||||||
|
use const DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profiler & logger event.
|
* Profiler & logger event.
|
||||||
@@ -29,10 +32,10 @@ class Event
|
|||||||
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
|
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
|
||||||
ALL = 1023;
|
ALL = 1023;
|
||||||
|
|
||||||
public Connection $connection;
|
public readonly Connection $connection;
|
||||||
public int $type;
|
public int $type;
|
||||||
public string $sql;
|
public readonly string $sql;
|
||||||
public Result|DriverException|null $result;
|
public readonly Result|DriverException|null $result;
|
||||||
public float $time;
|
public float $time;
|
||||||
public ?int $count = null;
|
public ?int $count = null;
|
||||||
public ?array $source = null;
|
public ?array $source = null;
|
||||||
|
@@ -15,7 +15,7 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Expression
|
class Expression
|
||||||
{
|
{
|
||||||
private array $values;
|
private readonly array $values;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(...$values)
|
public function __construct(...$values)
|
||||||
|
@@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function array_key_exists, count, func_get_args, is_array, is_string;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL builder via fluent interfaces.
|
* SQL builder via fluent interfaces.
|
||||||
@@ -45,7 +47,13 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Fluent implements IDataSource
|
class Fluent implements IDataSource
|
||||||
{
|
{
|
||||||
public const REMOVE = false;
|
public const
|
||||||
|
AffectedRows = 'a',
|
||||||
|
Identifier = 'n',
|
||||||
|
Remove = false;
|
||||||
|
|
||||||
|
/** @deprecated use Fluent::Remove */
|
||||||
|
public const REMOVE = self::Remove;
|
||||||
|
|
||||||
public static array $masks = [
|
public static array $masks = [
|
||||||
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
|
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
|
||||||
@@ -91,7 +99,7 @@ class Fluent implements IDataSource
|
|||||||
'RIGHT JOIN' => 'FROM',
|
'RIGHT JOIN' => 'FROM',
|
||||||
];
|
];
|
||||||
|
|
||||||
private Connection $connection;
|
private readonly Connection $connection;
|
||||||
private array $setups = [];
|
private array $setups = [];
|
||||||
private ?string $command = null;
|
private ?string $command = null;
|
||||||
private array $clauses = [];
|
private array $clauses = [];
|
||||||
@@ -107,7 +115,7 @@ class Fluent implements IDataSource
|
|||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
|
|
||||||
if (!isset(self::$normalizer)) {
|
if (!isset(self::$normalizer)) {
|
||||||
self::$normalizer = new HashMap([self::class, '_formatClause']);
|
self::$normalizer = new HashMap(self::_formatClause(...));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +148,7 @@ class Fluent implements IDataSource
|
|||||||
$this->cursor = &$this->clauses[$clause];
|
$this->cursor = &$this->clauses[$clause];
|
||||||
|
|
||||||
// TODO: really delete?
|
// TODO: really delete?
|
||||||
if ($args === [self::REMOVE]) {
|
if ($args === [self::Remove]) {
|
||||||
$this->cursor = null;
|
$this->cursor = null;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -156,7 +164,7 @@ class Fluent implements IDataSource
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// append to currect flow
|
// append to currect flow
|
||||||
if ($args === [self::REMOVE]) {
|
if ($args === [self::Remove]) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,15 +287,15 @@ class Fluent implements IDataSource
|
|||||||
/**
|
/**
|
||||||
* Generates and executes SQL query.
|
* Generates and executes SQL query.
|
||||||
* Returns result set or number of affected rows
|
* Returns result set or number of affected rows
|
||||||
* @return ($return is \dibi::IDENTIFIER|\dibi::AFFECTED_ROWS ? int : Result)
|
* @return ($return is self::Identifier|self::AffectedRows ? int : Result)
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function execute(?string $return = null): Result|int|null
|
public function execute(?string $return = null): Result|int|null
|
||||||
{
|
{
|
||||||
$res = $this->query($this->_export());
|
$res = $this->query($this->_export());
|
||||||
return match ($return) {
|
return match ($return) {
|
||||||
\dibi::IDENTIFIER => $this->connection->getInsertId(),
|
self::Identifier => $this->connection->getInsertId(),
|
||||||
\dibi::AFFECTED_ROWS => $this->connection->getAffectedRows(),
|
self::AffectedRows => $this->connection->getAffectedRows(),
|
||||||
default => $res,
|
default => $res,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function array_map, array_unique, explode, fclose, fgets, fopen, fstat, getenv, htmlspecialchars, is_float, is_int, is_string, levenshtein, max, mb_strlen, ob_end_flush, ob_get_clean, ob_start, preg_match, preg_replace, preg_replace_callback, rtrim, set_time_limit, str_ends_with, str_repeat, str_starts_with, strlen, strtoupper, substr, trim, wordwrap;
|
||||||
|
use const PHP_SAPI;
|
||||||
|
|
||||||
|
|
||||||
class Helpers
|
class Helpers
|
||||||
{
|
{
|
||||||
@@ -159,12 +162,12 @@ class Helpers
|
|||||||
public static function escape(Driver $driver, $value, string $type): string
|
public static function escape(Driver $driver, $value, string $type): string
|
||||||
{
|
{
|
||||||
$types = [
|
$types = [
|
||||||
Type::TEXT => 'text',
|
Type::Text => 'text',
|
||||||
Type::BINARY => 'binary',
|
Type::Binary => 'binary',
|
||||||
Type::BOOL => 'bool',
|
Type::Bool => 'bool',
|
||||||
Type::DATE => 'date',
|
Type::Date => 'date',
|
||||||
Type::DATETIME => 'datetime',
|
Type::DateTime => 'datetime',
|
||||||
\dibi::IDENTIFIER => 'identifier',
|
Fluent::Identifier => 'identifier',
|
||||||
];
|
];
|
||||||
if (isset($types[$type])) {
|
if (isset($types[$type])) {
|
||||||
return $driver->{'escape' . $types[$type]}($value);
|
return $driver->{'escape' . $types[$type]}($value);
|
||||||
@@ -181,16 +184,16 @@ class Helpers
|
|||||||
public static function detectType(string $type): ?string
|
public static function detectType(string $type): ?string
|
||||||
{
|
{
|
||||||
$patterns = [
|
$patterns = [
|
||||||
'^_' => Type::TEXT, // PostgreSQL arrays
|
'^_' => Type::Text, // PostgreSQL arrays
|
||||||
'RANGE$' => Type::TEXT, // PostgreSQL range types
|
'RANGE$' => Type::Text, // PostgreSQL range types
|
||||||
'BYTEA|BLOB|BIN' => Type::BINARY,
|
'BYTEA|BLOB|BIN' => Type::Binary,
|
||||||
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
|
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::Text,
|
||||||
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
|
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::Integer,
|
||||||
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
|
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::Float,
|
||||||
'^TIME$' => Type::TIME,
|
'^TIME$' => Type::Time,
|
||||||
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
|
'TIME' => Type::DateTime, // DATETIME, TIMESTAMP
|
||||||
'DATE' => Type::DATE,
|
'DATE' => Type::Date,
|
||||||
'BOOL' => Type::BOOL,
|
'BOOL' => Type::Bool,
|
||||||
'JSON' => Type::JSON,
|
'JSON' => Type::JSON,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -208,7 +211,7 @@ class Helpers
|
|||||||
public static function getTypeCache(): HashMap
|
public static function getTypeCache(): HashMap
|
||||||
{
|
{
|
||||||
if (!isset(self::$types)) {
|
if (!isset(self::$types)) {
|
||||||
self::$types = new HashMap([self::class, 'detectType']);
|
self::$types = new HashMap(self::detectType(...));
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$types;
|
return self::$types;
|
||||||
|
@@ -15,7 +15,7 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Literal
|
class Literal
|
||||||
{
|
{
|
||||||
private string $value;
|
private readonly string $value;
|
||||||
|
|
||||||
|
|
||||||
public function __construct($value)
|
public function __construct($value)
|
||||||
|
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Loggers;
|
namespace Dibi\Loggers;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function sprintf;
|
||||||
|
use const FILE_APPEND, LOCK_EX;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,17 +19,11 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class FileLogger
|
class FileLogger
|
||||||
{
|
{
|
||||||
/** Name of the file where SQL errors should be logged */
|
public function __construct(
|
||||||
public string $file;
|
public string $file,
|
||||||
public int $filter;
|
public int $filter = Dibi\Event::QUERY,
|
||||||
private bool $errorsOnly;
|
private bool $errorsOnly = false,
|
||||||
|
) {
|
||||||
|
|
||||||
public function __construct(string $file, ?int $filter = null, bool $errorsOnly = false)
|
|
||||||
{
|
|
||||||
$this->file = $file;
|
|
||||||
$this->filter = $filter ?: Dibi\Event::QUERY;
|
|
||||||
$this->errorsOnly = $errorsOnly;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -27,17 +27,10 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Column
|
class Column
|
||||||
{
|
{
|
||||||
/** when created by Result */
|
public function __construct(
|
||||||
private ?Dibi\Reflector $reflector;
|
private readonly ?Dibi\Reflector $reflector,
|
||||||
|
private array $info,
|
||||||
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
|
) {
|
||||||
private array $info;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(?Dibi\Reflector $reflector, array $info)
|
|
||||||
{
|
|
||||||
$this->reflector = $reflector;
|
|
||||||
$this->info = $info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Reflection;
|
namespace Dibi\Reflection;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function array_values, strtolower;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,17 +22,14 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
private Dibi\Reflector $reflector;
|
|
||||||
private ?string $name;
|
|
||||||
|
|
||||||
/** @var Table[] */
|
/** @var Table[] */
|
||||||
private array $tables;
|
private array $tables;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(Dibi\Reflector $reflector, ?string $name = null)
|
public function __construct(
|
||||||
{
|
private readonly Dibi\Reflector $reflector,
|
||||||
$this->reflector = $reflector;
|
private ?string $name = null,
|
||||||
$this->name = $name;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -19,16 +19,10 @@ namespace Dibi\Reflection;
|
|||||||
*/
|
*/
|
||||||
class ForeignKey
|
class ForeignKey
|
||||||
{
|
{
|
||||||
private string $name;
|
public function __construct(
|
||||||
|
private readonly string $name,
|
||||||
/** @var array of [local, foreign, onDelete, onUpdate] */
|
private readonly array $references,
|
||||||
private array $references;
|
) {
|
||||||
|
|
||||||
|
|
||||||
public function __construct(string $name, array $references)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->references = $references;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -21,13 +21,9 @@ namespace Dibi\Reflection;
|
|||||||
*/
|
*/
|
||||||
class Index
|
class Index
|
||||||
{
|
{
|
||||||
/** @var array (name, columns, [unique], [primary]) */
|
public function __construct(
|
||||||
private array $info;
|
private readonly array $info,
|
||||||
|
) {
|
||||||
|
|
||||||
public function __construct(array $info)
|
|
||||||
{
|
|
||||||
$this->info = $info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Reflection;
|
namespace Dibi\Reflection;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function array_values, strtolower;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,8 +21,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Result
|
class Result
|
||||||
{
|
{
|
||||||
private Dibi\ResultDriver $driver;
|
|
||||||
|
|
||||||
/** @var Column[]|null */
|
/** @var Column[]|null */
|
||||||
private ?array $columns;
|
private ?array $columns;
|
||||||
|
|
||||||
@@ -29,9 +28,9 @@ class Result
|
|||||||
private ?array $names;
|
private ?array $names;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(Dibi\ResultDriver $driver)
|
public function __construct(
|
||||||
{
|
private readonly Dibi\ResultDriver $driver,
|
||||||
$this->driver = $driver;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Dibi\Reflection;
|
namespace Dibi\Reflection;
|
||||||
|
|
||||||
use Dibi;
|
use Dibi;
|
||||||
|
use function array_values, strtolower;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +26,7 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Table
|
class Table
|
||||||
{
|
{
|
||||||
private Dibi\Reflector $reflector;
|
private readonly Dibi\Reflector $reflector;
|
||||||
private string $name;
|
private string $name;
|
||||||
private bool $view;
|
private bool $view;
|
||||||
|
|
||||||
|
@@ -9,6 +9,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function array_keys, array_pop, count, explode, is_float, is_string, json_decode, ltrim, preg_match, preg_split, property_exists, reset, rtrim, str_contains, str_replace, str_starts_with, strpos;
|
||||||
|
use const PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query result.
|
* Query result.
|
||||||
@@ -457,16 +460,22 @@ class Result implements IDataSource
|
|||||||
if ($type === null || $format === 'native') {
|
if ($type === null || $format === 'native') {
|
||||||
$row[$key] = $value;
|
$row[$key] = $value;
|
||||||
|
|
||||||
} elseif ($type === Type::TEXT) {
|
} elseif ($type === Type::Text) {
|
||||||
$row[$key] = (string) $value;
|
$row[$key] = (string) $value;
|
||||||
|
|
||||||
} elseif ($type === Type::INTEGER) {
|
} elseif ($type === Type::Integer) {
|
||||||
$row[$key] = is_float($tmp = $value * 1)
|
$row[$key] = is_float($tmp = $value * 1)
|
||||||
? (is_string($value) ? $value : (int) $value)
|
? (is_string($value) ? $value : (int) $value)
|
||||||
: $tmp;
|
: $tmp;
|
||||||
|
|
||||||
} elseif ($type === Type::FLOAT) {
|
} elseif ($type === Type::Float) {
|
||||||
$value = ltrim((string) $value, '0');
|
if (!is_string($value)) {
|
||||||
|
$row[$key] = (float) $value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$negative = ($value[0] ?? null) === '-';
|
||||||
|
$value = ltrim($value, '0-');
|
||||||
$p = strpos($value, '.');
|
$p = strpos($value, '.');
|
||||||
$e = strpos($value, 'e');
|
$e = strpos($value, 'e');
|
||||||
if ($p !== false && $e === false) {
|
if ($p !== false && $e === false) {
|
||||||
@@ -479,27 +488,31 @@ class Result implements IDataSource
|
|||||||
$value = '0' . $value;
|
$value = '0' . $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$value = '-' . $value;
|
||||||
|
}
|
||||||
|
|
||||||
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
|
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
|
||||||
? $float
|
? $float
|
||||||
: $value;
|
: $value;
|
||||||
|
|
||||||
} elseif ($type === Type::BOOL) {
|
} elseif ($type === Type::Bool) {
|
||||||
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
||||||
|
|
||||||
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
} elseif ($type === Type::DateTime || $type === Type::Date || $type === Type::Time) {
|
||||||
if ($value && !str_starts_with((string) $value, '0000-00')) { // '', null, false, '0000-00-00', ...
|
if ($value && !str_starts_with((string) $value, '0000-00')) { // '', null, false, '0000-00-00', ...
|
||||||
$value = new DateTime($value);
|
$value = new DateTime($value);
|
||||||
$row[$key] = $format ? $value->format($format) : $value;
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = null;
|
$row[$key] = null;
|
||||||
}
|
}
|
||||||
} elseif ($type === Type::TIME_INTERVAL) {
|
} elseif ($type === Type::TimeInterval) {
|
||||||
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
||||||
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
||||||
$value->invert = (int) (bool) $m[1];
|
$value->invert = (int) (bool) $m[1];
|
||||||
$row[$key] = $format ? $value->format($format) : $value;
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
|
|
||||||
} elseif ($type === Type::BINARY) {
|
} elseif ($type === Type::Binary) {
|
||||||
$row[$key] = is_string($value)
|
$row[$key] = is_string($value)
|
||||||
? $this->getResultDriver()->unescapeBinary($value)
|
? $this->getResultDriver()->unescapeBinary($value)
|
||||||
: $value;
|
: $value;
|
||||||
|
@@ -15,14 +15,13 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class ResultIterator implements \Iterator, \Countable
|
class ResultIterator implements \Iterator, \Countable
|
||||||
{
|
{
|
||||||
private Result $result;
|
|
||||||
private mixed $row;
|
private mixed $row;
|
||||||
private int $pointer = 0;
|
private int $pointer = 0;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(Result $result)
|
public function __construct(
|
||||||
{
|
private readonly Result $result,
|
||||||
$this->result = $result;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function array_keys, count, str_starts_with;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result set single row.
|
* Result set single row.
|
||||||
|
@@ -9,14 +9,16 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use function array_filter, array_keys, array_splice, array_values, count, explode, get_debug_type, gettype, implode, is_array, is_bool, is_float, is_int, is_numeric, is_object, is_scalar, is_string, iterator_to_array, key, ltrim, number_format, preg_last_error, preg_match, preg_replace_callback, reset, rtrim, str_contains, str_replace, strcspn, strlen, strncasecmp, strtoupper, substr, trim;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL translator.
|
* SQL translator.
|
||||||
*/
|
*/
|
||||||
final class Translator
|
final class Translator
|
||||||
{
|
{
|
||||||
private Connection $connection;
|
private readonly Connection $connection;
|
||||||
private Driver $driver;
|
private readonly Driver $driver;
|
||||||
private int $cursor = 0;
|
private int $cursor = 0;
|
||||||
private array $args;
|
private array $args;
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ final class Translator
|
|||||||
{
|
{
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->driver = $connection->getDriver();
|
$this->driver = $connection->getDriver();
|
||||||
$this->identifiers = new HashMap([$this, 'delimite']);
|
$this->identifiers = new HashMap($this->delimite(...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@ final class Translator
|
|||||||
(\?) ## 11) placeholder
|
(\?) ## 11) placeholder
|
||||||
)/xs
|
)/xs
|
||||||
XX,
|
XX,
|
||||||
[$this, 'cb'],
|
$this->cb(...),
|
||||||
substr($arg, $toSkip),
|
substr($arg, $toSkip),
|
||||||
);
|
);
|
||||||
if (preg_last_error()) {
|
if (preg_last_error()) {
|
||||||
@@ -219,7 +221,7 @@ final class Translator
|
|||||||
|
|
||||||
case 'a': // key=val, key=val, ...
|
case 'a': // key=val, key=val, ...
|
||||||
foreach ($value as $k => $v) {
|
foreach ($value as $k => $v) {
|
||||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
$pair = explode('%', (string) $k, 2); // split into identifier & modifier
|
||||||
$vx[] = $this->identifiers->{$pair[0]} . '='
|
$vx[] = $this->identifiers->{$pair[0]} . '='
|
||||||
. $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
. $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
||||||
}
|
}
|
||||||
@@ -434,7 +436,7 @@ final class Translator
|
|||||||
:(\S*?:)([a-zA-Z0-9._]?)
|
:(\S*?:)([a-zA-Z0-9._]?)
|
||||||
)/sx
|
)/sx
|
||||||
XX,
|
XX,
|
||||||
[$this, 'cb'],
|
$this->cb(...),
|
||||||
substr($value, $toSkip),
|
substr($value, $toSkip),
|
||||||
);
|
);
|
||||||
if (preg_last_error()) {
|
if (preg_last_error()) {
|
||||||
|
@@ -16,16 +16,43 @@ namespace Dibi;
|
|||||||
class Type
|
class Type
|
||||||
{
|
{
|
||||||
public const
|
public const
|
||||||
TEXT = 's', // as 'string'
|
Text = 's', // as 'string'
|
||||||
BINARY = 'bin',
|
Binary = 'bin',
|
||||||
JSON = 'json',
|
JSON = 'json',
|
||||||
BOOL = 'b',
|
Bool = 'b',
|
||||||
INTEGER = 'i',
|
Integer = 'i',
|
||||||
FLOAT = 'f',
|
Float = 'f',
|
||||||
DATE = 'd',
|
Date = 'd',
|
||||||
DATETIME = 'dt',
|
DateTime = 'dt',
|
||||||
TIME = 't',
|
Time = 't',
|
||||||
TIME_INTERVAL = 'ti';
|
TimeInterval = 'ti';
|
||||||
|
|
||||||
|
/** @deprecated use Type::Text */
|
||||||
|
public const TEXT = self::Text;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Binary */
|
||||||
|
public const BINARY = self::Binary;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Bool */
|
||||||
|
public const BOOL = self::Bool;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Integer */
|
||||||
|
public const INTEGER = self::Integer;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Float */
|
||||||
|
public const FLOAT = self::Float;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Date */
|
||||||
|
public const DATE = self::Date;
|
||||||
|
|
||||||
|
/** @deprecated use Type::DateTime */
|
||||||
|
public const DATETIME = self::DateTime;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Time */
|
||||||
|
public const TIME = self::Time;
|
||||||
|
|
||||||
|
/** @deprecated use Type::TimeInterval */
|
||||||
|
public const TIME_INTERVAL = self::TimeInterval;
|
||||||
|
|
||||||
|
|
||||||
final public function __construct()
|
final public function __construct()
|
||||||
|
@@ -37,12 +37,16 @@ declare(strict_types=1);
|
|||||||
*/
|
*/
|
||||||
class dibi
|
class dibi
|
||||||
{
|
{
|
||||||
public const
|
public const Version = '5.1-dev';
|
||||||
AFFECTED_ROWS = 'a',
|
|
||||||
IDENTIFIER = 'n';
|
|
||||||
|
|
||||||
/** version */
|
/** @deprecated use dibi::Version */
|
||||||
public const VERSION = '5.0.1';
|
public const VERSION = self::Version;
|
||||||
|
|
||||||
|
/** @deprecated use Dibi\Fluent::AffectedRows */
|
||||||
|
public const AFFECTED_ROWS = Dibi\Fluent::AffectedRows;
|
||||||
|
|
||||||
|
/** @deprecated use Dibi\Fluent::Identifier */
|
||||||
|
public const IDENTIFIER = Dibi\Fluent::Identifier;
|
||||||
|
|
||||||
/** sorting order */
|
/** sorting order */
|
||||||
public const
|
public const
|
||||||
|
@@ -11,7 +11,7 @@ namespace Dibi;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dibi common exception.
|
* A database operation failed.
|
||||||
*/
|
*/
|
||||||
class Exception extends \Exception
|
class Exception extends \Exception
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ class Exception extends \Exception
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* database server exception.
|
* The database server reported an error.
|
||||||
*/
|
*/
|
||||||
class DriverException extends Exception
|
class DriverException extends Exception
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ class DriverException extends Exception
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PCRE exception.
|
* Regular expression pattern or execution failed.
|
||||||
*/
|
*/
|
||||||
class PcreException extends Exception
|
class PcreException extends Exception
|
||||||
{
|
{
|
||||||
@@ -63,18 +63,24 @@ class PcreException extends Exception
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The requested feature is not implemented.
|
||||||
|
*/
|
||||||
class NotImplementedException extends Exception
|
class NotImplementedException extends Exception
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The requested operation is not supported.
|
||||||
|
*/
|
||||||
class NotSupportedException extends Exception
|
class NotSupportedException extends Exception
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database procedure exception.
|
* A database stored procedure failed.
|
||||||
*/
|
*/
|
||||||
class ProcedureException extends Exception
|
class ProcedureException extends Exception
|
||||||
{
|
{
|
||||||
@@ -102,7 +108,7 @@ class ProcedureException extends Exception
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all constraint violation related exceptions.
|
* A database constraint was violated.
|
||||||
*/
|
*/
|
||||||
class ConstraintViolationException extends DriverException
|
class ConstraintViolationException extends DriverException
|
||||||
{
|
{
|
||||||
@@ -110,7 +116,7 @@ class ConstraintViolationException extends DriverException
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for a foreign key constraint violation.
|
* The foreign key constraint check failed.
|
||||||
*/
|
*/
|
||||||
class ForeignKeyConstraintViolationException extends ConstraintViolationException
|
class ForeignKeyConstraintViolationException extends ConstraintViolationException
|
||||||
{
|
{
|
||||||
@@ -118,7 +124,7 @@ class ForeignKeyConstraintViolationException extends ConstraintViolationExceptio
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for a NOT NULL constraint violation.
|
* The NOT NULL constraint check failed.
|
||||||
*/
|
*/
|
||||||
class NotNullConstraintViolationException extends ConstraintViolationException
|
class NotNullConstraintViolationException extends ConstraintViolationException
|
||||||
{
|
{
|
||||||
@@ -126,7 +132,7 @@ class NotNullConstraintViolationException extends ConstraintViolationException
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for a unique constraint violation.
|
* The unique constraint check failed.
|
||||||
*/
|
*/
|
||||||
class UniqueConstraintViolationException extends ConstraintViolationException
|
class UniqueConstraintViolationException extends ConstraintViolationException
|
||||||
{
|
{
|
||||||
|
@@ -12,7 +12,7 @@ use Tester\Assert;
|
|||||||
require __DIR__ . '/bootstrap.php';
|
require __DIR__ . '/bootstrap.php';
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('immediate connection and disconnection state', function () use ($config) {
|
||||||
$conn = new Connection($config);
|
$conn = new Connection($config);
|
||||||
Assert::true($conn->isConnected());
|
Assert::true($conn->isConnected());
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ test('', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('lazy', function () use ($config) {
|
test('lazy connection initiated on first query', function () use ($config) {
|
||||||
$conn = new Connection($config + ['lazy' => true]);
|
$conn = new Connection($config + ['lazy' => true]);
|
||||||
Assert::false($conn->isConnected());
|
Assert::false($conn->isConnected());
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ test('lazy', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('config retrieval and driver instance access', function () use ($config) {
|
||||||
$conn = new Connection($config);
|
$conn = new Connection($config);
|
||||||
Assert::true($conn->isConnected());
|
Assert::true($conn->isConnected());
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ test('', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('idempotent disconnect calls', function () use ($config) {
|
||||||
$conn = new Connection($config);
|
$conn = new Connection($config);
|
||||||
Assert::true($conn->isConnected());
|
Assert::true($conn->isConnected());
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ test('', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('reconnect after disconnection', function () use ($config) {
|
||||||
$conn = new Connection($config);
|
$conn = new Connection($config);
|
||||||
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
|
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ test('', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('destructor disconnects active connection', function () use ($config) {
|
||||||
$conn = new Connection($config);
|
$conn = new Connection($config);
|
||||||
Assert::true($conn->isConnected());
|
Assert::true($conn->isConnected());
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ test('', function () use ($config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('invalid onConnect option triggers exceptions', function () use ($config) {
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
fn() => new Connection($config + ['onConnect' => '']),
|
fn() => new Connection($config + ['onConnect' => '']),
|
||||||
InvalidArgumentException::class,
|
InvalidArgumentException::class,
|
||||||
|
@@ -57,28 +57,28 @@ $fluent = $conn->select('*')
|
|||||||
->orderBy('customer_id');
|
->orderBy('customer_id');
|
||||||
|
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY'),
|
||||||
(string) $fluent,
|
(string) $fluent,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
$fluent->fetch();
|
$fluent->fetch();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
'SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t',
|
'SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
$fluent->fetchSingle();
|
$fluent->fetchSingle();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
$fluent->fetchAll(0, 3);
|
$fluent->fetchAll(0, 3);
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (3) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY'),
|
||||||
(string) $fluent,
|
(string) $fluent,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -86,16 +86,16 @@ Assert::same(
|
|||||||
$fluent->limit(0);
|
$fluent->limit(0);
|
||||||
$fluent->fetch();
|
$fluent->fetch();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 0 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
$fluent->fetchSingle();
|
$fluent->fetchSingle();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 0 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 0 ROWS ONLY'),
|
||||||
(string) $fluent,
|
(string) $fluent,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,12 +104,12 @@ $fluent->removeClause('limit');
|
|||||||
$fluent->removeClause('offset');
|
$fluent->removeClause('offset');
|
||||||
$fluent->fetch();
|
$fluent->fetch();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
$fluent->fetchSingle();
|
$fluent->fetchSingle();
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'),
|
reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY'),
|
||||||
dibi::$sql,
|
dibi::$sql,
|
||||||
);
|
);
|
||||||
Assert::same(
|
Assert::same(
|
||||||
|
@@ -74,7 +74,7 @@ Assert::same(
|
|||||||
(string) $fluent,
|
(string) $fluent,
|
||||||
);
|
);
|
||||||
|
|
||||||
$fluent->orderBy(Dibi\Fluent::REMOVE);
|
$fluent->orderBy(Dibi\Fluent::Remove);
|
||||||
|
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'),
|
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'),
|
||||||
|
@@ -36,5 +36,5 @@ Assert::exception(
|
|||||||
|
|
||||||
test(
|
test(
|
||||||
'PDO error mode: explicitly set silent',
|
'PDO error mode: explicitly set silent',
|
||||||
fn() => buildPdoDriver(PDO::ERRMODE_SILENT)
|
fn() => buildPdoDriver(PDO::ERRMODE_SILENT),
|
||||||
);
|
);
|
||||||
|
@@ -18,9 +18,9 @@ $tests = function ($conn) {
|
|||||||
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A%B')->fetchSingle());
|
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A%B')->fetchSingle());
|
||||||
Assert::true($conn->query("SELECT 'AA%BB' LIKE %~like~", 'A%B')->fetchSingle());
|
Assert::true($conn->query("SELECT 'AA%BB' LIKE %~like~", 'A%B')->fetchSingle());
|
||||||
|
|
||||||
Assert::same('AA\\BB', $conn->query("SELECT 'AA\\BB'")->fetchSingle());
|
Assert::same('AA\BB', $conn->query("SELECT 'AA\\BB'")->fetchSingle());
|
||||||
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A\\B')->fetchSingle());
|
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A\B')->fetchSingle());
|
||||||
Assert::true($conn->query("SELECT 'AA\\BB' LIKE %~like~", 'A\\B')->fetchSingle());
|
Assert::true($conn->query("SELECT 'AA\\BB' LIKE %~like~", 'A\B')->fetchSingle());
|
||||||
};
|
};
|
||||||
|
|
||||||
$conn = new Dibi\Connection($config);
|
$conn = new Dibi\Connection($config);
|
||||||
|
@@ -25,10 +25,10 @@ class MockResult extends Dibi\Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('native text conversion preserves boolean values', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TEXT);
|
$result->setType('col', Type::Text);
|
||||||
$result->setFormat(Type::TEXT, 'native');
|
$result->setFormat(Type::Text, 'native');
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => true], $result->test(['col' => true]));
|
Assert::same(['col' => true], $result->test(['col' => true]));
|
||||||
@@ -36,9 +36,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('boolean conversion from diverse representations', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::BOOL);
|
$result->setType('col', Type::Bool);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => true], $result->test(['col' => true]));
|
Assert::same(['col' => true], $result->test(['col' => true]));
|
||||||
@@ -58,9 +58,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('text conversion of booleans and numerics', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TEXT);
|
$result->setType('col', Type::Text);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => '1'], $result->test(['col' => true]));
|
Assert::same(['col' => '1'], $result->test(['col' => true]));
|
||||||
@@ -74,9 +74,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('float conversion with various numeric formats', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::FLOAT);
|
$result->setType('col', Type::Float);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => true]));
|
Assert::same(['col' => 1.0], $result->test(['col' => true]));
|
||||||
@@ -117,6 +117,37 @@ test('', function () {
|
|||||||
Assert::same(['col' => '1.1e+10'], $result->test(['col' => '001.1e+10']));
|
Assert::same(['col' => '1.1e+10'], $result->test(['col' => '001.1e+10']));
|
||||||
Assert::notSame(['col' => '1.1e+1'], $result->test(['col' => '1.1e+10']));
|
Assert::notSame(['col' => '1.1e+1'], $result->test(['col' => '1.1e+10']));
|
||||||
|
|
||||||
|
// negative
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.000']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.0']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.1']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-001.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-001.100']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-10']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-11']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-0010']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-0011']));
|
||||||
|
Assert::same(['col' => '-0.00000000000000000001'], $result->test(['col' => '-0.00000000000000000001']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-012345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890.000']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890.1'], $result->test(['col' => '-012345678901234567890.100']));
|
||||||
|
|
||||||
|
Assert::same(['col' => '-1.1e+10'], $result->test(['col' => '-1.1e+10']));
|
||||||
|
Assert::same(['col' => '-1.1e-10'], $result->test(['col' => '-1.1e-10']));
|
||||||
|
Assert::same(['col' => '-1.1e+10'], $result->test(['col' => '-001.1e+10']));
|
||||||
|
Assert::notSame(['col' => '-1.1e+1'], $result->test(['col' => '-1.1e+10']));
|
||||||
|
|
||||||
setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'deu_deu');
|
setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'deu_deu');
|
||||||
Assert::same(['col' => 0.0], $result->test(['col' => '']));
|
Assert::same(['col' => 0.0], $result->test(['col' => '']));
|
||||||
Assert::same(['col' => 0.0], $result->test(['col' => '0']));
|
Assert::same(['col' => 0.0], $result->test(['col' => '0']));
|
||||||
@@ -147,26 +178,54 @@ test('', function () {
|
|||||||
Assert::same(['col' => 0.0], $result->test(['col' => 0.0]));
|
Assert::same(['col' => 0.0], $result->test(['col' => 0.0]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => 1]));
|
Assert::same(['col' => 1.0], $result->test(['col' => 1]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => 1.0]));
|
Assert::same(['col' => 1.0], $result->test(['col' => 1.0]));
|
||||||
|
|
||||||
|
// Same but negative
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.000']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.0']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.1']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-001.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-001.100']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-10']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-11']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-0010']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-0011']));
|
||||||
|
Assert::same(['col' => '-0.00000000000000000001'], $result->test(['col' => '-0.00000000000000000001']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-012345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890.000']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890.1'], $result->test(['col' => '-012345678901234567890.100']));
|
||||||
|
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => -0]));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => -0.0]));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => -1]));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => -1.0]));
|
||||||
|
|
||||||
setlocale(LC_NUMERIC, 'C');
|
setlocale(LC_NUMERIC, 'C');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('strict integer conversion with error on empty string', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::INTEGER);
|
$result->setType('col', Type::Integer);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => 1], $result->test(['col' => true]));
|
Assert::same(['col' => 1], $result->test(['col' => true]));
|
||||||
Assert::same(['col' => 0], $result->test(['col' => false]));
|
Assert::same(['col' => 0], $result->test(['col' => false]));
|
||||||
|
|
||||||
if (PHP_VERSION_ID < 80000) {
|
Assert::exception(
|
||||||
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
|
fn() => Assert::same(['col' => 0], $result->test(['col' => ''])),
|
||||||
} else {
|
TypeError::class,
|
||||||
Assert::exception(
|
);
|
||||||
fn() => Assert::same(['col' => 0], $result->test(['col' => ''])),
|
|
||||||
TypeError::class,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
||||||
Assert::same(['col' => 1], $result->test(['col' => '1']));
|
Assert::same(['col' => 1], $result->test(['col' => '1']));
|
||||||
@@ -185,9 +244,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('dateTime conversion with object instantiation', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATETIME);
|
$result->setType('col', Type::DateTime);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -204,10 +263,10 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('dateTime conversion using custom format', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATETIME);
|
$result->setType('col', Type::DateTime);
|
||||||
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
$result->setFormat(Type::DateTime, 'Y-m-d H:i:s');
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -224,9 +283,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('date conversion to DateTime instance', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATE);
|
$result->setType('col', Type::Date);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -241,9 +300,9 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('time conversion to DateTime instance', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TIME);
|
$result->setType('col', Type::Time);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
|
@@ -12,7 +12,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
|||||||
$res = $conn->query('SELECT * FROM [customers]');
|
$res = $conn->query('SELECT * FROM [customers]');
|
||||||
|
|
||||||
// auto-converts this column to integer
|
// auto-converts this column to integer
|
||||||
$res->setType('customer_id', Dibi\Type::DATETIME);
|
$res->setType('customer_id', Dibi\Type::DateTime);
|
||||||
|
|
||||||
Assert::equal(new Dibi\Row([
|
Assert::equal(new Dibi\Row([
|
||||||
'customer_id' => new Dibi\DateTime('1970-01-01 01:00:01'),
|
'customer_id' => new Dibi\DateTime('1970-01-01 01:00:01'),
|
||||||
|
@@ -11,82 +11,59 @@ use Tester\Assert;
|
|||||||
require __DIR__ . '/bootstrap.php';
|
require __DIR__ . '/bootstrap.php';
|
||||||
|
|
||||||
$tests = function ($conn) {
|
$tests = function ($conn) {
|
||||||
$resource = $conn->getDriver()->getResource();
|
// Limit and offset
|
||||||
$version = is_resource($resource)
|
Assert::same(
|
||||||
? sqlsrv_server_info($resource)['SQLServerVersion']
|
'SELECT 1 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY',
|
||||||
: $resource->getAttribute(PDO::ATTR_SERVER_VERSION);
|
$conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
||||||
|
);
|
||||||
|
|
||||||
// MsSQL2012+
|
// Limit only
|
||||||
if (version_compare($version, '11.0') >= 0) {
|
Assert::same(
|
||||||
// Limit and offset
|
'SELECT 1 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
|
||||||
Assert::same(
|
$conn->translate('SELECT 1 %lmt', 10),
|
||||||
'SELECT 1 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY',
|
);
|
||||||
$conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Limit only
|
// Offset only
|
||||||
Assert::same(
|
Assert::same(
|
||||||
'SELECT 1 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
|
'SELECT 1 OFFSET 10 ROWS',
|
||||||
$conn->translate('SELECT 1 %lmt', 10),
|
$conn->translate('SELECT 1 %ofs', 10),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Offset only
|
// Offset invalid
|
||||||
Assert::same(
|
Assert::error(
|
||||||
'SELECT 1 OFFSET 10 ROWS',
|
function () use ($conn) {
|
||||||
$conn->translate('SELECT 1 %ofs', 10),
|
$conn->translate('SELECT 1 %ofs', -10);
|
||||||
);
|
},
|
||||||
|
Dibi\NotSupportedException::class,
|
||||||
|
'Negative offset or limit.',
|
||||||
|
);
|
||||||
|
|
||||||
// Offset invalid
|
// Limit invalid
|
||||||
Assert::error(
|
Assert::error(
|
||||||
function () use ($conn) {
|
function () use ($conn) {
|
||||||
$conn->translate('SELECT 1 %ofs', -10);
|
$conn->translate('SELECT 1 %lmt', -10);
|
||||||
},
|
},
|
||||||
Dibi\NotSupportedException::class,
|
Dibi\NotSupportedException::class,
|
||||||
'Negative offset or limit.',
|
'Negative offset or limit.',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Limit invalid
|
// Limit invalid, offset valid
|
||||||
Assert::error(
|
Assert::error(
|
||||||
function () use ($conn) {
|
function () use ($conn) {
|
||||||
$conn->translate('SELECT 1 %lmt', -10);
|
$conn->translate('SELECT 1 %ofs %lmt', 10, -10);
|
||||||
},
|
},
|
||||||
Dibi\NotSupportedException::class,
|
Dibi\NotSupportedException::class,
|
||||||
'Negative offset or limit.',
|
'Negative offset or limit.',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Limit invalid, offset valid
|
// Limit valid, offset invalid
|
||||||
Assert::error(
|
Assert::error(
|
||||||
function () use ($conn) {
|
function () use ($conn) {
|
||||||
$conn->translate('SELECT 1 %ofs %lmt', 10, -10);
|
$conn->translate('SELECT 1 %ofs %lmt', -10, 10);
|
||||||
},
|
},
|
||||||
Dibi\NotSupportedException::class,
|
Dibi\NotSupportedException::class,
|
||||||
'Negative offset or limit.',
|
'Negative offset or limit.',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Limit valid, offset invalid
|
|
||||||
Assert::error(
|
|
||||||
function () use ($conn) {
|
|
||||||
$conn->translate('SELECT 1 %ofs %lmt', -10, 10);
|
|
||||||
},
|
|
||||||
Dibi\NotSupportedException::class,
|
|
||||||
'Negative offset or limit.',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Assert::same(
|
|
||||||
'SELECT TOP (1) * FROM (SELECT 1) t',
|
|
||||||
$conn->translate('SELECT 1 %lmt', 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert::same(
|
|
||||||
'SELECT 1',
|
|
||||||
$conn->translate('SELECT 1 %lmt', -10),
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert::exception(
|
|
||||||
fn() => $conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
|
||||||
Dibi\NotSupportedException::class,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$conn = new Dibi\Connection($config);
|
$conn = new Dibi\Connection($config);
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpVersion 8.1
|
|
||||||
* @dataProvider ../databases.ini
|
* @dataProvider ../databases.ini
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ enum PureEnum
|
|||||||
|
|
||||||
Assert::equal('1', $translator->formatValue(EnumInt::One, null));
|
Assert::equal('1', $translator->formatValue(EnumInt::One, null));
|
||||||
|
|
||||||
Assert::equal(match ($config['driver']) {
|
Assert::equal(match ($config['system']) {
|
||||||
'sqlsrv' => "N'one'",
|
'sqlsrv' => "N'one'",
|
||||||
default => "'one'",
|
default => "'one'",
|
||||||
}, $translator->formatValue(EnumString::One, null));
|
}, $translator->formatValue(EnumString::One, null));
|
||||||
|
@@ -32,7 +32,7 @@ Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', "a'a", "a'"));
|
|||||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', "b'", "%'"));
|
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', "b'", "%'"));
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', "%'", "%'"));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', "%'", "%'"));
|
||||||
|
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', 'a\\a', 'a\\'));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', 'a\a', 'a\\'));
|
||||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'b\\', '%\\'));
|
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'b\\', '%\\'));
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%\\', '%\\'));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%\\', '%\\'));
|
||||||
|
|
||||||
@@ -60,9 +60,9 @@ Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', "a'a", "'a"));
|
|||||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', "'b", "'%"));
|
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', "'b", "'%"));
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', "'%", "'%"));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', "'%", "'%"));
|
||||||
|
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'a\\a', '\\a'));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'a\a', '\a'));
|
||||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', '\\b', '\\%'));
|
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', '\b', '\%'));
|
||||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', '\\%', '\\%'));
|
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', '\%', '\%'));
|
||||||
|
|
||||||
|
|
||||||
// contains
|
// contains
|
||||||
|
Reference in New Issue
Block a user