From c9ce9fd321572d63b361ff28049d701009c454c5 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Fri, 15 Nov 2024 17:04:50 -0600 Subject: [PATCH] Do not attempt to parse int2vector and oidvector Fixes #68 --- src/Internal/PgSqlHandle.php | 18 +++++++++---- src/Internal/PgSqlResultIterator.php | 38 +++++++++++++++++----------- src/Internal/PgSqlType.php | 5 ++-- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/Internal/PgSqlHandle.php b/src/Internal/PgSqlHandle.php index e37bd55..b0b5fb8 100644 --- a/src/Internal/PgSqlHandle.php +++ b/src/Internal/PgSqlHandle.php @@ -175,7 +175,7 @@ public function __construct( */ private static function fetchTypes(\PgSql\Connection $handle): array { - $result = \pg_query($handle, "SELECT t.oid, t.typcategory, t.typdelim, t.typelem + $result = \pg_query($handle, "SELECT t.oid, t.typcategory, t.typname, t.typdelim, t.typelem FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON t.typnamespace=n.oid WHERE t.typisdefined AND n.nspname IN ('pg_catalog', 'public') ORDER BY t.oid"); @@ -185,10 +185,18 @@ private static function fetchTypes(\PgSql\Connection $handle): array $types = []; while ($row = \pg_fetch_array($result, mode: \PGSQL_NUM)) { - [$oid, $type, $delimiter, $element] = $row; - \assert(\is_numeric($oid) && \is_numeric($element), "OID and element type expected to be integers"); - \assert(\is_string($type) && \is_string($delimiter), "Unexpected types in type catalog query results"); - $types[(int) $oid] = new PgSqlType($type, $delimiter, (int) $element); + [$oid, $typeCategory, $typeName, $delimiter, $element] = $row; + + \assert( + \is_numeric($oid) && \is_numeric($element), + "OID and element type expected to be integers", + ); + \assert( + \is_string($typeCategory) && \is_string($typeName) && \is_string($delimiter), + "Unexpected types in type catalog query results", + ); + + $types[(int) $oid] = new PgSqlType($typeCategory, $typeName, $delimiter, (int) $element); } return $types; diff --git a/src/Internal/PgSqlResultIterator.php b/src/Internal/PgSqlResultIterator.php index 856a04f..d970ffc 100644 --- a/src/Internal/PgSqlResultIterator.php +++ b/src/Internal/PgSqlResultIterator.php @@ -81,23 +81,31 @@ private function cast(int $oid, ?string $value): array|bool|int|float|string|nul $type = $this->types[$oid] ?? PgSqlType::getDefaultType(); - return match ($type->type) { - 'A' => ArrayParser::parse( // Array - $value, - fn (string $data) => $this->cast($type->element, $data), - $type->delimiter, - ), - 'B' => $value === 't', // Boolean - 'N' => match ($oid) { // Numeric - 700, 701 => (float) $value, // "float4" and "float8" to float - 1700 => $value, // Return "numeric" as string to retain precision - 790 => $value, // "money" includes currency symbol as string - default => (int) $value, // All other numeric types cast to an integer + return match ($type->category) { + 'A' => match ($type->name) { // Array + 'int2vector', 'oidvector' => $value, // Deprecated array types + default => ArrayParser::parse( + $value, + fn (string $data) => $this->cast($type->element, $data), + $type->delimiter, + ), }, - default => match ($oid) { // String - 17 => \pg_unescape_bytea($value), - default => $value, // Return a string for all other types + 'B' => match ($value) { + 't' => true, + 'f' => false, + default => throw new PostgresParseException('Unexpected value for boolean field: ' . $value), + }, // Boolean + 'N' => match ($type->name) { // Numeric + 'float4', 'float8' => (float) $value, + 'int2', 'int4', 'oid' => (int) $value, + 'int8' => \PHP_INT_SIZE >= 8 ? (int) $value : $value, // String on 32-bit systems + default => $value, // Return a string for all other numeric types }, + 'U' => match ($type->name) { + 'bytea' => \pg_unescape_bytea($value), + default => $value, + }, + default => $value, // Return a string for all other types }; } } diff --git a/src/Internal/PgSqlType.php b/src/Internal/PgSqlType.php index f158ff7..6cd3b10 100644 --- a/src/Internal/PgSqlType.php +++ b/src/Internal/PgSqlType.php @@ -8,7 +8,8 @@ final class PgSqlType private static ?self $default = null; public function __construct( - public readonly string $type, + public readonly string $category, + public readonly string $name, public readonly string $delimiter, public readonly int $element, ) { @@ -16,6 +17,6 @@ public function __construct( public static function getDefaultType(): self { - return self::$default ??= new self('S', ',', 0); + return self::$default ??= new self('S', 'text', ',', 0); } }