packages/ztd-query-mysql/tests/Unit/Sql/Value/MySqlValueRendererTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql\Value;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use RuntimeException;
11use Stringable;
12use ZtdQuery\Platform\MySql\Sql\Value\MySqlValueRenderer;
13use ZtdQuery\Schema\ColumnDeclaration;
14use ZtdQuery\Schema\ColumnTypeFamily;
15
16#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\CastTypeResolver::class)]
17
18
19#[CoversClass(MySqlValueRenderer::class)]
20#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\MySqlCastRenderer::class)]
21#[CoversClass(\ZtdQuery\Platform\MySql\Sql\Value\ScalarExpression::class)]
22#[CoversClass(\ZtdQuery\Platform\MySql\Sql\Value\StringCoercion::class)]
23final class MySqlValueRendererTest extends TestCase
24{
25 public function testRenderValueTextUsesHexEncodingInsteadOfSqlEscapeMode(): void
26 {
27 $renderer = new MySqlValueRenderer();
28
29 self::assertSame(
30 "CAST(CONVERT(X'706174685c746f5c66696c65' USING utf8mb4) AS CHAR)",
31 $renderer->renderValue('path\\to\\file', new ColumnDeclaration(ColumnTypeFamily::STRING, 'VARCHAR(255)')),
32 );
33 }
34
35 public function testBinaryUsesLosslessHexLiteral(): void
36 {
37 $renderer = new MySqlValueRenderer();
38
39 self::assertSame(
40 "CAST(X'0001ff' AS BINARY)",
41 $renderer->renderValue("\x00\x01\xFF", new ColumnDeclaration(ColumnTypeFamily::BINARY, 'BLOB')),
42 );
43 }
44
45 public function testNullRetainsDeclaredType(): void
46 {
47 $renderer = new MySqlValueRenderer();
48
49 self::assertSame('NULL', $renderer->renderValue(null, new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT')));
50 }
51
52 public function testInferredFloatUsesRoundTripRepresentation(): void
53 {
54 $renderer = new MySqlValueRenderer();
55
56 self::assertSame('2.718281828459045', $renderer->renderValue(2.718281828459045));
57 }
58
59 public function testInferredAndDeclaredBooleansRemainDistinct(): void
60 {
61 $renderer = new MySqlValueRenderer();
62
63 self::assertSame('TRUE', $renderer->renderValue(true));
64 self::assertSame(
65 "CAST('1' AS UNSIGNED)",
66 $renderer->renderValue(true, new ColumnDeclaration(ColumnTypeFamily::BOOLEAN, 'BOOLEAN')),
67 );
68 }
69
70 public function testInferredAndDeclaredIntegersRemainDistinct(): void
71 {
72 $renderer = new MySqlValueRenderer();
73
74 self::assertSame('CAST(42 AS SIGNED)', $renderer->renderValue(42));
75 self::assertSame(
76 "CAST('42' AS SIGNED)",
77 $renderer->renderValue(42, new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT')),
78 );
79 }
80
81 public function testInferredStringableUsesItsSqlRepresentation(): void
82 {
83 $value = new class () implements Stringable {
84 public function __toString(): string
85 {
86 return 'CURRENT_TIMESTAMP';
87 }
88 };
89
90 self::assertSame('CURRENT_TIMESTAMP', (new MySqlValueRenderer())->renderValue($value));
91 }
92
93 public function testTextEscapesSingleQuotes(): void
94 {
95 self::assertSame(
96 "CAST('O''Reilly' AS CHAR)",
97 (new MySqlValueRenderer())->renderValue("O'Reilly"),
98 );
99 }
100
101 public function testStreamValueIsReadWithoutChangingItsPosition(): void
102 {
103 $stream = fopen('php://memory', 'r+');
104 self::assertIsResource($stream);
105 fwrite($stream, 'stream value');
106 fseek($stream, 3);
107
108 self::assertSame(
109 "CAST('stream value' AS CHAR)",
110 (new MySqlValueRenderer())->renderValue($stream),
111 );
112 self::assertSame(3, ftell($stream));
113 fclose($stream);
114 }
115
116 public function testRejectsNonScalarValues(): void
117 {
118 $renderer = new MySqlValueRenderer();
119
120 $this->expectException(RuntimeException::class);
121 $renderer->renderValue([]);
122 }
123 public function testStoredYearZeroIsNotReinterpretedAsAStringYear(): void
124 {
125 $renderer = new MySqlValueRenderer();
126 $year = new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'year(4)');
127 self::assertSame("CAST('0' AS SIGNED)", $renderer->renderValue(0, $year));
128 self::assertSame("CAST('0' AS SIGNED)", $renderer->renderValue('0', $year));
129 self::assertSame("CAST('1978' AS SIGNED)", $renderer->renderValue(1978, $year));
130 }
131
132 public function testStringFamilyYearRemainsTextual(): void
133 {
134 self::assertSame("CAST('0' AS CHAR)", (new MySqlValueRenderer())->renderValue('0', new ColumnDeclaration(ColumnTypeFamily::STRING, 'YEAR')));
135 }
136
137}
138