204 lines
6.4 KiB
PHP
204 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace Ulmus\Adapter;
|
|
|
|
use Ulmus\Common\PdoObject;
|
|
use Ulmus\ConnectionAdapter;
|
|
|
|
use Ulmus\Entity\Sqlite\Table;
|
|
use Ulmus\Exception\AdapterConfigurationException;
|
|
use Ulmus\Migration\FieldDefinition;
|
|
use Ulmus\{Migration\MigrateInterface, Repository, QueryBuilder, Ulmus};
|
|
|
|
class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|
use SqlAdapterTrait;
|
|
|
|
const ALLOWED_ATTRIBUTES = [
|
|
'default', 'primary_key', 'auto_increment'
|
|
];
|
|
|
|
const DSN_PREFIX = "sqlite";
|
|
|
|
public string $path;
|
|
|
|
public array $pragma;
|
|
|
|
public function __construct(
|
|
? string $path = null,
|
|
? array $pragma = null
|
|
) {
|
|
if ($path !== null) {
|
|
$this->path = $path;
|
|
}
|
|
|
|
if ($pragma !== null) {
|
|
$this->pragma = $pragma;
|
|
}
|
|
}
|
|
|
|
public function connect() : PdoObject
|
|
{
|
|
try {
|
|
$pdo = new PdoObject($this->buildDataSourceName(), null, null);
|
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
|
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
|
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
|
|
|
$this->exportFunctions($pdo);
|
|
}
|
|
catch(PDOException $ex){
|
|
throw $ex;
|
|
}
|
|
|
|
return $pdo;
|
|
}
|
|
|
|
public function buildDataSourceName() : string
|
|
{
|
|
$parts[] = $this->path;
|
|
|
|
return static::DSN_PREFIX . ":" . implode(';', $parts);
|
|
}
|
|
|
|
public function setup(array $configuration) : void
|
|
{
|
|
$this->path = $configuration['path'] ?? "";
|
|
$this->pragma = $configuration['pragma'] ?? [];
|
|
}
|
|
|
|
# https://sqlite.org/lang_keywords.html
|
|
public function escapeIdentifier(string $segment, int $type) : string
|
|
{
|
|
switch($type) {
|
|
case static::IDENTIFIER_DATABASE:
|
|
case static::IDENTIFIER_TABLE:
|
|
case static::IDENTIFIER_FIELD:
|
|
return '"' . trim(str_replace('"', '""', $segment), '"') . '"';
|
|
|
|
case static::IDENTIFIER_VALUE:
|
|
return "'$segment'";
|
|
}
|
|
}
|
|
|
|
public function defaultEngine(): ? string
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function databaseName() : string
|
|
{
|
|
$base = basename($this->path);
|
|
|
|
return substr($base, 0, strrpos($base, '.') ?: strlen($base));
|
|
}
|
|
|
|
public function schemaTable(ConnectionAdapter $adapter, string $databaseName, string $tableName) : null|object
|
|
{
|
|
return Table::repository(Repository::DEFAULT_ALIAS, $adapter)
|
|
->select(\Ulmus\Common\Sql::raw('this.*'))
|
|
->loadOneFromField(Table::field('tableName'), $tableName);
|
|
}
|
|
|
|
public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string
|
|
{
|
|
$type = $field->type;
|
|
|
|
$length = $field->length;
|
|
|
|
if ( is_a($type, Entity\Field\Date::class, true) || is_a($type, Entity\Field\Time::class, true) || is_a($type, \DateTime::class, true) ) {
|
|
$type = "TEXT";
|
|
$length = strlen((string) $type);
|
|
}
|
|
else {
|
|
switch($type) {
|
|
case "bool":
|
|
$check = sprintf("CHECK (%s IN (0, 1))", $field->getColumnName());
|
|
|
|
case "bigint":
|
|
case "int":
|
|
$type = "INTEGER";
|
|
break;
|
|
|
|
case "array":
|
|
case "string":
|
|
$type = "TEXT";
|
|
$length = null;
|
|
break;
|
|
|
|
case "float":
|
|
$type = "REAL";
|
|
break;
|
|
|
|
default:
|
|
$type = "BLOB";
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $typeOnly ? $type : $type . ( $length ? "($length" . ( isset($precision) ? ",$precision" : "" ) . ")" : "" );
|
|
}
|
|
|
|
public function tableSyntax() : array
|
|
{
|
|
return [
|
|
'ai' => "AUTOINCREMENT",
|
|
'pk' => "PRIMARY KEY",
|
|
'unsigned' => "",
|
|
];
|
|
}
|
|
|
|
public function repositoryClass() : string
|
|
{
|
|
return Repository\SqliteRepository::class;
|
|
}
|
|
|
|
public function queryBuilderClass() : string
|
|
{
|
|
return QueryBuilder\SqliteQueryBuilder::class;
|
|
}
|
|
|
|
public function exportFunctions(PdoObject $pdo) : void
|
|
{
|
|
$pdo->sqliteCreateFunction('if', fn($comparison, $yes, $no) => $comparison ? $yes : $no, 3);
|
|
$pdo->sqliteCreateFunction('length', fn($string) => strlen($string), 1);
|
|
$pdo->sqliteCreateFunction('lcase', fn($string) => strtolower($string), 1);
|
|
$pdo->sqliteCreateFunction('ucase', fn($string) => strtoupper($string), 1);
|
|
$pdo->sqliteCreateFunction('left', fn($string, $length) => substr($string, 0, $length), 2);
|
|
$pdo->sqliteCreateFunction('right', fn($string, $length) => substr($string, -$length), 2);
|
|
$pdo->sqliteCreateFunction('strcmp', fn($s1, $s2) => strcmp($s1, $s2), 2);
|
|
$pdo->sqliteCreateFunction('lpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_LEFT), 3);
|
|
$pdo->sqliteCreateFunction('rpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_RIGHT), 3);
|
|
$pdo->sqliteCreateFunction('concat', fn(...$args) => implode('', $args), -1);
|
|
$pdo->sqliteCreateFunction('concat_ws', fn($separator, ...$args) => implode($separator, $args), -1);
|
|
$pdo->sqliteCreateFunction('find_in_set', function($string, $string_list) {
|
|
if ( $string === null || $string_list === null ) {
|
|
return null;
|
|
}
|
|
|
|
if ($string_list === "") {
|
|
return 0;
|
|
}
|
|
|
|
return (int) in_array($string, explode(',', $string_list));
|
|
}, 2);
|
|
$pdo->sqliteCreateFunction('day', fn($date) => ( new \DateTime($date) )->format('d'), 1);
|
|
$pdo->sqliteCreateFunction('month', fn($date) => ( new \DateTime($date) )->format('m'), 1);
|
|
$pdo->sqliteCreateFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1);
|
|
}
|
|
|
|
public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
|
|
{
|
|
return implode(" ", [
|
|
strtoupper($field['action']),
|
|
$this->escapeIdentifier($definition->getSqlName(), static::IDENTIFIER_FIELD),
|
|
$definition->getSqlType(),
|
|
$definition->getSqlParams(),
|
|
]);
|
|
}
|
|
|
|
public function splitAlterQuery() : bool
|
|
{
|
|
return true;
|
|
}
|
|
}
|