mirror of
https://github.com/dg/dibi.git
synced 2025-09-05 12:02:36 +02:00
Compare commits
1 Commits
v4.2.2
...
revert-363
Author | SHA1 | Date | |
---|---|---|---|
|
dbbf0ca673 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -2,9 +2,5 @@
|
|||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
.github export-ignore
|
.github export-ignore
|
||||||
.travis.yml export-ignore
|
.travis.yml export-ignore
|
||||||
ecs.php export-ignore
|
appveyor.yml export-ignore
|
||||||
phpstan.neon export-ignore
|
|
||||||
tests/ export-ignore
|
tests/ export-ignore
|
||||||
|
|
||||||
*.sh eol=lf
|
|
||||||
*.php* diff=php linguist-language=PHP
|
|
||||||
|
1
.github/funding.yml
vendored
1
.github/funding.yml
vendored
@@ -1 +0,0 @@
|
|||||||
github: dg
|
|
@@ -1,9 +1,9 @@
|
|||||||
language: php
|
language: php
|
||||||
php:
|
php:
|
||||||
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
- 7.3
|
- 7.3
|
||||||
- 7.4
|
- 7.4
|
||||||
- 8.0snapshot
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
@@ -32,6 +32,7 @@ after_failure:
|
|||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- name: Nette Code Checker
|
- name: Nette Code Checker
|
||||||
|
php: 7.4
|
||||||
install:
|
install:
|
||||||
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
||||||
script:
|
script:
|
||||||
@@ -41,9 +42,9 @@ jobs:
|
|||||||
- name: Nette Coding Standard
|
- name: Nette Coding Standard
|
||||||
php: 7.4
|
php: 7.4
|
||||||
install:
|
install:
|
||||||
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
|
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
|
||||||
script:
|
script:
|
||||||
- php temp/coding-standard/ecs check src tests
|
- php temp/coding-standard/ecs check src tests examples --config tests/coding-standard.yml
|
||||||
|
|
||||||
|
|
||||||
- stage: Static Analysis (informative)
|
- stage: Static Analysis (informative)
|
||||||
@@ -66,7 +67,7 @@ jobs:
|
|||||||
- stage: Code Coverage
|
- stage: Code Coverage
|
||||||
|
|
||||||
|
|
||||||
dist: xenial
|
sudo: false
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
|
10
appveyor.yml
10
appveyor.yml
@@ -15,17 +15,17 @@ init:
|
|||||||
- SET ANSICON=121x90 (121x90)
|
- SET ANSICON=121x90 (121x90)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# Install PHP 7.2
|
# Install PHP 7.1
|
||||||
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
|
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
|
||||||
- IF %PHP%==1 mkdir c:\php7
|
- IF %PHP%==1 mkdir c:\php7
|
||||||
- IF %PHP%==1 cd c:\php7
|
- IF %PHP%==1 cd c:\php7
|
||||||
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.2.18-Win32-VC15-x64.zip --output php.zip
|
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.1.5-Win32-VC14-x64.zip --output php.zip
|
||||||
- IF %PHP%==1 7z x php.zip >nul
|
- IF %PHP%==1 7z x php.zip >nul
|
||||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||||
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
||||||
- IF %PHP%==1 curl https://github.com/microsoft/msphpsql/releases/download/v5.8.0/Windows-7.2.zip -L --output sqlsrv.zip
|
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
|
||||||
- IF %PHP%==1 7z x sqlsrv.zip >nul
|
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
|
||||||
- IF %PHP%==1 copy Windows-7.2\x64\php_sqlsrv_72_ts.dll ext\php_sqlsrv_ts.dll
|
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_71_ts.dll ext\php_sqlsrv_71_ts.dll
|
||||||
- IF %PHP%==1 del /Q *.zip
|
- IF %PHP%==1 del /Q *.zip
|
||||||
|
|
||||||
# Install Microsoft Access Database Engine x64
|
# Install Microsoft Access Database Engine x64
|
||||||
|
@@ -11,12 +11,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2"
|
"php": ">=7.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"tracy/tracy": "~2.2",
|
"tracy/tracy": "~2.2",
|
||||||
"nette/tester": "~2.0",
|
"nette/tester": "~2.0",
|
||||||
"nette/di": "^3.0",
|
|
||||||
"phpstan/phpstan": "^0.12"
|
"phpstan/phpstan": "^0.12"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
@@ -26,12 +25,12 @@
|
|||||||
"classmap": ["src/"]
|
"classmap": ["src/"]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"phpstan": "phpstan analyse",
|
"phpstan": "phpstan analyse --autoload-file vendor/autoload.php --level 5 --configuration tests/phpstan.neon src",
|
||||||
"tester": "tester tests -s"
|
"tester": "tester tests -s"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "4.2-dev"
|
"dev-master": "4.1-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
ecs.php
21
ecs.php
@@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for Nette Coding Standard
|
|
||||||
* https://github.com/nette/coding-standard
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
|
||||||
return function (Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator $containerConfigurator): void {
|
|
||||||
$containerConfigurator->import(PRESET_DIR . '/php71.php');
|
|
||||||
|
|
||||||
$parameters = $containerConfigurator->parameters();
|
|
||||||
|
|
||||||
$parameters->set('skip', [
|
|
||||||
// issue #260
|
|
||||||
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer::class => ['src/Dibi/HashMap.php'],
|
|
||||||
SlevomatCodingStandard\Sniffs\ControlStructures\RequireNullCoalesceOperatorSniff::class => ['src/Dibi/HashMap.php'],
|
|
||||||
]);
|
|
||||||
};
|
|
48
readme.md
48
readme.md
@@ -14,15 +14,7 @@ Introduction
|
|||||||
Database access functions in PHP are not standardised. This library
|
Database access functions in PHP are not standardised. This library
|
||||||
hides the differences between them, and above all, it gives you a very handy interface.
|
hides the differences between them, and above all, it gives you a very handy interface.
|
||||||
|
|
||||||
|
If you like Dibi, **[please make a donation now](https://nette.org/make-donation?to=dibi)**. Thank you!
|
||||||
Support Me
|
|
||||||
----------
|
|
||||||
|
|
||||||
Do you like Dibi? Are you looking forward to the new features?
|
|
||||||
|
|
||||||
[](https://github.com/sponsors/dg)
|
|
||||||
|
|
||||||
Thank you!
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
@@ -34,7 +26,7 @@ Install Dibi via Composer:
|
|||||||
composer require dibi/dibi
|
composer require dibi/dibi
|
||||||
```
|
```
|
||||||
|
|
||||||
The Dibi 4.2 requires PHP version 7.2 and supports PHP up to 8.0.
|
The Dibi 4.1 requires PHP version 7.1 and supports PHP up to 7.4.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
@@ -50,11 +42,11 @@ The database connection is represented by the object `Dibi\Connection`:
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$database = new Dibi\Connection([
|
$database = new Dibi\Connection([
|
||||||
'driver' => 'mysqli',
|
'driver' => 'mysqli',
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'username' => 'root',
|
'username' => 'root',
|
||||||
'password' => '***',
|
'password' => '***',
|
||||||
'database' => 'table',
|
'database' => 'table',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = $database->query('SELECT * FROM users');
|
$result = $database->query('SELECT * FROM users');
|
||||||
@@ -64,12 +56,12 @@ Alternatively, you can use the `dibi` static register, which maintains a connect
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
dibi::connect([
|
dibi::connect([
|
||||||
'driver' => 'mysqli',
|
'driver' => 'mysqli',
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'username' => 'root',
|
'username' => 'root',
|
||||||
'password' => '***',
|
'password' => '***',
|
||||||
'database' => 'test',
|
'database' => 'test',
|
||||||
'charset' => 'utf8',
|
'charset' => 'utf8',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = dibi::query('SELECT * FROM users');
|
$result = dibi::query('SELECT * FROM users');
|
||||||
@@ -237,8 +229,8 @@ Example:
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
$arr = [
|
$arr = [
|
||||||
'a' => 'hello',
|
'a' => 'hello',
|
||||||
'b' => true,
|
'b' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$database->query('INSERT INTO table %v', $arr);
|
$database->query('INSERT INTO table %v', $arr);
|
||||||
@@ -510,7 +502,7 @@ $all = $result->fetchAssoc('customer_id|order_id');
|
|||||||
// we will iterate like this:
|
// we will iterate like this:
|
||||||
foreach ($all as $customerId => $orders) {
|
foreach ($all as $customerId => $orders) {
|
||||||
foreach ($orders as $orderId => $order) {
|
foreach ($orders as $orderId => $order) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -542,7 +534,7 @@ $all = $result->fetchAssoc('name[]order_id');
|
|||||||
// we get all the Arnolds in the results
|
// we get all the Arnolds in the results
|
||||||
foreach ($all['Arnold Rimmer'] as $arnoldOrders) {
|
foreach ($all['Arnold Rimmer'] as $arnoldOrders) {
|
||||||
foreach ($arnoldOrders as $orderId => $order) {
|
foreach ($arnoldOrders as $orderId => $order) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -556,8 +548,8 @@ foreach ($all as $customerId => $orders) {
|
|||||||
echo "Customer $customerId":
|
echo "Customer $customerId":
|
||||||
|
|
||||||
foreach ($orders as $orderId => $order) {
|
foreach ($orders as $orderId => $order) {
|
||||||
echo "ID number: $order->number";
|
echo "ID number: $order->number";
|
||||||
// customer name is in $order->name
|
// customer name is in $order->name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -579,7 +571,7 @@ foreach ($all as $customerId => $row) {
|
|||||||
echo "Customer $row->name":
|
echo "Customer $row->name":
|
||||||
|
|
||||||
foreach ($row->order_id as $orderId => $order) {
|
foreach ($row->order_id as $orderId => $order) {
|
||||||
echo "ID number: $order->number";
|
echo "ID number: $order->number";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@@ -45,7 +45,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
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([__CLASS__, 'renderException']);
|
||||||
$connection->onEvent[] = [$this, 'logEvent'];
|
$connection->onEvent[] = [$this, 'logEvent'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,15 +105,6 @@ class Panel implements Tracy\IBarPanel
|
|||||||
}
|
}
|
||||||
|
|
||||||
$totalTime = $s = null;
|
$totalTime = $s = null;
|
||||||
|
|
||||||
$singleConnection = reset($this->events)->connection;
|
|
||||||
foreach ($this->events as $event) {
|
|
||||||
if ($event->connection !== $singleConnection) {
|
|
||||||
$singleConnection = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->events as $event) {
|
foreach ($this->events as $event) {
|
||||||
$totalTime += $event->time;
|
$totalTime += $event->time;
|
||||||
$connection = $event->connection;
|
$connection = $event->connection;
|
||||||
@@ -121,9 +112,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
if ($this->explain && $event->type === Event::SELECT) {
|
if ($this->explain && $event->type === Event::SELECT) {
|
||||||
$backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
|
$backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
|
||||||
$connection->onEvent = null;
|
$connection->onEvent = null;
|
||||||
$cmd = is_string($this->explain)
|
$cmd = is_string($this->explain) ? $this->explain : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
||||||
? $this->explain
|
|
||||||
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
|
||||||
try {
|
try {
|
||||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
||||||
} catch (Dibi\Exception $e) {
|
} catch (Dibi\Exception $e) {
|
||||||
@@ -131,7 +120,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
[$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime] = $backup;
|
[$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime] = $backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
$s .= '<tr><td data-order="' . $event->time . '">' . number_format($event->time * 1000, 3, '.', ' ');
|
$s .= '<tr><td>' . number_format($event->time * 1000, 3, '.', ' ');
|
||||||
if ($explain) {
|
if ($explain) {
|
||||||
static $counter;
|
static $counter;
|
||||||
$counter++;
|
$counter++;
|
||||||
@@ -143,13 +132,10 @@ class Panel implements Tracy\IBarPanel
|
|||||||
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
||||||
}
|
}
|
||||||
if ($event->source) {
|
if ($event->source) {
|
||||||
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]); //->class('tracy-DibiProfiler-source');
|
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]);//->class('tracy-DibiProfiler-source');
|
||||||
}
|
}
|
||||||
|
|
||||||
$s .= "</td><td>{$event->count}</td>";
|
$s .= "</td><td>{$event->count}</td></tr>";
|
||||||
if (!$singleConnection) {
|
|
||||||
$s .= '<td>' . htmlspecialchars($this->getConnectionName($connection)) . '</td></tr>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
|
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
|
||||||
@@ -157,21 +143,12 @@ class Panel implements Tracy\IBarPanel
|
|||||||
#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: ' . count($this->events)
|
<h1>Queries: ' . count($this->events)
|
||||||
. ($totalTime === null ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', ' ') . ' ms') . ', '
|
. ($totalTime === null ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', ' ') . ' ms') . ', '
|
||||||
. htmlspecialchars($this->getConnectionName($singleConnection)) . '</h1>
|
. htmlspecialchars($connection->getConfig('driver') . ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
||||||
|
. ($connection->getConfig('host') ? ' @ ' . $connection->getConfig('host') : '')) . '</h1>
|
||||||
<div class="tracy-inner tracy-DibiProfiler">
|
<div class="tracy-inner tracy-DibiProfiler">
|
||||||
<table class="tracy-sortable">
|
<table>
|
||||||
<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></tr>' . $s . '
|
||||||
' . $s . '
|
|
||||||
</table>
|
</table>
|
||||||
</div>';
|
</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function getConnectionName(Dibi\Connection $connection): string
|
|
||||||
{
|
|
||||||
$driver = $connection->getConfig('driver');
|
|
||||||
return (is_object($driver) ? get_class($driver) : $driver)
|
|
||||||
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
|
||||||
. ($connection->getConfig('host') ? ' @ ' . $connection->getConfig('host') : '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -28,9 +28,6 @@ class Connection implements IConnection
|
|||||||
/** @var array Current connection configuration */
|
/** @var array Current connection configuration */
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
/** @var string[] resultset formats */
|
|
||||||
private $formats;
|
|
||||||
|
|
||||||
/** @var Driver|null */
|
/** @var Driver|null */
|
||||||
private $driver;
|
private $driver;
|
||||||
|
|
||||||
@@ -40,26 +37,17 @@ class Connection implements IConnection
|
|||||||
/** @var HashMap Substitutes for identifiers */
|
/** @var HashMap Substitutes for identifiers */
|
||||||
private $substitutes;
|
private $substitutes;
|
||||||
|
|
||||||
private $transactionDepth = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection options: (see driver-specific options too)
|
* Connection options: (see driver-specific options too)
|
||||||
* - lazy (bool) => if true, connection will be established only when required
|
* - lazy (bool) => if true, connection will be established only when required
|
||||||
* - result (array) => result set options
|
* - result (array) => result set options
|
||||||
* - normalize => normalizes result fields (default: true)
|
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
|
||||||
* - formatDateTime => date-time format
|
* - formatJson => json format (
|
||||||
* empty for decoding as Dibi\DateTime (default)
|
* "string" for leaving value as is,
|
||||||
* "..." formatted according to given format, see https://www.php.net/manual/en/datetime.format.php
|
* "object" for decoding json as \stdClass,
|
||||||
* "native" for leaving value as is
|
* "array" for decoding json as an array - default
|
||||||
* - formatTimeInterval => time-interval format
|
* )
|
||||||
* empty for decoding as DateInterval (default)
|
|
||||||
* "..." formatted according to given format, see https://www.php.net/manual/en/dateinterval.format.php
|
|
||||||
* "native" for leaving value as is
|
|
||||||
* - formatJson => json format
|
|
||||||
* "array" for decoding json as an array (default)
|
|
||||||
* "object" for decoding json as \stdClass
|
|
||||||
* "native" for leaving value as is
|
|
||||||
* - profiler (array)
|
* - profiler (array)
|
||||||
* - run (bool) => enable profiler?
|
* - run (bool) => enable profiler?
|
||||||
* - file => file to log
|
* - file => file to log
|
||||||
@@ -77,15 +65,9 @@ class Connection implements IConnection
|
|||||||
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
||||||
$config['driver'] = $config['driver'] ?? 'mysqli';
|
$config['driver'] = $config['driver'] ?? 'mysqli';
|
||||||
$config['name'] = $name;
|
$config['name'] = $name;
|
||||||
|
$config['result']['formatJson'] = $config['result']['formatJson'] ?? 'array';
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
|
||||||
$this->formats = [
|
|
||||||
Type::DATE => $this->config['result']['formatDate'],
|
|
||||||
Type::DATETIME => $this->config['result']['formatDateTime'],
|
|
||||||
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
|
||||||
Type::TIME_INTERVAL => $this->config['result']['formatTimeInterval'] ?? null,
|
|
||||||
];
|
|
||||||
|
|
||||||
// profiler
|
// profiler
|
||||||
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
||||||
$filter = $config['profiler']['filter'] ?? Event::QUERY;
|
$filter = $config['profiler']['filter'] ?? Event::QUERY;
|
||||||
@@ -218,7 +200,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function query(...$args): Result
|
final public function query(...$args): Result
|
||||||
{
|
{
|
||||||
return $this->nativeQuery($this->translate(...$args));
|
return $this->nativeQuery($this->translateArgs($args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -229,10 +211,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function translate(...$args): string
|
final public function translate(...$args): string
|
||||||
{
|
{
|
||||||
if (!$this->driver) {
|
return $this->translateArgs($args);
|
||||||
$this->connect();
|
|
||||||
}
|
|
||||||
return (clone $this->translator)->translate($args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -243,7 +222,7 @@ class Connection implements IConnection
|
|||||||
final public function test(...$args): bool
|
final public function test(...$args): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Helpers::dump($this->translate(...$args));
|
Helpers::dump($this->translateArgs($args));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -264,7 +243,19 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function dataSource(...$args): DataSource
|
final public function dataSource(...$args): DataSource
|
||||||
{
|
{
|
||||||
return new DataSource($this->translate(...$args), $this);
|
return new DataSource($this->translateArgs($args), $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates SQL query.
|
||||||
|
*/
|
||||||
|
protected function translateArgs(array $args): string
|
||||||
|
{
|
||||||
|
if (!$this->driver) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
return (clone $this->translator)->translate($args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -337,10 +328,6 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
public function begin(string $savepoint = null): void
|
public function begin(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
if ($this->transactionDepth !== 0) {
|
|
||||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
}
|
}
|
||||||
@@ -365,10 +352,6 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
public function commit(string $savepoint = null): void
|
public function commit(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
if ($this->transactionDepth !== 0) {
|
|
||||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
}
|
}
|
||||||
@@ -393,10 +376,6 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
public function rollback(string $savepoint = null): void
|
public function rollback(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
if ($this->transactionDepth !== 0) {
|
|
||||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
}
|
}
|
||||||
@@ -416,42 +395,15 @@ class Connection implements IConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function transaction(callable $callback)
|
|
||||||
{
|
|
||||||
if ($this->transactionDepth === 0) {
|
|
||||||
$this->begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->transactionDepth++;
|
|
||||||
try {
|
|
||||||
$res = $callback($this);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->transactionDepth--;
|
|
||||||
if ($this->transactionDepth === 0) {
|
|
||||||
$this->rollback();
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->transactionDepth--;
|
|
||||||
if ($this->transactionDepth === 0) {
|
|
||||||
$this->commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result set factory.
|
* Result set factory.
|
||||||
*/
|
*/
|
||||||
public function createResultSet(ResultDriver $resultDriver): Result
|
public function createResultSet(ResultDriver $resultDriver): Result
|
||||||
{
|
{
|
||||||
return (new Result($resultDriver, $this->config['result']['normalize'] ?? true))
|
$res = new Result($resultDriver);
|
||||||
->setFormats($this->formats);
|
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
|
||||||
|
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime'])
|
||||||
|
->setFormat(Type::JSON, $this->config['result']['formatJson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -610,7 +562,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
public function __wakeup()
|
public function __wakeup()
|
||||||
{
|
{
|
||||||
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -619,7 +571,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
public function __sleep()
|
public function __sleep()
|
||||||
{
|
{
|
||||||
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
|
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -53,9 +53,11 @@ class DataSource implements IDataSource
|
|||||||
*/
|
*/
|
||||||
public function __construct(string $sql, Connection $connection)
|
public function __construct(string $sql, Connection $connection)
|
||||||
{
|
{
|
||||||
$this->sql = strpbrk($sql, " \t\r\n") === false
|
if (strpbrk($sql, " \t\r\n") === false) {
|
||||||
? $connection->getDriver()->escapeIdentifier($sql) // table name
|
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
|
||||||
: '(' . $sql . ') t'; // SQL command
|
} else {
|
||||||
|
$this->sql = '(' . $sql . ') t'; // SQL command
|
||||||
|
}
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +84,12 @@ class DataSource implements IDataSource
|
|||||||
*/
|
*/
|
||||||
public function where($cond): self
|
public function where($cond): self
|
||||||
{
|
{
|
||||||
$this->conds[] = is_array($cond)
|
if (is_array($cond)) {
|
||||||
? $cond // TODO: not consistent with select and orderBy
|
// TODO: not consistent with select and orderBy
|
||||||
: func_get_args();
|
$this->conds[] = $cond;
|
||||||
|
} else {
|
||||||
|
$this->conds[] = func_get_args();
|
||||||
|
}
|
||||||
$this->result = $this->count = null;
|
$this->result = $this->count = null;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -227,18 +232,12 @@ class DataSource implements IDataSource
|
|||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->connection->translate(
|
return $this->connection->translate('
|
||||||
"\nSELECT %n",
|
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
|
||||||
(empty($this->cols) ? '*' : $this->cols),
|
FROM %SQL', $this->sql, '
|
||||||
"\nFROM %SQL",
|
%ex', $this->conds ? ['WHERE %and', $this->conds] : null, '
|
||||||
$this->sql,
|
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : null, '
|
||||||
"\n%ex",
|
%ofs %lmt', $this->offset, $this->limit
|
||||||
$this->conds ? ['WHERE %and', $this->conds] : null,
|
|
||||||
"\n%ex",
|
|
||||||
$this->sorting ? ['ORDER BY %by', $this->sorting] : null,
|
|
||||||
"\n%ofs %lmt",
|
|
||||||
$this->offset,
|
|
||||||
$this->limit
|
|
||||||
);
|
);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
trigger_error($e->getMessage(), E_USER_ERROR);
|
trigger_error($e->getMessage(), E_USER_ERROR);
|
||||||
|
@@ -62,9 +62,11 @@ class FirebirdDriver implements Dibi\Driver
|
|||||||
'buffers' => 0,
|
'buffers' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->connection = empty($config['persistent'])
|
if (empty($config['persistent'])) {
|
||||||
? @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']) // intentionally @
|
$this->connection = @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
|
||||||
: @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
|
} else {
|
||||||
|
$this->connection = @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_resource($this->connection)) {
|
if (!is_resource($this->connection)) {
|
||||||
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
|
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
|
||||||
@@ -88,13 +90,11 @@ class FirebirdDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function query(string $sql): ?Dibi\ResultDriver
|
public function query(string $sql): ?Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
$resource = $this->inTransaction
|
$resource = $this->inTransaction ? $this->transaction : $this->connection;
|
||||||
? $this->transaction
|
|
||||||
: $this->connection;
|
|
||||||
$res = ibase_query($resource, $sql);
|
$res = ibase_query($resource, $sql);
|
||||||
|
|
||||||
if ($res === false) {
|
if ($res === false) {
|
||||||
if (ibase_errcode() === self::ERROR_EXCEPTION_THROWN) {
|
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
|
||||||
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
|
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
|
||||||
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
|
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
|
||||||
|
|
||||||
|
@@ -38,8 +38,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
SELECT TRIM(RDB\$RELATION_NAME),
|
SELECT TRIM(RDB\$RELATION_NAME),
|
||||||
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
|
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
|
||||||
FROM RDB\$RELATIONS
|
FROM RDB\$RELATIONS
|
||||||
WHERE RDB\$SYSTEM_FLAG = 0;
|
WHERE RDB\$SYSTEM_FLAG = 0;"
|
||||||
");
|
);
|
||||||
$tables = [];
|
$tables = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$tables[] = [
|
$tables[] = [
|
||||||
@@ -84,8 +84,9 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
FROM RDB\$RELATION_FIELDS r
|
FROM RDB\$RELATION_FIELDS r
|
||||||
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
|
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
|
||||||
WHERE r.RDB\$RELATION_NAME = '$table'
|
WHERE r.RDB\$RELATION_NAME = '$table'
|
||||||
ORDER BY r.RDB\$FIELD_POSITION;
|
ORDER BY r.RDB\$FIELD_POSITION;"
|
||||||
");
|
|
||||||
|
);
|
||||||
$columns = [];
|
$columns = [];
|
||||||
while ($row = $res->fetch(true)) {
|
while ($row = $res->fetch(true)) {
|
||||||
$key = $row['FIELD_NAME'];
|
$key = $row['FIELD_NAME'];
|
||||||
@@ -120,8 +121,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||||
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||||
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
||||||
ORDER BY s.RDB\$FIELD_POSITION
|
ORDER BY s.RDB\$FIELD_POSITION"
|
||||||
");
|
);
|
||||||
$indexes = [];
|
$indexes = [];
|
||||||
while ($row = $res->fetch(true)) {
|
while ($row = $res->fetch(true)) {
|
||||||
$key = $row['INDEX_NAME'];
|
$key = $row['INDEX_NAME'];
|
||||||
@@ -148,8 +149,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
|
||||||
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
|
||||||
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
|
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
|
||||||
ORDER BY s.RDB\$FIELD_POSITION
|
ORDER BY s.RDB\$FIELD_POSITION"
|
||||||
");
|
);
|
||||||
$keys = [];
|
$keys = [];
|
||||||
while ($row = $res->fetch(true)) {
|
while ($row = $res->fetch(true)) {
|
||||||
$key = $row['INDEX_NAME'];
|
$key = $row['INDEX_NAME'];
|
||||||
@@ -173,8 +174,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
FROM RDB\$INDICES
|
FROM RDB\$INDICES
|
||||||
WHERE RDB\$RELATION_NAME = UPPER('$table')
|
WHERE RDB\$RELATION_NAME = UPPER('$table')
|
||||||
AND RDB\$UNIQUE_FLAG IS NULL
|
AND RDB\$UNIQUE_FLAG IS NULL
|
||||||
AND RDB\$FOREIGN_KEY IS NULL;
|
AND RDB\$FOREIGN_KEY IS NULL;"
|
||||||
");
|
);
|
||||||
$indices = [];
|
$indices = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$indices[] = $row[0];
|
$indices[] = $row[0];
|
||||||
@@ -195,8 +196,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
AND (
|
AND (
|
||||||
RDB\$UNIQUE_FLAG IS NOT NULL
|
RDB\$UNIQUE_FLAG IS NOT NULL
|
||||||
OR RDB\$FOREIGN_KEY IS NOT NULL
|
OR RDB\$FOREIGN_KEY IS NOT NULL
|
||||||
);
|
);"
|
||||||
");
|
);
|
||||||
$constraints = [];
|
$constraints = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$constraints[] = $row[0];
|
$constraints[] = $row[0];
|
||||||
@@ -211,8 +212,7 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
*/
|
*/
|
||||||
public function getTriggersMeta(string $table = null): array
|
public function getTriggersMeta(string $table = null): array
|
||||||
{
|
{
|
||||||
$res = $this->driver->query(
|
$res = $this->driver->query("
|
||||||
"
|
|
||||||
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
|
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
|
||||||
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
|
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
|
||||||
CASE RDB\$TRIGGER_TYPE
|
CASE RDB\$TRIGGER_TYPE
|
||||||
@@ -261,9 +261,7 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
$q = 'SELECT TRIM(RDB$TRIGGER_NAME)
|
$q = 'SELECT TRIM(RDB$TRIGGER_NAME)
|
||||||
FROM RDB$TRIGGERS
|
FROM RDB$TRIGGERS
|
||||||
WHERE RDB$SYSTEM_FLAG = 0';
|
WHERE RDB$SYSTEM_FLAG = 0';
|
||||||
$q .= $table === null
|
$q .= $table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
|
||||||
? ';'
|
|
||||||
: " AND RDB\$RELATION_NAME = UPPER('$table')";
|
|
||||||
|
|
||||||
$res = $this->driver->query($q);
|
$res = $this->driver->query($q);
|
||||||
$triggers = [];
|
$triggers = [];
|
||||||
@@ -309,8 +307,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
|
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
|
||||||
FROM RDB\$PROCEDURE_PARAMETERS p
|
FROM RDB\$PROCEDURE_PARAMETERS p
|
||||||
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
|
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
|
||||||
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;
|
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
|
||||||
");
|
);
|
||||||
$procedures = [];
|
$procedures = [];
|
||||||
while ($row = $res->fetch(true)) {
|
while ($row = $res->fetch(true)) {
|
||||||
$key = $row['PROCEDURE_NAME'];
|
$key = $row['PROCEDURE_NAME'];
|
||||||
@@ -332,8 +330,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
{
|
{
|
||||||
$res = $this->driver->query('
|
$res = $this->driver->query('
|
||||||
SELECT TRIM(RDB$PROCEDURE_NAME)
|
SELECT TRIM(RDB$PROCEDURE_NAME)
|
||||||
FROM RDB$PROCEDURES;
|
FROM RDB$PROCEDURES;'
|
||||||
');
|
);
|
||||||
$procedures = [];
|
$procedures = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$procedures[] = $row[0];
|
$procedures[] = $row[0];
|
||||||
@@ -350,8 +348,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
$res = $this->driver->query('
|
$res = $this->driver->query('
|
||||||
SELECT TRIM(RDB$GENERATOR_NAME)
|
SELECT TRIM(RDB$GENERATOR_NAME)
|
||||||
FROM RDB$GENERATORS
|
FROM RDB$GENERATORS
|
||||||
WHERE RDB$SYSTEM_FLAG = 0;
|
WHERE RDB$SYSTEM_FLAG = 0;'
|
||||||
');
|
);
|
||||||
$generators = [];
|
$generators = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$generators[] = $row[0];
|
$generators[] = $row[0];
|
||||||
@@ -368,8 +366,8 @@ class FirebirdReflector implements Dibi\Reflector
|
|||||||
$res = $this->driver->query('
|
$res = $this->driver->query('
|
||||||
SELECT TRIM(RDB$FUNCTION_NAME)
|
SELECT TRIM(RDB$FUNCTION_NAME)
|
||||||
FROM RDB$FUNCTIONS
|
FROM RDB$FUNCTIONS
|
||||||
WHERE RDB$SYSTEM_FLAG = 0;
|
WHERE RDB$SYSTEM_FLAG = 0;'
|
||||||
');
|
);
|
||||||
$functions = [];
|
$functions = [];
|
||||||
while ($row = $res->fetch(false)) {
|
while ($row = $res->fetch(false)) {
|
||||||
$functions[] = $row[0];
|
$functions[] = $row[0];
|
||||||
|
@@ -62,12 +62,10 @@ class FirebirdResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function fetch(bool $assoc): ?array
|
public function fetch(bool $assoc): ?array
|
||||||
{
|
{
|
||||||
$result = $assoc
|
$result = $assoc ? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
|
||||||
? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT)
|
|
||||||
: @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
|
|
||||||
|
|
||||||
if (ibase_errcode()) {
|
if (ibase_errcode()) {
|
||||||
if (ibase_errcode() === FirebirdDriver::ERROR_EXCEPTION_THROWN) {
|
if (ibase_errcode() == FirebirdDriver::ERROR_EXCEPTION_THROWN) {
|
||||||
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
|
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
|
||||||
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
|
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
|
||||||
|
|
||||||
|
@@ -130,15 +130,6 @@ class MySqliDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pings a server connection, or tries to reconnect if the connection has gone down.
|
|
||||||
*/
|
|
||||||
public function ping(): bool
|
|
||||||
{
|
|
||||||
return $this->connection->ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the SQL query.
|
* Executes the SQL query.
|
||||||
* @throws Dibi\DriverException
|
* @throws Dibi\DriverException
|
||||||
@@ -157,9 +148,6 @@ class MySqliDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int|string $code
|
|
||||||
*/
|
|
||||||
public static function createException(string $message, $code, string $sql): Dibi\DriverException
|
public static function createException(string $message, $code, string $sql): Dibi\DriverException
|
||||||
{
|
{
|
||||||
if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) {
|
if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) {
|
||||||
@@ -200,9 +188,7 @@ class MySqliDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function getAffectedRows(): ?int
|
public function getAffectedRows(): ?int
|
||||||
{
|
{
|
||||||
return $this->connection->affected_rows === -1
|
return $this->connection->affected_rows === -1 ? null : $this->connection->affected_rows;
|
||||||
? null
|
|
||||||
: $this->connection->affected_rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -319,7 +305,7 @@ class MySqliDriver implements Dibi\Driver
|
|||||||
if ($value->y || $value->m || $value->d) {
|
if ($value->y || $value->m || $value->d) {
|
||||||
throw new Dibi\NotSupportedException('Only time interval is supported.');
|
throw new Dibi\NotSupportedException('Only time interval is supported.');
|
||||||
}
|
}
|
||||||
return $value->format("'%r%H:%I:%S.%f'");
|
return $value->format('%r%H:%I:%S.%f');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -54,9 +54,11 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
'dsn' => ini_get('odbc.default_db'),
|
'dsn' => ini_get('odbc.default_db'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->connection = empty($config['persistent'])
|
if (empty($config['persistent'])) {
|
||||||
? @odbc_connect($config['dsn'], $config['username'] ?? '', $config['password'] ?? '') // intentionally @
|
$this->connection = @odbc_connect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
|
||||||
: @odbc_pconnect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
|
} else {
|
||||||
|
$this->connection = @odbc_pconnect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_resource($this->connection)) {
|
if (!is_resource($this->connection)) {
|
||||||
@@ -92,9 +94,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
|
|
||||||
} elseif (is_resource($res)) {
|
} elseif (is_resource($res)) {
|
||||||
$this->affectedRows = Dibi\Helpers::false2Null(odbc_num_rows($res));
|
$this->affectedRows = Dibi\Helpers::false2Null(odbc_num_rows($res));
|
||||||
return odbc_num_fields($res)
|
return odbc_num_fields($res) ? $this->createResultDriver($res) : null;
|
||||||
? $this->createResultDriver($res)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function begin(string $savepoint = null): void
|
public function begin(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
if (!odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 0 : false)) {
|
if (!odbc_autocommit($this->connection, 0/*false*/)) {
|
||||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
if (!odbc_commit($this->connection)) {
|
if (!odbc_commit($this->connection)) {
|
||||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||||
}
|
}
|
||||||
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
|
odbc_autocommit($this->connection, 1/*true*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
if (!odbc_rollback($this->connection)) {
|
if (!odbc_rollback($this->connection)) {
|
||||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||||
}
|
}
|
||||||
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
|
odbc_autocommit($this->connection, 1/*true*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -96,9 +96,7 @@ class OracleDriver implements Dibi\Driver
|
|||||||
|
|
||||||
} elseif (is_resource($res)) {
|
} elseif (is_resource($res)) {
|
||||||
$this->affectedRows = Dibi\Helpers::false2Null(oci_num_rows($res));
|
$this->affectedRows = Dibi\Helpers::false2Null(oci_num_rows($res));
|
||||||
return oci_num_fields($res)
|
return oci_num_fields($res) ? $this->createResultDriver($res) : null;
|
||||||
? $this->createResultDriver($res)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$err = oci_error($this->connection);
|
$err = oci_error($this->connection);
|
||||||
|
@@ -56,15 +56,9 @@ class PdoDriver implements Dibi\Driver
|
|||||||
if ($config['resource'] instanceof PDO) {
|
if ($config['resource'] instanceof PDO) {
|
||||||
$this->connection = $config['resource'];
|
$this->connection = $config['resource'];
|
||||||
unset($config['resource'], $config['pdo']);
|
unset($config['resource'], $config['pdo']);
|
||||||
|
|
||||||
if ($this->connection->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_SILENT) {
|
|
||||||
throw new Dibi\DriverException('PDO connection in exception or warning error mode is not supported.');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
|
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
|
||||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
if ($e->getMessage() === 'could not find driver') {
|
if ($e->getMessage() === 'could not find driver') {
|
||||||
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
|
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
|
||||||
@@ -73,6 +67,10 @@ class PdoDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->connection->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_SILENT) {
|
||||||
|
throw new Dibi\DriverException('PDO connection in exception or warning error mode is not supported.');
|
||||||
|
}
|
||||||
|
|
||||||
$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
|
$this->serverVersion = (string) ($config['version'] ?? @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION)); // @ - may be not supported
|
||||||
}
|
}
|
||||||
@@ -234,17 +232,21 @@ class PdoDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function escapeText(string $value): string
|
public function escapeText(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
if ($this->driverName === 'odbc') {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
return "'" . str_replace("'", "''", $value) . "'";
|
||||||
: $this->connection->quote($value, PDO::PARAM_STR);
|
} else {
|
||||||
|
return $this->connection->quote($value, PDO::PARAM_STR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function escapeBinary(string $value): string
|
public function escapeBinary(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
if ($this->driverName === 'odbc') {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
return "'" . str_replace("'", "''", $value) . "'";
|
||||||
: $this->connection->quote($value, PDO::PARAM_LOB);
|
} else {
|
||||||
|
return $this->connection->quote($value, PDO::PARAM_LOB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -85,7 +85,7 @@ class PdoResult implements Dibi\ResultDriver
|
|||||||
if ($row === false) {
|
if ($row === false) {
|
||||||
throw new Dibi\NotSupportedException('Driver does not support meta data.');
|
throw new Dibi\NotSupportedException('Driver does not support meta data.');
|
||||||
}
|
}
|
||||||
$row += [
|
$row = $row + [
|
||||||
'table' => null,
|
'table' => null,
|
||||||
'native_type' => 'VAR_STRING',
|
'native_type' => 'VAR_STRING',
|
||||||
];
|
];
|
||||||
|
@@ -23,7 +23,6 @@ use Dibi\Helpers;
|
|||||||
* - 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 (resource) => existing connection resource
|
||||||
* - connect_type (int) => see pg_connect()
|
|
||||||
*/
|
*/
|
||||||
class PostgreDriver implements Dibi\Driver
|
class PostgreDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
@@ -63,14 +62,15 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$connectType = $config['connect_type'] ?? PGSQL_CONNECT_FORCE_NEW;
|
|
||||||
|
|
||||||
set_error_handler(function (int $severity, string $message) use (&$error) {
|
set_error_handler(function (int $severity, string $message) use (&$error) {
|
||||||
$error = $message;
|
$error = $message;
|
||||||
});
|
});
|
||||||
$this->connection = empty($config['persistent'])
|
if (empty($config['persistent'])) {
|
||||||
? pg_connect($string, $connectType)
|
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
|
||||||
: pg_pconnect($string, $connectType);
|
} else {
|
||||||
|
$this->connection = pg_pconnect($string);
|
||||||
|
}
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,9 +169,12 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function getInsertId(?string $sequence): ?int
|
public function getInsertId(?string $sequence): ?int
|
||||||
{
|
{
|
||||||
$res = $sequence === null
|
if ($sequence === null) {
|
||||||
? $this->query('SELECT LASTVAL()') // PostgreSQL 8.1 is needed
|
// PostgreSQL 8.1 is needed
|
||||||
: $this->query("SELECT CURRVAL('$sequence')");
|
$res = $this->query('SELECT LASTVAL()');
|
||||||
|
} else {
|
||||||
|
$res = $this->query("SELECT CURRVAL('$sequence')");
|
||||||
|
}
|
||||||
|
|
||||||
if (!$res) {
|
if (!$res) {
|
||||||
return null;
|
return null;
|
||||||
@@ -188,7 +191,7 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function begin(string $savepoint = null): void
|
public function begin(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
$this->query($savepoint ? "SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'START TRANSACTION');
|
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -198,7 +201,7 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function commit(string $savepoint = null): void
|
public function commit(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
$this->query($savepoint ? "RELEASE SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'COMMIT');
|
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -208,7 +211,7 @@ class PostgreDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function rollback(string $savepoint = null): void
|
public function rollback(string $savepoint = null): void
|
||||||
{
|
{
|
||||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'ROLLBACK');
|
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -28,6 +28,9 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
|
|
||||||
public function __construct(Dibi\Driver $driver, string $version)
|
public function __construct(Dibi\Driver $driver, string $version)
|
||||||
{
|
{
|
||||||
|
if ($version < 7.4) {
|
||||||
|
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
|
||||||
|
}
|
||||||
$this->driver = $driver;
|
$this->driver = $driver;
|
||||||
$this->version = $version;
|
$this->version = $version;
|
||||||
}
|
}
|
||||||
@@ -248,10 +251,7 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
$references[$row['name']] = array_combine($l, $f);
|
$references[$row['name']] = array_combine($l, $f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
|
||||||
isset($references[$row['name']][$row['lnum']])
|
|
||||||
&& $references[$row['name']][$row['lnum']] === $row['fnum']
|
|
||||||
) {
|
|
||||||
$fKeys[$row['name']]['local'][] = $row['local'];
|
$fKeys[$row['name']]['local'][] = $row['local'];
|
||||||
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
|
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
|
||||||
}
|
}
|
||||||
|
@@ -97,9 +97,7 @@ class PostgreResult implements Dibi\ResultDriver
|
|||||||
'table' => pg_field_table($this->resultSet, $i),
|
'table' => pg_field_table($this->resultSet, $i),
|
||||||
'nativetype' => pg_field_type($this->resultSet, $i),
|
'nativetype' => pg_field_type($this->resultSet, $i),
|
||||||
];
|
];
|
||||||
$row['fullname'] = $row['table']
|
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
|
||||||
? $row['table'] . '.' . $row['name']
|
|
||||||
: $row['name'];
|
|
||||||
$columns[] = $row;
|
$columns[] = $row;
|
||||||
}
|
}
|
||||||
return $columns;
|
return $columns;
|
||||||
|
@@ -286,12 +286,8 @@ class SqliteDriver implements Dibi\Driver
|
|||||||
/**
|
/**
|
||||||
* Registers an aggregating user defined function for use in SQL statements.
|
* Registers an aggregating user defined function for use in SQL statements.
|
||||||
*/
|
*/
|
||||||
public function registerAggregateFunction(
|
public function registerAggregateFunction(string $name, callable $rowCallback, callable $agrCallback, int $numArgs = -1): void
|
||||||
string $name,
|
{
|
||||||
callable $rowCallback,
|
|
||||||
callable $agrCallback,
|
|
||||||
int $numArgs = -1
|
|
||||||
): void {
|
|
||||||
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
|
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -64,7 +64,7 @@ class SqliteReflector implements Dibi\Reflector
|
|||||||
'fullname' => "$table.$column",
|
'fullname' => "$table.$column",
|
||||||
'nativetype' => strtoupper($type[0]),
|
'nativetype' => strtoupper($type[0]),
|
||||||
'size' => isset($type[1]) ? (int) $type[1] : null,
|
'size' => isset($type[1]) ? (int) $type[1] : null,
|
||||||
'nullable' => $row['notnull'] === 0,
|
'nullable' => $row['notnull'] == '0',
|
||||||
'default' => $row['dflt_value'],
|
'default' => $row['dflt_value'],
|
||||||
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
|
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
@@ -98,7 +98,7 @@ class SqliteReflector implements Dibi\Reflector
|
|||||||
$column = $indexes[$index]['columns'][0];
|
$column = $indexes[$index]['columns'][0];
|
||||||
$primary = false;
|
$primary = false;
|
||||||
foreach ($columns as $info) {
|
foreach ($columns as $info) {
|
||||||
if ($column === $info['name']) {
|
if ($column == $info['name']) {
|
||||||
$primary = $info['vendor']['pk'];
|
$primary = $info['vendor']['pk'];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -63,13 +63,11 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
$options['UID'] = (string) $options['UID'];
|
$options['UID'] = (string) $options['UID'];
|
||||||
$options['Database'] = (string) $options['Database'];
|
$options['Database'] = (string) $options['Database'];
|
||||||
|
|
||||||
sqlsrv_configure('WarningsReturnAsErrors', 0);
|
|
||||||
$this->connection = sqlsrv_connect($config['host'], $options);
|
$this->connection = sqlsrv_connect($config['host'], $options);
|
||||||
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_resource($this->connection)) {
|
if (!is_resource($this->connection)) {
|
||||||
$info = sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
$info = sqlsrv_errors();
|
||||||
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
||||||
}
|
}
|
||||||
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
||||||
@@ -100,9 +98,7 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
|
|
||||||
} elseif (is_resource($res)) {
|
} elseif (is_resource($res)) {
|
||||||
$this->affectedRows = Helpers::false2Null(sqlsrv_rows_affected($res));
|
$this->affectedRows = Helpers::false2Null(sqlsrv_rows_affected($res));
|
||||||
return sqlsrv_num_fields($res)
|
return sqlsrv_num_fields($res) ? $this->createResultDriver($res) : null;
|
||||||
? $this->createResultDriver($res)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -204,7 +200,7 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
|
|
||||||
public function escapeBinary(string $value): string
|
public function escapeBinary(string $value): string
|
||||||
{
|
{
|
||||||
return '0x' . bin2hex($value);
|
return "'" . str_replace("'", "''", $value) . "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -32,12 +32,6 @@ namespace Dibi;
|
|||||||
* @method Fluent and(...$cond)
|
* @method Fluent and(...$cond)
|
||||||
* @method Fluent or(...$cond)
|
* @method Fluent or(...$cond)
|
||||||
* @method Fluent using(...$cond)
|
* @method Fluent using(...$cond)
|
||||||
* @method Fluent update(...$cond)
|
|
||||||
* @method Fluent insert(...$cond)
|
|
||||||
* @method Fluent delete(...$cond)
|
|
||||||
* @method Fluent into(...$cond)
|
|
||||||
* @method Fluent values(...$cond)
|
|
||||||
* @method Fluent set(...$args)
|
|
||||||
* @method Fluent asc()
|
* @method Fluent asc()
|
||||||
* @method Fluent desc()
|
* @method Fluent desc()
|
||||||
*/
|
*/
|
||||||
@@ -119,7 +113,7 @@ class Fluent implements IDataSource
|
|||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
|
|
||||||
if (self::$normalizer === null) {
|
if (self::$normalizer === null) {
|
||||||
self::$normalizer = new HashMap([self::class, '_formatClause']);
|
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,9 +305,11 @@ class Fluent implements IDataSource
|
|||||||
*/
|
*/
|
||||||
public function fetch()
|
public function fetch()
|
||||||
{
|
{
|
||||||
return $this->command === 'SELECT' && !$this->clauses['LIMIT']
|
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
|
||||||
? $this->query($this->_export(null, ['%lmt', 1]))->fetch()
|
return $this->query($this->_export(null, ['%lmt', 1]))->fetch();
|
||||||
: $this->query($this->_export())->fetch();
|
} else {
|
||||||
|
return $this->query($this->_export())->fetch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -323,9 +319,11 @@ class Fluent implements IDataSource
|
|||||||
*/
|
*/
|
||||||
public function fetchSingle()
|
public function fetchSingle()
|
||||||
{
|
{
|
||||||
return $this->command === 'SELECT' && !$this->clauses['LIMIT']
|
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
|
||||||
? $this->query($this->_export(null, ['%lmt', 1]))->fetchSingle()
|
return $this->query($this->_export(null, ['%lmt', 1]))->fetchSingle();
|
||||||
: $this->query($this->_export())->fetchSingle();
|
} else {
|
||||||
|
return $this->query($this->_export())->fetchSingle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -143,8 +143,8 @@ class Helpers
|
|||||||
{
|
{
|
||||||
$best = null;
|
$best = null;
|
||||||
$min = (strlen($value) / 4 + 1) * 10 + .1;
|
$min = (strlen($value) / 4 + 1) * 10 + .1;
|
||||||
$items = array_map('strval', $items);
|
foreach (array_unique($items, SORT_REGULAR) as $item) {
|
||||||
foreach (array_unique($items) as $item) {
|
$item = is_object($item) ? $item->getName() : $item;
|
||||||
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
|
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
|
||||||
$min = $len;
|
$min = $len;
|
||||||
$best = $item;
|
$best = $item;
|
||||||
@@ -205,7 +205,7 @@ class Helpers
|
|||||||
public static function getTypeCache(): HashMap
|
public static function getTypeCache(): HashMap
|
||||||
{
|
{
|
||||||
if (self::$types === null) {
|
if (self::$types === null) {
|
||||||
self::$types = new HashMap([self::class, 'detectType']);
|
self::$types = new HashMap([__CLASS__, 'detectType']);
|
||||||
}
|
}
|
||||||
return self::$types;
|
return self::$types;
|
||||||
}
|
}
|
||||||
|
@@ -73,9 +73,8 @@ class FileLogger
|
|||||||
|
|
||||||
private function writeToFile(Dibi\Event $event, string $message): void
|
private function writeToFile(Dibi\Event $event, string $message): void
|
||||||
{
|
{
|
||||||
$driver = $event->connection->getConfig('driver');
|
|
||||||
$message .=
|
$message .=
|
||||||
"\n-- driver: " . (is_object($driver) ? get_class($driver) : $driver) . '/' . $event->connection->getConfig('name')
|
"\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
|
||||||
. "\n-- " . date('Y-m-d H:i:s')
|
. "\n-- " . date('Y-m-d H:i:s')
|
||||||
. "\n\n";
|
. "\n\n";
|
||||||
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
|
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
|
||||||
|
@@ -72,9 +72,7 @@ class Column
|
|||||||
|
|
||||||
public function getTableName(): ?string
|
public function getTableName(): ?string
|
||||||
{
|
{
|
||||||
return isset($this->info['table']) && $this->info['table'] != null // intentionally ==
|
return isset($this->info['table']) && $this->info['table'] != null ? $this->info['table'] : null; // intentionally ==
|
||||||
? $this->info['table']
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -82,9 +82,7 @@ class Result
|
|||||||
{
|
{
|
||||||
if ($this->columns === null) {
|
if ($this->columns === null) {
|
||||||
$this->columns = [];
|
$this->columns = [];
|
||||||
$reflector = $this->driver instanceof Dibi\Reflector
|
$reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : null;
|
||||||
? $this->driver
|
|
||||||
: null;
|
|
||||||
foreach ($this->driver->getResultColumns() as $info) {
|
foreach ($this->driver->getResultColumns() as $info) {
|
||||||
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
|
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@ class Result implements IDataSource
|
|||||||
{
|
{
|
||||||
use Strict;
|
use Strict;
|
||||||
|
|
||||||
/** @var ResultDriver|null */
|
/** @var ResultDriver */
|
||||||
private $driver;
|
private $driver;
|
||||||
|
|
||||||
/** @var array Translate table */
|
/** @var array Translate table */
|
||||||
@@ -41,12 +41,10 @@ class Result implements IDataSource
|
|||||||
private $formats = [];
|
private $formats = [];
|
||||||
|
|
||||||
|
|
||||||
public function __construct(ResultDriver $driver, bool $normalize = true)
|
public function __construct(ResultDriver $driver)
|
||||||
{
|
{
|
||||||
$this->driver = $driver;
|
$this->driver = $driver;
|
||||||
if ($normalize) {
|
$this->detectTypes();
|
||||||
$this->detectTypes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -85,9 +83,7 @@ class Result implements IDataSource
|
|||||||
*/
|
*/
|
||||||
final public function seek(int $row): bool
|
final public function seek(int $row): bool
|
||||||
{
|
{
|
||||||
return ($row !== 0 || $this->fetched)
|
return ($row !== 0 || $this->fetched) ? $this->getResultDriver()->seek($row) : true;
|
||||||
? $this->getResultDriver()->seek($row)
|
|
||||||
: true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -203,7 +199,7 @@ class Result implements IDataSource
|
|||||||
*/
|
*/
|
||||||
final public function fetchAll(int $offset = null, int $limit = null): array
|
final public function fetchAll(int $offset = null, int $limit = null): array
|
||||||
{
|
{
|
||||||
$limit = $limit ?? -1;
|
$limit = $limit === null ? -1 : $limit;
|
||||||
$this->seek($offset ?: 0);
|
$this->seek($offset ?: 0);
|
||||||
$row = $this->fetch();
|
$row = $this->fetch();
|
||||||
if (!$row) {
|
if (!$row) {
|
||||||
@@ -299,7 +295,6 @@ class Result implements IDataSource
|
|||||||
} while ($row = $this->fetch());
|
} while ($row = $this->fetch());
|
||||||
|
|
||||||
unset($x);
|
unset($x);
|
||||||
/** @var mixed[] $data */
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,9 +356,11 @@ class Result implements IDataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($x === null) { // build leaf
|
if ($x === null) { // build leaf
|
||||||
$x = $leaf === '='
|
if ($leaf === '=') {
|
||||||
? $row->toArray()
|
$x = $row->toArray();
|
||||||
: $row;
|
} else {
|
||||||
|
$x = $row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while ($row = $this->fetch());
|
} while ($row = $this->fetch());
|
||||||
|
|
||||||
@@ -456,12 +453,8 @@ class Result implements IDataSource
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$value = $row[$key];
|
$value = $row[$key];
|
||||||
$format = $this->formats[$type] ?? null;
|
|
||||||
|
|
||||||
if ($type === null || $format === 'native') {
|
if ($type === Type::TEXT) {
|
||||||
$row[$key] = $value;
|
|
||||||
|
|
||||||
} elseif ($type === Type::TEXT) {
|
|
||||||
$row[$key] = (string) $value;
|
$row[$key] = (string) $value;
|
||||||
|
|
||||||
} elseif ($type === Type::INTEGER) {
|
} elseif ($type === Type::INTEGER) {
|
||||||
@@ -491,29 +484,29 @@ class Result implements IDataSource
|
|||||||
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
||||||
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
|
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
|
||||||
$value = new DateTime($value);
|
$value = new DateTime($value);
|
||||||
$row[$key] = $format ? $value->format($format) : $value;
|
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = null;
|
$row[$key] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($type === Type::TIME_INTERVAL) {
|
} elseif ($type === Type::TIME_INTERVAL) {
|
||||||
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");
|
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
||||||
$value->invert = (int) (bool) $m[1];
|
$row[$key]->invert = (int) (bool) $m[1];
|
||||||
$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) : $value;
|
||||||
? $this->getResultDriver()->unescapeBinary($value)
|
|
||||||
: $value;
|
|
||||||
|
|
||||||
} elseif ($type === Type::JSON) {
|
} elseif ($type === Type::JSON) {
|
||||||
if ($format === 'string') { // back compatibility with 'native'
|
if ($this->formats[$type] === 'string') {
|
||||||
$row[$key] = $value;
|
$row[$key] = $value;
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = json_decode($value, $format === 'array');
|
$row[$key] = json_decode($value, $this->formats[$type] === 'array');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} elseif ($type === null) {
|
||||||
|
$row[$key] = $value;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('Unexpected type ' . $type);
|
throw new \RuntimeException('Unexpected type ' . $type);
|
||||||
}
|
}
|
||||||
@@ -551,7 +544,7 @@ class Result implements IDataSource
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets type format.
|
* Sets date format.
|
||||||
*/
|
*/
|
||||||
final public function setFormat(string $type, ?string $format): self
|
final public function setFormat(string $type, ?string $format): self
|
||||||
{
|
{
|
||||||
@@ -560,16 +553,6 @@ class Result implements IDataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets type formats.
|
|
||||||
*/
|
|
||||||
final public function setFormats(array $formats): self
|
|
||||||
{
|
|
||||||
$this->formats = $formats;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns data format.
|
* Returns data format.
|
||||||
*/
|
*/
|
||||||
|
@@ -53,12 +53,6 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function __isset(string $key): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
|
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
|
||||||
|
|
||||||
|
|
||||||
|
@@ -29,12 +29,9 @@ trait Strict
|
|||||||
*/
|
*/
|
||||||
public function __call(string $name, array $args)
|
public function __call(string $name, array $args)
|
||||||
{
|
{
|
||||||
$class = method_exists($this, $name) ? 'parent' : static::class;
|
$class = method_exists($this, $name) ? 'parent' : get_class($this);
|
||||||
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
|
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $t()?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Call to undefined method $class::$name()$hint");
|
throw new \LogicException("Call to undefined method $class::$name()$hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,12 +42,9 @@ trait Strict
|
|||||||
*/
|
*/
|
||||||
public static function __callStatic(string $name, array $args)
|
public static function __callStatic(string $name, array $args)
|
||||||
{
|
{
|
||||||
$rc = new ReflectionClass(static::class);
|
$rc = new ReflectionClass(get_called_class());
|
||||||
$items = array_filter($rc->getMethods(\ReflectionMethod::IS_STATIC), function ($m) { return $m->isPublic(); });
|
$items = array_intersect($rc->getMethods(ReflectionMethod::IS_PUBLIC), $rc->getMethods(ReflectionMethod::IS_STATIC));
|
||||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $t()?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
|
throw new \LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +62,8 @@ trait Strict
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
$rc = new ReflectionClass($this);
|
$rc = new ReflectionClass($this);
|
||||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); });
|
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
|
||||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $$t?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
|
throw new \LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,11 +75,8 @@ trait Strict
|
|||||||
public function __set(string $name, $value)
|
public function __set(string $name, $value)
|
||||||
{
|
{
|
||||||
$rc = new ReflectionClass($this);
|
$rc = new ReflectionClass($this);
|
||||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); });
|
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
|
||||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $$t?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
|
throw new \LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +93,7 @@ trait Strict
|
|||||||
*/
|
*/
|
||||||
public function __unset(string $name)
|
public function __unset(string $name)
|
||||||
{
|
{
|
||||||
$class = static::class;
|
$class = get_class($this);
|
||||||
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
|
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -92,26 +92,24 @@ final class Translator
|
|||||||
$sql[] = $arg;
|
$sql[] = $arg;
|
||||||
} else {
|
} else {
|
||||||
$sql[] = substr($arg, 0, $toSkip)
|
$sql[] = substr($arg, 0, $toSkip)
|
||||||
// note: this can change $this->args & $this->cursor & ...
|
/*
|
||||||
. preg_replace_callback(
|
. preg_replace_callback('/
|
||||||
<<<'XX'
|
(?=[`[\'":%?]) ## speed-up
|
||||||
/
|
(?:
|
||||||
(?=[`['":%?]) ## speed-up
|
`(.+?)`| ## 1) `identifier`
|
||||||
(?:
|
\[(.+?)\]| ## 2) [identifier]
|
||||||
`(.+?)`| ## 1) `identifier`
|
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
|
||||||
\[(.+?)\]| ## 2) [identifier]
|
(")((?:""|[^"])*)"| ## 5,6) "string"
|
||||||
(')((?:''|[^'])*)'| ## 3,4) string
|
(\'|")| ## 7) lone quote
|
||||||
(")((?:""|[^"])*)"| ## 5,6) "string"
|
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
|
||||||
('|")| ## 7) lone quote
|
%([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
|
||||||
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
|
(\?) ## 11) placeholder
|
||||||
%([a-zA-Z~][a-zA-Z0-9~]{0,5})| ## 10) modifier
|
)/xs',
|
||||||
(\?) ## 11) placeholder
|
*/ // note: this can change $this->args & $this->cursor & ...
|
||||||
)/xs
|
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
|
||||||
XX
|
|
||||||
,
|
|
||||||
[$this, 'cb'],
|
[$this, 'cb'],
|
||||||
substr($arg, $toSkip)
|
substr($arg, $toSkip)
|
||||||
);
|
);
|
||||||
if (preg_last_error()) {
|
if (preg_last_error()) {
|
||||||
throw new PcreException;
|
throw new PcreException;
|
||||||
}
|
}
|
||||||
@@ -291,7 +289,7 @@ XX
|
|||||||
if (is_array($v)) {
|
if (is_array($v)) {
|
||||||
$vx[] = $this->formatValue($v, 'ex');
|
$vx[] = $this->formatValue($v, 'ex');
|
||||||
} elseif (is_string($k)) {
|
} elseif (is_string($k)) {
|
||||||
$v = (is_string($v) ? strncasecmp($v, 'd', 1) : $v > 0) ? 'ASC' : 'DESC';
|
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
|
||||||
$vx[] = $this->identifiers->$k . ' ' . $v;
|
$vx[] = $this->identifiers->$k . ' ' . $v;
|
||||||
} else {
|
} else {
|
||||||
$vx[] = $this->identifiers->$v;
|
$vx[] = $this->identifiers->$v;
|
||||||
@@ -324,13 +322,7 @@ XX
|
|||||||
} elseif ($value instanceof Expression && $modifier === 'ex') {
|
} elseif ($value instanceof Expression && $modifier === 'ex') {
|
||||||
return $this->connection->translate(...$value->getValues());
|
return $this->connection->translate(...$value->getValues());
|
||||||
|
|
||||||
} elseif (
|
} elseif ($value instanceof \DateTimeInterface && ($modifier === 'd' || $modifier === 't' || $modifier === 'dt')) {
|
||||||
$value instanceof \DateTimeInterface
|
|
||||||
&& ($modifier === 'd'
|
|
||||||
|| $modifier === 't'
|
|
||||||
|| $modifier === 'dt'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// continue
|
// continue
|
||||||
} else {
|
} else {
|
||||||
$type = is_object($value) ? get_class($value) : gettype($value);
|
$type = is_object($value) ? get_class($value) : gettype($value);
|
||||||
@@ -340,29 +332,21 @@ XX
|
|||||||
|
|
||||||
switch ($modifier) {
|
switch ($modifier) {
|
||||||
case 's': // string
|
case 's': // string
|
||||||
return $value === null
|
return $value === null ? 'NULL' : $this->driver->escapeText((string) $value);
|
||||||
? 'NULL'
|
|
||||||
: $this->driver->escapeText((string) $value);
|
|
||||||
|
|
||||||
case 'bin':// binary
|
case 'bin':// binary
|
||||||
return $value === null
|
return $value === null ? 'NULL' : $this->driver->escapeBinary($value);
|
||||||
? 'NULL'
|
|
||||||
: $this->driver->escapeBinary($value);
|
|
||||||
|
|
||||||
case 'b': // boolean
|
case 'b': // boolean
|
||||||
return $value === null
|
return $value === null ? 'NULL' : $this->driver->escapeBool((bool) $value);
|
||||||
? 'NULL'
|
|
||||||
: $this->driver->escapeBool((bool) $value);
|
|
||||||
|
|
||||||
case 'sN': // string or null
|
case 'sN': // string or null
|
||||||
case 'sn':
|
case 'sn':
|
||||||
return $value === '' || $value === 0 || $value === null
|
return $value == '' ? 'NULL' : $this->driver->escapeText((string) $value); // notice two equal signs
|
||||||
? 'NULL'
|
|
||||||
: $this->driver->escapeText((string) $value);
|
|
||||||
|
|
||||||
case 'iN': // signed int or null
|
case 'iN': // signed int or null
|
||||||
if ($value === '' || $value === 0 || $value === null) {
|
if ($value == '') {
|
||||||
return 'NULL';
|
$value = null;
|
||||||
}
|
}
|
||||||
// break omitted
|
// break omitted
|
||||||
case 'i': // signed int
|
case 'i': // signed int
|
||||||
@@ -400,9 +384,7 @@ XX
|
|||||||
} elseif (!$value instanceof \DateTimeInterface) {
|
} elseif (!$value instanceof \DateTimeInterface) {
|
||||||
$value = new DateTime($value);
|
$value = new DateTime($value);
|
||||||
}
|
}
|
||||||
return $modifier === 'd'
|
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
|
||||||
? $this->driver->escapeDate($value)
|
|
||||||
: $this->driver->escapeDateTime($value);
|
|
||||||
|
|
||||||
case 'by':
|
case 'by':
|
||||||
case 'n': // composed identifier name
|
case 'n': // composed identifier name
|
||||||
@@ -418,23 +400,11 @@ XX
|
|||||||
$toSkip = strcspn($value, '`[\'":');
|
$toSkip = strcspn($value, '`[\'":');
|
||||||
if (strlen($value) !== $toSkip) {
|
if (strlen($value) !== $toSkip) {
|
||||||
$value = substr($value, 0, $toSkip)
|
$value = substr($value, 0, $toSkip)
|
||||||
. preg_replace_callback(
|
. preg_replace_callback(
|
||||||
<<<'XX'
|
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
|
||||||
/
|
[$this, 'cb'],
|
||||||
(?=[`['":])
|
substr($value, $toSkip)
|
||||||
(?:
|
);
|
||||||
`(.+?)`|
|
|
||||||
\[(.+?)\]|
|
|
||||||
(')((?:''|[^'])*)'|
|
|
||||||
(")((?:""|[^"])*)"|
|
|
||||||
('|")|
|
|
||||||
:(\S*?:)([a-zA-Z0-9._]?)
|
|
||||||
)/sx
|
|
||||||
XX
|
|
||||||
,
|
|
||||||
[$this, 'cb'],
|
|
||||||
substr($value, $toSkip)
|
|
||||||
);
|
|
||||||
if (preg_last_error()) {
|
if (preg_last_error()) {
|
||||||
throw new PcreException;
|
throw new PcreException;
|
||||||
}
|
}
|
||||||
@@ -627,9 +597,7 @@ XX
|
|||||||
if ($matches[8]) { // SQL identifier substitution
|
if ($matches[8]) { // SQL identifier substitution
|
||||||
$m = substr($matches[8], 0, -1);
|
$m = substr($matches[8], 0, -1);
|
||||||
$m = $this->connection->getSubstitutes()->$m;
|
$m = $this->connection->getSubstitutes()->$m;
|
||||||
return $matches[9] === ''
|
return $matches[9] == '' ? $this->formatValue($m, null) : $m . $matches[9]; // value or identifier
|
||||||
? $this->formatValue($m, null)
|
|
||||||
: $m . $matches[9]; // value or identifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \Exception('this should be never executed');
|
throw new \Exception('this should be never executed');
|
||||||
|
@@ -30,6 +30,6 @@ class Type
|
|||||||
|
|
||||||
final public function __construct()
|
final public function __construct()
|
||||||
{
|
{
|
||||||
throw new \LogicException('Cannot instantiate static class ' . self::class);
|
throw new \LogicException('Cannot instantiate static class ' . __CLASS__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
|||||||
* @method static void begin(string $savepoint = null)
|
* @method static void begin(string $savepoint = null)
|
||||||
* @method static void commit(string $savepoint = null)
|
* @method static void commit(string $savepoint = null)
|
||||||
* @method static void rollback(string $savepoint = null)
|
* @method static void rollback(string $savepoint = null)
|
||||||
* @method static mixed transaction(callable $callback)
|
|
||||||
* @method static Dibi\Reflection\Database getDatabaseInfo()
|
* @method static Dibi\Reflection\Database getDatabaseInfo()
|
||||||
* @method static Dibi\Fluent command()
|
* @method static Dibi\Fluent command()
|
||||||
* @method static Dibi\Fluent select(...$args)
|
* @method static Dibi\Fluent select(...$args)
|
||||||
@@ -45,7 +44,7 @@ class dibi
|
|||||||
|
|
||||||
/** version */
|
/** version */
|
||||||
public const
|
public const
|
||||||
VERSION = '4.2.2';
|
VERSION = '4.1.3';
|
||||||
|
|
||||||
/** sorting order */
|
/** sorting order */
|
||||||
public const
|
public const
|
||||||
@@ -76,7 +75,7 @@ class dibi
|
|||||||
*/
|
*/
|
||||||
final public function __construct()
|
final public function __construct()
|
||||||
{
|
{
|
||||||
throw new LogicException('Cannot instantiate static class ' . static::class);
|
throw new LogicException('Cannot instantiate static class ' . get_class($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
7
tests/coding-standard.yml
Normal file
7
tests/coding-standard.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: '../temp/coding-standard/coding-standard-php71.yml' }
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
skip:
|
||||||
|
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer:
|
||||||
|
- src/Dibi/HashMap.php # issue #260
|
@@ -12,7 +12,7 @@ use Tester\Assert;
|
|||||||
require __DIR__ . '/bootstrap.php';
|
require __DIR__ . '/bootstrap.php';
|
||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test(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(function () use ($config) { // lazy
|
||||||
$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(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(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(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(function () use ($config) {
|
||||||
Assert::exception(function () use ($config) {
|
Assert::exception(function () use ($config) {
|
||||||
new Connection($config + ['onConnect' => '']);
|
new Connection($config + ['onConnect' => '']);
|
||||||
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
|
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
|
||||||
|
@@ -30,102 +30,21 @@ Assert::exception(function () use ($conn) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
test('begin() & rollback()', function () use ($conn) {
|
$conn->begin();
|
||||||
$conn->begin();
|
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
$conn->query('INSERT INTO [products]', [
|
||||||
$conn->query('INSERT INTO [products]', [
|
'title' => 'Test product',
|
||||||
'title' => 'Test product',
|
]);
|
||||||
]);
|
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
$conn->rollback();
|
||||||
$conn->rollback();
|
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('begin() & commit()', function () use ($conn) {
|
|
||||||
$conn->begin();
|
|
||||||
$conn->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
$conn->commit();
|
|
||||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('transaction() fail', function () use ($conn) {
|
$conn->begin();
|
||||||
Assert::exception(function () use ($conn) {
|
$conn->query('INSERT INTO [products]', [
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
'title' => 'Test product',
|
||||||
$connection->query('INSERT INTO [products]', [
|
]);
|
||||||
'title' => 'Test product',
|
$conn->commit();
|
||||||
]);
|
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
throw new Exception('my exception');
|
|
||||||
});
|
|
||||||
}, \Throwable::class, 'my exception');
|
|
||||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('transaction() success', function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('nested transaction() call fail', function () use ($conn) {
|
|
||||||
Assert::exception(function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$connection->transaction(function (Dibi\Connection $connection2) {
|
|
||||||
$connection2->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
throw new Exception('my exception');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, \Throwable::class, 'my exception');
|
|
||||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('nested transaction() call success', function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$connection->transaction(function (Dibi\Connection $connection2) {
|
|
||||||
$connection2->query('INSERT INTO [products]', [
|
|
||||||
'title' => 'Test product',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Assert::same(7, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('begin(), commit() & rollback() calls are forbidden in transaction()', function () use ($conn) {
|
|
||||||
Assert::exception(function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->begin();
|
|
||||||
});
|
|
||||||
}, \LogicException::class, Dibi\Connection::class . '::begin() call is forbidden inside a transaction() callback');
|
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->commit();
|
|
||||||
});
|
|
||||||
}, \LogicException::class, Dibi\Connection::class . '::commit() call is forbidden inside a transaction() callback');
|
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
|
||||||
$connection->rollback();
|
|
||||||
});
|
|
||||||
}, \LogicException::class, Dibi\Connection::class . '::rollback() call is forbidden inside a transaction() callback');
|
|
||||||
});
|
|
||||||
|
@@ -29,6 +29,13 @@ Assert::exception(function () {
|
|||||||
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
||||||
|
|
||||||
|
|
||||||
test('PDO error mode: explicitly set silent', function () {
|
// PDO error mode: explicitly set silent
|
||||||
|
test(function () {
|
||||||
buildPdoDriver(PDO::ERRMODE_SILENT);
|
buildPdoDriver(PDO::ERRMODE_SILENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// PDO error mode: implicitly set silent
|
||||||
|
test(function () {
|
||||||
|
buildPdoDriver(null);
|
||||||
|
});
|
||||||
|
@@ -32,7 +32,7 @@ Assert::same(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (!in_array($config['driver'], ['sqlite', 'sqlite3', 'pdo', 'sqlsrv'], true)) {
|
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||||
Assert::same(
|
Assert::same(
|
||||||
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
||||||
$info->getColumnNames(true)
|
$info->getColumnNames(true)
|
||||||
@@ -43,7 +43,7 @@ if (!in_array($config['driver'], ['sqlite', 'sqlite3', 'pdo', 'sqlsrv'], true))
|
|||||||
$columns = $info->getColumns();
|
$columns = $info->getColumns();
|
||||||
|
|
||||||
Assert::same('product_id', $columns[0]->getName());
|
Assert::same('product_id', $columns[0]->getName());
|
||||||
if (!in_array($config['driver'], ['sqlite', 'sqlite3', 'pdo', 'sqlsrv'], true)) {
|
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||||
Assert::same('products', $columns[0]->getTableName());
|
Assert::same('products', $columns[0]->getTableName());
|
||||||
}
|
}
|
||||||
Assert::null($columns[0]->getVendorInfo('xxx'));
|
Assert::null($columns[0]->getVendorInfo('xxx'));
|
||||||
|
@@ -24,18 +24,7 @@ class MockResult extends Dibi\Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
|
||||||
$result->setType('col', Type::TEXT);
|
|
||||||
$result->setFormat(Type::TEXT, 'native');
|
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
|
||||||
Assert::same(['col' => true], $result->test(['col' => true]));
|
|
||||||
Assert::same(['col' => false], $result->test(['col' => false]));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::BOOL);
|
$result->setType('col', Type::BOOL);
|
||||||
|
|
||||||
@@ -57,7 +46,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TEXT);
|
$result->setType('col', Type::TEXT);
|
||||||
|
|
||||||
@@ -73,7 +62,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::FLOAT);
|
$result->setType('col', Type::FLOAT);
|
||||||
|
|
||||||
@@ -150,7 +139,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::INTEGER);
|
$result->setType('col', Type::INTEGER);
|
||||||
|
|
||||||
@@ -158,14 +147,7 @@ test('', function () {
|
|||||||
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::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning in PHP 7.1
|
||||||
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
|
|
||||||
} else {
|
|
||||||
Assert::exception(function () use ($result) {
|
|
||||||
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']));
|
||||||
Assert::same(['col' => 10], $result->test(['col' => '10']));
|
Assert::same(['col' => 10], $result->test(['col' => '10']));
|
||||||
@@ -183,7 +165,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATETIME);
|
$result->setType('col', Type::DATETIME);
|
||||||
|
|
||||||
@@ -201,7 +183,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(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');
|
||||||
@@ -220,7 +202,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATE);
|
$result->setType('col', Type::DATE);
|
||||||
|
|
||||||
@@ -236,7 +218,7 @@ test('', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test(function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TIME);
|
$result->setType('col', Type::TIME);
|
||||||
|
|
||||||
|
@@ -35,10 +35,6 @@ Assert::error(function () use ($row) {
|
|||||||
Assert::false(isset($row->missing));
|
Assert::false(isset($row->missing));
|
||||||
Assert::false(isset($row['missing']));
|
Assert::false(isset($row['missing']));
|
||||||
|
|
||||||
// ??
|
|
||||||
Assert::same(123, $row->missing ?? 123);
|
|
||||||
Assert::same(123, $row['missing'] ?? 123);
|
|
||||||
|
|
||||||
|
|
||||||
// suggestions
|
// suggestions
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(function () use ($row) {
|
||||||
|
@@ -23,9 +23,7 @@ Assert::equal(1, $conn->getInsertId());
|
|||||||
|
|
||||||
$conn->query(
|
$conn->query(
|
||||||
'CREATE TRIGGER %n ON %n AFTER INSERT AS INSERT INTO %n DEFAULT VALUES',
|
'CREATE TRIGGER %n ON %n AFTER INSERT AS INSERT INTO %n DEFAULT VALUES',
|
||||||
'UpdAAB',
|
'UpdAAB', 'aab', 'aaa'
|
||||||
'aab',
|
|
||||||
'aaa'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aab');
|
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aab');
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpversion 5.5
|
||||||
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Tester\Assert;
|
use Tester\Assert;
|
||||||
|
@@ -19,17 +19,12 @@ Assert::same(
|
|||||||
SELECT *
|
SELECT *
|
||||||
FROM [customers]
|
FROM [customers]
|
||||||
/* WHERE ... LIKE ... */'),
|
/* WHERE ... LIKE ... */'),
|
||||||
$conn->translate(
|
|
||||||
'
|
$conn->translate('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM [customers]
|
FROM [customers]
|
||||||
%if',
|
%if', isset($name), 'WHERE [name] LIKE %s', 'xxx', '%end'
|
||||||
isset($name),
|
));
|
||||||
'WHERE [name] LIKE %s',
|
|
||||||
'xxx',
|
|
||||||
'%end'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// if & else & end (last end is optional)
|
// if & else & end (last end is optional)
|
||||||
@@ -37,14 +32,11 @@ Assert::same(
|
|||||||
reformat('
|
reformat('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM [customers] /* ... */'),
|
FROM [customers] /* ... */'),
|
||||||
$conn->translate(
|
|
||||||
'
|
$conn->translate('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM %if',
|
FROM %if', true, '[customers] %else [products]'
|
||||||
true,
|
));
|
||||||
'[customers] %else [products]'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// if & else & (optional) end
|
// if & else & (optional) end
|
||||||
@@ -56,14 +48,14 @@ WHERE [id] > 0
|
|||||||
/* AND ...=...
|
/* AND ...=...
|
||||||
*/ AND [bar]=1
|
*/ AND [bar]=1
|
||||||
'),
|
'),
|
||||||
|
|
||||||
$conn->translate('
|
$conn->translate('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM [people]
|
FROM [people]
|
||||||
WHERE [id] > 0
|
WHERE [id] > 0
|
||||||
%if', false, 'AND [foo]=%i', 1, '
|
%if', false, 'AND [foo]=%i', 1, '
|
||||||
%else %if', true, 'AND [bar]=%i', 1, '
|
%else %if', true, 'AND [bar]=%i', 1, '
|
||||||
')
|
'));
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// nested condition
|
// nested condition
|
||||||
@@ -84,35 +76,24 @@ WHERE
|
|||||||
/* AND ...=1 */
|
/* AND ...=1 */
|
||||||
/* 1 LIMIT 10 */",
|
/* 1 LIMIT 10 */",
|
||||||
]),
|
]),
|
||||||
$conn->translate(
|
|
||||||
'
|
$conn->translate('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM [customers]
|
FROM [customers]
|
||||||
WHERE
|
WHERE
|
||||||
%if',
|
%if', true, '[name] LIKE %s', 'xxx', '
|
||||||
true,
|
%if', false, 'AND [admin]=1 %end
|
||||||
'[name] LIKE %s',
|
|
||||||
'xxx',
|
|
||||||
'
|
|
||||||
%if',
|
|
||||||
false,
|
|
||||||
'AND [admin]=1 %end
|
|
||||||
%else 1 LIMIT 10 %end'
|
%else 1 LIMIT 10 %end'
|
||||||
)
|
));
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// limit & offset
|
// limit & offset
|
||||||
Assert::same(
|
Assert::same(
|
||||||
'SELECT * FROM foo /* (limit 3) (offset 5) */',
|
'SELECT * FROM foo /* (limit 3) (offset 5) */',
|
||||||
$conn->translate(
|
$conn->translate(
|
||||||
'SELECT * FROM foo',
|
'SELECT * FROM foo',
|
||||||
'%if',
|
'%if', false,
|
||||||
false,
|
'%lmt', 3,
|
||||||
'%lmt',
|
'%ofs', 5,
|
||||||
3,
|
'%end'
|
||||||
'%ofs',
|
));
|
||||||
5,
|
|
||||||
'%end'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
@@ -261,7 +261,7 @@ if ($config['system'] === 'postgre') {
|
|||||||
'sqlite' => "SELECT * FROM products WHERE (title LIKE 'C%' ESCAPE '\\' AND title LIKE '%r' ESCAPE '\\') OR title LIKE '%a\n\\%\\_\\\\''\"%' ESCAPE '\\'",
|
'sqlite' => "SELECT * FROM products WHERE (title LIKE 'C%' ESCAPE '\\' AND title LIKE '%r' ESCAPE '\\') OR title LIKE '%a\n\\%\\_\\\\''\"%' ESCAPE '\\'",
|
||||||
'odbc' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
'odbc' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
||||||
'sqlsrv' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
'sqlsrv' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
|
||||||
"SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\\n\\%\\_\\\\\\\\\\'\"%'",
|
"SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\\n\\%\\_\\\\\\\\\'\"%'",
|
||||||
]),
|
]),
|
||||||
$conn->translate($args[0], $args[1], $args[2], $args[3])
|
$conn->translate($args[0], $args[1], $args[2], $args[3])
|
||||||
);
|
);
|
||||||
@@ -279,7 +279,7 @@ Assert::match(
|
|||||||
CONCAT(last_name, ', ', first_name) AS full_name
|
CONCAT(last_name, ', ', first_name) AS full_name
|
||||||
GROUP BY `user`
|
GROUP BY `user`
|
||||||
HAVING MAX(salary) > %i 123
|
HAVING MAX(salary) > %i 123
|
||||||
INTO OUTFILE '/tmp/result\\'.txt'
|
INTO OUTFILE '/tmp/result\'.txt'
|
||||||
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
|
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
|
||||||
LINES TERMINATED BY '\\\\n'
|
LINES TERMINATED BY '\\\\n'
|
||||||
",
|
",
|
||||||
@@ -344,7 +344,7 @@ WHERE (`test`.`a` LIKE '1995-03-01'
|
|||||||
OR `b8` IN (RAND() `col1` > `col2` )
|
OR `b8` IN (RAND() `col1` > `col2` )
|
||||||
OR `b9` IN (RAND(), [col1] > [col2] )
|
OR `b9` IN (RAND(), [col1] > [col2] )
|
||||||
OR `b10` IN ( )
|
OR `b10` IN ( )
|
||||||
AND `c` = 'embedded \\' string'
|
AND `c` = 'embedded \' string'
|
||||||
OR `d`=10
|
OR `d`=10
|
||||||
OR `e`=NULL
|
OR `e`=NULL
|
||||||
OR `true`= 1
|
OR `true`= 1
|
||||||
@@ -437,6 +437,7 @@ WHERE ([test].[a] LIKE '1995-03-01'
|
|||||||
OR [str_not_null]='hello'
|
OR [str_not_null]='hello'
|
||||||
LIMIT 10",
|
LIMIT 10",
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$conn->translate('SELECT *
|
$conn->translate('SELECT *
|
||||||
FROM [db.table]
|
FROM [db.table]
|
||||||
WHERE ([test.a] LIKE %d', '1995-03-01', '
|
WHERE ([test.a] LIKE %d', '1995-03-01', '
|
||||||
@@ -670,6 +671,7 @@ Assert::same(
|
|||||||
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
|
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
|
||||||
"UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
|
"UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$conn->translate('UPDATE [colors] SET', [
|
$conn->translate('UPDATE [colors] SET', [
|
||||||
'color' => 'blue',
|
'color' => 'blue',
|
||||||
'price' => -12.4,
|
'price' => -12.4,
|
||||||
|
@@ -37,7 +37,7 @@ if ($config['system'] === 'odbc') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function test(string $title, Closure $function): void
|
function test(Closure $function)
|
||||||
{
|
{
|
||||||
$function();
|
$function();
|
||||||
}
|
}
|
||||||
|
@@ -12,5 +12,5 @@ extension=php_pdo_pgsql.dll
|
|||||||
extension=php_pdo_sqlite.dll
|
extension=php_pdo_sqlite.dll
|
||||||
extension=php_pgsql.dll
|
extension=php_pgsql.dll
|
||||||
extension=php_sqlite3.dll
|
extension=php_sqlite3.dll
|
||||||
extension=php_sqlsrv_ts.dll
|
extension=php_sqlsrv_71_ts.dll
|
||||||
extension=php_odbc.dll
|
extension=php_odbc.dll
|
||||||
|
@@ -1,9 +1,4 @@
|
|||||||
parameters:
|
parameters:
|
||||||
level: 5
|
|
||||||
|
|
||||||
paths:
|
|
||||||
- src
|
|
||||||
|
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
# The namespace is referenced, not the class.
|
# The namespace is referenced, not the class.
|
||||||
- '#Class dibi referenced with incorrect case: Dibi#'
|
- '#Class dibi referenced with incorrect case: Dibi#'
|
Reference in New Issue
Block a user