1
0
mirror of https://github.com/dg/dibi.git synced 2025-09-04 03:35:26 +02:00

Compare commits

..

10 Commits

Author SHA1 Message Date
David Grudl
44b420f70d Released version 4.0.3 2020-03-26 03:54:02 +01:00
David Grudl
4689101b88 SqliteResult: workaround for PHP bug 79414 2020-03-26 03:54:02 +01:00
David Grudl
db16b9e87a Result: does not drop the value if detection fails 2020-03-26 03:54:02 +01:00
Adam Klvač
803c9d539c MySqliDriver: coalesced password to an empty string (#360)
mysqli::real_connect() expects parameter 3 to be string, yields an error on NULL.
2020-03-26 03:54:02 +01:00
David Grudl
e137dfa28b Result::fetchAssoc() DateTime in key is converted to string [Closes #359] 2020-03-26 03:54:02 +01:00
Milan Pála
9651835f5b tests: added reconnect test (#352) 2020-03-26 03:54:02 +01:00
David Grudl
2cbebc02c4 Connection: translator is created/destructed in connect/disconnect [Closes #352][Closes #354] 2020-03-26 03:54:02 +01:00
David Grudl
4d647c2aed travis: fixed databases 2020-03-26 03:51:13 +01:00
David Grudl
5c5838aee4 travis: added PHP 7.4 2020-03-26 03:47:28 +01:00
David Grudl
7df72fd6cf fixes for PHP 7.4 2020-03-26 03:45:50 +01:00
45 changed files with 555 additions and 739 deletions

1
.github/funding.yml vendored
View File

@@ -1 +0,0 @@
github: dg

View File

@@ -32,7 +32,6 @@ 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:
@@ -40,7 +39,6 @@ jobs:
- name: Nette Coding Standard - name: Nette Coding Standard
php: 7.4
install: install:
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress - travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
script: script:
@@ -48,13 +46,15 @@ jobs:
- stage: Static Analysis (informative) - stage: Static Analysis (informative)
php: 7.4 install:
# Install PHPStan
- travis_retry composer create-project phpstan/phpstan-shim temp/phpstan --no-progress
- travis_retry composer install --no-progress --prefer-dist
script: script:
- composer run-script phpstan - php temp/phpstan/phpstan.phar analyse --autoload-file vendor/autoload.php --level 5 src
- stage: Code Coverage - stage: Code Coverage
php: 7.4
script: script:
- vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src - vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src
after_script: after_script:

View File

@@ -3,7 +3,7 @@
"description": "Dibi is Database Abstraction Library for PHP", "description": "Dibi is Database Abstraction Library for PHP",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"], "keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
"homepage": "https://dibiphp.com", "homepage": "https://dibiphp.com",
"license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [ "authors": [
{ {
"name": "David Grudl", "name": "David Grudl",
@@ -15,9 +15,7 @@
}, },
"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"
}, },
"replace": { "replace": {
"dg/dibi": "*" "dg/dibi": "*"
@@ -25,13 +23,9 @@
"autoload": { "autoload": {
"classmap": ["src/"] "classmap": ["src/"]
}, },
"scripts": {
"phpstan": "phpstan analyse --autoload-file vendor/autoload.php --level 5 --configuration tests/phpstan.neon src",
"tester": "tester tests -s"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.0-dev"
} }
} }
} }

View File

@@ -20,7 +20,6 @@ $dibi = new Dibi\Connection([
// enable query logging to this file // enable query logging to this file
'profiler' => [ 'profiler' => [
'file' => 'log/log.sql', 'file' => 'log/log.sql',
'errorsOnly' => false,
], ],
]); ]);

View File

@@ -14,13 +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 Project
---------------
Do you like Dibi? Are you looking forward to the new features?
[![Donate](https://files.nette.org/icons/donation-1.svg?)](https://nette.org/make-donation?to=dibi)
Installation Installation
@@ -32,7 +26,7 @@ Install Dibi via Composer:
composer require dibi/dibi composer require dibi/dibi
``` ```
The Dibi 4.1 requires PHP version 7.1 and supports PHP up to 7.4. The Dibi 4.0 requires PHP version 7.1 and supports PHP up to 7.4.
Usage Usage
@@ -48,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');
@@ -62,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');
@@ -81,8 +75,6 @@ In the event of a connection error, it throws `Dibi\Exception`.
We query the database queries by the method `query()` which returns `Dibi\Result`. Rows are objects `Dibi\Row`. We query the database queries by the method `query()` which returns `Dibi\Result`. Rows are objects `Dibi\Row`.
You can try all the examples [online at the playground](https://repl.it/@DavidGrudl/dibi-playground).
```php ```php
$result = $database->query('SELECT * FROM users'); $result = $database->query('SELECT * FROM users');
@@ -191,7 +183,7 @@ $result = $database->query('SELECT * FROM users WHERE id IN (%i)', $ids);
// SELECT * FROM users WHERE id IN (10, 20, 30) // SELECT * FROM users WHERE id IN (10, 20, 30)
``` ```
The modifier `%n` is used if the table or column name is a variable. (Beware, do not allow the user to manipulate the content of such a variable): The modifier '%n' is used if the table or column name is a variable. (Beware, do not allow the user to manipulate the content of such a variable):
```php ```php
$table = 'blog.users'; $table = 'blog.users';
@@ -207,7 +199,6 @@ Three special modifiers are available for LIKE:
| `%like~` | the expression starts with a string | `%like~` | the expression starts with a string
| `%~like` | the expression ends with a string | `%~like` | the expression ends with a string
| `%~like~` | the expression contains a string | `%~like~` | the expression contains a string
| `%like` | the expression matches a string
Search for names beginning with a string: Search for names beginning with a string:
@@ -235,8 +226,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);
@@ -508,7 +499,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) {
... ...
} }
} }
``` ```
@@ -540,7 +531,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) {
... ...
} }
} }
``` ```
@@ -554,8 +545,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
} }
} }
``` ```
@@ -577,7 +568,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";
} }
} }
``` ```

View File

@@ -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;
@@ -144,10 +135,7 @@ class Panel implements Tracy\IBarPanel
$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 }
@@ -155,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> <table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr> <tr><th>Time&nbsp;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') : '');
}
} }

View File

