Skip to content

Commit

Permalink
add PDO Sqlite driver WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Nov 15, 2020
1 parent dd7f413 commit 36d1321
Show file tree
Hide file tree
Showing 20 changed files with 1,169 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .idea/sqldialects.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions doc/default.texy
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ Supported platforms:

- **MySQL** via `mysqli` or `pdo_mysql` extension,
- **Postgres** via `pgsql` or `pdo_pgsql` extension,
- **MS SQL Server** via `sqlsrv` or `pdo_sqlsrv` extension.
- **MS SQL Server** via `sqlsrv` or `pdo_sqlsrv` extension,
- **Sqlite** via `pdo_sqlite` extension.

Connection
==========

The connection instance is a main object that provides an API for accessing your database. Connection's constructor accepts a configuration array. The possible keys depend on the specific driver; some configuration keys are the shared for all drivers:

|* driver | driver name, use `mysqli`, `pgsql`, `sqlsrv`, `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv`
|* driver | driver name, use `mysqli`, `pgsql`, `sqlsrv`, `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv`, `pdo_sqlite`
|* host | database server name
|* username | username for authentication
|* password | password for authentication
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Supported platforms:
- **MySQL** via `mysqli` or `pdo_mysql` extension,
- **PostgreSQL** via `pgsql` or `pdo_pgsql` extension,
- **MS SQL Server** via `sqlsrv` or `pdo_sqlsrv` extension.
- **Sqlite** via `pdo_sqlite` extension.

Integrations:
- Symfony Bundle
Expand Down
100 changes: 100 additions & 0 deletions src/Drivers/PdoSqlite/PdoSqliteDriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php declare(strict_types = 1);

namespace Nextras\Dbal\Drivers\PdoSqlite;


use DateTimeZone;
use Exception;
use Nextras\Dbal\Connection;
use Nextras\Dbal\Drivers\Exception\ConnectionException;
use Nextras\Dbal\Drivers\Exception\DriverException;
use Nextras\Dbal\Drivers\Exception\ForeignKeyConstraintViolationException;
use Nextras\Dbal\Drivers\Exception\NotNullConstraintViolationException;
use Nextras\Dbal\Drivers\Exception\QueryException;
use Nextras\Dbal\Drivers\Exception\UniqueConstraintViolationException;
use Nextras\Dbal\Drivers\Pdo\PdoDriver;
use Nextras\Dbal\Exception\NotSupportedException;
use Nextras\Dbal\ILogger;
use Nextras\Dbal\Platforms\IPlatform;
use Nextras\Dbal\Platforms\SqlitePlatform;
use Nextras\Dbal\Result\IResultAdapter;
use PDOStatement;


