packages/ztd-query-mysql/tests/Unit/Rewrite/Transformer/MySqlTransformerTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Rewrite\Transformer;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\MySql\Rewrite\Transformer\DeleteTransformer;
11use ZtdQuery\Platform\MySql\Rewrite\Transformer\InsertTransformer;
12use ZtdQuery\Platform\MySql\Rewrite\Transformer\MySqlTransformer;
13use ZtdQuery\Platform\MySql\Rewrite\Transformer\ReplaceTransformer;
14use ZtdQuery\Platform\MySql\Rewrite\Transformer\SelectTransformer;
15use ZtdQuery\Platform\MySql\Rewrite\Transformer\UpdateTransformer;
16use ZtdQuery\Platform\MySql\Sql\Dml\DmlWhereClauseExtractor;
17use ZtdQuery\Platform\MySql\Sql\Dml\InsertSelectSourceExtractor;
18use ZtdQuery\Platform\MySql\Sql\Dml\UpdateSourceExtractor;
19use ZtdQuery\Platform\MySql\Sql\MySqlIdentifierQuoter;
20use ZtdQuery\Platform\MySql\Sql\MySqlParser;
21use ZtdQuery\Platform\MySql\Sql\Upsert\MySqlUpsertAssignmentExtractor;
22use ZtdQuery\Platform\MySql\Sql\Value\MySqlCastRenderer;
23
24#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Partition\MySqlPartitionSelectionRewriter::class)]
25#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Dml\AssignmentExpression::class)]
26#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Cte\HeaderParser::class)]
27#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Cte\IdentifierReferences::class)]
28#[UsesClass(\ZtdQuery\Platform\MySql\Sql\OptionalInsertIntoNormalizer::class)]
29#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Relation\ExpressionNames::class)]
30#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Relation\ReferenceReader::class)]
31#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Upsert\AssignmentReader::class)]
32#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\FullText\ExpressionEditor::class)]
33#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Partition\SelectionReader::class)]
34#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Partition\SourceProjection::class)]
35#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Upsert\ConflictPredicate::class)]
36#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Upsert\ExpressionBinder::class)]
37#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Upsert\MetadataColumns::class)]
38#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Upsert\QualifiedColumn::class)]
39#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Delete\ResultSelect::class)]
40#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Delete\TargetProjection::class)]
41#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Insert\InsertTarget::class)]
42#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Insert\ReplaceStatementConverter::class)]
43#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Insert\ResultProjection::class)]
44#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Select\ExpressionAliaser::class)]
45#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Set\OrderRewriter::class)]
46#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Set\ValueNormalizer::class)]
47#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Shadow\CteRows::class)]
48#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Update\ResultSelect::class)]
49#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\Update\TargetProjection::class)]
50#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\CastTypeResolver::class)]
51#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Type\RankEdits::class)]
52#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\ScalarExpression::class)]
53#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\StringCoercion::class)]
54#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Dml\UpdateAssignmentExtractor::class)]
55#[CoversClass(MySqlTransformer::class)]
56#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Relation\MySqlSelectRelationParser::class)]
57#[UsesClass(MySqlParser::class)]
58#[UsesClass(MySqlUpsertAssignmentExtractor::class)]
59#[UsesClass(SelectTransformer::class)]
60#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\FullText\MySqlFullTextSearchRewriter::class)]
61#[UsesClass(InsertTransformer::class)]
62#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\InsertRowRenderer::class)]
63#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\InsertSelectRenderer::class)]
64#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Transformer\MySqlSelectListAliaser::class)]
65#[UsesClass(InsertSelectSourceExtractor::class)]
66#[UsesClass(UpdateTransformer::class)]
67#[UsesClass(DeleteTransformer::class)]
68#[UsesClass(DmlWhereClauseExtractor::class)]
69#[UsesClass(UpdateSourceExtractor::class)]
70#[UsesClass(ReplaceTransformer::class)]
71#[UsesClass(MySqlCastRenderer::class)]
72#[UsesClass(MySqlIdentifierQuoter::class)]
73#[UsesClass(\ZtdQuery\Platform\MySql\Sql\Value\MySqlValueRenderer::class)]
74#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Type\MySqlTypeSemantics::class)]
75#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Cte\MySqlCteShadowComposer::class)]
76#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\Upsert\MySqlNativeUpsertProjector::class)]
77#[UsesClass(\ZtdQuery\Platform\MySql\Rewrite\GeneratedColumn\MySqlGeneratedColumnProjector::class)]
78#[UsesClass(\ZtdQuery\Platform\MySql\Sql\MySqlLexerProfile::class)]
79final class MySqlTransformerTest extends TestCase
80{
81 public function testTransformSelectPassthrough(): void
82 {
83 $parser = new MySqlParser();
84 $selectTransformer = new SelectTransformer();
85 $insertTransformer = new InsertTransformer($parser, $selectTransformer);
86 $updateTransformer = new UpdateTransformer($parser, $selectTransformer);
87 $deleteTransformer = new DeleteTransformer($parser, $selectTransformer);
88 $replaceTransformer = new ReplaceTransformer($parser, $selectTransformer);
89
90 $transformer = new MySqlTransformer($parser, $selectTransformer, $insertTransformer, $updateTransformer, $deleteTransformer, $replaceTransformer);
91
92 $sql = 'SELECT 1';
93 $result = $transformer->transform($sql, []);
94 self::assertSame($sql, $result);
95 }
96
97 public function testTransformSelectWithShadowData(): void
98 {
99 $parser = new MySqlParser();
100 $selectTransformer = new SelectTransformer();
101 $insertTransformer = new InsertTransformer($parser, $selectTransformer);
102 $updateTransformer = new UpdateTransformer($parser, $selectTransformer);
103 $deleteTransformer = new DeleteTransformer($parser, $selectTransformer);
104 $replaceTransformer = new ReplaceTransformer($parser, $selectTransformer);
105
106 $transformer = new MySqlTransformer($parser, $selectTransformer, $insertTransformer, $updateTransformer, $deleteTransformer, $replaceTransformer);
107
108 $sql = 'SELECT * FROM users';
109 $tables = [
110 'users' => [
111 'rows' => [['id' => '1', 'name' => 'Alice']],
112 'columns' => ['id', 'name'],
113 'columnTypes' => [],
114 ],
115 ];
116
117 $result = $transformer->transform($sql, $tables);
118 self::assertStringContainsString('WITH', $result);
119 }
120
121 public function testTransformInsertDelegatesToInsertTransformer(): void
122 {
123 $parser = new MySqlParser();
124 $selectTransformer = new SelectTransformer();
125 $insertTransformer = new InsertTransformer($parser, $selectTransformer);
126 $updateTransformer = new UpdateTransformer($parser, $selectTransformer);
127 $deleteTransformer = new DeleteTransformer($parser, $selectTransformer);
128 $replaceTransformer = new ReplaceTransformer($parser, $selectTransformer);
129
130 $transformer = new MySqlTransformer($parser, $selectTransformer, $insertTransformer, $updateTransformer, $deleteTransformer, $replaceTransformer);
131
132 $sql = "INSERT INTO users (id, name) VALUES (1, 'Alice')";
133 $tables = [];
134
135 $result = $transformer->transform($sql, $tables);
136 self::assertStringContainsString('SELECT 1 AS `id`', $result);
137 self::assertStringContainsString('AS `name`', $result);
138 }
139
140 public function testTransformDeleteDelegatesToDeleteTransformer(): void
141 {
142 $parser = new MySqlParser();
143 $selectTransformer = new SelectTransformer();
144 $insertTransformer = new InsertTransformer($parser, $selectTransformer);
145 $updateTransformer = new UpdateTransformer($parser, $selectTransformer);
146 $deleteTransformer = new DeleteTransformer($parser, $selectTransformer);
147 $replaceTransformer = new ReplaceTransformer($parser, $selectTransformer);
148
149 $transformer = new MySqlTransformer($parser, $selectTransformer, $insertTransformer, $updateTransformer, $deleteTransformer, $replaceTransformer);
150
151 $sql = 'DELETE FROM users WHERE id = 1';
152 $tables = [
153 'users' => [
154 'rows' => [['id' => 1]],
155 'columns' => ['id'],
156 'columnTypes' => [],
157 ],
158 ];
159
160 $result = $transformer->transform($sql, $tables);
161 self::assertStringContainsString('SELECT', $result);
162 self::assertStringContainsString('FROM', $result);
163 self::assertStringContainsString('WHERE', $result);
164 }
165
166 public function testTransformReplaceDelegatesToReplaceTransformer(): void
167 {
168 $parser = new MySqlParser();
169 $selectTransformer = new SelectTransformer();
170 $insertTransformer = new InsertTransformer($parser, $selectTransformer);
171 $updateTransformer = new UpdateTransformer($parser, $selectTransformer);
172 $deleteTransformer = new DeleteTransformer($parser, $selectTransformer);
173 $replaceTransformer = new ReplaceTransformer($parser, $selectTransformer);
174
175 $transformer = new MySqlTransformer($parser, $selectTransformer, $insertTransformer, $updateTransformer, $deleteTransformer, $replaceTransformer);
176
177 $sql = "REPLACE INTO users (id, name) VALUES (1, 'Bob')";
178 $tables = [];
179
180 $result = $transformer->transform($sql, $tables);
181 self::assertStringContainsString('SELECT 1 AS `id`', $result);
182 }
183
184 public function testCommitRewriteState(): void
185 {
186 $parser = new MySqlParser();
187 $store = new \ZtdQuery\Shadow\ShadowStore();
188 $registry = new \ZtdQuery\Schema\TableDefinitionRegistry();
189 $select = new SelectTransformer();
190 $insert = new InsertTransformer($parser, $select);
191 $update = new UpdateTransformer($parser, $select);
192 $delete = new DeleteTransformer($parser, $select);
193 $replace = new ReplaceTransformer($parser, $select);
194 $transformer = new MySqlTransformer($parser, $select, $insert, $update, $delete, $replace);
195 $tables = ['users' => ['rows' => [], 'columns' => ['id', 'name'], 'columnTypes' => [], 'identityStrategies' => ['id' => \ZtdQuery\Schema\Key\IdentityGenerationStrategy::MaxValue]]];
196 self::assertSame("SELECT 1 AS `id`, 'a' AS `name`", $transformer->transform("INSERT INTO users (name) VALUES ('a')", $tables));
197 self::assertSame("SELECT 1 AS `id`, 'a' AS `name`", $transformer->transform("REPLACE INTO users (name) VALUES ('a')", $tables));
198 $transformer->commitRewriteState();
199 self::assertSame("SELECT 2 AS `id`, 'b' AS `name`", $transformer->transform("INSERT INTO users (name) VALUES ('b')", $tables));
200 self::assertSame("SELECT 2 AS `id`, 'b' AS `name`", $transformer->transform("REPLACE INTO users (name) VALUES ('b')", $tables));
201 }
202}
203