@@ -43,21 +43,32 @@ class Connection implements IConnection
* - 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
* - formatDateTime => date-time format (if empty, DateTime objects will be returned) * - formatDateTime => date-time format (if empty, DateTime objects will be returned)
* - formatJson => json format (
* "string" for leaving value as is,
* "object" for decoding json as \stdClass,
* "array" for decoding json as an array - default
* )
* - profiler (array) * - profiler (array)
* - run (bool) => enable profiler? * - run (bool) => enable profiler?
* - file => file to log * - file => file to log
* - errorsOnly (bool) => log only errors
* - substitutes (array) => map of driver specific substitutes (under development) * - substitutes (array) => map of driver specific substitutes (under development)
* - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established * - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established
* @param array $config connection parameters
* @throws Exception * @throws Exception
*/ */
public function __construct(array $config, string $name = null) public function __construct($config, string $name = null)
{ {
if (is_string($config)) {
trigger_error(__METHOD__ . '() Configuration should be array.', E_USER_DEPRECATED);
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
trigger_error(__METHOD__ . '() Configuration should be array.', E_USER_DEPRECATED);
$tmp = [];
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} elseif (!is_array($config)) {
throw new \InvalidArgumentException('Configuration must be array.');
}
Helpers::alias($config, 'username', 'user'); Helpers::alias($config, 'username', 'user');
Helpers::alias($config, 'password', 'pass'); Helpers::alias($config, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname'); Helpers::alias($config, 'host', 'hostname');
@@ -65,14 +76,12 @@ 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;
// 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;
$errorsOnly = $config['profiler']['errorsOnly'] ?? false; $this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter), 'logEvent'];
$this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter, $errorsOnly), 'logEvent'];
} }
$this->substitutes = new HashMap(function (string $expr) { return ":$expr:"; }); $this->substitutes = new HashMap(function (string $expr) { return ":$expr:"; });
@@ -306,6 +315,16 @@ class Connection implements IConnection
} }
/**
* @deprecated
*/
public function affectedRows(): int
{
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
return $this->getAffectedRows();
}
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @throws Exception * @throws Exception
@@ -316,13 +335,23 @@ class Connection implements IConnection
$this->connect(); $this->connect();
} }
$id = $this->driver->getInsertId($sequence); $id = $this->driver->getInsertId($sequence);
if ($id === null) { if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.'); throw new Exception('Cannot retrieve last generated ID.');
} }
return $id; return $id;
} }
/**
* @deprecated
*/
public function insertId(string $sequence = null): int
{
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
return $this->getInsertId($sequence);
}
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
*/ */
@@ -402,8 +431,7 @@ class Connection implements IConnection
{ {
$res = new Result($resultDriver); $res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate']) return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']) ->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
->setFormat(Type::JSON, $this->config['result']['formatJson']);
} }

View File

@@ -32,8 +32,77 @@ class DateTime extends \DateTimeImmutable
} }
/** @deprecated use modify() */
public function modifyClone(string $modify = ''): self
{
trigger_error(__METHOD__ . '() is deprecated, use modify()', E_USER_DEPRECATED);
$dolly = clone $this;
return $modify ? $dolly->modify($modify) : $dolly;
}
public function __toString(): string public function __toString(): string
{ {
return $this->format('Y-m-d H:i:s.u'); return $this->format('Y-m-d H:i:s.u');
} }
/********************* immutable usage detector ****************d*g**/
public function __destruct()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
if (isset($trace[0]['file'], $trace[1]['function']) && $trace[0]['file'] === __FILE__ && $trace[1]['function'] !== '__construct') {
trigger_error(__CLASS__ . ' is immutable now, check how it is used in ' . $trace[1]['file'] . ':' . $trace[1]['line'], E_USER_WARNING);
}
}
public function add($interval)
{
return parent::add($interval);
}
public function modify($modify)
{
return parent::modify($modify);
}
public function setDate($year, $month, $day)
{
return parent::setDate($year, $month, $day);
}
public function setISODate($year, $week, $day = 1)
{
return parent::setISODate($year, $week, $day);
}
public function setTime($hour, $minute, $second = 0, $micro = 0)
{
return parent::setTime($hour, $minute, $second, $micro);
}
public function setTimestamp($unixtimestamp)
{
return parent::setTimestamp($unixtimestamp);
}
public function setTimezone($timezone)
{
return parent::setTimezone($timezone);
}
public function sub($interval)
{
return parent::sub($interval);
}
} }

View File

@@ -1,219 +0,0 @@
<?php
/**
* This file is part of the Dibi, smart database abstraction layer (https://dibiphp.com)
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Dibi\Drivers;
use Dibi;
/**
* The dummy driver for testing purposes.
*/
class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
public function disconnect(): void
{
}
public function query(string $sql): ?Dibi\ResultDriver
{
return null;
}
public function getAffectedRows(): ?int
{
return null;
}
public function getInsertId(?string $sequence): ?int
{
return null;
}
public function begin(string $savepoint = null): void
{
}
public function commit(string $savepoint = null): void
{
}
public function rollback(string $savepoint = null): void
{
}
public function getResource()
{
return null;
}
/**
* Returns the connection reflector.
*/
public function getReflector(): Dibi\Reflector
{
return $this;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
*/
public function escapeText(string $value): string
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary(string $value): string
{
return "N'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier(string $value): string
{
return '[' . strtr($value, '[]', ' ') . ']';
}
public function escapeBool(bool $value): string
{
return $value ? '1' : '0';
}
public function escapeDate(\DateTimeInterface $value): string
{
return $value->format("'Y-m-d'");
}
public function escapeDateTime(\DateTimeInterface $value): string
{
return $value->format("'Y-m-d H:i:s.u'");
}
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/**
* Encodes string for use in a LIKE statement.
*/
public function escapeLike(string $value, int $pos): string
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
}
/**
* Injects LIMIT/OFFSET to the SQL query.
*/
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit ?? '-1')
. ($offset ? ' OFFSET ' . $offset : '');
}
}
/********************* Result ****************d*g**/
public function getRowCount(): int
{
return 0;
}
public function fetch(bool $assoc): ?array
{
return null;
}
public function seek(int $row): bool
{
return false;
}
public function free(): void
{
}
public function getResultResource()
{
}
public function getResultColumns(): array
{
return [];
}
/**
* Decodes data from result set.
*/
public function unescapeBinary(string $value): string
{
return $value;
}
/********************* Reflector ****************d*g**/
public function getTables(): array
{
return [];
}
public function getColumns(string $table): array
{
return [];
}
public function getIndexes(string $table): array
{
return [];
}
public function getForeignKeys(string $table): array
{
return [];
}
}

View File