/**
* Driver for php_pdo_sqlite ext.
*
* Supported configuration options:
* - filename - file path to database or `:memory:`; defaults to :memory:
*/
class PdoSqliteDriver extends PdoDriver
{
public function connect(array $params, ILogger $logger): void
{
$file = $params['filename'] ?? ':memory:';
$dsn = "sqlite:$file";
$this->connectPdo($dsn, '', '', [], $logger);

$this->connectionTz = new DateTimeZone('UTC');
$this->loggedQuery('PRAGMA foreign_keys = 1');
}


public function createPlatform(Connection $connection): IPlatform
{
return new SqlitePlatform($connection);
}


public function setTransactionIsolationLevel(int $level): void
{
static $levels = [
Connection::TRANSACTION_READ_UNCOMMITTED => 'READ UNCOMMITTED',
Connection::TRANSACTION_READ_COMMITTED => 'READ COMMITTED',
Connection::TRANSACTION_REPEATABLE_READ => 'REPEATABLE READ',
Connection::TRANSACTION_SERIALIZABLE => 'SERIALIZABLE',
];
if (!isset($levels[$level])) {
throw new NotSupportedException("Unsupported transaction level $level");
}
$this->loggedQuery("SET SESSION TRANSACTION ISOLATION LEVEL {$levels[$level]}");
}


protected function createResultAdapter(PDOStatement $statement): IResultAdapter
{
return (new PdoSqliteResultAdapter($statement))->toBuffered();
}


protected function convertIdentifierToSql(string $identifier): string
{
return '[' . strtr($identifier, '[]', ' ') . ']';
}


protected function createException(string $error, int $errorNo, string $sqlState, ?string $query = null): Exception
{
if (stripos($error, 'FOREIGN KEY constraint failed') !== false) {
return new ForeignKeyConstraintViolationException($error, $errorNo, '', null, $query);
} elseif (
strpos($error, 'must be unique') !== false
|| strpos($error, 'is not unique') !== false
|| strpos($error, 'are not unique') !== false
|| strpos($error, 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($error, $errorNo, '', null, $query);
} elseif (
strpos($error, 'may not be NULL') !== false
|| strpos($error, 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($error, $errorNo, '', null, $query);
} elseif (stripos($error, 'unable to open database') !== false) {
return new ConnectionException($error, $errorNo, '');
} elseif ($query !== null) {
return new QueryException($error, $errorNo, '', null, $query);
} else {
return new DriverException($error, $errorNo, '');
}
}
}
119 changes: 119 additions & 0 deletions src/Drivers/PdoSqlite/PdoSqliteResultAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php declare(strict_types = 1);

namespace Nextras\Dbal\Drivers\PdoSqlite;


use Nextras\Dbal\Exception\NotSupportedException;
use Nextras\Dbal\Result\FullyBufferedResultAdapter;
use Nextras\Dbal\Result\IResultAdapter;
use Nextras\Dbal\Utils\StrictObjectTrait;
use PDO;
use PDOStatement;
use function strtolower;


class PdoSqliteResultAdapter implements IResultAdapter
{
use StrictObjectTrait;


/** @var array<string, int> */
protected static $types = [
'int' => self::TYPE_INT,
'integer' => self::TYPE_INT,
'tinyint' => self::TYPE_INT,
'smallint' => self::TYPE_INT,
'mediumint' => self::TYPE_INT,
'bigint' => self::TYPE_INT,
'unsigned big int' => self::TYPE_INT,
'int2' => self::TYPE_INT,
'int8' => self::TYPE_INT,

'real' => self::TYPE_FLOAT,
'double' => self::TYPE_FLOAT,
'double precision' => self::TYPE_FLOAT,
'float' => self::TYPE_FLOAT,
'numeric' => self::TYPE_FLOAT,
'decimal' => self::TYPE_FLOAT,

'bool' => self::TYPE_BOOL,

'date' => self::TYPE_DATETIME,
'datetime' => self::TYPE_DATETIME,
];

/** @var PDOStatement<mixed> */
protected $statement;

/** @var bool */
protected $beforeFirstFetch = true;


/**
* @param PDOStatement<mixed> $statement
*/
public function __construct(PDOStatement $statement)
{
$this->statement = $statement;
}


public function toBuffered(): IResultAdapter
{
return new FullyBufferedResultAdapter($this);
}


public function toUnbuffered(): IResultAdapter
{
return $this;
}


public function seek(int $index): void
{
if ($index === 0 && $this->beforeFirstFetch) {
return;
}

throw new NotSupportedException("PDO does not support rewinding or seeking. Use Result::buffered() before first consume of the result.");
}


public function fetch(): ?array
{
$this->beforeFirstFetch = false;
$fetched = $this->statement->fetch(PDO::FETCH_ASSOC);
return $fetched !== false ? $fetched : null;
}


public function getTypes(): array
{
$types = [];
$count = $this->statement->columnCount();

for ($i = 0; $i < $count; $i++) {
$field = $this->statement->getColumnMeta($i);
if ($field === false) { // @phpstan-ignore-line
// Sqlite does not return meta for special queries (PRAGMA, etc.)
continue;
}

$type = strtolower($field['sqlite:decl_type'] ?? $field['native_type'] ?? '');

$types[(string) $field['name']] = [
0 => self::$types[$type] ?? dump(self::TYPE_AS_IS, $field),
1 => $type,
];
}

return $types;
}


public function getRowsCount(): int
{
return $this->statement->rowCount();
}
}
1 change: 1 addition & 0 deletions src/Platforms/IPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface IPlatform
public const SUPPORT_MULTI_COLUMN_IN = 1;
public const SUPPORT_QUERY_EXPLAIN = 2;
public const SUPPORT_WHITESPACE_EXPLAIN = 3;
public const SUPPORT_INSERT_DEFAULT_KEYWORD = 4;


/**
Expand Down
Loading

0 comments on commit 36d1321

Please sign in to comment.