packages/ztd-query-mysql/tests/Unit/Sql/Value/MySqlCastRendererTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Value;
6
7use Generator;
8use PHPUnit\Framework\Attributes\CoversClass;
9use PHPUnit\Framework\Attributes\DataProvider;
10use PHPUnit\Framework\TestCase;
11use ZtdQuery\Platform\MySql\Sql\Value\MySqlCastRenderer;
12use ZtdQuery\Schema\ColumnDeclaration;
13use ZtdQuery\Schema\ColumnTypeFamily;
14
15#[CoversClass(\ZtdQuery\Platform\MySql\Sql\Value\CastTypeResolver::class)]
16#[CoversClass(MySqlCastRenderer::class)]
17final class MySqlCastRendererTest extends TestCase
18{
19    public function testRenderCastInteger(): void
20    {
21        $renderer = new MySqlCastRenderer();
22        $type = new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT');
23        $result = $renderer->renderCast('1', $type);
24        self::assertSame('CAST(1 AS SIGNED)', $result);
25    }
26
27    public function testRenderCastPreservesYearCoercionForIntegerFamily(): void
28    {
29        $renderer = new MySqlCastRenderer();
30        self::assertSame('CAST(CAST(78 AS YEAR) AS SIGNED)', $renderer->renderCast('78', new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'YEAR')));
31        self::assertSame('CAST(CAST(69 AS YEAR) AS SIGNED)', $renderer->renderCast('69', new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'year(4)')));
32        self::assertSame('CAST(NULL AS SIGNED)', $renderer->renderNullCast(new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'YEAR')));
33    }
34
35    public function testRenderCastString(): void
36    {
37        $type = new ColumnDeclaration(ColumnTypeFamily::STRING, 'VARCHAR(255)');
38        $result = (new MySqlCastRenderer())->renderCast("'Alice'", $type);
39        self::assertSame("CAST('Alice' AS CHAR)", $result);
40    }
41
42    public function testRenderCastText(): void
43    {
44        $type = new ColumnDeclaration(ColumnTypeFamily::TEXT, 'TEXT');
45        $result = (new MySqlCastRenderer())->renderCast("'content'", $type);
46        self::assertSame("CAST('content' AS CHAR)", $result);
47    }
48
49    public function testRenderCastDecimalWithPrecision(): void
50    {
51        $type = new ColumnDeclaration(ColumnTypeFamily::DECIMAL, 'DECIMAL(10,2)');
52        $result = (new MySqlCastRenderer())->renderCast("'99.99'", $type);
53        self::assertSame("CAST('99.99' AS DECIMAL(10,2))", $result);
54    }
55
56    public function testRenderCastDecimalWithoutPrecision(): void
57    {
58        $type = new ColumnDeclaration(ColumnTypeFamily::DECIMAL, 'DECIMAL');
59        $result = (new MySqlCastRenderer())->renderCast("'99.99'", $type);
60        self::assertSame("CAST('99.99' AS DECIMAL(65,30))", $result);
61    }
62
63    public function testRenderCastDecimalWithSinglePrecision(): void
64    {
65        $type = new ColumnDeclaration(ColumnTypeFamily::DECIMAL, 'DECIMAL(10)');
66        $result = (new MySqlCastRenderer())->renderCast("'99'", $type);
67        self::assertSame("CAST('99' AS DECIMAL(10,0))", $result);
68    }
69
70    public function testRenderCastFloat(): void
71    {
72        $type = new ColumnDeclaration(ColumnTypeFamily::FLOAT, 'FLOAT');
73        $result = (new MySqlCastRenderer())->renderCast("'1.5'", $type);
74        self::assertSame("CAST('1.5' AS FLOAT)", $result);
75    }
76
77    public function testRenderCastDouble(): void
78    {
79        $type = new ColumnDeclaration(ColumnTypeFamily::DOUBLE, 'DOUBLE');
80        $result = (new MySqlCastRenderer())->renderCast("'1.5'", $type);
81        self::assertSame("CAST('1.5' AS DOUBLE)", $result);
82    }
83
84    public function testRenderCastBoolean(): void
85    {
86        $type = new ColumnDeclaration(ColumnTypeFamily::BOOLEAN, 'BOOLEAN');
87        $result = (new MySqlCastRenderer())->renderCast("'1'", $type);
88        self::assertSame("CAST('1' AS UNSIGNED)", $result);
89    }
90
91    public function testRenderCastDate(): void
92    {
93        $type = new ColumnDeclaration(ColumnTypeFamily::DATE, 'DATE');
94        $result = (new MySqlCastRenderer())->renderCast("'2024-01-01'", $type);
95        self::assertSame("CAST('2024-01-01' AS DATE)", $result);
96    }
97
98    public function testRenderCastDatetime(): void
99    {
100        $type = new ColumnDeclaration(ColumnTypeFamily::DATETIME, 'DATETIME');
101        $result = (new MySqlCastRenderer())->renderCast("'2024-01-01 00:00:00'", $type);
102        self::assertSame("CAST('2024-01-01 00:00:00' AS DATETIME)", $result);
103    }
104
105    public function testRenderCastTimestamp(): void
106    {
107        $type = new ColumnDeclaration(ColumnTypeFamily::TIMESTAMP, 'TIMESTAMP');
108        $result = (new MySqlCastRenderer())->renderCast("'2024-01-01 00:00:00'", $type);
109        self::assertSame("CAST('2024-01-01 00:00:00' AS DATETIME)", $result);
110    }
111
112    public function testRenderCastTime(): void
113    {
114        $type = new ColumnDeclaration(ColumnTypeFamily::TIME, 'TIME');
115        $result = (new MySqlCastRenderer())->renderCast("'12:00:00'", $type);
116        self::assertSame("CAST('12:00:00' AS TIME)", $result);
117    }
118
119    public function testRenderCastJson(): void
120    {
121        $type = new ColumnDeclaration(ColumnTypeFamily::JSON, 'JSON');
122        $result = (new MySqlCastRenderer())->renderCast("'{}'", $type);
123        self::assertSame("CAST('{}' AS JSON)", $result);
124    }
125
126    public function testRenderCastBinary(): void
127    {
128        $type = new ColumnDeclaration(ColumnTypeFamily::BINARY, 'BINARY');
129        $result = (new MySqlCastRenderer())->renderCast("'data'", $type);
130        self::assertSame("CAST('data' AS BINARY)", $result);
131    }
132
133    public function testRenderNullCastInteger(): void
134    {
135        $type = new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT');
136        $result = (new MySqlCastRenderer())->renderNullCast($type);
137        self::assertSame('CAST(NULL AS SIGNED)', $result);
138    }
139
140    public function testRenderNullCastString(): void
141    {
142        $type = new ColumnDeclaration(ColumnTypeFamily::STRING, 'VARCHAR(255)');
143        $result = (new MySqlCastRenderer())->renderNullCast($type);
144        self::assertSame('CAST(NULL AS CHAR)', $result);
145    }
146
147    public function testRenderNullCastJson(): void
148    {
149        $type = new ColumnDeclaration(ColumnTypeFamily::JSON, 'JSON');
150        $result = (new MySqlCastRenderer())->renderNullCast($type);
151        self::assertSame('CAST(NULL AS JSON)', $result);
152    }
153
154    public function testRenderCastUnknownFamilyFallsBackToNativeType(): void
155    {
156        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'YEAR');
157        $result = (new MySqlCastRenderer())->renderCast("'2024'", $type);
158        self::assertSame("CAST('2024' AS YEAR)", $result);
159    }
160
161    public function testRenderCastUnknownFamilyUnknownNativeType(): void
162    {
163        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'CUSTOM_TYPE');
164        $result = (new MySqlCastRenderer())->renderCast("'value'", $type);
165        self::assertSame("CAST('value' AS CHAR)", $result);
166    }
167
168    #[DataProvider('providerAllFamilies')]
169    public function testAllColumnTypeFamiliesProduceNonEmptyOutput(ColumnTypeFamily $family): void
170    {
171        $type = new ColumnDeclaration($family, 'TEST_TYPE');
172        $result = (new MySqlCastRenderer())->renderNullCast($type);
173        self::assertNotEmpty($result, "renderNullCast() returned empty for family {$family->value}");
174    }
175
176    #[DataProvider('providerAllFamilies')]
177    public function testOutputContainsCastKeyword(ColumnTypeFamily $family): void
178    {
179        $type = new ColumnDeclaration($family, 'TEST_TYPE');
180        $result = (new MySqlCastRenderer())->renderNullCast($type);
181        self::assertStringContainsString('CAST(', $result, "renderNullCast() missing CAST keyword for family {$family->value}");
182    }
183
184    public function testRenderCastIsDeterministic(): void
185    {
186        $type = new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT');
187        $result1 = (new MySqlCastRenderer())->renderCast('42', $type);
188        $result2 = (new MySqlCastRenderer())->renderCast('42', $type);
189        self::assertSame($result1, $result2);
190    }
191
192    #[DataProvider('providerAllFamilies')]
193    public function testRenderNullCastContainsNull(ColumnTypeFamily $family): void
194    {
195        $type = new ColumnDeclaration($family, 'TEST_TYPE');
196        $result = (new MySqlCastRenderer())->renderNullCast($type);
197        self::assertStringContainsString('NULL', $result, "renderNullCast() missing NULL for family {$family->value}");
198    }
199
200    public function testUnknownNativeIntMapsToSigned(): void
201    {
202        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'INT');
203        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
204    }
205
206    public function testUnknownNativeIntegerMapsToSigned(): void
207    {
208        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'INTEGER');
209        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
210    }
211
212    public function testUnknownNativeTinyintMapsToSigned(): void
213    {
214        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'TINYINT');
215        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
216    }
217
218    public function testUnknownNativeSmallintMapsToSigned(): void
219    {
220        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'SMALLINT');
221        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
222    }
223
224    public function testUnknownNativeMediumintMapsToSigned(): void
225    {
226        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'MEDIUMINT');
227        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
228    }
229
230    public function testUnknownNativeBigintMapsToSigned(): void
231    {
232        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'BIGINT');
233        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
234    }
235
236    public function testUnknownNativeDecimalMapsToDecimal(): void
237    {
238        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'DECIMAL(10,2)');
239        self::assertSame("CAST('1' AS DECIMAL(10,2))", (new MySqlCastRenderer())->renderCast("'1'", $type));
240    }
241
242    public function testUnknownNativeNumericMapsToDecimal(): void
243    {
244        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'NUMERIC');
245        self::assertSame("CAST('1' AS DECIMAL(65,30))", (new MySqlCastRenderer())->renderCast("'1'", $type));
246    }
247
248    public function testUnknownNativeFloatMapsToFloat(): void
249    {
250        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'FLOAT');
251        self::assertSame("CAST('1' AS FLOAT)", (new MySqlCastRenderer())->renderCast("'1'", $type));
252    }
253
254    public function testUnknownNativeDoubleMapsToDouble(): void
255    {
256        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'DOUBLE');
257        self::assertSame("CAST('1' AS DOUBLE)", (new MySqlCastRenderer())->renderCast("'1'", $type));
258    }
259
260    public function testUnknownNativeRealMapsToDouble(): void
261    {
262        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'REAL');
263        self::assertSame("CAST('1' AS DOUBLE)", (new MySqlCastRenderer())->renderCast("'1'", $type));
264    }
265
266    public function testUnknownNativeDateMapsToDate(): void
267    {
268        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'DATE');
269        self::assertSame("CAST('1' AS DATE)", (new MySqlCastRenderer())->renderCast("'1'", $type));
270    }
271
272    public function testUnknownNativeDatetimeMapsToDatetime(): void
273    {
274        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'DATETIME');
275        self::assertSame("CAST('1' AS DATETIME)", (new MySqlCastRenderer())->renderCast("'1'", $type));
276    }
277
278    public function testUnknownNativeTimestampMapsToDatetime(): void
279    {
280        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'TIMESTAMP');
281        self::assertSame("CAST('1' AS DATETIME)", (new MySqlCastRenderer())->renderCast("'1'", $type));
282    }
283
284    public function testUnknownNativeTimeMapsToTime(): void
285    {
286        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'TIME');
287        self::assertSame("CAST('1' AS TIME)", (new MySqlCastRenderer())->renderCast("'1'", $type));
288    }
289
290    public function testUnknownNativeJsonMapsToJson(): void
291    {
292        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'JSON');
293        self::assertSame("CAST('1' AS JSON)", (new MySqlCastRenderer())->renderCast("'1'", $type));
294    }
295
296    public function testUnknownNativeBinaryMapsToBinary(): void
297    {
298        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'BINARY');
299        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
300    }
301
302    public function testUnknownNativeVarbinaryMapsToBinary(): void
303    {
304        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'VARBINARY');
305        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
306    }
307
308    public function testUnknownNativeBlobMapsToBinary(): void
309    {
310        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'BLOB');
311        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
312    }
313
314    public function testUnknownNativeTinyblobMapsToBinary(): void
315    {
316        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'TINYBLOB');
317        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
318    }
319
320    public function testUnknownNativeMediumblobMapsToBinary(): void
321    {
322        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'MEDIUMBLOB');
323        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
324    }
325
326    public function testUnknownNativeLongblobMapsToBinary(): void
327    {
328        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'LONGBLOB');
329        self::assertSame("CAST('1' AS BINARY)", (new MySqlCastRenderer())->renderCast("'1'", $type));
330    }
331
332    public function testUnknownNativeWithParenthesesStrippedForMatch(): void
333    {
334        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'INT(11)');
335        self::assertSame("CAST('1' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'1'", $type));
336    }
337
338    public function testRenderCastExpressionContainedInOutput(): void
339    {
340        $type = new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT');
341        $result = (new MySqlCastRenderer())->renderCast('my_column', $type);
342        self::assertStringContainsString('my_column', $result);
343    }
344
345    public function testRenderNullCastDecimal(): void
346    {
347        $type = new ColumnDeclaration(ColumnTypeFamily::DECIMAL, 'DECIMAL(10,2)');
348        $result = (new MySqlCastRenderer())->renderNullCast($type);
349        self::assertSame('CAST(NULL AS DECIMAL(10,2))', $result);
350    }
351
352    public function testRenderNullCastFloat(): void
353    {
354        $type = new ColumnDeclaration(ColumnTypeFamily::FLOAT, 'FLOAT');
355        $result = (new MySqlCastRenderer())->renderNullCast($type);
356        self::assertSame('CAST(NULL AS FLOAT)', $result);
357    }
358
359    public function testRenderNullCastDouble(): void
360    {
361        $type = new ColumnDeclaration(ColumnTypeFamily::DOUBLE, 'DOUBLE');
362        $result = (new MySqlCastRenderer())->renderNullCast($type);
363        self::assertSame('CAST(NULL AS DOUBLE)', $result);
364    }
365
366    public function testRenderNullCastBoolean(): void
367    {
368        $type = new ColumnDeclaration(ColumnTypeFamily::BOOLEAN, 'BOOLEAN');
369        $result = (new MySqlCastRenderer())->renderNullCast($type);
370        self::assertSame('CAST(NULL AS UNSIGNED)', $result);
371    }
372
373    public function testRenderNullCastDate(): void
374    {
375        $type = new ColumnDeclaration(ColumnTypeFamily::DATE, 'DATE');
376        $result = (new MySqlCastRenderer())->renderNullCast($type);
377        self::assertSame('CAST(NULL AS DATE)', $result);
378    }
379
380    public function testRenderNullCastDatetime(): void
381    {
382        $type = new ColumnDeclaration(ColumnTypeFamily::DATETIME, 'DATETIME');
383        $result = (new MySqlCastRenderer())->renderNullCast($type);
384        self::assertSame('CAST(NULL AS DATETIME)', $result);
385    }
386
387    public function testRenderNullCastTimestamp(): void
388    {
389        $type = new ColumnDeclaration(ColumnTypeFamily::TIMESTAMP, 'TIMESTAMP');
390        $result = (new MySqlCastRenderer())->renderNullCast($type);
391        self::assertSame('CAST(NULL AS DATETIME)', $result);
392    }
393
394    public function testRenderNullCastTime(): void
395    {
396        $type = new ColumnDeclaration(ColumnTypeFamily::TIME, 'TIME');
397        $result = (new MySqlCastRenderer())->renderNullCast($type);
398        self::assertSame('CAST(NULL AS TIME)', $result);
399    }
400
401    public function testRenderNullCastBinary(): void
402    {
403        $type = new ColumnDeclaration(ColumnTypeFamily::BINARY, 'BINARY');
404        $result = (new MySqlCastRenderer())->renderNullCast($type);
405        self::assertSame('CAST(NULL AS BINARY)', $result);
406    }
407
408    public function testRenderNullCastText(): void
409    {
410        $type = new ColumnDeclaration(ColumnTypeFamily::TEXT, 'TEXT');
411        $result = (new MySqlCastRenderer())->renderNullCast($type);
412        self::assertSame('CAST(NULL AS CHAR)', $result);
413    }
414
415    public function testRenderCastDecimalWithLowercaseNativeType(): void
416    {
417        $type = new ColumnDeclaration(ColumnTypeFamily::DECIMAL, 'decimal(10,2)');
418        $result = (new MySqlCastRenderer())->renderCast("'1'", $type);
419        self::assertSame("CAST('1' AS DECIMAL(10,2))", $result);
420    }
421
422    public function testRenderCastUnknownFamilyWithLowercaseNativeType(): void
423    {
424        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'int');
425        $result = (new MySqlCastRenderer())->renderCast("'1'", $type);
426        self::assertSame("CAST('1' AS SIGNED)", $result);
427    }
428
429    public function testRenderCastUnknownFamilyWithLowercaseNativeTypeWithParams(): void
430    {
431        $type = new ColumnDeclaration(ColumnTypeFamily::UNKNOWN, 'int(11)');
432        $result = (new MySqlCastRenderer())->renderCast("'1'", $type);
433        self::assertSame("CAST('1' AS SIGNED)", $result);
434    }
435
436    #[DataProvider('providerFamilyWithExpectedCastType')]
437    public function testEachFamilyProducesDistinctCastType(ColumnTypeFamily $family, string $expectedType): void
438    {
439        $type = new ColumnDeclaration($family, 'NATIVE');
440        $result = (new MySqlCastRenderer())->renderCast("'x'", $type);
441        self::assertSame("CAST('x' AS $expectedType)", $result, "Family {$family->value} did not produce expected cast type");
442    }
443
444    /**
445     * @return Generator<string, array{ColumnTypeFamily}>
446     */
447    public static function providerAllFamilies(): Generator
448    {
449        foreach (ColumnTypeFamily::cases() as $family) {
450            yield $family->value => [$family];
451        }
452    }
453
454    /**
455     * @return Generator<string, array{ColumnTypeFamily, string}>
456     */
457    public static function providerFamilyWithExpectedCastType(): Generator
458    {
459        yield 'INTEGER' => [ColumnTypeFamily::INTEGER, 'SIGNED'];
460        yield 'FLOAT' => [ColumnTypeFamily::FLOAT, 'FLOAT'];
461        yield 'DOUBLE' => [ColumnTypeFamily::DOUBLE, 'DOUBLE'];
462        yield 'BOOLEAN' => [ColumnTypeFamily::BOOLEAN, 'UNSIGNED'];
463        yield 'DATE' => [ColumnTypeFamily::DATE, 'DATE'];
464        yield 'DATETIME' => [ColumnTypeFamily::DATETIME, 'DATETIME'];
465        yield 'TIME' => [ColumnTypeFamily::TIME, 'TIME'];
466        yield 'JSON' => [ColumnTypeFamily::JSON, 'JSON'];
467        yield 'BINARY' => [ColumnTypeFamily::BINARY, 'BINARY'];
468        yield 'STRING' => [ColumnTypeFamily::STRING, 'CHAR'];
469    }
470
471    /**
472     * @return iterable<string, array{ColumnTypeFamily, string}>
473     */
474    public static function providerNativeTypes(): iterable
475    {
476        yield 'integer' => [ColumnTypeFamily::INTEGER, 'INT'];
477        yield 'float' => [ColumnTypeFamily::FLOAT, 'FLOAT'];
478        yield 'double' => [ColumnTypeFamily::DOUBLE, 'DOUBLE'];
479        yield 'decimal' => [ColumnTypeFamily::DECIMAL, 'DECIMAL(10,2)'];
480        yield 'string' => [ColumnTypeFamily::STRING, 'VARCHAR(255)'];
481        yield 'text' => [ColumnTypeFamily::TEXT, 'TEXT'];
482        yield 'boolean' => [ColumnTypeFamily::BOOLEAN, 'TINYINT(1)'];
483        yield 'date' => [ColumnTypeFamily::DATE, 'DATE'];
484        yield 'time' => [ColumnTypeFamily::TIME, 'TIME'];
485        yield 'datetime' => [ColumnTypeFamily::DATETIME, 'DATETIME'];
486        yield 'timestamp' => [ColumnTypeFamily::TIMESTAMP, 'TIMESTAMP'];
487        yield 'binary' => [ColumnTypeFamily::BINARY, 'BLOB'];
488        yield 'json' => [ColumnTypeFamily::JSON, 'JSON'];
489        yield 'unknown' => [ColumnTypeFamily::UNKNOWN, 'GEOMETRY'];
490    }
491
492    #[DataProvider('providerNativeTypes')]
493    public function testRenderCastHandlesEveryNativeType(ColumnTypeFamily $family, string $nativeType): void
494    {
495        $renderer = new MySqlCastRenderer();
496        $type = new ColumnDeclaration($family, $nativeType);
497
498        self::assertNotEmpty($renderer->renderCast("'test'", $type));
499        self::assertNotEmpty($renderer->renderCast("'value'", $type));
500        self::assertNotEmpty($renderer->renderNullCast($type));
501        self::assertStringContainsString('CAST(', $renderer->renderNullCast($type));
502        self::assertStringContainsString('NULL', $renderer->renderNullCast($type));
503    }
504
505    public function testRenderCastForIntegerProducesExactForm(): void
506    {
507        self::assertSame("CAST('42' AS SIGNED)", (new MySqlCastRenderer())->renderCast("'42'", new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT')));
508    }
509
510    public function testRenderNullCastProducesExactForm(): void
511    {
512        self::assertSame('CAST(NULL AS SIGNED)', (new MySqlCastRenderer())->renderNullCast(new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT')));
513    }
514
515    public function testRenderCastForStringUsesCharType(): void
516    {
517        self::assertSame("CAST('hello' AS CHAR)", (new MySqlCastRenderer())->renderCast("'hello'", new ColumnDeclaration(ColumnTypeFamily::STRING, 'VARCHAR(255)')));
518    }
519}
520