@@ -40,7 +40,9 @@ class FirebirdDriver implements Dibi\Driver
private $inTransaction = false; private $inTransaction = false;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('interbase')) { if (!extension_loaded('interbase')) {
@@ -245,31 +247,37 @@ class FirebirdDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'"); return $value->format("'Y-m-d'");
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'"; return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'";
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
public function escapeLike(string $value, int $pos): string public function escapeLike(string $value, int $pos): string
{ {
$value = addcslashes($this->escapeText($value), '%_\\'); $value = addcslashes($this->escapeText($value), '%_\\');
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
} }

View File

@@ -47,7 +47,9 @@ class MySqliDriver implements Dibi\Driver
private $buffered; private $buffered;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('mysqli')) { if (!extension_loaded('mysqli')) {
@@ -130,15 +132,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 +150,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)) {
@@ -209,7 +199,7 @@ class MySqliDriver implements Dibi\Driver
*/ */
public function getInsertId(?string $sequence): ?int public function getInsertId(?string $sequence): ?int
{ {
return $this->connection->insert_id ?: null; return $this->connection->insert_id;
} }
@@ -300,24 +290,27 @@ class MySqliDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'"); return $value->format("'Y-m-d'");
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
return $value->format("'Y-m-d H:i:s.u'"); if (!$value instanceof \DateTimeInterface) {
} $value = new Dibi\DateTime($value);
public function escapeDateInterval(\DateInterval $value): string
{
if ($value->y || $value->m || $value->d) {
throw new Dibi\NotSupportedException('Only time interval is supported.');
} }
return $value->format("'%r%H:%I:%S.%f'"); return $value->format("'Y-m-d H:i:s.u'");
} }
@@ -327,7 +320,7 @@ class MySqliDriver implements Dibi\Driver
public function escapeLike(string $value, int $pos): string public function escapeLike(string $value, int $pos): string
{ {
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }
@@ -341,7 +334,7 @@ class MySqliDriver implements Dibi\Driver
} elseif ($limit !== null || $offset) { } elseif ($limit !== null || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') $sql .= ' LIMIT ' . ($limit === null ? '18446744073709551615' : $limit)
. ($offset ? ' OFFSET ' . $offset : ''); . ($offset ? ' OFFSET ' . $offset : '');
} }
} }

View File

@@ -37,7 +37,9 @@ class OdbcDriver implements Dibi\Driver
private $microseconds = true; private $microseconds = true;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('odbc')) { if (!extension_loaded('odbc')) {
@@ -224,31 +226,37 @@ class OdbcDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format('#m/d/Y#'); return $value->format('#m/d/Y#');
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#'); return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#');
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
public function escapeLike(string $value, int $pos): string public function escapeLike(string $value, int $pos): string
{ {
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }

View File

@@ -42,7 +42,9 @@ class OracleDriver implements Dibi\Driver
private $affectedRows; private $affectedRows;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('oci8')) { if (!extension_loaded('oci8')) {
@@ -50,6 +52,10 @@ class OracleDriver implements Dibi\Driver
} }
$foo = &$config['charset']; $foo = &$config['charset'];
if (isset($config['formatDate']) || isset($config['formatDateTime'])) {
trigger_error('OracleDriver: options formatDate and formatDateTime are deprecated.', E_USER_DEPRECATED);
}
$this->nativeDate = $config['nativeDate'] ?? true; $this->nativeDate = $config['nativeDate'] ?? true;
if (isset($config['resource'])) { if (isset($config['resource'])) {
@@ -239,28 +245,34 @@ class OracleDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $this->nativeDate return $this->nativeDate
? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')" ? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')"
: $value->format('U'); : $value->format('U');
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $this->nativeDate return $this->nativeDate
? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')" ? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')"
: $value->format('U'); : $value->format('U');
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
@@ -268,7 +280,7 @@ class OracleDriver implements Dibi\Driver
{ {
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_"); $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value); $value = str_replace("'", "''", $value);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }

View File

@@ -96,7 +96,6 @@ class OracleResult implements Dibi\ResultDriver
'name' => oci_field_name($this->resultSet, $i), 'name' => oci_field_name($this->resultSet, $i),
'table' => null, 'table' => null,
'fullname' => oci_field_name($this->resultSet, $i), 'fullname' => oci_field_name($this->resultSet, $i),
'type' => $type === 'LONG' ? Dibi\Type::TEXT : null,
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type, 'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
]; ];
} }

View File

@@ -42,7 +42,9 @@ class PdoDriver implements Dibi\Driver
private $serverVersion = ''; private $serverVersion = '';
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('pdo')) { if (!extension_loaded('pdo')) {
@@ -287,14 +289,26 @@ class PdoDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'"); return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'");
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
switch ($this->driverName) { switch ($this->driverName) {
case 'odbc': case 'odbc':
return $value->format('#m/d/Y H:i:s.u#'); return $value->format('#m/d/Y H:i:s.u#');
@@ -308,12 +322,6 @@ class PdoDriver implements Dibi\Driver
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
@@ -322,29 +330,29 @@ class PdoDriver implements Dibi\Driver
switch ($this->driverName) { switch ($this->driverName) {
case 'mysql': case 'mysql':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'oci': case 'oci':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_"); $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value); $value = str_replace("'", "''", $value);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'pgsql': case 'pgsql':
$bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off $bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off
$value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1); $value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite': case 'sqlite':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\'); $value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc': case 'odbc':
case 'mssql': case 'mssql':
case 'dblib': case 'dblib':
case 'sqlsrv': case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default: default:
throw new Dibi\NotImplementedException; throw new Dibi\NotImplementedException;
@@ -365,7 +373,7 @@ class PdoDriver implements Dibi\Driver
case 'mysql': case 'mysql':
if ($limit !== null || $offset) { if ($limit !== null || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') $sql .= ' LIMIT ' . ($limit === null ? '18446744073709551615' : $limit)
. ($offset ? ' OFFSET ' . $offset : ''); . ($offset ? ' OFFSET ' . $offset : '');
} }
break; break;
@@ -381,7 +389,7 @@ class PdoDriver implements Dibi\Driver
case 'sqlite': case 'sqlite':
if ($limit !== null || $offset) { if ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit ?? '-1') $sql .= ' LIMIT ' . ($limit === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : ''); . ($offset ? ' OFFSET ' . $offset : '');
} }
break; break;

View File

@@ -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
{ {
@@ -36,7 +35,9 @@ class PostgreDriver implements Dibi\Driver
private $affectedRows; private $affectedRows;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('pgsql')) { if (!extension_loaded('pgsql')) {
@@ -63,15 +64,14 @@ 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;
}); });
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = pg_connect($string, $connectType); $this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else { } else {
$this->connection = pg_pconnect($string, $connectType); $this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
} }
restore_error_handler(); restore_error_handler();
} }
@@ -292,24 +292,30 @@ class PostgreDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'"); return $value->format("'Y-m-d'");
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s.u'"); return $value->format("'Y-m-d H:i:s.u'");
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
@@ -318,7 +324,7 @@ class PostgreDriver implements Dibi\Driver
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off $bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value); $value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }

View File

@@ -37,7 +37,9 @@ class SqliteDriver implements Dibi\Driver
private $fmtDateTime; private $fmtDateTime;
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('sqlite3')) { if (!extension_loaded('sqlite3')) {
@@ -137,7 +139,7 @@ class SqliteDriver implements Dibi\Driver
*/ */
public function getInsertId(?string $sequence): ?int public function getInsertId(?string $sequence): ?int
{ {
return $this->connection->lastInsertRowID() ?: null; return $this->connection->lastInsertRowID();
} }
@@ -228,31 +230,37 @@ class SqliteDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate); return $value->format($this->fmtDate);
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime); return $value->format($this->fmtDateTime);
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
public function escapeLike(string $value, int $pos): string public function escapeLike(string $value, int $pos): string
{ {
$value = addcslashes($this->connection->escapeString($value), '%_\\'); $value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
} }
@@ -265,7 +273,7 @@ class SqliteDriver implements Dibi\Driver
throw new Dibi\NotSupportedException('Negative offset or limit.'); throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== null || $offset) { } elseif ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit ?? '-1') $sql .= ' LIMIT ' . ($limit === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : ''); . ($offset ? ' OFFSET ' . $offset : '');
} }
} }

