Skip to content

Commit 4058134

Browse files
authored
Realize Schema::loadResultColumn() method (#316)
1 parent 873645b commit 4058134

File tree

8 files changed

+376
-21
lines changed

8 files changed

+376
-21
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- Enh #315: Remove `getCacheKey()` and `getCacheTag()` methods from `Schema` class (@Tigrov)
4444
- Enh #319: Support `boolean` type (@Tigrov)
4545
- Enh #318, #320: Use `DbArrayHelper::arrange()` instead of `DbArrayHelper::index()` method (@Tigrov)
46+
- New #316: Realize `Schema::loadResultColumn()` method (@Tigrov)
4647

4748
## 1.3.0 March 21, 2024
4849

src/Schema.php

+66
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Throwable;
88
use Yiisoft\Db\Cache\SchemaCache;
99
use Yiisoft\Db\Connection\ConnectionInterface;
10+
use Yiisoft\Db\Constant\ColumnType;
1011
use Yiisoft\Db\Constant\ReferentialAction;
1112
use Yiisoft\Db\Constraint\CheckConstraint;
1213
use Yiisoft\Db\Constraint\Constraint;
@@ -25,6 +26,7 @@
2526
use function array_map;
2627
use function array_reverse;
2728
use function implode;
29+
use function in_array;
2830
use function is_array;
2931
use function preg_replace;
3032
use function strtolower;
@@ -175,6 +177,70 @@ protected function findTableNames(string $schema = ''): array
175177
return $names;
176178
}
177179

