mirror of
https://github.com/dg/dibi.git
synced 2025-07-31 19:30:30 +02:00
Sqlite3Driver becomes alias for SqliteDriver
This commit is contained in:
@@ -16,7 +16,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
echo '<p>Connecting to Sqlite: ';
|
echo '<p>Connecting to Sqlite: ';
|
||||||
try {
|
try {
|
||||||
dibi::connect([
|
dibi::connect([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
echo 'OK';
|
echo 'OK';
|
||||||
@@ -30,7 +30,7 @@ echo "</p>\n";
|
|||||||
echo '<p>Connecting to Sqlite: ';
|
echo '<p>Connecting to Sqlite: ';
|
||||||
try {
|
try {
|
||||||
$connection = new Dibi\Connection([
|
$connection = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
echo 'OK';
|
echo 'OK';
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ Tracy\Debugger::enable();
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ Tracy\Debugger::enable();
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ Tracy\Debugger::enable();
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
|
|
||||||
// CHANGE TO REAL PARAMETERS!
|
// CHANGE TO REAL PARAMETERS!
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
'formatDate' => "'Y-m-d'",
|
'formatDate' => "'Y-m-d'",
|
||||||
'formatDateTime' => "'Y-m-d H-i-s'",
|
'formatDateTime' => "'Y-m-d H-i-s'",
|
||||||
|
@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
// enable query logging to this file
|
// enable query logging to this file
|
||||||
'profiler' => [
|
'profiler' => [
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
|
|||||||
|
|
||||||
|
|
||||||
$dibi = new Dibi\Connection([
|
$dibi = new Dibi\Connection([
|
||||||
'driver' => 'sqlite3',
|
'driver' => 'sqlite',
|
||||||
'database' => 'data/sample.s3db',
|
'database' => 'data/sample.s3db',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -110,7 +110,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
throw PostgreDriver::createException($message, $sqlState, $sql);
|
throw PostgreDriver::createException($message, $sqlState, $sql);
|
||||||
|
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
throw Sqlite3Driver::createException($message, $code, $sql);
|
throw SqliteDriver::createException($message, $code, $sql);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Dibi\DriverException($message, $code, $sql);
|
throw new Dibi\DriverException($message, $code, $sql);
|
||||||
|
@@ -9,293 +9,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
|
||||||
use Dibi\Helpers;
|
|
||||||
use SQLite3;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The driver for SQLite3 database.
|
* Alias for SqliteDriver driver.
|
||||||
*
|
|
||||||
* 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 implements Dibi\Driver
|
class Sqlite3Driver extends SqliteDriver
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -9,115 +9,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi\Drivers;
|
namespace Dibi\Drivers;
|
||||||
|
|
||||||
use Dibi;
|
|
||||||
use Dibi\Helpers;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The driver for SQLite3 result set.
|
* Alias for SqliteResult driver.
|
||||||
*/
|
*/
|
||||||
class Sqlite3Result implements Dibi\ResultDriver
|
class Sqlite3Result extends SqliteResult
|
||||||
{
|
{
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
301
src/Dibi/Drivers/SqliteDriver.php
Normal file
301
src/Dibi/Drivers/SqliteDriver.php
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
123
src/Dibi/Drivers/SqliteResult.php
Normal file
123
src/Dibi/Drivers/SqliteResult.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?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)],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
[sqlite] ; default
|
[sqlite] ; default
|
||||||
driver = sqlite3
|
driver = sqlite
|
||||||
database = :memory:
|
database = :memory:
|
||||||
system = sqlite
|
system = sqlite
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
[sqlite] ; default
|
[sqlite] ; default
|
||||||
driver = sqlite3
|
driver = sqlite
|
||||||
database = :memory:
|
database = :memory:
|
||||||
system = sqlite
|
system = sqlite
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
[sqlite] ; default
|
[sqlite] ; default
|
||||||
driver = sqlite3
|
driver = sqlite
|
||||||
database = :memory:
|
database = :memory:
|
||||||
system = sqlite
|
system = sqlite
|
||||||
|
|
||||||
|
@@ -32,7 +32,7 @@ Assert::same(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
|
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||||
Assert::same(
|
Assert::same(
|
||||||
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
||||||
$info->getColumnNames(true)
|
$info->getColumnNames(true)
|
||||||
@@ -43,7 +43,7 @@ if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
|
|||||||
$columns = $info->getColumns();
|
$columns = $info->getColumns();
|
||||||
|
|
||||||
Assert::same('product_id', $columns[0]->getName());
|
Assert::same('product_id', $columns[0]->getName());
|
||||||
if (!in_array($config['driver'], ['sqlite3', 'pdo', 'sqlsrv'], true)) {
|
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||||
Assert::same('products', $columns[0]->getTableName());
|
Assert::same('products', $columns[0]->getTableName());
|
||||||
}
|
}
|
||||||
Assert::null($columns[0]->getVendorInfo('xxx'));
|
Assert::null($columns[0]->getVendorInfo('xxx'));
|
||||||
|
@@ -68,7 +68,7 @@ function reformat($s)
|
|||||||
function num($n)
|
function num($n)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
if (substr($config['dsn'] ?? '', 0, 5) === 'odbc:' || $config['driver'] === 'sqlite') {
|
if (substr($config['dsn'] ?? '', 0, 5) === 'odbc:') {
|
||||||
$n = is_float($n) ? "$n.0" : (string) $n;
|
$n = is_float($n) ? "$n.0" : (string) $n;
|
||||||
}
|
}
|
||||||
return $n;
|
return $n;
|
||||||
|
Reference in New Issue
Block a user