View File

@@ -39,7 +39,9 @@ class SqlsrvDriver implements Dibi\Driver
private $version = ''; private $version = '';
/** @throws Dibi\NotSupportedException */ /**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config) public function __construct(array $config)
{ {
if (!extension_loaded('sqlsrv')) { if (!extension_loaded('sqlsrv')) {
@@ -194,13 +196,13 @@ class SqlsrvDriver implements Dibi\Driver
*/ */
public function escapeText(string $value): string public function escapeText(string $value): string
{ {
return "N'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
} }
public function escapeBinary(string $value): string public function escapeBinary(string $value): string
{ {
return '0x' . bin2hex($value); return "'" . str_replace("'", "''", $value) . "'";
} }
@@ -217,31 +219,37 @@ class SqlsrvDriver implements Dibi\Driver
} }
public function escapeDate(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'"); return $value->format("'Y-m-d'");
} }
public function escapeDateTime(\DateTimeInterface $value): string /**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{ {
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')'; return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
} }
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
*/ */
public function escapeLike(string $value, int $pos): string public function escapeLike(string $value, int $pos): string
{ {
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }

View File

@@ -100,7 +100,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/ */
public function getIndexes(string $table): array public function getIndexes(string $table): array
{ {
$keyUsagesRes = $this->driver->query(sprintf('EXEC [sys].[sp_helpindex] @objname = %s', $this->driver->escapeText($table))); $keyUsagesRes = $this->driver->query(sprintf('EXEC [sys].[sp_helpindex] @objname = N%s', $this->driver->escapeText($table)));
$keyUsages = []; $keyUsages = [];
while ($row = $keyUsagesRes->fetch(true)) { while ($row = $keyUsagesRes->fetch(true)) {
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']); $keyUsages[$row['index_name']] = explode(',', $row['index_keys']);

View File

@@ -61,7 +61,7 @@ class SqlsrvResult implements Dibi\ResultDriver
*/ */
public function fetch(bool $assoc): ?array public function fetch(bool $assoc): ?array
{ {
return Dibi\Helpers::false2Null(sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC)); return sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
} }

View File

@@ -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()
*/ */

View File

@@ -201,7 +201,9 @@ class Helpers
} }
/** @internal */ /**
* @internal
*/
public static function getTypeCache(): HashMap public static function getTypeCache(): HashMap
{ {
if (self::$types === null) { if (self::$types === null) {
@@ -277,14 +279,18 @@ class Helpers
} }
/** @internal */ /**
* @internal
*/
public static function false2Null($val) public static function false2Null($val)
{ {
return $val === false ? null : $val; return $val === false ? null : $val;
} }
/** @internal */ /**
* @internal
*/
public static function intVal($value): int public static function intVal($value): int
{ {
if (is_int($value)) { if (is_int($value)) {

View File

@@ -25,15 +25,11 @@ class FileLogger
/** @var int */ /** @var int */
public $filter; public $filter;
/** @var bool */
private $errorsOnly;
public function __construct(string $file, int $filter = null)
public function __construct(string $file, int $filter = null, bool $errorsOnly = false)
{ {
$this->file = $file; $this->file = $file;
$this->filter = $filter ?: Dibi\Event::QUERY; $this->filter = $filter ?: Dibi\Event::QUERY;
$this->errorsOnly = $errorsOnly;
} }
@@ -42,42 +38,39 @@ class FileLogger
*/ */
public function logEvent(Dibi\Event $event): void public function logEvent(Dibi\Event $event): void
{ {
if ( if (($event->type & $this->filter) === 0) {
(($event->type & $this->filter) === 0)
|| ($this->errorsOnly === true && !$event->result instanceof \Exception)
) {
return; return;
} }
$handle = fopen($this->file, 'a');
if (!$handle) {
return; // or throw exception?
}
flock($handle, LOCK_EX);
if ($event->result instanceof \Exception) { if ($event->result instanceof \Exception) {
$message = $event->result->getMessage(); $message = $event->result->getMessage();
if ($code = $event->result->getCode()) { if ($code = $event->result->getCode()) {
$message = "[$code] $message"; $message = "[$code] $message";
} }
$this->writeToFile( fwrite($handle,
$event,
"ERROR: $message" "ERROR: $message"
. "\n-- SQL: " . $event->sql . "\n-- SQL: " . $event->sql
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. ";\n-- " . date('Y-m-d H:i:s')
. "\n\n"
); );
} else { } else {
$this->writeToFile( fwrite($handle,
$event,
'OK: ' . $event->sql 'OK: ' . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '') . ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000) . "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
. "\n-- source: " . implode(':', $event->source) . "\n-- source: " . implode(':', $event->source)
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. "\n-- " . date('Y-m-d H:i:s')
. "\n\n"
); );
} }
} fclose($handle);
private function writeToFile(Dibi\Event $event, string $message): void
{
$driver = $event->connection->getConfig('driver');
$message .=
"\n-- driver: " . (is_object($driver) ? get_class($driver) : $driver) . '/' . $event->connection->getConfig('name')
. "\n-- " . date('Y-m-d H:i:s')
. "\n\n";
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
} }
} }

View File

@@ -106,14 +106,18 @@ class Column
} }
/** @return mixed */ /**
* @return mixed
*/
public function getDefault() public function getDefault()
{ {
return $this->info['default'] ?? null; return $this->info['default'] ?? null;
} }
/** @return mixed */ /**
* @return mixed
*/
public function getVendorInfo(string $key) public function getVendorInfo(string $key)
{ {
return $this->info['vendor'][$key] ?? null; return $this->info['vendor'][$key] ?? null;

View File

@@ -46,7 +46,9 @@ class Database
} }
/** @return Table[] */ /**
* @return Table[]
*/
public function getTables(): array public function getTables(): array
{ {
$this->init(); $this->init();
@@ -54,7 +56,9 @@ class Database
} }
/** @return string[] */ /**
* @return string[]
*/
public function getTableNames(): array public function getTableNames(): array
{ {
$this->init(); $this->init();

View File

@@ -28,7 +28,7 @@ class Result
/** @var Column[]|null */ /** @var Column[]|null */
private $columns; private $columns;
/** @var Column[]|null */ /** @var string[]|null */
private $names; private $names;
@@ -38,7 +38,9 @@ class Result
} }
/** @return Column[] */ /**
* @return Column[]
*/
public function getColumns(): array public function getColumns(): array
{ {
$this->initColumns(); $this->initColumns();
@@ -46,7 +48,9 @@ class Result
} }
/** @return string[] */ /**
* @return string[]
*/
public function getColumnNames(bool $fullNames = false): array public function getColumnNames(bool $fullNames = false): array
{ {
$this->initColumns(); $this->initColumns();

View File

@@ -69,7 +69,9 @@ class Table
} }
/** @return Column[] */ /**
* @return Column[]
*/
public function getColumns(): array public function getColumns(): array
{ {
$this->initColumns(); $this->initColumns();
@@ -77,7 +79,9 @@ class Table
} }
/** @return string[] */ /**
* @return string[]
*/
public function getColumnNames(): array public function getColumnNames(): array
{ {
$this->initColumns(); $this->initColumns();
@@ -109,7 +113,9 @@ class Table
} }
/** @return ForeignKey[] */ /**
* @return ForeignKey[]
*/
public function getForeignKeys(): array public function getForeignKeys(): array
{ {
$this->initForeignKeys(); $this->initForeignKeys();
@@ -117,7 +123,9 @@ class Table
} }
/** @return Index[] */ /**
* @return Index[]
*/
public function getIndexes(): array public function getIndexes(): array
{ {
$this->initIndexes(); $this->initIndexes();

View File

@@ -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 */
@@ -242,9 +242,6 @@ class Result implements IDataSource
$data = null; $data = null;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
if (!$assoc) {
throw new \InvalidArgumentException("Invalid descriptor '$assoc'.");
}
// check columns // check columns
foreach ($assoc as $as) { foreach ($assoc as $as) {
@@ -295,12 +292,13 @@ class Result implements IDataSource
} while ($row = $this->fetch()); } while ($row = $this->fetch());
unset($x); unset($x);
/** @var mixed[] $data */
return $data; return $data;
} }
/** @deprecated */ /**
* @deprecated
*/
private function oldFetchAssoc(string $assoc) private function oldFetchAssoc(string $assoc)
{ {
$this->seek(0); $this->seek(0);
@@ -499,17 +497,10 @@ class Result implements IDataSource
$row[$key] = is_string($value) ? $this->getResultDriver()->unescapeBinary($value) : $value; $row[$key] = is_string($value) ? $this->getResultDriver()->unescapeBinary($value) : $value;
} elseif ($type === Type::JSON) { } elseif ($type === Type::JSON) {
if ($this->formats[$type] === 'string') { $row[$key] = json_decode($value, true);
$row[$key] = $value;
} else {
$row[$key] = json_decode($value, $this->formats[$type] === 'array');
}
} elseif ($type === null) {
$row[$key] = $value;
} else { } else {
throw new \RuntimeException('Unexpected type ' . $type); $row[$key] = $value;
} }
} }
} }
@@ -578,7 +569,9 @@ class Result implements IDataSource
} }
/** @return Reflection\Column[] */ /**
* @return Reflection\Column[]
*/
final public function getColumns(): array final public function getColumns(): array
{ {
return $this->getInfo()->getColumns(); return $this->getInfo()->getColumns();

View File

@@ -29,6 +29,12 @@ trait Strict
*/ */
public function __call(string $name, array $args) public function __call(string $name, array $args)
{ {
$class = get_class($this);
if ($cb = self::extensionMethod($class . '::' . $name)) { // back compatiblity
trigger_error("Extension methods such as $class::$name() are deprecated", E_USER_DEPRECATED);
array_unshift($args, $this);
return $cb(...$args);
}
$class = method_exists($this, $name) ? 'parent' : get_class($this); $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);
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.'; $hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
@@ -96,4 +102,39 @@ trait Strict
$class = get_class($this); $class = get_class($this);
throw new \LogicException("Attempt to unset undeclared property $class::$$name."); throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
} }
/**
* @return mixed
* @deprecated
*/
public static function extensionMethod(string $name, callable $callback = null)
{
if (strpos($name, '::') === false) {
$class = get_called_class();
} else {
[$class, $name] = explode('::', $name);
$class = (new ReflectionClass($class))->getName();
}
$list = &self::$extMethods[strtolower($name)];
if ($callback === null) { // getter
$cache = &$list[''][$class];
if (isset($cache)) {
return $cache;
}
foreach ([$class] + class_parents($class) + class_implements($class) as $cl) {
if (isset($list[$cl])) {
return $cache = $list[$cl];
}
}
return $cache = false;
} else { // setter
trigger_error("Extension methods such as $class::$name() are deprecated", E_USER_DEPRECATED);
$list[$class] = $callback;
$list[''] = null;
}
}
} }

