mirror of
https://github.com/dg/dibi.git
synced 2025-09-03 02:52:47 +02:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
97053089e0 | ||
|
2c7b35c29d | ||
|
c04d2197e3 | ||
|
d342d8d78f | ||
|
7d8c39f42a | ||
|
29b58d64dd | ||
|
0a32bb5bdf | ||
|
d707b4ba0e | ||
|
490cf143ba | ||
|
12ffa0ffd1 | ||
|
23f65ef837 | ||
|
86a71dde28 | ||
|
bb1f7d4b93 | ||
|
680026747e | ||
|
7ca47508cb | ||
|
beba7b3592 | ||
|
6cc7ce8e44 | ||
|
92b8e6077e | ||
|
e45638eab4 | ||
|
8564217bc1 |
4
.github/workflows/coding-style.yml
vendored
4
.github/workflows/coding-style.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
name: Nette Code Checker
|
name: Nette Code Checker
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
name: Nette Coding Standard
|
name: Nette Coding Standard
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
|
2
.github/workflows/static-analysis.yml
vendored
2
.github/workflows/static-analysis.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
name: PHPStan
|
name: PHPStan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.0
|
php-version: 8.0
|
||||||
|
11
.github/workflows/tests.yml
vendored
11
.github/workflows/tests.yml
vendored
@@ -3,7 +3,7 @@ name: Tests
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.10.0beta2, pdo_sqlsrv-5.10.0beta2
|
php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.12.0, pdo_sqlsrv-5.12.0
|
||||||
php-tools: "composer:v2, pecl"
|
php-tools: "composer:v2, pecl"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.0', '8.1', '8.2', '8.3']
|
php: ['8.0', '8.1', '8.2', '8.3', '8.4']
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ jobs:
|
|||||||
--health-retries=5
|
--health-retries=5
|
||||||
-e MYSQL_ROOT_PASSWORD=root
|
-e MYSQL_ROOT_PASSWORD=root
|
||||||
-e MYSQL_DATABASE=dibi_test
|
-e MYSQL_DATABASE=dibi_test
|
||||||
--entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
|
||||||
|
|
||||||
postgres96:
|
postgres96:
|
||||||
image: postgres:9.6
|
image: postgres:9.6
|
||||||
@@ -83,13 +82,13 @@ jobs:
|
|||||||
- 1433:1433
|
- 1433:1433
|
||||||
options: >-
|
options: >-
|
||||||
--name=mssql
|
--name=mssql
|
||||||
--health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'"
|
--health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1' -N -C"
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
@@ -101,7 +100,7 @@ jobs:
|
|||||||
run: cp ./tests/databases.github.ini ./tests/databases.ini
|
run: cp ./tests/databases.github.ini ./tests/databases.ini
|
||||||
|
|
||||||
- name: Create MS SQL Database
|
- name: Create MS SQL Database
|
||||||
run: docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test'
|
run: docker exec -i mssql /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test' -N -C
|
||||||
|
|
||||||
- run: composer install --no-progress --prefer-dist
|
- run: composer install --no-progress --prefer-dist
|
||||||
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
|
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
|
||||||
|
@@ -11,13 +11,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "8.0 - 8.3"
|
"php": "8.0 - 8.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"tracy/tracy": "^2.9",
|
"tracy/tracy": "^2.9",
|
||||||
"nette/tester": "^2.5",
|
"nette/tester": "^2.5",
|
||||||
"nette/di": "^3.1",
|
"nette/di": "^3.1",
|
||||||
"phpstan/phpstan": "^1.0"
|
"phpstan/phpstan": "^1.0",
|
||||||
|
"jetbrains/phpstorm-attributes": "^1.0"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
"dg/dibi": "*"
|
"dg/dibi": "*"
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": ["src/"]
|
"classmap": ["src/"]
|
||||||
},
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"phpstan": "phpstan analyse",
|
"phpstan": "phpstan analyse",
|
||||||
"tester": "tester tests -s"
|
"tester": "tester tests -s"
|
||||||
|
@@ -27,9 +27,9 @@ $dibi = new Dibi\Connection([
|
|||||||
// using manual hints
|
// using manual hints
|
||||||
$res = $dibi->query('SELECT * FROM [customers]');
|
$res = $dibi->query('SELECT * FROM [customers]');
|
||||||
|
|
||||||
$res->setType('customer_id', Type::INTEGER)
|
$res->setType('customer_id', Type::Integer)
|
||||||
->setType('added', Type::DATETIME)
|
->setType('added', Type::DateTime)
|
||||||
->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
->setFormat(Type::DateTime, 'Y-m-d H:i:s');
|
||||||
|
|
||||||
|
|
||||||
Tracy\Dumper::dump($res->fetch());
|
Tracy\Dumper::dump($res->fetch());
|
||||||
|
@@ -34,7 +34,7 @@ Install Dibi via Composer:
|
|||||||
composer require dibi/dibi
|
composer require dibi/dibi
|
||||||
```
|
```
|
||||||
|
|
||||||
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.3.
|
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.4.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
@@ -608,7 +608,7 @@ $database->query("UPDATE [:blog:items] SET [text]='Hello World'");
|
|||||||
Dibi automatically detects the types of query columns and converts fields them to native PHP types. We can also specify the type manually. You can find the possible types in the `Dibi\Type` class.
|
Dibi automatically detects the types of query columns and converts fields them to native PHP types. We can also specify the type manually. You can find the possible types in the `Dibi\Type` class.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$result->setType('id', Dibi\Type::INTEGER); // id will be integer
|
$result->setType('id', Dibi\Type::Integer); // id will be integer
|
||||||
$row = $result->fetch();
|
$row = $result->fetch();
|
||||||
|
|
||||||
is_int($row->id) // true
|
is_int($row->id) // true
|
||||||
@@ -639,7 +639,7 @@ In the configuration file, we will register the DI extensions and add the `dibi`
|
|||||||
|
|
||||||
```neon
|
```neon
|
||||||
extensions:
|
extensions:
|
||||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||||
|
|
||||||
dibi:
|
dibi:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
96
src/Dibi/Bridges/Nette/DibiExtension3.php
Normal file
96
src/Dibi/Bridges/Nette/DibiExtension3.php
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?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\Bridges\Nette;
|
||||||
|
|
||||||
|
use Dibi;
|
||||||
|
use Nette;
|
||||||
|
use Nette\Schema\Expect;
|
||||||
|
use Tracy;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dibi extension for Nette Framework 3. Creates 'connection' & 'panel' services.
|
||||||
|
*/
|
||||||
|
class DibiExtension3 extends Nette\DI\CompilerExtension
|
||||||
|
{
|
||||||
|
private ?bool $debugMode;
|
||||||
|
private ?bool $cliMode;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(?bool $debugMode = null, ?bool $cliMode = null)
|
||||||
|
{
|
||||||
|
$this->debugMode = $debugMode;
|
||||||
|
$this->cliMode = $cliMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getConfigSchema(): Nette\Schema\Schema
|
||||||
|
{
|
||||||
|
return Expect::structure([
|
||||||
|
'autowired' => Expect::bool(true),
|
||||||
|
'flags' => Expect::anyOf(Expect::arrayOf('string'), Expect::type('dynamic')),
|
||||||
|
'profiler' => Expect::bool(),
|
||||||
|
'explain' => Expect::bool(true),
|
||||||
|
'filter' => Expect::bool(true),
|
||||||
|
'driver' => Expect::string()->dynamic(),
|
||||||
|
'name' => Expect::string()->dynamic(),
|
||||||
|
'lazy' => Expect::bool(false)->dynamic(),
|
||||||
|
'onConnect' => Expect::array()->dynamic(),
|
||||||
|
'substitutes' => Expect::arrayOf('string')->dynamic(),
|
||||||
|
'result' => Expect::structure([
|
||||||
|
'normalize' => Expect::bool(true),
|
||||||
|
'formatDateTime' => Expect::string(),
|
||||||
|
'formatTimeInterval' => Expect::string(),
|
||||||
|
'formatJson' => Expect::string(),
|
||||||
|
])->castTo('array'),
|
||||||
|
])->otherItems(Expect::type('mixed'))
|
||||||
|
->castTo('array');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function loadConfiguration()
|
||||||
|
{
|
||||||
|
$container = $this->getContainerBuilder();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$this->debugMode ??= $container->parameters['debugMode'];
|
||||||
|
$this->cliMode ??= $container->parameters['consoleMode'];
|
||||||
|
|
||||||
|
$useProfiler = $config['profiler'] ?? (class_exists(Tracy\Debugger::class) && $this->debugMode && !$this->cliMode);
|
||||||
|
unset($config['profiler']);
|
||||||
|
|
||||||
|
if (is_array($config['flags'])) {
|
||||||
|
$flags = 0;
|
||||||
|
foreach ((array) $config['flags'] as $flag) {
|
||||||
|
$flags |= constant($flag);
|
||||||
|
}
|
||||||
|
$config['flags'] = $flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
$connection = $container->addDefinition($this->prefix('connection'))
|
||||||
|
->setCreator(Dibi\Connection::class, [$config])
|
||||||
|
->setAutowired($config['autowired']);
|
||||||
|
|
||||||
|
if (class_exists(Tracy\Debugger::class)) {
|
||||||
|
$connection->addSetup(
|
||||||
|
[new Nette\DI\Definitions\Statement('Tracy\Debugger::getBlueScreen'), 'addPanel'],
|
||||||
|
[[Dibi\Bridges\Tracy\Panel::class, 'renderException']],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($useProfiler) {
|
||||||
|
$panel = $container->addDefinition($this->prefix('panel'))
|
||||||
|
->setCreator(Dibi\Bridges\Tracy\Panel::class, [
|
||||||
|
$config['explain'],
|
||||||
|
$config['filter'] ? Dibi\Event::QUERY : Dibi\Event::ALL,
|
||||||
|
]);
|
||||||
|
$connection->addSetup([$panel, 'register'], [$connection]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
# This will create service named 'dibi.connection'.
|
# This will create service named 'dibi.connection'.
|
||||||
# Requires Nette Framework 2.2 or later
|
# Requires Nette Framework 3 or later
|
||||||
|
|
||||||
extensions:
|
extensions:
|
||||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||||
|
|
||||||
dibi:
|
dibi:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
@@ -62,7 +62,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
if ($e instanceof Dibi\Exception && $e->getSql()) {
|
if ($e instanceof Dibi\Exception && $e->getSql()) {
|
||||||
return [
|
return [
|
||||||
'tab' => 'SQL',
|
'tab' => 'SQL',
|
||||||
'panel' => Helpers::dump($e->getSql(), true),
|
'panel' => Helpers::dump($e->getSql(), return: true),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
? $this->explain
|
? $this->explain
|
||||||
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
||||||
try {
|
try {
|
||||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), return: true);
|
||||||
} catch (Dibi\Exception $e) {
|
} catch (Dibi\Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
|
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, true);
|
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, return: true);
|
||||||
if ($explain) {
|
if ($explain) {
|
||||||
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
||||||
}
|
}
|
||||||
@@ -151,8 +151,8 @@ class Panel implements Tracy\IBarPanel
|
|||||||
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
|
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
|
||||||
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
|
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
|
||||||
<h1>Queries:' . "\u{a0}" . count($this->events)
|
<h1>Queries:' . "\u{a0}" . count($this->events)
|
||||||
. ($totalTime === null ? '' : ", time:\u{a0}" . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms") . ', '
|
. ($totalTime === null ? '' : ", time:\u{a0}" . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms")
|
||||||
. htmlspecialchars($this->getConnectionName($singleConnection)) . '</h1>
|
. ($singleConnection === null ? '' : ', ' . htmlspecialchars($this->getConnectionName($singleConnection))) . '</h1>
|
||||||
<div class="tracy-inner tracy-DibiProfiler">
|
<div class="tracy-inner tracy-DibiProfiler">
|
||||||
<table class="tracy-sortable">
|
<table class="tracy-sortable">
|
||||||
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
|
<tr><th>Time ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
|
||||||
|
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use JetBrains\PhpStorm\Language;
|
||||||
use Traversable;
|
use Traversable;
|
||||||
|
|
||||||
|
|
||||||
@@ -73,10 +74,10 @@ class Connection implements IConnection
|
|||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
|
||||||
$this->formats = [
|
$this->formats = [
|
||||||
Type::DATE => $this->config['result']['formatDate'],
|
Type::Date => $this->config['result']['formatDate'],
|
||||||
Type::DATETIME => $this->config['result']['formatDateTime'],
|
Type::DateTime => $this->config['result']['formatDateTime'],
|
||||||
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
||||||
Type::TIME_INTERVAL => $this->config['result']['formatTimeInterval'] ?? null,
|
Type::TimeInterval => $this->config['result']['formatTimeInterval'] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// profiler
|
// profiler
|
||||||
@@ -209,7 +210,7 @@ class Connection implements IConnection
|
|||||||
* Generates (translates) and executes SQL query.
|
* Generates (translates) and executes SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function query(mixed ...$args): Result
|
final public function query(#[Language('GenericSQL')] mixed ...$args): Result
|
||||||
{
|
{
|
||||||
return $this->nativeQuery($this->translate(...$args));
|
return $this->nativeQuery($this->translate(...$args));
|
||||||
}
|
}
|
||||||
@@ -219,7 +220,7 @@ class Connection implements IConnection
|
|||||||
* Generates SQL query.
|
* Generates SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function translate(mixed ...$args): string
|
final public function translate(#[Language('GenericSQL')] mixed ...$args): string
|
||||||
{
|
{
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
@@ -232,7 +233,7 @@ class Connection implements IConnection
|
|||||||
/**
|
/**
|
||||||
* Generates and prints SQL query.
|
* Generates and prints SQL query.
|
||||||
*/
|
*/
|
||||||
final public function test(mixed ...$args): bool
|
final public function test(#[Language('GenericSQL')] mixed ...$args): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Helpers::dump($this->translate(...$args));
|
Helpers::dump($this->translate(...$args));
|
||||||
@@ -254,7 +255,7 @@ class Connection implements IConnection
|
|||||||
* Generates (translates) and returns SQL query as DataSource.
|
* Generates (translates) and returns SQL query as DataSource.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function dataSource(mixed ...$args): DataSource
|
final public function dataSource(#[Language('GenericSQL')] mixed ...$args): DataSource
|
||||||
{
|
{
|
||||||
return new DataSource($this->translate(...$args), $this);
|
return new DataSource($this->translate(...$args), $this);
|
||||||
}
|
}
|
||||||
@@ -264,7 +265,7 @@ class Connection implements IConnection
|
|||||||
* Executes the SQL query.
|
* Executes the SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function nativeQuery(string $sql): Result
|
final public function nativeQuery(#[Language('SQL')] string $sql): Result
|
||||||
{
|
{
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
@@ -593,7 +594,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetch(mixed ...$args): ?Row
|
public function fetch(#[Language('GenericSQL')] mixed ...$args): ?Row
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetch();
|
return $this->query($args)->fetch();
|
||||||
}
|
}
|
||||||
@@ -604,7 +605,7 @@ class Connection implements IConnection
|
|||||||
* @return Row[]|array[]
|
* @return Row[]|array[]
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchAll(mixed ...$args): array
|
public function fetchAll(#[Language('GenericSQL')] mixed ...$args): array
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchAll();
|
return $this->query($args)->fetchAll();
|
||||||
}
|
}
|
||||||
@@ -614,7 +615,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchSingle(mixed ...$args): mixed
|
public function fetchSingle(#[Language('GenericSQL')] mixed ...$args): mixed
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchSingle();
|
return $this->query($args)->fetchSingle();
|
||||||
}
|
}
|
||||||
@@ -624,7 +625,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchPairs(mixed ...$args): array
|
public function fetchPairs(#[Language('GenericSQL')] mixed ...$args): array
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchPairs();
|
return $this->query($args)->fetchPairs();
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,10 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class FirebirdDriver implements Dibi\Driver
|
class FirebirdDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
public const ERROR_EXCEPTION_THROWN = -836;
|
public const ErrorExceptionThrown = -836;
|
||||||
|
|
||||||
|
/** @deprecated use FirebirdDriver::ErrorExceptionThrown */
|
||||||
|
public const ERROR_EXCEPTION_THROWN = self::ErrorExceptionThrown;
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
@@ -32,9 +32,18 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqliDriver implements Dibi\Driver
|
class MySqliDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
public const ERROR_ACCESS_DENIED = 1045;
|
public const ErrorAccessDenied = 1045;
|
||||||
public const ERROR_DUPLICATE_ENTRY = 1062;
|
public const ErrorDuplicateEntry = 1062;
|
||||||
public const ERROR_DATA_TRUNCATED = 1265;
|
public const ErrorDataTruncated = 1265;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorAccessDenied */
|
||||||
|
public const ERROR_ACCESS_DENIED = self::ErrorAccessDenied;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorDuplicateEntry */
|
||||||
|
public const ERROR_DUPLICATE_ENTRY = self::ErrorDuplicateEntry;
|
||||||
|
|
||||||
|
/** @deprecated use MySqliDriver::ErrorDataTruncated */
|
||||||
|
public const ERROR_DATA_TRUNCATED = self::ErrorDataTruncated;
|
||||||
|
|
||||||
private \mysqli $connection;
|
private \mysqli $connection;
|
||||||
private bool $buffered = false;
|
private bool $buffered = false;
|
||||||
|
@@ -103,7 +103,7 @@ class MySqliResult implements Dibi\ResultDriver
|
|||||||
'table' => $row['orgtable'],
|
'table' => $row['orgtable'],
|
||||||
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
||||||
'nativetype' => $types[$row['type']] ?? $row['type'],
|
'nativetype' => $types[$row['type']] ?? $row['type'],
|
||||||
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : null,
|
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TimeInterval : null,
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -119,7 +119,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function begin(?string $savepoint = null): void
|
public function begin(?string $savepoint = null): void
|
||||||
{
|
{
|
||||||
if (!odbc_autocommit($this->connection, false)) {
|
if (!odbc_autocommit($this->connection)) {
|
||||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -80,7 +80,7 @@ class OracleResult implements Dibi\ResultDriver
|
|||||||
'name' => oci_field_name($this->resultSet, $i),
|
'name' => oci_field_name($this->resultSet, $i),
|
||||||
'table' => null,
|
'table' => null,
|
||||||
'fullname' => oci_field_name($this->resultSet, $i),
|
'fullname' => oci_field_name($this->resultSet, $i),
|
||||||
'type' => $type === 'LONG' ? Dibi\Type::TEXT : null,
|
'type' => $type === 'LONG' ? Dibi\Type::Text : null,
|
||||||
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
|
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -93,23 +93,15 @@ class PdoDriver implements Dibi\Driver
|
|||||||
$this->affectedRows = null;
|
$this->affectedRows = null;
|
||||||
|
|
||||||
[$sqlState, $code, $message] = $this->connection->errorInfo();
|
[$sqlState, $code, $message] = $this->connection->errorInfo();
|
||||||
|
$code ??= 0;
|
||||||
$message = "SQLSTATE[$sqlState]: $message";
|
$message = "SQLSTATE[$sqlState]: $message";
|
||||||
switch ($this->driverName) {
|
throw match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => MySqliDriver::createException($message, $code, $sql),
|
||||||
throw MySqliDriver::createException($message, $code, $sql);
|
'oci' => OracleDriver::createException($message, $code, $sql),
|
||||||
|
'pgsql' => PostgreDriver::createException($message, $sqlState, $sql),
|
||||||
case 'oci':
|
'sqlite' => SqliteDriver::createException($message, $code, $sql),
|
||||||
throw OracleDriver::createException($message, $code, $sql);
|
default => new Dibi\DriverException($message, $code, $sql),
|
||||||
|
};
|
||||||
case 'pgsql':
|
|
||||||
throw PostgreDriver::createException($message, $sqlState, $sql);
|
|
||||||
|
|
||||||
case 'sqlite':
|
|
||||||
throw SqliteDriver::createException($message, $code, $sql);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Dibi\DriverException($message, $code, $sql);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +131,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->beginTransaction()) {
|
if (!$this->connection->beginTransaction()) {
|
||||||
$err = $this->connection->errorInfo();
|
$err = $this->connection->errorInfo();
|
||||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +144,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->commit()) {
|
if (!$this->connection->commit()) {
|
||||||
$err = $this->connection->errorInfo();
|
$err = $this->connection->errorInfo();
|
||||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +157,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->rollBack()) {
|
if (!$this->connection->rollBack()) {
|
||||||
$err = $this->connection->errorInfo();
|
$err = $this->connection->errorInfo();
|
||||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,27 +176,14 @@ class PdoDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function getReflector(): Dibi\Reflector
|
public function getReflector(): Dibi\Reflector
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => new MySqlReflector($this),
|
||||||
return new MySqlReflector($this);
|
'oci' => new OracleReflector($this),
|
||||||
|
'pgsql' => new PostgreReflector($this, $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION)),
|
||||||
case 'oci':
|
'sqlite' => new SqliteReflector($this),
|
||||||
return new OracleReflector($this);
|
'mssql', 'dblib', 'sqlsrv' => new SqlsrvReflector($this),
|
||||||
|
default => throw new Dibi\NotSupportedException,
|
||||||
case 'pgsql':
|
};
|
||||||
return new PostgreReflector($this, $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION));
|
|
||||||
|
|
||||||
case 'sqlite':
|
|
||||||
return new SqliteReflector($this);
|
|
||||||
|
|
||||||
case 'mssql':
|
|
||||||
case 'dblib':
|
|
||||||
case 'sqlsrv':
|
|
||||||
return new SqlsrvReflector($this);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Dibi\NotSupportedException;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -225,44 +204,34 @@ class PdoDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function escapeText(string $value): string
|
public function escapeText(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
return match ($this->driverName) {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
'odbc' => "'" . str_replace("'", "''", $value) . "'",
|
||||||
: $this->connection->quote($value, PDO::PARAM_STR);
|
'sqlsrv' => "N'" . str_replace("'", "''", $value) . "'",
|
||||||
|
default => $this->connection->quote($value, PDO::PARAM_STR),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function escapeBinary(string $value): string
|
public function escapeBinary(string $value): string
|
||||||
{
|
{
|
||||||
return $this->driverName === 'odbc'
|
return match ($this->driverName) {
|
||||||
? "'" . str_replace("'", "''", $value) . "'"
|
'odbc' => "'" . str_replace("'", "''", $value) . "'",
|
||||||
: $this->connection->quote($value, PDO::PARAM_LOB);
|
'sqlsrv' => '0x' . bin2hex($value),
|
||||||
|
default => $this->connection->quote($value, PDO::PARAM_LOB),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function escapeIdentifier(string $value): string
|
public function escapeIdentifier(string $value): string
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => '`' . str_replace('`', '``', $value) . '`',
|
||||||
return '`' . str_replace('`', '``', $value) . '`';
|
'oci', 'pgsql' => '"' . str_replace('"', '""', $value) . '"',
|
||||||
|
'sqlite' => '[' . strtr($value, '[]', ' ') . ']',
|
||||||
case 'oci':
|
'odbc', 'mssql' => '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']',
|
||||||
case 'pgsql':
|
'dblib', 'sqlsrv' => '[' . str_replace(']', ']]', $value) . ']',
|
||||||
return '"' . str_replace('"', '""', $value) . '"';
|
default => $value,
|
||||||
|
};
|
||||||
case 'sqlite':
|
|
||||||
return '[' . strtr($value, '[]', ' ') . ']';
|
|
||||||
|
|
||||||
case 'odbc':
|
|
||||||
case 'mssql':
|
|
||||||
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
|
|
||||||
|
|
||||||
case 'dblib':
|
|
||||||
case 'sqlsrv':
|
|
||||||
return '[' . str_replace(']', ']]', $value) . ']';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -284,16 +253,11 @@ class PdoDriver implements Dibi\Driver
|
|||||||
|
|
||||||
public function escapeDateTime(\DateTimeInterface $value): string
|
public function escapeDateTime(\DateTimeInterface $value): string
|
||||||
{
|
{
|
||||||
switch ($this->driverName) {
|
return match ($this->driverName) {
|
||||||
case 'odbc':
|
'odbc' => $value->format('#m/d/Y H:i:s.u#'),
|
||||||
return $value->format('#m/d/Y H:i:s.u#');
|
'mssql', 'dblib', 'sqlsrv' => 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')',
|
||||||
case 'mssql':
|
default => $value->format("'Y-m-d H:i:s.u'"),
|
||||||
case 'dblib':
|
};
|
||||||
case 'sqlsrv':
|
|
||||||
return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')';
|
|
||||||
default:
|
|
||||||
return $value->format("'Y-m-d H:i:s.u'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -90,7 +90,7 @@ class PdoResult implements Dibi\ResultDriver
|
|||||||
'name' => $row['name'],
|
'name' => $row['name'],
|
||||||
'table' => $row['table'],
|
'table' => $row['table'],
|
||||||
'nativetype' => $row['native_type'],
|
'nativetype' => $row['native_type'],
|
||||||
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : null,
|
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TimeInterval : null,
|
||||||
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
|
@@ -72,13 +72,6 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
public function getColumns(string $table): array
|
public function getColumns(string $table): array
|
||||||
{
|
{
|
||||||
$_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
|
$_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
|
||||||
$res = $this->driver->query("
|
|
||||||
SELECT indkey
|
|
||||||
FROM pg_class
|
|
||||||
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
|
|
||||||
WHERE pg_class.oid = $_table::regclass
|
|
||||||
");
|
|
||||||
$primary = (int) $res->fetch(true)['indkey'];
|
|
||||||
|
|
||||||
$res = $this->driver->query("
|
$res = $this->driver->query("
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -98,7 +91,8 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
a.atttypmod-4 AS character_maximum_length,
|
a.atttypmod-4 AS character_maximum_length,
|
||||||
NOT a.attnotnull AS is_nullable,
|
NOT a.attnotnull AS is_nullable,
|
||||||
a.attnum AS ordinal_position,
|
a.attnum AS ordinal_position,
|
||||||
pg_get_expr(adef.adbin, adef.adrelid) AS column_default
|
pg_get_expr(adef.adbin, adef.adrelid) AS column_default,
|
||||||
|
CASE WHEN a.attidentity IN ('a', 'd') THEN 'YES' ELSE 'NO' END AS is_identity
|
||||||
FROM
|
FROM
|
||||||
pg_attribute a
|
pg_attribute a
|
||||||
JOIN pg_type ON a.atttypid = pg_type.oid
|
JOIN pg_type ON a.atttypid = pg_type.oid
|
||||||
@@ -123,7 +117,7 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
'size' => $size > 0 ? $size : null,
|
'size' => $size > 0 ? $size : null,
|
||||||
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
|
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
|
||||||
'default' => $row['column_default'],
|
'default' => $row['column_default'],
|
||||||
'autoincrement' => (int) $row['ordinal_position'] === $primary && str_starts_with($row['column_default'] ?? '', 'nextval'),
|
'autoincrement' => $row['is_identity'] === 'YES' || str_starts_with($row['column_default'] ?? '', 'nextval('),
|
||||||
'vendor' => $row,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,13 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Fluent implements IDataSource
|
class Fluent implements IDataSource
|
||||||
{
|
{
|
||||||
public const REMOVE = false;
|
public const
|
||||||
|
AffectedRows = 'a',
|
||||||
|
Identifier = 'n',
|
||||||
|
Remove = false;
|
||||||
|
|
||||||
|
/** @deprecated use Fluent::Remove */
|
||||||
|
public const REMOVE = self::Remove;
|
||||||
|
|
||||||
public static array $masks = [
|
public static array $masks = [
|
||||||
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
|
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
|
||||||
@@ -140,7 +146,7 @@ class Fluent implements IDataSource
|
|||||||
$this->cursor = &$this->clauses[$clause];
|
$this->cursor = &$this->clauses[$clause];
|
||||||
|
|
||||||
// TODO: really delete?
|
// TODO: really delete?
|
||||||
if ($args === [self::REMOVE]) {
|
if ($args === [self::Remove]) {
|
||||||
$this->cursor = null;
|
$this->cursor = null;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -156,7 +162,7 @@ class Fluent implements IDataSource
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// append to currect flow
|
// append to currect flow
|
||||||
if ($args === [self::REMOVE]) {
|
if ($args === [self::Remove]) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,19 +285,17 @@ class Fluent implements IDataSource
|
|||||||
/**
|
/**
|
||||||
* Generates and executes SQL query.
|
* Generates and executes SQL query.
|
||||||
* Returns result set or number of affected rows
|
* Returns result set or number of affected rows
|
||||||
|
* @return ($return is self::Identifier|self::AffectedRows ? int : Result)
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function execute(?string $return = null): Result|int|null
|
public function execute(?string $return = null): Result|int|null
|
||||||
{
|
{
|
||||||
$res = $this->query($this->_export());
|
$res = $this->query($this->_export());
|
||||||
switch ($return) {
|
return match ($return) {
|
||||||
case \dibi::IDENTIFIER:
|
self::Identifier => $this->connection->getInsertId(),
|
||||||
return $this->connection->getInsertId();
|
self::AffectedRows => $this->connection->getAffectedRows(),
|
||||||
case \dibi::AFFECTED_ROWS:
|
default => $res,
|
||||||
return $this->connection->getAffectedRows();
|
};
|
||||||
default:
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -46,7 +46,7 @@ abstract class HashMapBase
|
|||||||
*/
|
*/
|
||||||
final class HashMap extends HashMapBase
|
final class HashMap extends HashMapBase
|
||||||
{
|
{
|
||||||
public function __set(string $nm, $val)
|
public function __set(string $nm, mixed $val): void
|
||||||
{
|
{
|
||||||
if ($nm === '') {
|
if ($nm === '') {
|
||||||
$nm = "\xFF";
|
$nm = "\xFF";
|
||||||
@@ -56,7 +56,7 @@ final class HashMap extends HashMapBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function __get(string $nm)
|
public function __get(string $nm): mixed
|
||||||
{
|
{
|
||||||
if ($nm === '') {
|
if ($nm === '') {
|
||||||
$nm = "\xFF";
|
$nm = "\xFF";
|
||||||
|
@@ -159,12 +159,12 @@ class Helpers
|
|||||||
public static function escape(Driver $driver, $value, string $type): string
|
public static function escape(Driver $driver, $value, string $type): string
|
||||||
{
|
{
|
||||||
$types = [
|
$types = [
|
||||||
Type::TEXT => 'text',
|
Type::Text => 'text',
|
||||||
Type::BINARY => 'binary',
|
Type::Binary => 'binary',
|
||||||
Type::BOOL => 'bool',
|
Type::Bool => 'bool',
|
||||||
Type::DATE => 'date',
|
Type::Date => 'date',
|
||||||
Type::DATETIME => 'datetime',
|
Type::DateTime => 'datetime',
|
||||||
\dibi::IDENTIFIER => 'identifier',
|
Fluent::Identifier => 'identifier',
|
||||||
];
|
];
|
||||||
if (isset($types[$type])) {
|
if (isset($types[$type])) {
|
||||||
return $driver->{'escape' . $types[$type]}($value);
|
return $driver->{'escape' . $types[$type]}($value);
|
||||||
@@ -181,16 +181,16 @@ class Helpers
|
|||||||
public static function detectType(string $type): ?string
|
public static function detectType(string $type): ?string
|
||||||
{
|
{
|
||||||
$patterns = [
|
$patterns = [
|
||||||
'^_' => Type::TEXT, // PostgreSQL arrays
|
'^_' => Type::Text, // PostgreSQL arrays
|
||||||
'RANGE$' => Type::TEXT, // PostgreSQL range types
|
'RANGE$' => Type::Text, // PostgreSQL range types
|
||||||
'BYTEA|BLOB|BIN' => Type::BINARY,
|
'BYTEA|BLOB|BIN' => Type::Binary,
|
||||||
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
|
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::Text,
|
||||||
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
|
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::Integer,
|
||||||
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
|
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::Float,
|
||||||
'^TIME$' => Type::TIME,
|
'^TIME$' => Type::Time,
|
||||||
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
|
'TIME' => Type::DateTime, // DATETIME, TIMESTAMP
|
||||||
'DATE' => Type::DATE,
|
'DATE' => Type::Date,
|
||||||
'BOOL' => Type::BOOL,
|
'BOOL' => Type::Bool,
|
||||||
'JSON' => Type::JSON,
|
'JSON' => Type::JSON,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -457,16 +457,22 @@ class Result implements IDataSource
|
|||||||
if ($type === null || $format === 'native') {
|
if ($type === null || $format === 'native') {
|
||||||
$row[$key] = $value;
|
$row[$key] = $value;
|
||||||
|
|
||||||
} elseif ($type === Type::TEXT) {
|
} elseif ($type === Type::Text) {
|
||||||
$row[$key] = (string) $value;
|
$row[$key] = (string) $value;
|
||||||
|
|
||||||
} elseif ($type === Type::INTEGER) {
|
} elseif ($type === Type::Integer) {
|
||||||
$row[$key] = is_float($tmp = $value * 1)
|
$row[$key] = is_float($tmp = $value * 1)
|
||||||
? (is_string($value) ? $value : (int) $value)
|
? (is_string($value) ? $value : (int) $value)
|
||||||
: $tmp;
|
: $tmp;
|
||||||
|
|
||||||
} elseif ($type === Type::FLOAT) {
|
} elseif ($type === Type::Float) {
|
||||||
$value = ltrim((string) $value, '0');
|
if (!is_string($value)) {
|
||||||
|
$row[$key] = (float) $value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$negative = ($value[0] ?? null) === '-';
|
||||||
|
$value = ltrim($value, '0-');
|
||||||
$p = strpos($value, '.');
|
$p = strpos($value, '.');
|
||||||
$e = strpos($value, 'e');
|
$e = strpos($value, 'e');
|
||||||
if ($p !== false && $e === false) {
|
if ($p !== false && $e === false) {
|
||||||
@@ -479,27 +485,31 @@ class Result implements IDataSource
|
|||||||
$value = '0' . $value;
|
$value = '0' . $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$value = '-' . $value;
|
||||||
|
}
|
||||||
|
|
||||||
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
|
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
|
||||||
? $float
|
? $float
|
||||||
: $value;
|
: $value;
|
||||||
|
|
||||||
} elseif ($type === Type::BOOL) {
|
} elseif ($type === Type::Bool) {
|
||||||
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
||||||
|
|
||||||
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
} elseif ($type === Type::DateTime || $type === Type::Date || $type === Type::Time) {
|
||||||
if ($value && !str_starts_with((string) $value, '0000-00')) { // '', null, false, '0000-00-00', ...
|
if ($value && !str_starts_with((string) $value, '0000-00')) { // '', null, false, '0000-00-00', ...
|
||||||
$value = new DateTime($value);
|
$value = new DateTime($value);
|
||||||
$row[$key] = $format ? $value->format($format) : $value;
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = null;
|
$row[$key] = null;
|
||||||
}
|
}
|
||||||
} elseif ($type === Type::TIME_INTERVAL) {
|
} elseif ($type === Type::TimeInterval) {
|
||||||
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
||||||
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
||||||
$value->invert = (int) (bool) $m[1];
|
$value->invert = (int) (bool) $m[1];
|
||||||
$row[$key] = $format ? $value->format($format) : $value;
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
|
|
||||||
} elseif ($type === Type::BINARY) {
|
} elseif ($type === Type::Binary) {
|
||||||
$row[$key] = is_string($value)
|
$row[$key] = is_string($value)
|
||||||
? $this->getResultDriver()->unescapeBinary($value)
|
? $this->getResultDriver()->unescapeBinary($value)
|
||||||
: $value;
|
: $value;
|
||||||
|
@@ -48,10 +48,11 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function __get(string $key)
|
public function __get(string $key): mixed
|
||||||
{
|
{
|
||||||
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
|
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
|
||||||
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
|
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -219,7 +219,7 @@ final class Translator
|
|||||||
|
|
||||||
case 'a': // key=val, key=val, ...
|
case 'a': // key=val, key=val, ...
|
||||||
foreach ($value as $k => $v) {
|
foreach ($value as $k => $v) {
|
||||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
$pair = explode('%', (string) $k, 2); // split into identifier & modifier
|
||||||
$vx[] = $this->identifiers->{$pair[0]} . '='
|
$vx[] = $this->identifiers->{$pair[0]} . '='
|
||||||
. $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
. $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
||||||
}
|
}
|
||||||
|
@@ -16,16 +16,43 @@ namespace Dibi;
|
|||||||
class Type
|
class Type
|
||||||
{
|
{
|
||||||
public const
|
public const
|
||||||
TEXT = 's', // as 'string'
|
Text = 's', // as 'string'
|
||||||
BINARY = 'bin',
|
Binary = 'bin',
|
||||||
JSON = 'json',
|
JSON = 'json',
|
||||||
BOOL = 'b',
|
Bool = 'b',
|
||||||
INTEGER = 'i',
|
Integer = 'i',
|
||||||
FLOAT = 'f',
|
Float = 'f',
|
||||||
DATE = 'd',
|
Date = 'd',
|
||||||
DATETIME = 'dt',
|
DateTime = 'dt',
|
||||||
TIME = 't',
|
Time = 't',
|
||||||
TIME_INTERVAL = 'ti';
|
TimeInterval = 'ti';
|
||||||
|
|
||||||
|
/** @deprecated use Type::Text */
|
||||||
|
public const TEXT = self::Text;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Binary */
|
||||||
|
public const BINARY = self::Binary;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Bool */
|
||||||
|
public const BOOL = self::Bool;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Integer */
|
||||||
|
public const INTEGER = self::Integer;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Float */
|
||||||
|
public const FLOAT = self::Float;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Date */
|
||||||
|
public const DATE = self::Date;
|
||||||
|
|
||||||
|
/** @deprecated use Type::DateTime */
|
||||||
|
public const DATETIME = self::DateTime;
|
||||||
|
|
||||||
|
/** @deprecated use Type::Time */
|
||||||
|
public const TIME = self::Time;
|
||||||
|
|
||||||
|
/** @deprecated use Type::TimeInterval */
|
||||||
|
public const TIME_INTERVAL = self::TimeInterval;
|
||||||
|
|
||||||
|
|
||||||
final public function __construct()
|
final public function __construct()
|
||||||
|
@@ -37,12 +37,16 @@ declare(strict_types=1);
|
|||||||
*/
|
*/
|
||||||
class dibi
|
class dibi
|
||||||
{
|
{
|
||||||
public const
|
public const Version = '5.0.2';
|
||||||
AFFECTED_ROWS = 'a',
|
|
||||||
IDENTIFIER = 'n';
|
|
||||||
|
|
||||||
/** version */
|
/** @deprecated use dibi::Version */
|
||||||
public const VERSION = '5.0.0';
|
public const VERSION = self::Version;
|
||||||
|
|
||||||
|
/** @deprecated use Dibi\Fluent::AffectedRows */
|
||||||
|
public const AFFECTED_ROWS = Dibi\Fluent::AffectedRows;
|
||||||
|
|
||||||
|
/** @deprecated use Dibi\Fluent::Identifier */
|
||||||
|
public const IDENTIFIER = Dibi\Fluent::Identifier;
|
||||||
|
|
||||||
/** sorting order */
|
/** sorting order */
|
||||||
public const
|
public const
|
||||||
|
@@ -74,7 +74,7 @@ Assert::same(
|
|||||||
(string) $fluent,
|
(string) $fluent,
|
||||||
);
|
);
|
||||||
|
|
||||||
$fluent->orderBy(Dibi\Fluent::REMOVE);
|
$fluent->orderBy(Dibi\Fluent::Remove);
|
||||||
|
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'),
|
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'),
|
||||||
|
@@ -27,8 +27,8 @@ class MockResult extends Dibi\Result
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TEXT);
|
$result->setType('col', Type::Text);
|
||||||
$result->setFormat(Type::TEXT, 'native');
|
$result->setFormat(Type::Text, 'native');
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => true], $result->test(['col' => true]));
|
Assert::same(['col' => true], $result->test(['col' => true]));
|
||||||
@@ -38,7 +38,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::BOOL);
|
$result->setType('col', Type::Bool);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => true], $result->test(['col' => true]));
|
Assert::same(['col' => true], $result->test(['col' => true]));
|
||||||
@@ -60,7 +60,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TEXT);
|
$result->setType('col', Type::Text);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => '1'], $result->test(['col' => true]));
|
Assert::same(['col' => '1'], $result->test(['col' => true]));
|
||||||
@@ -76,7 +76,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::FLOAT);
|
$result->setType('col', Type::Float);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => true]));
|
Assert::same(['col' => 1.0], $result->test(['col' => true]));
|
||||||
@@ -117,6 +117,37 @@ test('', function () {
|
|||||||
Assert::same(['col' => '1.1e+10'], $result->test(['col' => '001.1e+10']));
|
Assert::same(['col' => '1.1e+10'], $result->test(['col' => '001.1e+10']));
|
||||||
Assert::notSame(['col' => '1.1e+1'], $result->test(['col' => '1.1e+10']));
|
Assert::notSame(['col' => '1.1e+1'], $result->test(['col' => '1.1e+10']));
|
||||||
|
|
||||||
|
// negative
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.000']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.0']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.1']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-001.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-001.100']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-10']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-11']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-0010']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-0011']));
|
||||||
|
Assert::same(['col' => '-0.00000000000000000001'], $result->test(['col' => '-0.00000000000000000001']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-012345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890.000']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890.1'], $result->test(['col' => '-012345678901234567890.100']));
|
||||||
|
|
||||||
|
Assert::same(['col' => '-1.1e+10'], $result->test(['col' => '-1.1e+10']));
|
||||||
|
Assert::same(['col' => '-1.1e-10'], $result->test(['col' => '-1.1e-10']));
|
||||||
|
Assert::same(['col' => '-1.1e+10'], $result->test(['col' => '-001.1e+10']));
|
||||||
|
Assert::notSame(['col' => '-1.1e+1'], $result->test(['col' => '-1.1e+10']));
|
||||||
|
|
||||||
setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'deu_deu');
|
setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'deu_deu');
|
||||||
Assert::same(['col' => 0.0], $result->test(['col' => '']));
|
Assert::same(['col' => 0.0], $result->test(['col' => '']));
|
||||||
Assert::same(['col' => 0.0], $result->test(['col' => '0']));
|
Assert::same(['col' => 0.0], $result->test(['col' => '0']));
|
||||||
@@ -147,13 +178,45 @@ test('', function () {
|
|||||||
Assert::same(['col' => 0.0], $result->test(['col' => 0.0]));
|
Assert::same(['col' => 0.0], $result->test(['col' => 0.0]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => 1]));
|
Assert::same(['col' => 1.0], $result->test(['col' => 1]));
|
||||||
Assert::same(['col' => 1.0], $result->test(['col' => 1.0]));
|
Assert::same(['col' => 1.0], $result->test(['col' => 1.0]));
|
||||||
|
|
||||||
|
// Same but negative
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.0']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.1']));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => '-0.000']));
|
||||||
|
Assert::same(['col' => -0.1], $result->test(['col' => '-0.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.0']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.1']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-1.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-1.100']));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => '-001.000']));
|
||||||
|
Assert::same(['col' => -1.1], $result->test(['col' => '-001.100']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-10']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-11']));
|
||||||
|
Assert::same(['col' => -10.0], $result->test(['col' => '-0010']));
|
||||||
|
Assert::same(['col' => -11.0], $result->test(['col' => '-0011']));
|
||||||
|
Assert::same(['col' => '-0.00000000000000000001'], $result->test(['col' => '-0.00000000000000000001']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-012345678901234567890']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890'], $result->test(['col' => '-12345678901234567890.000']));
|
||||||
|
Assert::same(['col' => '-12345678901234567890.1'], $result->test(['col' => '-012345678901234567890.100']));
|
||||||
|
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => -0]));
|
||||||
|
Assert::same(['col' => -0.0], $result->test(['col' => -0.0]));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => -1]));
|
||||||
|
Assert::same(['col' => -1.0], $result->test(['col' => -1.0]));
|
||||||
|
|
||||||
setlocale(LC_NUMERIC, 'C');
|
setlocale(LC_NUMERIC, 'C');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::INTEGER);
|
$result->setType('col', Type::Integer);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::same(['col' => 1], $result->test(['col' => true]));
|
Assert::same(['col' => 1], $result->test(['col' => true]));
|
||||||
@@ -187,7 +250,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATETIME);
|
$result->setType('col', Type::DateTime);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -206,8 +269,8 @@ 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');
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -226,7 +289,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::DATE);
|
$result->setType('col', Type::Date);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
@@ -243,7 +306,7 @@ test('', function () {
|
|||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::TIME);
|
$result->setType('col', Type::Time);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
|
@@ -12,7 +12,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
|||||||
$res = $conn->query('SELECT * FROM [customers]');
|
$res = $conn->query('SELECT * FROM [customers]');
|
||||||
|
|
||||||
// auto-converts this column to integer
|
// auto-converts this column to integer
|
||||||
$res->setType('customer_id', Dibi\Type::DATETIME);
|
$res->setType('customer_id', Dibi\Type::DateTime);
|
||||||
|
|
||||||
Assert::equal(new Dibi\Row([
|
Assert::equal(new Dibi\Row([
|
||||||
'customer_id' => new Dibi\DateTime('1970-01-01 01:00:01'),
|
'customer_id' => new Dibi\DateTime('1970-01-01 01:00:01'),
|
||||||
|
@@ -24,13 +24,17 @@ Assert::true(isset($row['title']));
|
|||||||
|
|
||||||
|
|
||||||
// missing
|
// missing
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row->missing;
|
fn() => $x = $row->missing,
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'missing'.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row['missing'];
|
fn() => $x = $row['missing'],
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'missing'.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::false(isset($row->missing));
|
Assert::false(isset($row->missing));
|
||||||
Assert::false(isset($row['missing']));
|
Assert::false(isset($row['missing']));
|
||||||
@@ -41,13 +45,17 @@ Assert::same(123, $row['missing'] ?? 123);
|
|||||||
|
|
||||||
|
|
||||||
// suggestions
|
// suggestions
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row->tilte;
|
fn() => $x = $row->tilte,
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row['tilte'];
|
fn() => $x = $row['tilte'],
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// to array
|
// to array
|
||||||
|
@@ -33,7 +33,7 @@ enum PureEnum
|
|||||||
|
|
||||||
Assert::equal('1', $translator->formatValue(EnumInt::One, null));
|
Assert::equal('1', $translator->formatValue(EnumInt::One, null));
|
||||||
|
|
||||||
Assert::equal(match ($config['driver']) {
|
Assert::equal(match ($config['system']) {
|
||||||
'sqlsrv' => "N'one'",
|
'sqlsrv' => "N'one'",
|
||||||
default => "'one'",
|
default => "'one'",
|
||||||
}, $translator->formatValue(EnumString::One, null));
|
}, $translator->formatValue(EnumString::One, null));
|
||||||
|
@@ -20,7 +20,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
try {
|
try {
|
||||||
$config = Tester\Environment::loadData();
|
$config = Tester\Environment::loadData();
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$config = parse_ini_file(__DIR__ . '/../databases.ini', true);
|
$config = parse_ini_file(__DIR__ . '/../databases.ini', process_sections: true);
|
||||||
$config = reset($config);
|
$config = reset($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user