packages/sql-semantics/tests/Unit/Statement/WriterTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Statement;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Medium;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11
12#[CoversClass(\SqlSemantics\Statement\Writer::class)]
13#[UsesClass(\SqlSemantics\Core\Analysis\Analyzer::class)]
14#[UsesClass(\SqlSemantics\Core\Analysis\ValueReader::class)]
15#[UsesClass(\SqlSemantics\Core\AnalysisException::class)]
16#[UsesClass(\SqlSemantics\Facade\Semantics::class)]
17#[UsesClass(\SqlSemantics\Statement\Element::class)]
18#[UsesClass(\SqlSemantics\Statement\Statement::class)]
19#[UsesClass(\SqlSemantics\Statement\Comments::class)]
20#[UsesClass(\SqlSemantics\Core\Analysis\TriviaReader::class)]
21#[UsesClass(\SqlSemantics\Core\Analysis\SourceComments::class)]
22#[UsesClass(\SqlSemantics\Core\Ast\DialectParser::class)]
23#[UsesClass(\SqlSemantics\Platform\MySql\Platform::class)]
24#[UsesClass(\SqlSemantics\Platform\PostgreSql\Platform::class)]
25#[UsesClass(\SqlSemantics\Platform\Sqlite\Platform::class)]
26#[Medium]
27final class WriterTest extends TestCase
28{
29    public function testRenderPreservesOperandGroupingAfterAnImmutableUpdate(): void
30    {
31        $one = new \SqlSemantics\Statement\Model\Sqlite\Value\TermWithInteger_298801b2('1');
32        $two = new \SqlSemantics\Statement\Model\Sqlite\Value\TermWithInteger_298801b2('2');
33        $three = new \SqlSemantics\Statement\Model\Sqlite\Value\TermWithInteger_298801b2('3');
34        $sum = new \SqlSemantics\Statement\Model\Sqlite\Value\ExprWithExprPlusMinusExpr_82e360dc($one, '+', $two);
35        $original = new \SqlSemantics\Statement\Model\Sqlite\Value\ExprWithExprStarSlashRemExpr_6ca99fe8($one, '*', $three);
36        $updated = $original->withExpr(new \SqlSemantics\Statement\Model\Sqlite\Value\ExprWithLpExprRp_ad646753($sum));
37        self::assertSame('1 * 3', \SqlSemantics\Statement\Writer::render($original));
38        self::assertSame('( 1 + 2 ) * 3', \SqlSemantics\Statement\Writer::render($updated));
39    }
40
41    public function testRenderWritesAFragmentWithoutTreatingItAsACompleteCommand(): void
42    {
43        $value = new \SqlSemantics\Statement\Model\Sqlite\Value\TermWithInteger_298801b2('42');
44        self::assertSame('42', \SqlSemantics\Statement\Writer::render($value));
45    }
46
47    public function testAppendKeepsIdentifierAndKeywordDotBoundaries(): void
48    {
49        $identifier = new \SqlSemantics\Statement\Writer();
50        $identifier->append('select', true);
51        $identifier->append('.');
52        $identifier->append('id', true);
53        self::assertSame('select.id', $identifier->toString());
54        $keyword = new \SqlSemantics\Statement\Writer();
55        $keyword->append('DEFAULT');
56        $keyword->append('.');
57        $keyword->append('id', true);
58        self::assertSame('DEFAULT .id', $keyword->toString());
59    }
60
61    public function testAppendKeepsFunctionAndIdentifierParenthesisBoundaries(): void
62    {
63        $function = new \SqlSemantics\Statement\Writer();
64        $function->append('COUNT');
65        $function->append('(');
66        self::assertSame('COUNT(', $function->toString());
67        $identifier = new \SqlSemantics\Statement\Writer();
68        $identifier->append('count', true);
69        $identifier->append('(');
70        self::assertSame('count (', $identifier->toString());
71    }
72
73    public function testAppendSeparatesOperatorsWithoutCreatingAComment(): void
74    {
75        $writer = new \SqlSemantics\Statement\Writer();
76        $writer->append('-');
77        $writer->append('-');
78        self::assertSame('- -', $writer->toString());
79    }
80
81    public function testToStringPreservesSeparateQuotedValues(): void
82    {
83        $writer = new \SqlSemantics\Statement\Writer();
84        $writer->append("'one'");
85        $writer->append("'two'");
86        self::assertSame("'one' 'two'", $writer->toString());
87    }
88
89    public function testAppendAttachesTheNextValueToAPrefixButSeparatesOperators(): void
90    {
91        $variable = new \SqlSemantics\Statement\Writer();
92        $variable->append('@', prefix: true);
93        $variable->append('@', prefix: true);
94        $variable->append('name', true);
95        self::assertSame('@@name', $variable->toString());
96        $operator = new \SqlSemantics\Statement\Writer();
97        $operator->append('@');
98        $operator->append('-');
99        $operator->append('1');
100        self::assertSame('@ - 1', $operator->toString());
101    }
102
103    public function testCommentsWritesEachCommentBeforeItsSymbolAndEndsALineComment(): void
104    {
105        $comments = new \SqlSemantics\Statement\Comments([0 => ['/* lead */'], 1 => ['-- one', '#two'], 2 => ['/* c */']]);
106        $writer = new \SqlSemantics\Statement\Writer();
107        $writer->comments($comments, 0);
108        $writer->append('SELECT');
109        $writer->comments($comments, 1);
110        $writer->append('COUNT');
111        $writer->comments($comments, 2);
112        $writer->append('(');
113        $writer->append('*');
114        $writer->append(')');
115        $writer->comments($comments, 3);
116        self::assertSame("/* lead */ SELECT -- one\n#two\nCOUNT /* c */ ( * )", $writer->toString());
117    }
118
119    public function testRenderWritesTheCommentsOfEveryValue(): void
120    {
121        $statement = (new \SqlSemantics\Facade\Semantics(\SqlSemantics\Platform\Sqlite\Dialect::Sqlite))->analyze("SELECT /* a */ foo -- b\n FROM items /* c */ WHERE foo = /* d */ 1");
122        self::assertSame("SELECT /* a */ foo -- b\nFROM items /* c */ WHERE foo = /* d */ 1", \SqlSemantics\Statement\Writer::render($statement->command));
123    }
124}
125