View File

@@ -92,25 +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(<<<'XX' . preg_replace_callback('/
/ (?=[`[\'":%?]) ## speed-up
(?=[`['":%?]) ## speed-up (?:
(?: `(.+?)`| ## 1) `identifier`
`(.+?)`| ## 1) `identifier` \[(.+?)\]| ## 2) [identifier]
\[(.+?)\]| ## 2) [identifier] (\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
(')((?:''|[^'])*)'| ## 3,4) string (")((?:""|[^"])*)"| ## 5,6) "string"
(")((?:""|[^"])*)"| ## 5,6) "string" (\'|")| ## 7) lone quote
('|")| ## 7) lone quote :(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution: %([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
%([a-zA-Z~][a-zA-Z0-9~]{0,5})| ## 10) modifier (\?) ## 11) placeholder
(\?) ## 11) placeholder )/xs',
)/xs */ // note: this can change $this->args & $this->cursor & ...
XX . preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
,
[$this, 'cb'], [$this, 'cb'],
substr($arg, $toSkip) substr($arg, $toSkip)
); );
if (preg_last_error()) { if (preg_last_error()) {
throw new PcreException; throw new PcreException;
} }
@@ -152,7 +151,7 @@ XX
$sql[] = '*/'; $sql[] = '*/';
} }
$sql = trim(implode(' ', $sql), ' '); $sql = implode(' ', $sql);
if ($this->errors) { if ($this->errors) {
throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql); throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql);
@@ -382,11 +381,10 @@ XX
case 'dt': // datetime case 'dt': // datetime
if ($value === null) { if ($value === null) {
return 'NULL'; return 'NULL';
} elseif (!$value instanceof \DateTimeInterface) { } else {
$value = new DateTime($value); return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
} }
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value); // break omitted
case 'by': case 'by':
case 'n': // composed identifier name case 'n': // composed identifier name
return $this->identifiers->$value; return $this->identifiers->$value;
@@ -401,22 +399,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(<<<'XX' . preg_replace_callback(
/ '/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\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;
} }
@@ -427,15 +414,12 @@ XX
return (string) $value; return (string) $value;
case 'like~': // LIKE string% case 'like~': // LIKE string%
return $this->driver->escapeLike($value, 2);
case '~like': // LIKE %string
return $this->driver->escapeLike($value, 1); return $this->driver->escapeLike($value, 1);
case '~like~': // LIKE %string% case '~like': // LIKE %string
return $this->driver->escapeLike($value, 3); return $this->driver->escapeLike($value, -1);
case 'like': // LIKE string case '~like~': // LIKE %string%
return $this->driver->escapeLike($value, 0); return $this->driver->escapeLike($value, 0);
case 'and': case 'and':
@@ -471,9 +455,6 @@ XX
} elseif ($value instanceof \DateTimeInterface) { } elseif ($value instanceof \DateTimeInterface) {
return $this->driver->escapeDateTime($value); return $this->driver->escapeDateTime($value);
} elseif ($value instanceof \DateInterval) {
return $this->driver->escapeDateInterval($value);
} elseif ($value instanceof Literal) { } elseif ($value instanceof Literal) {
return (string) $value; return (string) $value;

View File

@@ -44,7 +44,7 @@ class dibi
/** version */ /** version */
public const public const
VERSION = '4.1.3'; VERSION = '4.0.3';
/** sorting order */ /** sorting order */
public const public const
@@ -145,6 +145,26 @@ class dibi
} }
/**
* @deprecated
*/
public static function affectedRows(): int
{
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
return self::getConnection()->getAffectedRows();
}
/**
* @deprecated
*/
public static function insertId(string $sequence = null): int
{
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
return self::getConnection()->getInsertId($sequence);
}
/********************* misc tools ****************d*g**/ /********************* misc tools ****************d*g**/

