Skip to content

Commit

Permalink
Result::getColumnTypes() redesigned, uses TypeConverter
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 19, 2024
1 parent c5617a0 commit 503d02c
Show file tree
Hide file tree
Showing 17 changed files with 198 additions and 158 deletions.
17 changes: 11 additions & 6 deletions src/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

use JetBrains\PhpStorm\Language;
use Nette\Utils\Arrays;
use Nette\Utils\DateTime;
use PDOException;


Expand All @@ -29,6 +28,7 @@ class Connection
private ?Drivers\Connection $connection = null;
private ?Drivers\Engine $engine;
private ?SqlPreprocessor $preprocessor;
private TypeConverter $typeConverter;

/** @var callable(array, ResultSet): array */
private $rowNormalizer = [Helpers::class, 'normalizeRow'];
Expand All @@ -43,13 +43,12 @@ public function __construct(
?string $password = null,
array $options = [],
) {
if (($options['newDateTime'] ?? null) === false) {
$this->rowNormalizer = fn($row, $resultSet) => Helpers::normalizeRow($row, $resultSet, DateTime::class);
}
$lazy = $options['lazy'] ?? false;
unset($options['newDateTime'], $options['lazy']);
unset($options['lazy']);

$this->driver = (new Factory)->createDriverFromDsn($dsn, $user, $password, $options);
$factory = new Factory;
$this->typeConverter = $factory->createTypeConverter($options);
$this->driver = $factory->createDriverFromDsn($dsn, $user, $password, $options);
if (!$lazy) {
$this->connect();
}
Expand Down Expand Up @@ -134,6 +133,12 @@ public function getReflection(): Reflection
}


public function getTypeConverter(): TypeConverter
{
return $this->typeConverter;
}


