1
0
mirror of https://github.com/dg/dibi.git synced 2025-08-30 17:29:53 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
David Grudl
7a775aad4f fetchSingle returns FALSE 2018-05-17 13:27:45 +02:00
52 changed files with 503 additions and 775 deletions

View File

@@ -6,12 +6,14 @@ about: "If you would like to support our efforts in maintaining this project
--------------^ Click "Preview" for a nicer view!
> https://nette.org/donate
Help support Dibi!
We develop Dibi for more than 14 years. In order to make your life more comfortable. Dibi cares about the safety of your sites. Dibi saves you time. Dibi earns you money. And is absolutely free.
To ensure future development and improving the documentation, we need your donation.
**[Please make a donation now](https://nette.org/make-donation?to=dibi)**.
[Please make a donation now](https://nette.org/donate).
Thank you!

View File

@@ -2,12 +2,6 @@ language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
services:
- mysql
- postgresql
before_install:
# turn off XDebug
@@ -31,27 +25,16 @@ after_failure:
jobs:
include:
- name: Nette Code Checker
- stage: Code Standard Checker
php: 7.1
install:
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
# Install Nette Code Checker
- travis_retry composer create-project nette/code-checker temp/code-checker ~2 --no-progress
# Install Nette Coding Standard
- travis_retry composer create-project nette/coding-standard temp/coding-standard --no-progress
script:
- php temp/code-checker/code-checker --strict-types
- name: Nette Coding Standard
install:
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
script:
- php temp/coding-standard/ecs check src tests examples --config tests/coding-standard.yml
- stage: Static Analysis (informative)
install:
# Install PHPStan
- travis_retry composer create-project phpstan/phpstan-shim temp/phpstan --no-progress
- travis_retry composer install --no-progress --prefer-dist
script:
- php temp/phpstan/phpstan.phar analyse --autoload-file vendor/autoload.php --level 5 src
- php temp/code-checker/src/code-checker.php --short-arrays --strict-types
- php temp/coding-standard/ecs check src tests examples --config tests/coding-standard.neon
- stage: Code Coverage
@@ -63,7 +46,7 @@ jobs:
allow_failures:
- stage: Static Analysis (informative)
- php: 7.2
- stage: Code Coverage

View File

@@ -16,7 +16,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
echo '<p>Connecting to Sqlite: ';
try {
dibi::connect([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
echo 'OK';
@@ -30,7 +30,7 @@ echo "</p>\n";
echo '<p>Connecting to Sqlite: ';
try {
$connection = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
echo 'OK';

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -15,7 +15,7 @@ Tracy\Debugger::enable();
<?php
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -19,7 +19,7 @@ date_default_timezone_set('Europe/Prague');
<?php
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -11,7 +11,7 @@ Tracy\Debugger::enable();
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -11,7 +11,7 @@ Tracy\Debugger::enable();
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -16,7 +16,7 @@ date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'formatDate' => "'Y-m-d'",
'formatDateTime' => "'Y-m-d H-i-s'",

View File

@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
// enable query logging to this file
'profiler' => [

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
$dibi = new Dibi\Connection([
'driver' => 'sqlite',
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);

View File

@@ -1,4 +1,4 @@
[Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://nette.org/make-donation?to=dibi)
[Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
=========================================================
[![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi)
@@ -14,8 +14,6 @@ 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.
If you like Dibi, **[please make a donation now](https://nette.org/make-donation?to=dibi)**. Thank you!
Installation
------------
@@ -26,7 +24,7 @@ Install Dibi via Composer:
composer require dibi/dibi
```
The Dibi 4.0 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.2. Older Dibi 3.x requires PHP 5.4 and supports PHP up to 7.2.
Usage
@@ -110,7 +108,7 @@ $ids = [10, 20, 30];
$result = $database->query('SELECT * FROM users WHERE id IN (?)', $ids);
```
**WARNING: Never concatenate parameters to SQL. It would create a [SQL injection](https://en.wikipedia.org/wiki/SQL_injection)** vulnerability.
**WARNING, never concencate parameters to SQL, the vulnerability would arise [SQL injection](https://en.wikipedia.org/wiki/SQL_injection)**
```
$result = $database->query('SELECT * FROM users WHERE id = ' . $id); // BAD!!!
```
@@ -147,7 +145,7 @@ $name = $database->fetchSingle('SELECT name FROM users WHERE id = ?', $id);
### Modifiers
In addition to the `?` wildcard char, we can also use modifiers:
In addition to the `?` wild char, we can also use modifiers:
| modifier | description
|----------|-----
@@ -161,7 +159,6 @@ In addition to the `?` wildcard char, we can also use modifiers:
| %d | date (accepts DateTime, string or UNIX timestamp)
| %dt | datetime (accepts DateTime, string or UNIX timestamp)
| %n | identifier, ie the name of the table or column
| %N | identifier, treats period as a common character, ie alias or a database name (`%n AS %N` or `DROP DATABASE %N`)
| %SQL | SQL - directly inserts into SQL (the alternative is Dibi\Literal)
| %ex | SQL expression or array of expressions
| %lmt | special - adds LIMIT to the query

View File

@@ -51,7 +51,7 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
}
$connection = $container->addDefinition($this->prefix('connection'))
->setFactory(Dibi\Connection::class, [$config])
->setClass(Dibi\Connection::class, [$config])
->setAutowired($config['autowired'] ?? true);
if (class_exists(Tracy\Debugger::class)) {
@@ -62,7 +62,7 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
}
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setFactory(Dibi\Bridges\Tracy\Panel::class, [
->setClass(Dibi\Bridges\Tracy\Panel::class, [
$config['explain'] ?? true,
isset($config['filter']) && $config['filter'] === false ? Dibi\Event::ALL : Dibi\Event::QUERY,
]);

View File

@@ -47,7 +47,6 @@ class Connection implements IConnection
* - run (bool) => enable profiler?
* - file => file to log
* - 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
*/
@@ -91,10 +90,6 @@ class Connection implements IConnection
}
}
if (isset($config['onConnect']) && !is_array($config['onConnect'])) {
throw new \InvalidArgumentException("Configuration option 'onConnect' must be array.");
}
if (empty($config['lazy'])) {
$this->connect();
}
@@ -117,14 +112,8 @@ class Connection implements IConnection
*/
final public function connect(): void
{
if ($this->config['driver'] instanceof Driver) {
$this->driver = $this->config['driver'];
$this->translator = new Translator($this);
return;
} elseif (is_subclass_of($this->config['driver'], Driver::class)) {
if (is_subclass_of($this->config['driver'], Driver::class)) {
$class = $this->config['driver'];
} else {
$class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($this->config['driver'])));
$class = "Dibi\\Drivers\\{$class}Driver";
@@ -136,16 +125,9 @@ class Connection implements IConnection
$event = $this->onEvent ? new Event($this, Event::CONNECT) : null;
try {
$this->driver = new $class($this->config);
$this->translator = new Translator($this);
if ($event) {
$this->onEvent($event->done());
}
if (isset($this->config['onConnect'])) {
foreach ($this->config['onConnect'] as $sql) {
$this->query($sql);
}
}
} catch (DriverException $e) {
if ($event) {
@@ -163,7 +145,7 @@ class Connection implements IConnection
{
if ($this->driver) {
$this->driver->disconnect();
$this->driver = $this->translator = null;
$this->driver = null;
}
}
@@ -264,7 +246,11 @@ class Connection implements IConnection
if (!$this->driver) {
$this->connect();
}
return (clone $this->translator)->translate($args);
if (!$this->translator) {
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
}
@@ -450,10 +436,7 @@ class Connection implements IConnection
}
/**
* @param string|string[] $table
*/
public function update($table, iterable $args): Fluent
public function update(string $table, iterable $args): Fluent
{
return $this->command()->update('%n', $table)->set($args);
}

View File

@@ -161,7 +161,7 @@ class DataSource implements IDataSource
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, null if no next record
* @return mixed value on success, false if no next record
*/
public function fetchSingle()
{

View File

@@ -43,7 +43,7 @@ class FirebirdDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('interbase')) {
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded.");
@@ -276,8 +276,7 @@ class FirebirdDriver implements Dibi\Driver
*/
public function escapeLike(string $value, int $pos): string
{
$value = addcslashes($this->escapeText($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
throw new Dibi\NotImplementedException;
}

View File

@@ -50,7 +50,7 @@ class MySqliDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('mysqli')) {
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
@@ -93,7 +93,7 @@ class MySqliDriver implements Dibi\Driver
@$this->connection->real_connect( // intentionally @
(empty($config['persistent']) ? '' : 'p:') . $config['host'],
$config['username'],
$config['password'] ?? '',
$config['password'],
$config['database'] ?? '',
$config['port'] ?? 0,
$config['socket'],

View File

@@ -21,7 +21,6 @@ use Dibi;
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - microseconds (bool) => use microseconds in datetime format?
*/
class OdbcDriver implements Dibi\Driver
{
@@ -33,14 +32,11 @@ class OdbcDriver implements Dibi\Driver
/** @var int|null Affected rows */
private $affectedRows;
/** @var bool */
private $microseconds = true;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('odbc')) {
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded.");
@@ -66,10 +62,6 @@ class OdbcDriver implements Dibi\Driver
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
}
if (isset($config['microseconds'])) {
$this->microseconds = (bool) $config['microseconds'];
}
}
@@ -246,7 +238,7 @@ class OdbcDriver implements Dibi\Driver
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#');
return $value->format('#m/d/Y H:i:s.u#');
}

View File

@@ -45,7 +45,7 @@ class OracleDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('oci8')) {
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded.");

View File

@@ -45,7 +45,7 @@ class PdoDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('pdo')) {
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded.");
@@ -69,10 +69,6 @@ class PdoDriver implements Dibi\Driver
}
}
if ($this->connection->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_SILENT) {
throw new Dibi\DriverException('PDO connection in exception or warning error mode is not supported.');
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->serverVersion = (string) ($config['version'] ?? @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION)); // @ - may be not supported
}
@@ -114,7 +110,7 @@ class PdoDriver implements Dibi\Driver
throw PostgreDriver::createException($message, $sqlState, $sql);
case 'sqlite':
throw SqliteDriver::createException($message, $code, $sql);
throw Sqlite3Driver::createException($message, $code, $sql);
default:
throw new Dibi\DriverException($message, $code, $sql);
@@ -136,7 +132,7 @@ class PdoDriver implements Dibi\Driver
*/
public function getInsertId(?string $sequence): ?int
{
return Helpers::intVal($this->connection->lastInsertId($sequence));
return Helpers::false2Null($this->connection->lastInsertId());
}

View File

@@ -38,7 +38,7 @@ class PostgreDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('pgsql')) {
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");

View File

@@ -130,7 +130,7 @@ class PostgreReflector implements Dibi\Reflector
'size' => $size > 0 ? $size : null,
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'] ?? '', 0, 7) === 'nextval',
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row,
];
}
@@ -176,11 +176,8 @@ class PostgreReflector implements Dibi\Reflector
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't' || $row['indisunique'] === true;
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't' || $row['indisprimary'] === true;
$indexes[$row['relname']]['columns'] = [];
foreach (explode(' ', $row['indkey']) as $index) {
if (isset($columns[$index])) {
$indexes[$row['relname']]['columns'][] = $columns[$index];
}
$indexes[$row['relname']]['columns'][] = $columns[$index];
}
}
return array_values($indexes);

View File

@@ -9,10 +9,293 @@ declare(strict_types=1);
namespace Dibi\Drivers;
use Dibi;
use Dibi\Helpers;
use SQLite3;
/**
* Alias for SqliteDriver driver.
* The driver for SQLite3 database.
*
* Driver options:
* - database (or file) => the filename of the SQLite3 database
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (SQLite3) => existing connection resource
*/
class Sqlite3Driver extends SqliteDriver
class Sqlite3Driver implements Dibi\Driver
{
use Dibi\Strict;
/** @var SQLite3 */
private $connection;
/** @var string Date format */
private $fmtDate;
/** @var string Datetime format */
private $fmtDateTime;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array &$config)
{
if (!extension_loaded('sqlite3')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
if (isset($config['dbcharset']) || isset($config['charset'])) {
throw new Dibi\NotSupportedException('Options dbcharset and charset are not longer supported.');
}
Helpers::alias($config, 'database', 'file');
$this->fmtDate = $config['formatDate'] ?? 'U';
$this->fmtDateTime = $config['formatDateTime'] ?? 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query('PRAGMA foreign_keys = ON');
}
}
/**
* Disconnects from a database.
*/
public function disconnect(): void
{
$this->connection->close();
}
/**
* Executes the SQL query.
* @throws Dibi\DriverException
*/
public function query(string $sql): ?Dibi\ResultDriver
{
$res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) {
throw static::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof \SQLite3Result && $res->numColumns()) {
return $this->createResultDriver($res);
}
return null;
}
public static function createException(string $message, $code, string $sql): Dibi\DriverException
{
if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== false
|| strpos($message, 'is not unique') !== false
|| strpos($message, 'UNIQUE constraint failed') !== false
) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be null') !== false
|| strpos($message, 'NOT NULL constraint failed') !== false
) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== false
|| strpos($message, 'FOREIGN KEY constraint failed') !== false
) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\ConstraintViolationException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
*/
public function getAffectedRows(): ?int
{
return $this->connection->changes();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
*/
public function getInsertId(?string $sequence): ?int
{
return $this->connection->lastInsertRowID();
}
/**
* Begins a transaction (if supported).
* @throws Dibi\DriverException
*/
public function begin(string $savepoint = null): void
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
}
/**
* Commits statements in a transaction.
* @throws Dibi\DriverException
*/
public function commit(string $savepoint = null): void
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @throws Dibi\DriverException
*/
public function rollback(string $savepoint = null): void
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
*/
public function getResource(): ?SQLite3
{
return $this->connection;
}
/**
* Returns the connection reflector.
*/
public function getReflector(): Dibi\Reflector
{
return new SqliteReflector($this);
}
/**
* Result set driver factory.
*/
public function createResultDriver(\SQLite3Result $result): Sqlite3Result
{
return new Sqlite3Result($result);
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
*/
public function escapeText(string $value): string
{
return "'" . $this->connection->escapeString($value) . "'";
}
public function escapeBinary(string $value): string
{
return "X'" . bin2hex($value) . "'";
}
public function escapeIdentifier(string $value): string
{
return '[' . strtr($value, '[]', ' ') . ']';
}
public function escapeBool(bool $value): string
{
return $value ? '1' : '0';
}
/**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
/**
* Encodes string for use in a LIKE statement.
*/
public function escapeLike(string $value, int $pos): string
{
$value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
}
/**
* 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 === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : '');
}
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
*/
public function registerFunction(string $name, callable $callback, int $numArgs = -1): void
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
*/
public function registerAggregateFunction(string $name, callable $rowCallback, callable $agrCallback, int $numArgs = -1): void
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -9,10 +9,115 @@ declare(strict_types=1);
namespace Dibi\Drivers;
use Dibi;
use Dibi\Helpers;
/**
* Alias for SqliteResult driver.
* The driver for SQLite3 result set.
*/
class Sqlite3Result extends SqliteResult
class Sqlite3Result implements Dibi\ResultDriver
{
use Dibi\Strict;
/** @var \SQLite3Result */
private $resultSet;
/** @var bool */
private $autoFree = true;
public function __construct(\SQLite3Result $resultSet)
{
$this->resultSet = $resultSet;
}
/**
* Automatically frees the resources allocated for this result set.
*/
public function __destruct()
{
if ($this->autoFree && $this->getResultResource()) {
@$this->free();
}
}
/**
* Returns the number of rows in a result set.
* @throws Dibi\NotSupportedException
*/
public function getRowCount(): int
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool $assoc true for associative array, false for numeric
*/
public function fetch(bool $assoc): ?array
{
return Helpers::false2Null($this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM));
}
/**
* Moves cursor position without fetching row.
* @throws Dibi\NotSupportedException
*/
public function seek(int $row): bool
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
*/
public function free(): void
{
$this->resultSet->finalize();
}
/**
* Returns metadata for all columns in a result set.
*/
public function getResultColumns(): array
{
$count = $this->resultSet->numColumns();
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
for ($i = 0; $i < $count; $i++) {
$columns[] = [
'name' => $this->resultSet->columnName($i),
'table' => null,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
];
}
return $columns;
}
/**
* Returns the result set resource.
*/
public function getResultResource(): \SQLite3Result
{
$this->autoFree = false;
return $this->resultSet;
}
/**
* Decodes data from result set.
*/
public function unescapeBinary(string $value): string
{
return $value;
}
}

View File

@@ -1,301 +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;
use Dibi\Helpers;
use SQLite3;
/**
* The driver for SQLite v3 database.
*
* Driver options:
* - database (or file) => the filename of the SQLite3 database
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (SQLite3) => existing connection resource
*/
class SqliteDriver implements Dibi\Driver
{
use Dibi\Strict;
/** @var SQLite3 */
private $connection;
/** @var string Date format */
private $fmtDate;
/** @var string Datetime format */
private $fmtDateTime;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
{
if (!extension_loaded('sqlite3')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
if (isset($config['dbcharset']) || isset($config['charset'])) {
throw new Dibi\NotSupportedException('Options dbcharset and charset are not longer supported.');
}
Helpers::alias($config, 'database', 'file');
$this->fmtDate = $config['formatDate'] ?? 'U';
$this->fmtDateTime = $config['formatDateTime'] ?? 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query('PRAGMA foreign_keys = ON');
}
}
/**
* Disconnects from a database.
*/
public function disconnect(): void
{
$this->connection->close();
}
/**
* Executes the SQL query.
* @throws Dibi\DriverException
*/
public function query(string $sql): ?Dibi\ResultDriver
{
$res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) {
throw static::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof \SQLite3Result && $res->numColumns()) {
return $this->createResultDriver($res);
}
return null;
}
public static function createException(string $message, $code, string $sql): Dibi\DriverException
{
if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== false
|| strpos($message, 'is not unique') !== false
|| strpos($message, 'UNIQUE constraint failed') !== false
) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be null') !== false
|| strpos($message, 'NOT NULL constraint failed') !== false
) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== false
|| strpos($message, 'FOREIGN KEY constraint failed') !== false
) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\ConstraintViolationException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
*/
public function getAffectedRows(): ?int
{
return $this->connection->changes();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
*/
public function getInsertId(?string $sequence): ?int
{
return $this->connection->lastInsertRowID();
}
/**
* Begins a transaction (if supported).
* @throws Dibi\DriverException
*/
public function begin(string $savepoint = null): void
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
}
/**
* Commits statements in a transaction.
* @throws Dibi\DriverException
*/
public function commit(string $savepoint = null): void
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @throws Dibi\DriverException
*/
public function rollback(string $savepoint = null): void
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
*/
public function getResource(): ?SQLite3
{
return $this->connection;
}
/**
* Returns the connection reflector.
*/
public function getReflector(): Dibi\Reflector
{
return new SqliteReflector($this);
}
/**
* Result set driver factory.
*/
public function createResultDriver(\SQLite3Result $result): SqliteResult
{
return new SqliteResult($result);
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
*/
public function escapeText(string $value): string
{
return "'" . $this->connection->escapeString($value) . "'";
}
public function escapeBinary(string $value): string
{
return "X'" . bin2hex($value) . "'";
}
public function escapeIdentifier(string $value): string
{
return '[' . strtr($value, '[]', ' ') . ']';
}
public function escapeBool(bool $value): string
{
return $value ? '1' : '0';
}
/**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDate($value): string
{
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTimeInterface|string|int $value
*/
public function escapeDateTime($value): string
{
if (!$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
/**
* Encodes string for use in a LIKE statement.
*/
public function escapeLike(string $value, int $pos): string
{
$value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
}
/**
* 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 === null ? '-1' : $limit)
. ($offset ? ' OFFSET ' . $offset : '');
}
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
*/
public function registerFunction(string $name, callable $callback, int $numArgs = -1): void
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
*/
public function registerAggregateFunction(string $name, callable $rowCallback, callable $agrCallback, int $numArgs = -1): void
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -1,123 +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;
use Dibi\Helpers;
/**
* The driver for SQLite result set.
*/
class SqliteResult implements Dibi\ResultDriver
{
use Dibi\Strict;
/** @var \SQLite3Result */
private $resultSet;
/** @var bool */
private $autoFree = true;
public function __construct(\SQLite3Result $resultSet)
{
$this->resultSet = $resultSet;
}
/**
* Automatically frees the resources allocated for this result set.
*/
public function __destruct()
{
if ($this->autoFree && $this->getResultResource()) {
@$this->free();
}
}
/**
* Returns the number of rows in a result set.
* @throws Dibi\NotSupportedException
*/
public function getRowCount(): int
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool $assoc true for associative array, false for numeric
*/
public function fetch(bool $assoc): ?array
{
return Helpers::false2Null($this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM));
}
/**
* Moves cursor position without fetching row.
* @throws Dibi\NotSupportedException
*/
public function seek(int $row): bool
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
*/
public function free(): void
{
$this->resultSet->finalize();
}
/**
* Returns metadata for all columns in a result set.
*/
public function getResultColumns(): array
{
$count = $this->resultSet->numColumns();
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
for ($i = 0; $i < $count; $i++) {
$columns[] = [
'name' => $this->resultSet->columnName($i),
'table' => null,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)] ?? null, // buggy in PHP 7.4.4 & 7.3.16, bug 79414
];
}
return $columns;
}
/**
* Returns the result set resource.
*/
public function getResultResource(): \SQLite3Result
{
$this->autoFree = false;
return $this->resultSet;
}
/**
* Decodes data from result set.
*/
public function unescapeBinary(string $value): string
{
return $value;
}
}

View File

@@ -42,7 +42,7 @@ class SqlsrvDriver implements Dibi\Driver
/**
* @throws Dibi\NotSupportedException
*/
public function __construct(array $config)
public function __construct(array &$config)
{
if (!extension_loaded('sqlsrv')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
@@ -65,13 +65,11 @@ class SqlsrvDriver implements Dibi\Driver
$options['UID'] = (string) $options['UID'];
$options['Database'] = (string) $options['Database'];
sqlsrv_configure('WarningsReturnAsErrors', 0);
$this->connection = sqlsrv_connect($config['host'], $options);
sqlsrv_configure('WarningsReturnAsErrors', 1);
}
if (!is_resource($this->connection)) {
$info = sqlsrv_errors(SQLSRV_ERR_ERRORS);
$info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
}
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];

View File

@@ -15,7 +15,7 @@ namespace Dibi;
*
* @method Fluent select(...$field)
* @method Fluent distinct()
* @method Fluent from($table, ...$args = null)
* @method Fluent from($table, ...$args)
* @method Fluent where(...$cond)
* @method Fluent groupBy(...$field)
* @method Fluent having(...$cond)
@@ -29,11 +29,7 @@ namespace Dibi;
* @method Fluent outerJoin(...$table)
* @method Fluent as(...$field)
* @method Fluent on(...$cond)
* @method Fluent and(...$cond)
* @method Fluent or(...$cond)
* @method Fluent using(...$cond)
* @method Fluent asc()
* @method Fluent desc()
*/
class Fluent implements IDataSource
{
@@ -315,7 +311,7 @@ class Fluent implements IDataSource
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, null if no next record
* @return mixed value on success, false if no next record
*/
public function fetchSingle()
{
@@ -423,7 +419,7 @@ class Fluent implements IDataSource
if ($clause === null) {
$data = $this->clauses;
if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) {
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0] ?? null, $data['OFFSET'][0] ?? null], $args);
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args);
unset($data['LIMIT'], $data['OFFSET']);
}

View File

@@ -76,7 +76,7 @@ class Column
}
public function getType(): ?string
public function getType(): string
{
return Dibi\Helpers::getTypeCache()->{$this->info['nativetype']};
}

View File

@@ -179,13 +179,13 @@ class Result implements IDataSource
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, null if no next record
* @return mixed value on success, false if no next record
*/
final public function fetchSingle()
{
$row = $this->getResultDriver()->fetch(true);
if ($row === null) {
return null;
return false;
}
$this->fetched = true;
$this->normalize($row);
@@ -282,7 +282,7 @@ class Result implements IDataSource
}
} elseif ($as !== '|') { // associative-array node
$x = &$x[(string) $row->$as];
$x = &$x[$row->$as];
}
}
@@ -350,7 +350,7 @@ class Result implements IDataSource
}
} else { // associative-array node
$x = &$x[(string) $row->$as];
$x = &$x[$row->$as];
}
}
@@ -452,7 +452,6 @@ class Result implements IDataSource
continue;
}
$value = $row[$key];
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
@@ -464,11 +463,8 @@ class Result implements IDataSource
} elseif ($type === Type::FLOAT) {
$value = ltrim((string) $value, '0');
$p = strpos($value, '.');
$e = strpos($value, 'e');
if ($p !== false && $e === false) {
if ($p !== false) {
$value = rtrim(rtrim($value, '0'), '.');
} elseif ($p !== false && $e !== false) {
$value = rtrim($value, '.');
}
if ($value === '' || $value[0] === '.') {
$value = '0' . $value;
@@ -498,9 +494,6 @@ class Result implements IDataSource
} elseif ($type === Type::JSON) {
$row[$key] = json_decode($value, true);
} else {
$row[$key] = $value;
}
}
}
@@ -508,9 +501,9 @@ class Result implements IDataSource
/**
* Define column type.
* @param string|null $type use constant Type::*
* @param string $type use constant Type::*
*/
final public function setType(string $column, ?string $type): self
final public function setType(string $column, string $type): self
{
$this->types[$column] = $type;
return $this;
@@ -520,21 +513,12 @@ class Result implements IDataSource
/**
* Returns column type.
*/
final public function getType(string $column): ?string
final public function getType(string $column): string
{
return $this->types[$column] ?? null;
}
/**
* Returns columns type.
*/
final public function getTypes(): array
{
return $this->types;
}
/**
* Sets date format.
*/

View File

@@ -11,28 +11,28 @@ declare(strict_types=1);
/**
* Static container class for Dibi connections.
*
* @method static void disconnect()
* @method static Dibi\Result query(...$args)
* @method static Dibi\Result nativeQuery(...$args)
* @method static bool test(...$args)
* @method static Dibi\DataSource dataSource(...$args)
* @method static Dibi\Row|null fetch(...$args)
* @method static array fetchAll(...$args)
* @method static mixed fetchSingle(...$args)
* @method static array fetchPairs(...$args)
* @method static int getAffectedRows()
* @method static int getInsertId(string $sequence = null)
* @method static void begin(string $savepoint = null)
* @method static void commit(string $savepoint = null)
* @method static void rollback(string $savepoint = null)
* @method static Dibi\Reflection\Database getDatabaseInfo()
* @method static Dibi\Fluent command()
* @method static Dibi\Fluent select(...$args)
* @method static Dibi\Fluent update(string|string[] $table, array $args)
* @method static Dibi\Fluent insert(string $table, array $args)
* @method static Dibi\Fluent delete(string $table)
* @method static Dibi\HashMap getSubstitutes()
* @method static int loadFile(string $file)
* @method void disconnect()
* @method Dibi\Result query(...$args)
* @method Dibi\Result nativeQuery(...$args)
* @method bool test(...$args)
* @method Dibi\DataSource dataSource(...$args)
* @method Dibi\Row|null fetch(...$args)
* @method array fetchAll(...$args)
* @method mixed fetchSingle(...$args)
* @method array fetchPairs(...$args)
* @method int getAffectedRows()
* @method int getInsertId(string $sequence = null)
* @method void begin(string $savepoint = null)
* @method void commit(string $savepoint = null)
* @method void rollback(string $savepoint = null)
* @method Dibi\Reflection\Database getDatabaseInfo()
* @method Dibi\Fluent command()
* @method Dibi\Fluent select(...$args)
* @method Dibi\Fluent update(string $table, array $args)
* @method Dibi\Fluent insert(string $table, array $args)
* @method Dibi\Fluent delete(string $table)
* @method Dibi\HashMap getSubstitutes()
* @method int loadFile(string $file)
*/
class dibi
{
@@ -44,7 +44,7 @@ class dibi
/** version */
public const
VERSION = '4.0.3';
VERSION = '4.0.0';
/** sorting order */
public const

View File

@@ -0,0 +1,7 @@
includes:
- ../temp/coding-standard/coding-standard-php71.neon
parameters:
skip:
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer:
- src/Dibi/HashMap.php # issue #260

View File

@@ -1,7 +0,0 @@
imports:
- { resource: '../temp/coding-standard/coding-standard-php71.yml' }
parameters:
skip:
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer:
- src/Dibi/HashMap.php # issue #260

View File

@@ -1,5 +1,5 @@
[sqlite] ; default
driver = sqlite
driver = sqlite3
database = :memory:
system = sqlite

View File

@@ -1,5 +1,5 @@
[sqlite] ; default
driver = sqlite
driver = sqlite3
database = :memory:
system = sqlite

View File

@@ -1,5 +1,5 @@
[sqlite] ; default
driver = sqlite
driver = sqlite3
database = :memory:
system = sqlite

View File

@@ -50,38 +50,3 @@ test(function () use ($config) {
$conn->disconnect();
Assert::false($conn->isConnected());
});
test(function () use ($config) {
$conn = new Connection($config);
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
$conn->disconnect();
$conn->connect();
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
});
test(function () use ($config) {
Assert::exception(function () use ($config) {
new Connection($config + ['onConnect' => '']);
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
$e = Assert::exception(function () use ($config) {
new Connection($config + ['onConnect' => ['STOP']]);
}, Dibi\DriverException::class);
Assert::same('STOP', $e->getSql());
$e = Assert::exception(function () use ($config) {
new Connection($config + ['onConnect' => [['STOP %i', 123]]]);
}, Dibi\DriverException::class);
Assert::same('STOP 123', $e->getSql());
// lazy
$conn = new Connection($config + ['lazy' => true, 'onConnect' => ['STOP']]);
$e = Assert::exception(function () use ($conn) {
$conn->query('SELECT 1');
}, Dibi\DriverException::class);
Assert::same('STOP', $e->getSql());
});

View File

@@ -18,6 +18,9 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch a single value
$res = $conn->query('SELECT [title] FROM [products] ORDER BY [product_id]');
Assert::same('Chair', $res->fetchSingle());
Assert::same('Table', $res->fetchSingle());
Assert::same('Computer', $res->fetchSingle());
Assert::false($res->fetchSingle());
// fetch complete result set

View File

@@ -29,15 +29,3 @@ Assert::same(
reformat('UPDATE IGNORE DELAYED [table] SET [title]=\'Super Product\', [price]=12, [brand]=NULL , [another]=123'),
(string) $fluent
);
$arr = [
'table1.title' => 'Super Product',
'table2.price' => 12,
'table2.brand' => null,
];
$fluent = $conn->update(['table1', 'table2'], $arr);
Assert::same(
reformat('UPDATE [table1], [table2] SET [table1].[title]=\'Super Product\', [table2].[price]=12, [table2].[brand]=NULL'),
(string) $fluent
);

View File

@@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
function buildPdoDriver(?int $errorMode)
{
$pdo = new PDO('sqlite::memory:');
if ($errorMode !== null) {
$pdo->setAttribute(PDO::ATTR_ERRMODE, $errorMode);
}
new Dibi\Drivers\PdoDriver(['resource' => $pdo]);
}
// PDO error mode: exception
Assert::exception(function () {
buildPdoDriver(PDO::ERRMODE_EXCEPTION);
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
// PDO error mode: warning
Assert::exception(function () {
buildPdoDriver(PDO::ERRMODE_WARNING);
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
// PDO error mode: explicitly set silent
test(function () {
buildPdoDriver(PDO::ERRMODE_SILENT);
});
// PDO error mode: implicitly set silent
test(function () {
buildPdoDriver(null);
});

View File

@@ -32,7 +32,7 @@ Assert::same(
);
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
Assert::same(
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
$info->getColumnNames(true)
@@ -43,7 +43,7 @@ if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
$columns = $info->getColumns();
Assert::same('product_id', $columns[0]->getName());
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
Assert::same('products', $columns[0]->getTableName());
}
Assert::null($columns[0]->getVendorInfo('xxx'));

View File

@@ -100,11 +100,6 @@ test(function () {
Assert::same(['col' => 1.0], $result->test(['col' => 1]));
Assert::same(['col' => 1.0], $result->test(['col' => 1.0]));
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');
Assert::same(['col' => 0.0], $result->test(['col' => '']));
Assert::same(['col' => 0.0], $result->test(['col' => '0']));

View File

@@ -1,73 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
declare(strict_types=1);
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
// starts with
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'a', 'b'));
Assert::falsey($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::falsey($conn->fetchSingle('SELECT ? LIKE %like~', '%ba', '%a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%ab', '%a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'aa', '_a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', '_b', '_a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '_ab', '_a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', 'a"a', 'a"'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'b"', '%"'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%"', '%"'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', "a'a", "a'"));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', "b'", "%'"));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', "%'", "%'"));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', 'a\\a', 'a\\'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'b\\', '%\\'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%\\', '%\\'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', 'a[a', 'a['));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like~', 'b[', '%['));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like~', '%[', '%['));
// ends with
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', 'a', 'b'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'baa', 'aa'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', 'aab', 'aa'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', 'bba', '%a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', 'a%b', '%a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'b%a', '%a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', 'aa', '_a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', '_b', '_a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'b_a', '_a'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'a"a', '"a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', '"b', '"%'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', '"%', '"%'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', "a'a", "'a"));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', "'b", "'%"));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', "'%", "'%"));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', 'a\\a', '\\a'));
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like', '\\b', '\\%'));
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like', '\\%', '\\%'));
// contains
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like~', 'a', 'b'));
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'));

View File

@@ -68,7 +68,7 @@ function reformat($s)
function num($n)
{
global $config;
if (substr($config['dsn'] ?? '', 0, 5) === 'odbc:') {
if (substr($config['dsn'] ?? '', 0, 5) === 'odbc:' || $config['driver'] === 'sqlite') {
$n = is_float($n) ? "$n.0" : (string) $n;
}
return $n;

View File

@@ -17,7 +17,7 @@ $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$e = Assert::exception(function () use ($conn) {
$conn->query('SELECT');
}, Dibi\DriverException::class, '%a%', 1);
}, Dibi\DriverException::class, '%a% syntax error', 1);
Assert::same('SELECT', $e->getSql());