packages/sql-fixture/src/Platform/PostgreSql/Schema/CatalogColumn.php
1<?php
2
3declare(strict_types=1);
4
5namespace SqlFixture\Platform\PostgreSql\Schema;
6
7/**
8 * Interprets PostgreSQL catalog type and default metadata.
9 *
10 * @visibility root
11 */
12final class CatalogColumn
13{
14 /**
15 * @param array{data_type: string, character_maximum_length: ?string, numeric_precision: ?string, numeric_scale: ?string, udt_name: string} $col
16 */
17 public function mapDataType(array $col): string
18 {
19 $type = strtoupper($col['data_type']);
20
21 if ($type === 'CHARACTER VARYING' && $col['character_maximum_length'] !== null) {
22 return 'VARCHAR(' . $col['character_maximum_length'] . ')';
23 }
24
25 if ($type === 'CHARACTER' && $col['character_maximum_length'] !== null) {
26 return 'CHAR(' . $col['character_maximum_length'] . ')';
27 }
28
29 if ($type === 'NUMERIC' && $col['numeric_precision'] !== null) {
30 $precision = $col['numeric_precision'];
31 if ($col['numeric_scale'] !== null && $col['numeric_scale'] !== '0') {
32 return "NUMERIC({$precision}, {$col['numeric_scale']})";
33 }
34 return "NUMERIC({$precision})";
35 }
36
37 if ($type === 'ARRAY') {
38 return $this->elementType($col['udt_name']) . '[]';
39 }
40
41 if ($type === 'USER-DEFINED') {
42 return strtoupper($col['udt_name']);
43 }
44
45 return $type;
46 }
47
48 /**
49 * Names the declared type of an array element from the catalog type of the array, such as `_int4`.
50 *
51 * @param string $udtName Catalog type name of the array, with its leading underscore
52 *
53 * @return string The type name a CREATE TABLE declares, such as `INTEGER`
54 */
55 public function elementType(string $udtName): string
56 {
57 $element = strtolower(ltrim($udtName, '_'));
58
59 return match ($element) {
60 'int2' => 'SMALLINT',
61 'int4' => 'INTEGER',
62 'int8' => 'BIGINT',
63 'float4' => 'REAL',
64 'float8' => 'DOUBLE PRECISION',
65 'bool' => 'BOOLEAN',
66 'varchar' => 'VARCHAR',
67 'bpchar' => 'CHAR',
68 default => strtoupper($element),
69 };
70 }
71
72 /**
73 * @param array{data_type: string, udt_name: string, character_maximum_length: ?string, numeric_precision: ?string, numeric_scale: ?string} $row
74 */
75 public function resolveType(array $row): string
76 {
77 $type = strtoupper($row['data_type']);
78
79 if ($type === 'ARRAY') {
80 return $this->elementType($row['udt_name']) . '_ARRAY';
81 }
82
83 if ($type === 'USER-DEFINED') {
84 return strtoupper($row['udt_name']);
85 }
86
87 return $type;
88 }
89
90 /**
91 * Parses default.
92 */
93 public function parseDefault(?string $value): int|float|bool|string|null
94 {
95 if ($value === null) {
96 return null;
97 }
98
99 if (strtoupper($value) === 'NULL' || strtoupper($value) === 'NULL::') {
100 return null;
101 }
102
103 if (str_contains($value, 'nextval(')) {
104 return null;
105 }
106
107 if (preg_match("/^'(.*)'::.*$/s", $value, $matches) === 1) {
108 return $matches[1];
109 }
110
111 if (preg_match("/^'(.*)'$/s", $value, $matches) === 1) {
112 return $matches[1];
113 }
114
115 if (strtolower($value) === 'true') {
116 return true;
117 }
118 if (strtolower($value) === 'false') {
119 return false;
120 }
121
122 if (is_numeric($value)) {
123 if (str_contains($value, '.')) {
124 return (float) $value;
125 }
126 return (int) $value;
127 }
128
129 return $value;
130 }
131}
132