public function setRowNormalizer(?callable $normalizer): static
{
$this->rowNormalizer = $normalizer;
Expand Down
7 changes: 2 additions & 5 deletions src/Database/Drivers/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Nette\Database\Drivers;

use Nette\Database;
use Nette\Database\TypeConverter;


/**
Expand Down Expand Up @@ -69,11 +70,7 @@ function getIndexes(string $table): array;
/** @return list<array{name: string, local: string, table: string, foreign: string}> */
function getForeignKeys(string $table): array;

/**
* Returns associative array of detected types (IStructure::FIELD_*) in result set.
* @return array<string, string>
*/
function getColumnTypes(\PDOStatement $statement): array;
function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure;

/**
* Cheks if driver supports specific property
Expand Down
5 changes: 3 additions & 2 deletions src/Database/Drivers/Engines/MSSQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Nette;
use Nette\Database\Drivers\Connection;
use Nette\Database\Drivers\Engine;
use Nette\Database\TypeConverter;


/**
Expand Down Expand Up @@ -213,9 +214,9 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
return Nette\Database\Helpers::detectTypes($statement);
return $converter->resolve($meta['nativeType']);
}


Expand Down
33 changes: 14 additions & 19 deletions src/Database/Drivers/Engines/MySQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
*/
class MySQLEngine implements Engine
{
public bool $convertBoolean = true;


public function __construct(
private readonly Connection $connection,
) {
Expand Down Expand Up @@ -176,23 +173,21 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
$types = [];
$count = $statement->columnCount();
for ($col = 0; $col < $count; $col++) {
$meta = $statement->getColumnMeta($col);
if (isset($meta['native_type'])) {
$types[$meta['name']] = match (true) {
$meta['native_type'] === 'NEWDECIMAL' && $meta['precision'] === 0 => Nette\Database\IStructure::FIELD_INTEGER,
$meta['native_type'] === 'TINY' && $meta['len'] === 1 && $this->convertBoolean => Nette\Database\IStructure::FIELD_BOOL,
$meta['native_type'] === 'TIME' => Nette\Database\IStructure::FIELD_TIME_INTERVAL,
default => TypeConverter::detectType($meta['native_type']),
};
}
}

return $types;
return match ($meta['nativeType']) {
'NEWDECIMAL' => $meta['precision'] === 0
? $converter->toInt(...)
: $converter->toFloat(...), // precision in PDOStatement::getColumnMeta() means scale
'TINY' => $meta['length'] === 1 && $converter->convertBoolean
? $converter->toBool(...)
: $converter->toInt(...),
'TIME' => $converter->toInterval(...),
'DATE', 'DATETIME', 'TIMESTAMP' => fn($value): ?\DateTimeInterface => str_starts_with($value, '0000-00')
? null
: $converter->toDateTime($value),
default => $converter->resolve($meta['nativeType']),
};
}


Expand Down
5 changes: 3 additions & 2 deletions src/Database/Drivers/Engines/ODBCEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use Nette;
use Nette\Database\Drivers\Engine;
use Nette\Database\TypeConverter;


/**
Expand Down Expand Up @@ -96,9 +97,9 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
return [];
return $converter->resolve($meta['nativeType']);
}


Expand Down
5 changes: 3 additions & 2 deletions src/Database/Drivers/Engines/OracleEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Nette;
use Nette\Database\Drivers\Connection;
use Nette\Database\Drivers\Engine;
use Nette\Database\TypeConverter;


/**
Expand Down Expand Up @@ -130,9 +131,9 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
return [];
return $converter->resolve($meta['nativeType']);
}


Expand Down
10 changes: 5 additions & 5 deletions src/Database/Drivers/Engines/PostgreSQLEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Nette;
use Nette\Database\Drivers\Connection;
use Nette\Database\Drivers\Engine;
use Nette\Database\TypeConverter;


/**
Expand Down Expand Up @@ -238,12 +239,11 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
static $cache;
$item = &$cache[$statement->queryString];
$item ??= Nette\Database\Helpers::detectTypes($statement);
return $item;
return $meta['nativeType'] === 'bool'
? fn($value): bool => ($value && $value !== 'f' && $value !== 'F')
: $converter->resolve($meta['nativeType']);
}


Expand Down
23 changes: 7 additions & 16 deletions src/Database/Drivers/Engines/SQLServerEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,23 +218,14 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
$types = [];
$count = $statement->columnCount();
for ($col = 0; $col < $count; $col++) {
$meta = $statement->getColumnMeta($col);
if (
isset($meta['sqlsrv:decl_type'])
&& $meta['sqlsrv:decl_type'] !== 'timestamp'
) { // timestamp does not mean time in sqlsrv
$types[$meta['name']] = TypeConverter::detectType($meta['sqlsrv:decl_type']);
} elseif (isset($meta['native_type'])) {
$types[$meta['name']] = TypeConverter::detectType($meta['native_type']);
}
}

return $types;
return match ($meta['nativeType']) {
'timestamp' => null, // timestamp does not mean time in sqlsrv
'decimal', 'numeric',
'double', 'double precision', 'float', 'real', 'money', 'smallmoney' => fn($value): float => (float) (is_string($value) && str_starts_with($value, '.') ? '0' . $value : $value),
default => $converter->resolve($meta['nativeType']),
};
}


Expand Down
20 changes: 5 additions & 15 deletions src/Database/Drivers/Engines/SQLiteEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Nette\Database\Drivers\Engines;

use Nette;
use Nette\Database\DateTime;
use Nette\Database\Drivers\Connection;
use Nette\Database\Drivers\Engine;
use Nette\Database\TypeConverter;
Expand Down Expand Up @@ -228,22 +229,11 @@ public function getForeignKeys(string $table): array
}


public function getColumnTypes(\PDOStatement $statement): array
public function resolveColumnConverter(array $meta, TypeConverter $converter): ?\Closure
{
$types = [];
$count = $statement->columnCount();
for ($col = 0; $col < $count; $col++) {
$meta = $statement->getColumnMeta($col);
if (isset($meta['sqlite:decl_type'])) {
$types[$meta['name']] = $this->formatDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true)
? Nette\Database\IStructure::FIELD_UNIX_TIMESTAMP
: TypeConverter::detectType($meta['sqlite:decl_type']);
} elseif (isset($meta['native_type'])) {
$types[$meta['name']] = TypeConverter::detectType($meta['native_type']);
}
}

return $types;
return in_array($meta['nativeType'], ['DATE', 'DATETIME'], true)
? (fn($value): \DateTimeInterface => is_int($value) ? (new DateTime)->setTimestamp($value) : new DateTime($value))
: $converter->resolve($meta['nativeType']);
}


Expand Down
3 changes: 2 additions & 1 deletion src/Database/Drivers/PDO/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

class Connection implements Drivers\Connection
{
protected readonly PDO $pdo;
public readonly PDO $pdo;
public string $metaTypeKey = 'native_type';


public function __construct(
Expand Down
7 changes: 1 addition & 6 deletions src/Database/Drivers/PDO/MySQL/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ public function connect(): Drivers\Connection

public function createDatabaseEngine(Drivers\Connection $connection): Drivers\Engine
{
$engine = new (self::EngineClass)($connection);
$options = $this->params['options'];
if (isset($options['convertBoolean'])) {
$engine->convertBoolean = (bool) $options['convertBoolean'];
}
return $engine;
return new (self::EngineClass)($connection);
}
}
4 changes: 3 additions & 1 deletion src/Database/Drivers/PDO/SQLSrv/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public function __construct(

public function connect(): Drivers\Connection
{
return new Drivers\PDO\Connection(...$this->params);
$connection = new Drivers\PDO\Connection(...$this->params);
$connection->metaTypeKey = 'sqlsrv:decl_type';
return $connection;
}


Expand Down
4 changes: 3 additions & 1 deletion src/Database/Drivers/PDO/SQLite/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public function __construct(

public function connect(): Drivers\Connection
{
return new Drivers\PDO\Connection(...$this->params);
$connection = new Drivers\PDO\Connection(...$this->params);
$connection->metaTypeKey = 'sqlite:decl_type';
return $connection;
}


Expand Down
13 changes: 13 additions & 0 deletions src/Database/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,17 @@ public function createDriverFromDsn(

return new $class(['dsn' => $dsn, 'username' => $username, 'password' => $password, 'options' => $options]);
}


public function createTypeConverter(array &$options): TypeConverter
{
$converter = new TypeConverter;
foreach (['convertBoolean', 'newDateTime'] as $opt) {
if (isset($options[$opt])) {
$converter->$opt = (bool) $options[$opt];
unset($options[$opt]);
}
}
return $converter;
}
}
Loading

0 comments on commit 503d02c

Please sign in to comment.