View File

@@ -87,11 +87,15 @@ interface Driver
function escapeBool(bool $value): string; function escapeBool(bool $value): string;
function escapeDate(\DateTimeInterface $value): string; /**
* @param \DateTimeInterface|string|int $value
*/
function escapeDate($value): string;
function escapeDateTime(\DateTimeInterface $value): string; /**
* @param \DateTimeInterface|string|int $value
function escapeDateInterval(\DateInterval $value): string; */
function escapeDateTime($value): string;
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.

View File

@@ -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.");

View File

@@ -77,7 +77,7 @@ SELECT [product_id]
FROM (SELECT * FROM products) t FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1) WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC ORDER BY [product_id] ASC
) t"), dibi::$sql); ) t"), dibi::$sql);
Assert::same(1, $ds->toDataSource()->count()); Assert::same(1, $ds->toDataSource()->count());
@@ -93,7 +93,7 @@ SELECT [product_id]
FROM (SELECT * FROM products) t FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1) WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC ORDER BY [product_id] ASC
"), "),
dibi::$sql dibi::$sql
); );
@@ -106,7 +106,7 @@ SELECT [product_id]
FROM (SELECT * FROM products) t FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1) WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC ORDER BY [product_id] ASC
) t"), ) t"),
(string) $fluent (string) $fluent
); );

View File

@@ -56,28 +56,28 @@ $fluent = $conn->select('*')
->orderBy('customer_id'); ->orderBy('customer_id');
Assert::same( Assert::same(
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent (string) $fluent
); );
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
'SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t', 'SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t',
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
$fluent->fetchAll(0, 3); $fluent->fetchAll(0, 3);
Assert::same( Assert::same(
reformat('SELECT TOP (3) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (3) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent (string) $fluent
); );
@@ -85,16 +85,16 @@ Assert::same(
$fluent->limit(0); $fluent->limit(0);
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat('SELECT TOP (0) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent (string) $fluent
); );
@@ -103,12 +103,12 @@ $fluent->removeClause('limit');
$fluent->removeClause('offset'); $fluent->removeClause('offset');
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT TOP (1) * FROM (SELECT * FROM [customers] ORDER BY [customer_id]) t'), reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(

View File

@@ -22,28 +22,28 @@ $fluent = $conn->select('*')
->orderBy('customer_id'); ->orderBy('customer_id');
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent (string) $fluent
); );
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchAll(2, 3); $fluent->fetchAll(2, 3);
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 3 OFFSET 2'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 3 OFFSET 2'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent (string) $fluent
); );
@@ -51,16 +51,16 @@ Assert::same(
$fluent->limit(0); $fluent->limit(0);
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
(string) $fluent (string) $fluent
); );
@@ -68,16 +68,16 @@ Assert::same(
$fluent->removeClause('limit'); $fluent->removeClause('limit');
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 18446744073709551615 OFFSET 3'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 18446744073709551615 OFFSET 3'),
(string) $fluent (string) $fluent
); );
@@ -85,12 +85,12 @@ Assert::same(
$fluent->removeClause('offset'); $fluent->removeClause('offset');
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'), reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(

View File

@@ -95,9 +95,9 @@ $fluent = $conn->select('*')
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'SELECT TOP 1 * FROM (SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123) t', 'odbc' => 'SELECT TOP 1 * FROM ( SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123) t',
'sqlsrv' => 'SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY', 'sqlsrv' => ' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
'SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 LIMIT 1', ' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 LIMIT 1',
]), ]),
(string) $fluent (string) $fluent
); );
@@ -134,10 +134,7 @@ $fluent = $conn->select('*')
->where(['x' => 'a', 'b', 'c']); ->where(['x' => 'a', 'b', 'c']);
Assert::same( Assert::same(
reformat([ reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'),
'sqlsrv' => "SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = N'a') AND (b) AND (c)",
"SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = 'a') AND (b) AND (c)",
]),
(string) $fluent (string) $fluent
); );

View File

@@ -29,11 +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);
}); });
test('PDO error mode: implicitly set silent', function () { // PDO error mode: implicitly set silent
test(function () {
buildPdoDriver(null); buildPdoDriver(null);
}); });

View File

@@ -24,7 +24,7 @@ class MockResult extends Dibi\Result
} }
test('', function () { test(function () {
$result = new MockResult; $result = new MockResult;
$result->setType('col', Type::BOOL); $result->setType('col', Type::BOOL);
@@ -46,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);
@@ -62,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);
@@ -139,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);
@@ -165,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);
@@ -183,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');
@@ -202,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);
@@ -218,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);

View File

@@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$translator = new Dibi\Translator($conn);
switch ($config['system']) {
case 'mysql':
Assert::equal('10:20:30.0', $translator->formatValue(new DateInterval('PT10H20M30S'), null));
Assert::equal('-1:00:00.0', $translator->formatValue(DateInterval::createFromDateString('-1 hour'), null));
Assert::exception(function () use ($translator) {
$translator->formatValue(new DateInterval('P2Y4DT6H8M'), null);
}, Dibi\NotSupportedException::class, 'Only time interval is supported.');
break;
default:
Assert::exception(function () use ($translator) {
$translator->formatValue(new DateInterval('PT10H20M30S'), null);
}, Dibi\Exception::class);
}

View File

@@ -60,22 +60,13 @@ WHERE [id] > 0
// nested condition // nested condition
Assert::match( Assert::match(
reformat([ reformat("
'sqlsrv' => "
SELECT *
FROM [customers]
WHERE
[name] LIKE N'xxx'
/* AND ...=1 */
/* 1 LIMIT 10 */",
"
SELECT * SELECT *
FROM [customers] FROM [customers]
WHERE WHERE
[name] LIKE 'xxx' [name] LIKE 'xxx'
/* AND ...=1 */ /* AND ...=1 */
/* 1 LIMIT 10 */", /* 1 LIMIT 10 */"),
]),
$conn->translate(' $conn->translate('
SELECT * SELECT *

View File

@@ -71,9 +71,3 @@ Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'baa', 'aa'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'aab', 'aa')); Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'aab', 'aa'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like~', 'bba', '%a')); Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like~', 'bba', '%a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'b%a', '%a')); Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'b%a', '%a'));
// matches
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'aa'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'b'));