180+
/**
181+
* @param array{
182+
* "oci:decl_type": int|string,
183+
* native_type: string,
184+
* pdo_type: int,
185+
* scale: int,
186+
* table?: string,
187+
* flags: string[],
188+
* name: string,
189+
* len: int,
190+
* precision: int,
191+
* } $metadata
192+
*
193+
* @psalm-suppress MoreSpecificImplementedParamType
194+
*/
195+
protected function loadResultColumn(array $metadata): ColumnInterface|null
196+
{
197+
if (empty($metadata['oci:decl_type'])) {
198+
return null;
199+
}
200+
201+
$dbType = match ($metadata['oci:decl_type']) {
202+
119 => 'json',
203+
'TIMESTAMP WITH TIMEZONE' => 'timestamp with time zone',
204+
'TIMESTAMP WITH LOCAL TIMEZONE' => 'timestamp with local time zone',
205+
default => strtolower((string) $metadata['oci:decl_type']),
206+
};
207+
208+
$columnInfo = ['fromResult' => true];
209+
210+
if (!empty($metadata['table'])) {
211+
$columnInfo['table'] = $metadata['table'];
212+
$columnInfo['name'] = $metadata['name'];
213+
} elseif (!empty($metadata['name'])) {
214+
$columnInfo['name'] = $metadata['name'];
215+
}
216+
217+
if ($metadata['pdo_type'] === 3) {
218+
$columnInfo['type'] = ColumnType::BINARY;
219+
}
220+
221+
if (!empty($metadata['precision'])) {
222+
$columnInfo['size'] = $metadata['precision'];
223+
}
224+
225+
/** @psalm-suppress PossiblyUndefinedArrayOffset, InvalidArrayOffset */
226+
match ($dbType) {
227+
'timestamp',
228+
'timestamp with time zone',
229+
'timestamp with local time zone' => $columnInfo['size'] = $metadata['scale'],
230+
'interval day to second',
231+
'interval year to month' =>
232+
[$columnInfo['size'], $columnInfo['scale']] = [$metadata['scale'], $metadata['precision']],
233+
'number' => $metadata['scale'] !== -127 ? $columnInfo['scale'] = $metadata['scale'] : null,
234+
'float' => null,
235+
default => $columnInfo['size'] = $metadata['len'],
236+
};
237+
238+
$columnInfo['notNull'] = in_array('not_null', $metadata['flags'], true);
239+
240+
/** @psalm-suppress MixedArgumentTypeCoercion */
241+
return $this->db->getColumnFactory()->fromDbType($dbType, $columnInfo);
242+
}
243+
178244
/**
179245
* @throws Exception
180246
* @throws InvalidConfigException

tests/ColumnTest.php

+132-16
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Yiisoft\Db\Expression\Expression;
1111
use Yiisoft\Db\Oracle\Column\BinaryColumn;
1212
use Yiisoft\Db\Oracle\Column\JsonColumn;
13+
use Yiisoft\Db\Oracle\Connection;
1314
use Yiisoft\Db\Oracle\Tests\Provider\ColumnProvider;
1415
use Yiisoft\Db\Oracle\Tests\Support\TestTrait;
1516
use Yiisoft\Db\Query\Query;
@@ -29,6 +30,135 @@ final class ColumnTest extends AbstractColumnTest
2930
{
3031
use TestTrait;
3132

33+
private function insertTypeValues(Connection $db): void
34+
{
35+
$db->createCommand()->insert(
36+
'type',
37+
[
38+
'int_col' => 1,
39+
'char_col' => str_repeat('x', 100),
40+
'char_col3' => null,
41+
'float_col' => 1.234,
42+
'blob_col' => "\x10\x11\x12",
43+
'timestamp_col' => new Expression("TIMESTAMP '2023-07-11 14:50:23'"),
44+
'bool_col' => false,
45+
'bit_col' => 0b0110_0110, // 102
46+
'json_col' => [['a' => 1, 'b' => null, 'c' => [1, 3, 5]]],
47+
]
48+
)->execute();
49+
}
50+
51+
private function assertResultValues(array $result, string $varsion): void
52+
{
53+
$this->assertSame(1, $result['int_col']);
54+
$this->assertSame(str_repeat('x', 100), $result['char_col']);
55+
$this->assertNull($result['char_col3']);
56+
$this->assertSame(1.234, $result['float_col']);
57+
$this->assertSame("\x10\x11\x12", stream_get_contents($result['blob_col']));
58+
$this->assertEquals(false, $result['bool_col']);
59+
$this->assertSame(0b0110_0110, $result['bit_col']);
60+
61+
if (version_compare($varsion, '21', '>=')) {
62+
$this->assertSame([['a' => 1, 'b' => null, 'c' => [1, 3, 5]]], $result['json_col']);
63+
} else {
64+
$this->assertSame('[{"a":1,"b":null,"c":[1,3,5]}]', stream_get_contents($result['json_col']));
65+
}
66+
}
67+
68+
public function testQueryWithTypecasting(): void
69+
{
70+
$db = $this->getConnection();
71+
$varsion = $db->getServerInfo()->getVersion();
72+
$db->close();
73+
74+
if (version_compare($varsion, '21', '>=')) {
75+
$this->fixture = 'oci21.sql';
76+
}
77+
78+
$db = $this->getConnection(true);
79+
80+
$this->insertTypeValues($db);
81+
82+
$query = (new Query($db))->from('type')->withTypecasting();
83+
84+
$result = $query->one();
85+
86+
$this->assertResultValues($result, $varsion);
87+
88+
$result = $query->all();
89+
90+
$this->assertResultValues($result[0], $varsion);
91+
92+
$db->close();
93+
}
94+
95+
public function testCommandWithPhpTypecasting(): void
96+
{
97+
$db = $this->getConnection();
98+
$varsion = $db->getServerInfo()->getVersion();
99+
$db->close();
100+
101+
if (version_compare($varsion, '21', '>=')) {
102+
$this->fixture = 'oci21.sql';
103+
}
104+
105+
$db = $this->getConnection(true);
106+
107+
$this->insertTypeValues($db);
108+
109+
$command = $db->createCommand('SELECT * FROM "type"');
110+
111+
$result = $command->withPhpTypecasting()->queryOne();
112+
113+
$this->assertResultValues($result, $varsion);
114+
115+
$result = $command->withPhpTypecasting()->queryAll();
116+
117+
$this->assertResultValues($result[0], $varsion);
118+
119+
$db->close();
120+
}
121+
122+
public function testSelectWithPhpTypecasting(): void
123+
{
124+
$db = $this->getConnection();
125+
126+
$sql = "SELECT null, 1, 2.5, 'string' FROM DUAL";
127+
128+
$expected = [
129+
'NULL' => null,
130+
1 => 1.0,
131+
'2.5' => 2.5,
132+
"'STRING'" => 'string',
133+
];
134+
135+
$result = $db->createCommand($sql)
136+
->withPhpTypecasting()
137+
->queryOne();
138+
139+
$this->assertSame($expected, $result);
140+
141+
$result = $db->createCommand($sql)
142+
->withPhpTypecasting()
143+
->queryAll();
144+
145+
$this->assertSame([$expected], $result);
146+
147+
$result = $db->createCommand('SELECT 2.5 FROM DUAL')
148+
->withPhpTypecasting()
149+
->queryScalar();
150+
151+
$this->assertSame(2.5, $result);
152+
153+
$result = $db->createCommand('SELECT 2.5 FROM DUAL UNION SELECT 3.3 FROM DUAL')
154+
->withPhpTypecasting()
155+
->queryColumn();
156+
157+
$this->assertSame([2.5, 3.3], $result);
158+
159+
$db->close();
160+
}
161+
32162
public function testPhpTypeCast(): void
33163
{
34164
$db = $this->getConnection();
@@ -39,26 +169,12 @@ public function testPhpTypeCast(): void
39169

40170
$db->close();
41171
$db = $this->getConnection(true);
42-
43-
$command = $db->createCommand();
44172
$schema = $db->getSchema();
45173
$tableSchema = $schema->getTableSchema('type');
46174

47-
$command->insert('type', [
48-
'int_col' => 1,
49-
'char_col' => str_repeat('x', 100),
50-
'char_col3' => null,
51-
'float_col' => 1.234,
52-
'blob_col' => "\x10\x11\x12",
53-
'timestamp_col' => new Expression("TIMESTAMP '2023-07-11 14:50:23'"),
54-
'bool_col' => false,
55-
'bit_col' => 0b0110_0110, // 102
56-
'json_col' => [['a' => 1, 'b' => null, 'c' => [1, 3, 5]]],
57-
]);
58-
$command->execute();
59-
$query = (new Query($db))->from('type')->one();
175+
$this->insertTypeValues($db);
60176

61-
$this->assertNotNull($tableSchema);
177+
$query = (new Query($db))->from('type')->one();
62178

63179
$intColPhpType = $tableSchema->getColumn('int_col')?->phpTypecast($query['int_col']);
64180
$charColPhpType = $tableSchema->getColumn('char_col')?->phpTypecast($query['char_col']);

0 commit comments

Comments
 (0)