1
0
mirror of https://github.com/dg/dibi.git synced 2025-09-01 18:12:51 +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:
include:
- name: Nette Code Checker
php: 7.4
install:
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
script:
@@ -40,7 +39,6 @@ jobs:
- name: Nette Coding Standard
php: 7.4
install:
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
script:
@@ -48,13 +46,15 @@ jobs:
- 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:
- composer run-script phpstan
- php temp/phpstan/phpstan.phar analyse --autoload-file vendor/autoload.php --level 5 src
- stage: Code Coverage
php: 7.4
script:
- vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src
after_script:

View File

@@ -3,7 +3,7 @@
"description": "Dibi is Database Abstraction Library for PHP",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
"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": [
{
"name": "David Grudl",
@@ -15,9 +15,7 @@
},
"require-dev": {
"tracy/tracy": "~2.2",
"nette/tester": "~2.0",
"nette/di": "^3.0",
"phpstan/phpstan": "^0.12"
"nette/tester": "~2.0"
},
"replace": {
"dg/dibi": "*"
@@ -25,13 +23,9 @@
"autoload": {
"classmap": ["src/"]
},
"scripts": {
"phpstan": "phpstan analyse --autoload-file vendor/autoload.php --level 5 --configuration tests/phpstan.neon src",
"tester": "tester tests -s"
},
"extra": {
"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
'profiler' => [
'file' => 'log/log.sql',
'errorsOnly' => false,
],
]);

View File

@@ -14,13 +14,7 @@ Introduction
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.
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)
If you like Dibi, **[please make a donation now](https://nette.org/make-donation?to=dibi)**. Thank you!
Installation
@@ -32,7 +26,7 @@ Install Dibi via Composer:
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
@@ -48,11 +42,11 @@ The database connection is represented by the object `Dibi\Connection`:
```php
$database = new Dibi\Connection([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'table',
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'table',
]);
$result = $database->query('SELECT * FROM users');
@@ -62,12 +56,12 @@ Alternatively, you can use the `dibi` static register, which maintains a connect
```php
dibi::connect([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'test',
'charset' => 'utf8',
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'test',
'charset' => 'utf8',
]);
$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`.
You can try all the examples [online at the playground](https://repl.it/@DavidGrudl/dibi-playground).
```php
$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)
```
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
$table = 'blog.users';
@@ -207,7 +199,6 @@ Three special modifiers are available for LIKE:
| `%like~` | the expression starts with a string
| `%~like` | the expression ends with a string
| `%~like~` | the expression contains a string
| `%like` | the expression matches a string
Search for names beginning with a string:
@@ -235,8 +226,8 @@ Example:
```php
$arr = [
'a' => 'hello',
'b' => true,
'a' => 'hello',
'b' => true,
];
$database->query('INSERT INTO table %v', $arr);
@@ -508,7 +499,7 @@ $all = $result->fetchAssoc('customer_id|order_id');
// we will iterate like this:
foreach ($all as $customerId => $orders) {
foreach ($orders as $orderId => $order) {
...
...
}
}
```
@@ -540,7 +531,7 @@ $all = $result->fetchAssoc('name[]order_id');
// we get all the Arnolds in the results
foreach ($all['Arnold Rimmer'] as $arnoldOrders) {
foreach ($arnoldOrders as $orderId => $order) {
...
...
}
}
```
@@ -554,8 +545,8 @@ foreach ($all as $customerId => $orders) {
echo "Customer $customerId":
foreach ($orders as $orderId => $order) {
echo "ID number: $order->number";
// customer name is in $order->name
echo "ID number: $order->number";
// customer name is in $order->name
}
}
```
@@ -577,7 +568,7 @@ foreach ($all as $customerId => $row) {
echo "Customer $row->name":
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;
$singleConnection = reset($this->events)->connection;
foreach ($this->events as $event) {
if ($event->connection !== $singleConnection) {
$singleConnection = null;
break;
}
}
foreach ($this->events as $event) {
$totalTime += $event->time;
$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 .= "</td><td>{$event->count}</td>";
if (!$singleConnection) {
$s .= '<td>' . htmlspecialchars($this->getConnectionName($connection)) . '</td></tr>';
}
$s .= "</td><td>{$event->count}</td></tr>";
}
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>
<h1>Queries: ' . count($this->events)
. ($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">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
' . $s . '
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
</table>
</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
* - result (array) => result set options
* - 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)
* - run (bool) => enable profiler?
* - file => file to log
* - errorsOnly (bool) => log only errors
* - substitutes (array) => map of driver specific substitutes (under development)
* - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established
* @param array $config connection parameters
* @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, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname');
@@ -65,14 +76,12 @@ class Connection implements IConnection
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
$config['driver'] = $config['driver'] ?? 'mysqli';
$config['name'] = $name;
$config['result']['formatJson'] = $config['result']['formatJson'] ?? 'array';
$this->config = $config;
// profiler
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
$filter = $config['profiler']['filter'] ?? Event::QUERY;
$errorsOnly = $config['profiler']['errorsOnly'] ?? false;
$this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter, $errorsOnly), 'logEvent'];
$this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter), 'logEvent'];
}
$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.
* @throws Exception
@@ -316,13 +335,23 @@ class Connection implements IConnection
$this->connect();
}
$id = $this->driver->getInsertId($sequence);
if ($id === null) {
if ($id < 1) {
throw new Exception('Cannot retrieve last generated 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).
*/
@@ -402,8 +431,7 @@ class Connection implements IConnection
{
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime'])
->setFormat(Type::JSON, $this->config['result']['formatJson']);
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
}

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
{
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;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
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'");
}
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) . "'";
}
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 = 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;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
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.
* @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
{
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
{
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'");
}
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'");
}
public function escapeDateInterval(\DateInterval $value): string
{
if ($value->y || $value->m || $value->d) {
throw new Dibi\NotSupportedException('Only time interval is supported.');
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
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
{
$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) {
// 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 : '');
}
}

View File

@@ -37,7 +37,9 @@ class OdbcDriver implements Dibi\Driver
private $microseconds = true;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
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#');
}
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#');
}
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 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}

View File

@@ -42,7 +42,9 @@ class OracleDriver implements Dibi\Driver
private $affectedRows;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
if (!extension_loaded('oci8')) {
@@ -50,6 +52,10 @@ class OracleDriver implements Dibi\Driver
}
$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;
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
? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')"
: $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
? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')"
: $value->format('U');
}
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/**
* Encodes string for use in a LIKE statement.
*/
@@ -268,7 +280,7 @@ class OracleDriver implements Dibi\Driver
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$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),
'table' => null,
'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,
];
}

View File

@@ -42,7 +42,9 @@ class PdoDriver implements Dibi\Driver
private $serverVersion = '';
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
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'");
}
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) {
case 'odbc':
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.
*/
@@ -322,29 +330,29 @@ class PdoDriver implements Dibi\Driver
switch ($this->driverName) {
case 'mysql':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'oci':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'pgsql':
$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 = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite':
$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 'mssql':
case 'dblib':
case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default:
throw new Dibi\NotImplementedException;
@@ -365,7 +373,7 @@ class PdoDriver implements Dibi\Driver
case 'mysql':
if ($limit !== null || $offset) {
// 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 : '');
}
break;
@@ -381,7 +389,7 @@ class PdoDriver implements Dibi\Driver
case 'sqlite':
if ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit ?? '-1')
$sql .= ' LIMIT ' . ($limit === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : '');
}
break;

View File

@@ -23,7 +23,6 @@ use Dibi\Helpers;
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - connect_type (int) => see pg_connect()
*/
class PostgreDriver implements Dibi\Driver
{
@@ -36,7 +35,9 @@ class PostgreDriver implements Dibi\Driver
private $affectedRows;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
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) {
$error = $message;
});
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, $connectType);
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($string, $connectType);
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
}
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'");
}
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'");
}
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/**
* 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
$value = pg_escape_string($this->connection, $value);
$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;
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
if (!extension_loaded('sqlite3')) {
@@ -137,7 +139,7 @@ class SqliteDriver implements Dibi\Driver
*/
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);
}
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);
}
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 = 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.');
} elseif ($limit !== null || $offset) {
$sql .= ' LIMIT ' . ($limit ?? '-1')
$sql .= ' LIMIT ' . ($limit === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : '');
}
}

View File

@@ -39,7 +39,9 @@ class SqlsrvDriver implements Dibi\Driver
private $version = '';
/** @throws Dibi\NotSupportedException */
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
if (!extension_loaded('sqlsrv')) {
@@ -194,13 +196,13 @@ class SqlsrvDriver implements Dibi\Driver
*/
public function escapeText(string $value): string
{
return "N'" . str_replace("'", "''", $value) . "'";
return "'" . str_replace("'", "''", $value) . "'";
}
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'");
}
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'") . ')';
}
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 ? "%'" : "'");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}

View File

@@ -100,7 +100,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/
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 = [];
while ($row = $keyUsagesRes->fetch(true)) {
$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
{
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 or(...$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 desc()
*/

View File

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

View File

@@ -25,15 +25,11 @@ class FileLogger
/** @var int */
public $filter;
/** @var bool */
private $errorsOnly;
public function __construct(string $file, int $filter = null, bool $errorsOnly = false)
public function __construct(string $file, int $filter = null)
{
$this->file = $file;
$this->filter = $filter ?: Dibi\Event::QUERY;
$this->errorsOnly = $errorsOnly;
}
@@ -42,42 +38,39 @@ class FileLogger
*/
public function logEvent(Dibi\Event $event): void
{
if (
(($event->type & $this->filter) === 0)
|| ($this->errorsOnly === true && !$event->result instanceof \Exception)
) {
if (($event->type & $this->filter) === 0) {
return;
}
$handle = fopen($this->file, 'a');
if (!$handle) {
return; // or throw exception?
}
flock($handle, LOCK_EX);
if ($event->result instanceof \Exception) {
$message = $event->result->getMessage();
if ($code = $event->result->getCode()) {
$message = "[$code] $message";
}
$this->writeToFile(
$event,
fwrite($handle,
"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 {
$this->writeToFile(
$event,
fwrite($handle,
'OK: ' . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
. "\n-- source: " . implode(':', $event->source)
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
. "\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"
);
}
}
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);
fclose($handle);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,7 +44,7 @@ class dibi
/** version */
public const
VERSION = '4.1.3';
VERSION = '4.0.3';
/** sorting order */
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**/

View File

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

View File

@@ -12,7 +12,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php';
test('', function () use ($config) {
test(function () use ($config) {
$conn = new Connection($config);
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]);
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);
Assert::true($conn->isConnected());
@@ -40,7 +40,7 @@ test('', function () use ($config) {
});
test('', function () use ($config) {
test(function () use ($config) {
$conn = new Connection($config);
Assert::true($conn->isConnected());
@@ -52,7 +52,7 @@ test('', function () use ($config) {
});
test('', function () use ($config) {
test(function () use ($config) {
$conn = new Connection($config);
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) {
new Connection($config + ['onConnect' => '']);
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");

View File

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

View File

@@ -56,28 +56,28 @@ $fluent = $conn->select('*')
->orderBy('customer_id');
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
);
$fluent->fetch();
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
);
$fluent->fetchSingle();
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
);
$fluent->fetchAll(0, 3);
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
);
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
);
@@ -85,16 +85,16 @@ Assert::same(
$fluent->limit(0);
$fluent->fetch();
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
);
$fluent->fetchSingle();
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
);
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
);
@@ -103,12 +103,12 @@ $fluent->removeClause('limit');
$fluent->removeClause('offset');
$fluent->fetch();
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
);
$fluent->fetchSingle();
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
);
Assert::same(

View File

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

View File

@@ -95,9 +95,9 @@ $fluent = $conn->select('*')
Assert::same(
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',
'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',
'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',
' 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
);
@@ -134,10 +134,7 @@ $fluent = $conn->select('*')
->where(['x' => 'a', 'b', 'c']);
Assert::same(
reformat([
'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)",
]),
reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'),
(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.');
test('PDO error mode: explicitly set silent', function () {
// PDO error mode: explicitly set silent
test(function () {
buildPdoDriver(PDO::ERRMODE_SILENT);
});
test('PDO error mode: implicitly set silent', function () {
// PDO error mode: implicitly set silent
test(function () {
buildPdoDriver(null);
});

View File

@@ -24,7 +24,7 @@ class MockResult extends Dibi\Result
}
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::BOOL);
@@ -46,7 +46,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::TEXT);
@@ -62,7 +62,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::FLOAT);
@@ -139,7 +139,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::INTEGER);
@@ -165,7 +165,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::DATETIME);
@@ -183,7 +183,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::DATETIME);
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
@@ -202,7 +202,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$result->setType('col', Type::DATE);
@@ -218,7 +218,7 @@ test('', function () {
});
test('', function () {
test(function () {
$result = new MockResult;
$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
Assert::match(
reformat([
'sqlsrv' => "
SELECT *
FROM [customers]
WHERE
[name] LIKE N'xxx'
/* AND ...=1 */
/* 1 LIMIT 10 */",
"
reformat("
SELECT *
FROM [customers]
WHERE
[name] LIKE 'xxx'
/* AND ...=1 */
/* 1 LIMIT 10 */",
]),
/* 1 LIMIT 10 */"),
$conn->translate('
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::falsey($conn->fetchSingle('SELECT ? LIKE %~like~', 'bba', '%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
Assert::same(
reformat([
'sqlsrv' => "REPLACE INTO [products] ([title], [price]) VALUES (N'Drticka', 318)",
"REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)",
]),
reformat("REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)"),
$conn->translate('REPLACE INTO [products]', [
'title' => 'Drticka',
'price' => 318,
@@ -34,10 +31,7 @@ $array = [
'brand' => null,
];
Assert::same(
reformat([
'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)",
]),
reformat('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)
);
@@ -49,20 +43,14 @@ $array = [
['pole' => 'hodnota3', 'bit' => 1],
];
Assert::same(
reformat([
'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)",
]),
reformat('INSERT INTO [products] ([pole], [bit]) VALUES (\'hodnota1\', 1) , (\'hodnota2\', 1) , (\'hodnota3\', 1)'),
$conn->translate('INSERT INTO [products] %ex', $array)
);
// Dibi detects UPDATE command
Assert::same(
reformat([
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [order]=12 WHERE [id]=123",
"UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123",
]),
reformat("UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123"),
$conn->translate('UPDATE [colors] SET', [
'color' => 'blue',
'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(
reformat([
'sqlsrv' => "SELECT * FROM [table] WHERE id=10 AND name=N'ahoj'",
"SELECT * FROM [table] WHERE id=10 AND name='ahoj'",
]),
reformat('SELECT * FROM [table] WHERE id=10 AND name=\'ahoj\''),
$conn->translate('SELECT * FROM [table] WHERE id=%i AND name=%s', 10, 'ahoj')
);
Assert::same(
reformat([
'sqlsrv' => "TEST ([cond] > 2) OR ([cond2] = N'3') OR (cond3 < RAND())",
"TEST ([cond] > 2) OR ([cond2] = '3') OR (cond3 < RAND())",
]),
reformat('TEST ([cond] > 2) OR ([cond2] = \'3\') OR (cond3 < RAND())'),
$conn->translate('TEST %or', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
);
Assert::same(
reformat([
'sqlsrv' => "TEST ([cond] > 2) AND ([cond2] = N'3') AND (cond3 < RAND())",
"TEST ([cond] > 2) AND ([cond2] = '3') AND (cond3 < RAND())",
]),
reformat('TEST ([cond] > 2) AND ([cond2] = \'3\') AND (cond3 < RAND())'),
$conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
);
@@ -135,10 +114,7 @@ $where['age'] = null;
$where['email'] = 'ahoj';
$where['id%l'] = [10, 20, 30];
Assert::same(
reformat([
'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))",
]),
reformat('SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = \'ahoj\') AND ([id] IN (10, 20, 30))'),
$conn->translate('SELECT * FROM [table] WHERE %and', $where)
);
@@ -168,9 +144,9 @@ Assert::same(
// with limit = 2
Assert::same(
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',
'SELECT * FROM [products] LIMIT 2',
'SELECT * FROM [products] LIMIT 2',
]),
$conn->translate('SELECT * FROM [products] %lmt', 2)
);
@@ -184,7 +160,7 @@ if ($config['system'] === 'odbc') {
Assert::same(
reformat([
'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)
);
@@ -192,10 +168,10 @@ if ($config['system'] === 'odbc') {
// with offset = 50
Assert::same(
reformat([
'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50',
'postgre' => 'SELECT * FROM "products" OFFSET 50',
'mysql' => 'SELECT * FROM `products` LIMIT 18446744073709551615 OFFSET 50',
'postgre' => 'SELECT * FROM "products" OFFSET 50',
'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)
);
@@ -283,13 +259,7 @@ INTO OUTFILE '/tmp/result\'.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
LINES TERMINATED BY '\\\\n'
",
'sqlsrv' => "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
"SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
CONCAT(last_name, ', ', first_name) AS full_name
GROUP BY [user]
HAVING MAX(salary) > %i 123
@@ -351,27 +321,6 @@ WHERE (`test`.`a` LIKE '1995-03-01'
OR `false`= 0
OR `str_null`=NULL
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",
'postgre' => 'SELECT *
FROM "db"."table"
@@ -463,10 +412,7 @@ LIMIT 10')
Assert::same(
reformat([
'sqlsrv' => "TEST [cond] > 2 [cond2] = N'3' cond3 < RAND() 123",
"TEST [cond] > 2 [cond2] = '3' cond3 < RAND() 123",
]),
reformat('TEST [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(
reformat([
'sqlsrv' => "TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE N'string')",
"TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE 'string')",
]),
reformat('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']])
);
Assert::same(
reformat([
'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',
'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10',
'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' ) t',
'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',
]),
$conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10)
);
@@ -512,32 +455,23 @@ Assert::same(
Assert::same(
reformat('SELECT FROM ...'),
reformat('SELECT FROM ... '),
$conn->translate('SELECT FROM ... %lmt', null)
);
Assert::same(
reformat([
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
reformat('SELECT \'%i\''),
$conn->translate("SELECT '%i'")
);
Assert::same(
reformat([
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
reformat('SELECT \'%i\''),
$conn->translate('SELECT "%i"')
);
Assert::same(
reformat([
'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'))",
]),
reformat('INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(\'Test product\')) , (1, SHA1(\'Test product\'))'),
$conn->translate('INSERT INTO [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -548,10 +482,7 @@ Assert::same(
);
Assert::same(
reformat([
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('UPDATE [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -559,10 +490,7 @@ Assert::same(
);
Assert::same(
reformat([
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('UPDATE [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -570,10 +498,7 @@ Assert::same(
);
Assert::same(
reformat([
'sqlsrv' => "SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(N'Test product')",
"SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1('Test product')",
]),
reformat('SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(\'Test product\')'),
$conn->translate('SELECT * FROM [products] WHERE', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@@ -610,10 +535,7 @@ $array6 = [
];
Assert::same(
reformat([
'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)",
]),
reformat('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)
);
@@ -667,10 +589,7 @@ Assert::same(
setlocale(LC_ALL, 'czech');
Assert::same(
reformat([
'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",
]),
reformat("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', [
'color' => 'blue',

View File

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

View File

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