View File

@@ -16,10 +16,7 @@ $conn = new Dibi\Connection($config + ['formatDateTime' => "'Y-m-d H:i:s.u'", 'f
// Dibi detects INSERT or REPLACE command & booleans // Dibi detects INSERT or REPLACE command & booleans
Assert::same( Assert::same(
reformat([ reformat("REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)"),
'sqlsrv' => "REPLACE INTO [products] ([title], [price]) VALUES (N'Drticka', 318)",
"REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)",
]),
$conn->translate('REPLACE INTO [products]', [ $conn->translate('REPLACE INTO [products]', [
'title' => 'Drticka', 'title' => 'Drticka',
'price' => 318, 'price' => 318,
@@ -34,10 +31,7 @@ $array = [
'brand' => null, 'brand' => null,
]; ];
Assert::same( Assert::same(
reformat([ reformat('INSERT INTO [products] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'),
'sqlsrv' => "INSERT INTO [products] ([title], [price], [brand]) VALUES (N'Super Product', 12, NULL) , (N'Super Product', 12, NULL) , (N'Super Product', 12, NULL)",
"INSERT INTO [products] ([title], [price], [brand]) VALUES ('Super Product', 12, NULL) , ('Super Product', 12, NULL) , ('Super Product', 12, NULL)",
]),
$conn->translate('INSERT INTO [products]', $array, $array, $array) $conn->translate('INSERT INTO [products]', $array, $array, $array)
); );
@@ -49,20 +43,14 @@ $array = [
['pole' => 'hodnota3', 'bit' => 1], ['pole' => 'hodnota3', 'bit' => 1],
]; ];
Assert::same( Assert::same(
reformat([ reformat('INSERT INTO [products] ([pole], [bit]) VALUES (\'hodnota1\', 1) , (\'hodnota2\', 1) , (\'hodnota3\', 1)'),
'sqlsrv' => "INSERT INTO [products] ([pole], [bit]) VALUES (N'hodnota1', 1) , (N'hodnota2', 1) , (N'hodnota3', 1)",
"INSERT INTO [products] ([pole], [bit]) VALUES ('hodnota1', 1) , ('hodnota2', 1) , ('hodnota3', 1)",
]),
$conn->translate('INSERT INTO [products] %ex', $array) $conn->translate('INSERT INTO [products] %ex', $array)
); );
// Dibi detects UPDATE command // Dibi detects UPDATE command
Assert::same( Assert::same(
reformat([ reformat("UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123"),
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [order]=12 WHERE [id]=123",
"UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123",
]),
$conn->translate('UPDATE [colors] SET', [ $conn->translate('UPDATE [colors] SET', [
'color' => 'blue', 'color' => 'blue',
'order' => 12, 'order' => 12,
@@ -97,26 +85,17 @@ $e = Assert::exception(function () use ($conn) {
Assert::same('SELECT **Invalid combination of type stdClass and modifier %s** , **Unknown or unexpected modifier %m**', $e->getSql()); Assert::same('SELECT **Invalid combination of type stdClass and modifier %s** , **Unknown or unexpected modifier %m**', $e->getSql());
Assert::same( Assert::same(
reformat([ reformat('SELECT * FROM [table] WHERE id=10 AND name=\'ahoj\''),
'sqlsrv' => "SELECT * FROM [table] WHERE id=10 AND name=N'ahoj'",
"SELECT * FROM [table] WHERE id=10 AND name='ahoj'",
]),
$conn->translate('SELECT * FROM [table] WHERE id=%i AND name=%s', 10, 'ahoj') $conn->translate('SELECT * FROM [table] WHERE id=%i AND name=%s', 10, 'ahoj')
); );
Assert::same( Assert::same(
reformat([ reformat('TEST ([cond] > 2) OR ([cond2] = \'3\') OR (cond3 < RAND())'),
'sqlsrv' => "TEST ([cond] > 2) OR ([cond2] = N'3') OR (cond3 < RAND())",
"TEST ([cond] > 2) OR ([cond2] = '3') OR (cond3 < RAND())",
]),
$conn->translate('TEST %or', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()']) $conn->translate('TEST %or', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
); );
Assert::same( Assert::same(
reformat([ reformat('TEST ([cond] > 2) AND ([cond2] = \'3\') AND (cond3 < RAND())'),
'sqlsrv' => "TEST ([cond] > 2) AND ([cond2] = N'3') AND (cond3 < RAND())",
"TEST ([cond] > 2) AND ([cond2] = '3') AND (cond3 < RAND())",
]),
$conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()']) $conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
); );
@@ -135,10 +114,7 @@ $where['age'] = null;
$where['email'] = 'ahoj'; $where['email'] = 'ahoj';
$where['id%l'] = [10, 20, 30]; $where['id%l'] = [10, 20, 30];
Assert::same( Assert::same(
reformat([ reformat('SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = \'ahoj\') AND ([id] IN (10, 20, 30))'),
'sqlsrv' => "SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = N'ahoj') AND ([id] IN (10, 20, 30))",
"SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = 'ahoj') AND ([id] IN (10, 20, 30))",
]),
$conn->translate('SELECT * FROM [table] WHERE %and', $where) $conn->translate('SELECT * FROM [table] WHERE %and', $where)
); );
@@ -168,9 +144,9 @@ Assert::same(
// with limit = 2 // with limit = 2
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'SELECT TOP 2 * FROM (SELECT * FROM [products]) t', 'odbc' => 'SELECT TOP 2 * FROM (SELECT * FROM [products] ) t',
'sqlsrv' => 'SELECT * FROM [products] OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY', 'sqlsrv' => 'SELECT * FROM [products] OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY',
'SELECT * FROM [products] LIMIT 2', 'SELECT * FROM [products] LIMIT 2',
]), ]),
$conn->translate('SELECT * FROM [products] %lmt', 2) $conn->translate('SELECT * FROM [products] %lmt', 2)
); );
@@ -184,7 +160,7 @@ if ($config['system'] === 'odbc') {
Assert::same( Assert::same(
reformat([ reformat([
'sqlsrv' => 'SELECT * FROM [products] OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY', 'sqlsrv' => 'SELECT * FROM [products] OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY',
'SELECT * FROM [products] LIMIT 2 OFFSET 1', 'SELECT * FROM [products] LIMIT 2 OFFSET 1',
]), ]),
$conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1) $conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1)
); );
@@ -192,10 +168,10 @@ if ($config['system'] === 'odbc') {
// with offset = 50 // with offset = 50
Assert::same( Assert::same(
reformat([ reformat([
'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50', 'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50',
'postgre' => 'SELECT * FROM "products" OFFSET 50', 'postgre' => 'SELECT * FROM "products" OFFSET 50',
'sqlsrv' => 'SELECT * FROM [products] OFFSET 50 ROWS', 'sqlsrv' => 'SELECT * FROM [products] OFFSET 50 ROWS',
'SELECT * FROM [products] LIMIT -1 OFFSET 50', 'SELECT * FROM [products] LIMIT -1 OFFSET 50',
]), ]),
$conn->translate('SELECT * FROM [products] %ofs', 50) $conn->translate('SELECT * FROM [products] %ofs', 50)
); );
@@ -283,13 +259,7 @@ 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'
", ",
'sqlsrv' => "SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT "SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
CONCAT(last_name, N', ', first_name) AS full_name
GROUP BY [user]
HAVING MAX(salary) > %i 123
INTO OUTFILE N'/tmp/result''.txt'
FIELDS TERMINATED BY N',' OPTIONALLY ENCLOSED BY N'\"'
LINES TERMINATED BY N'\\n'", "SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
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
@@ -351,27 +321,6 @@ WHERE (`test`.`a` LIKE '1995-03-01'
OR `false`= 0 OR `false`= 0
OR `str_null`=NULL OR `str_null`=NULL
OR `str_not_null`='hello' OR `str_not_null`='hello'
LIMIT 10",
'sqlsrv' => "SELECT *
FROM [db].[table]
WHERE ([test].[a] LIKE '1995-03-01'
OR [b1] IN ( 1, 2, 3 )
OR [b2] IN (N'1', N'2', N'3' )
OR [b3] IN ( )
OR [b4] IN ( N'one', N'two', N'three' )
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
OR [b6] IN (N'one', N'two', N'thr.ee')
OR [b7] IN (NULL)
OR [b8] IN (RAND() [col1] > [col2] )
OR [b9] IN (RAND(), [col1] > [col2] )
OR [b10] IN ( )
AND [c] = N'embedded '' string'
OR [d]=10
OR [e]=NULL
OR [true]= 1
OR [false]= 0
OR [str_null]=NULL
OR [str_not_null]=N'hello'
LIMIT 10", LIMIT 10",
'postgre' => 'SELECT * 'postgre' => 'SELECT *
FROM "db"."table" FROM "db"."table"
@@ -463,10 +412,7 @@ LIMIT 10')
Assert::same( Assert::same(
reformat([ reformat('TEST [cond] > 2 [cond2] = \'3\' cond3 < RAND() 123'),
'sqlsrv' => "TEST [cond] > 2 [cond2] = N'3' cond3 < RAND() 123",
"TEST [cond] > 2 [cond2] = '3' cond3 < RAND() 123",
]),
$conn->translate('TEST %ex', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'], 123) $conn->translate('TEST %ex', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'], 123)
); );
@@ -484,19 +430,16 @@ Assert::same(
Assert::same( Assert::same(
reformat([ reformat('TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE \'string\')'),
'sqlsrv' => "TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE N'string')",
"TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE 'string')",
]),
$conn->translate('TEST %or', ['cond1%ex' => 3, 'cond2%ex' => 'RAND()', 'cond3%ex' => ['LIKE %s', 'string']]) $conn->translate('TEST %or', ['cond1%ex' => 3, 'cond2%ex' => 'RAND()', 'cond3%ex' => ['LIKE %s', 'string']])
); );
Assert::same( Assert::same(
reformat([ reformat([
'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\') t', 'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' ) t',
'sqlsrv' => 'SELECT * FROM [test] WHERE [id] LIKE N\'%d%t\' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', 'sqlsrv' => 'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10', 'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10',
]), ]),
$conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10) $conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10)
); );
@@ -512,32 +455,23 @@ Assert::same(
Assert::same( Assert::same(
reformat('SELECT FROM ...'), reformat('SELECT FROM ... '),
$conn->translate('SELECT FROM ... %lmt', null) $conn->translate('SELECT FROM ... %lmt', null)
); );
Assert::same( Assert::same(
reformat([ reformat('SELECT \'%i\''),
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
$conn->translate("SELECT '%i'") $conn->translate("SELECT '%i'")
); );
Assert::same( Assert::same(
reformat([ reformat('SELECT \'%i\''),
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
$conn->translate('SELECT "%i"') $conn->translate('SELECT "%i"')
); );
Assert::same( Assert::same(
reformat([ reformat('INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(\'Test product\')) , (1, SHA1(\'Test product\'))'),
'sqlsrv' => "INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(N'Test product')) , (1, SHA1(N'Test product'))",
"INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1('Test product')) , (1, SHA1('Test product'))",
]),
$conn->translate('INSERT INTO [products]', [ $conn->translate('INSERT INTO [products]', [
'product_id' => 1, 'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'), 'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -548,10 +482,7 @@ Assert::same(
); );
Assert::same( Assert::same(
reformat([ reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('UPDATE [products]', [ $conn->translate('UPDATE [products]', [
'product_id' => 1, 'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'), 'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -559,10 +490,7 @@ Assert::same(
); );
Assert::same( Assert::same(
reformat([ reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('UPDATE [products]', [ $conn->translate('UPDATE [products]', [
'product_id' => 1, 'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'), 'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -570,10 +498,7 @@ Assert::same(
); );
Assert::same( Assert::same(
reformat([ reformat('SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(\'Test product\')'),
'sqlsrv' => "SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(N'Test product')",
"SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('SELECT * FROM [products] WHERE', [ $conn->translate('SELECT * FROM [products] WHERE', [
'product_id' => 1, 'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'), 'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -610,10 +535,7 @@ $array6 = [
]; ];
Assert::same( Assert::same(
reformat([ reformat('INSERT INTO test ([id], [text], [num]) VALUES (1, \'ahoj\', 1), (2, \'jak\', -1), (3, \'se\', 10), (4, SUM(5), 1)'),
'sqlsrv' => "INSERT INTO test ([id], [text], [num]) VALUES (1, N'ahoj', 1), (2, N'jak', -1), (3, N'se', 10), (4, SUM(5), 1)",
"INSERT INTO test ([id], [text], [num]) VALUES (1, 'ahoj', 1), (2, 'jak', -1), (3, 'se', 10), (4, SUM(5), 1)",
]),
$conn->translate('INSERT INTO test %m', $array6) $conn->translate('INSERT INTO test %m', $array6)
); );
@@ -667,10 +589,7 @@ Assert::same(
setlocale(LC_ALL, 'czech'); setlocale(LC_ALL, 'czech');
Assert::same( Assert::same(
reformat([ reformat("UPDATE [colors] SET [color]='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",
]),
$conn->translate('UPDATE [colors] SET', [ $conn->translate('UPDATE [colors] SET', [
'color' => 'blue', 'color' => 'blue',

View File

@@ -37,7 +37,7 @@ if ($config['system'] === 'odbc') {
} }
function test(string $title, Closure $function): void function test(Closure $function)
{ {
$function(); $function();
} }

View File

@@ -1,4 +0,0 @@
parameters:
ignoreErrors:
# The namespace is referenced, not the class.
- '#Class dibi referenced with incorrect case: Dibi#'