packages/sql-semantics/tests/Unit/Core/Ast/TokenGroupsTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Core\Ast;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Medium;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlSemantics\Core\SemanticException;
12use SqlSemantics\Platform\PostgreSql\Dialect as PostgreSqlDialect;
13
14#[CoversClass(\SqlSemantics\Core\Ast\TokenGroups::class)]
15#[UsesClass(\SqlSemantics\Core\Binding\ExpressionBinder::class)]
16#[UsesClass(\SqlSemantics\Core\Binding\ExpressionRules::class)]
17#[UsesClass(\SqlSemantics\Core\Binding\FromBinder::class)]
18#[UsesClass(\SqlSemantics\Core\Binding\LiteralBinder::class)]
19#[UsesClass(\SqlSemantics\Core\Binding\NullFacts::class)]
20#[UsesClass(\SqlSemantics\Core\Binding\ProjectionBinder::class)]
21#[UsesClass(\SqlSemantics\Core\Binding\SelectBinder::class)]
22#[UsesClass(\SqlSemantics\Core\Binding\SyntaxGuard::class)]
23#[UsesClass(\SqlSemantics\Core\Binding\SelectModifiersBinder::class)]
24#[UsesClass(\SqlSemantics\Core\Binding\TypeResolution::class)]
25#[UsesClass(\SqlSemantics\Core\Binder::class)]
26#[UsesClass(\SqlSemantics\Core\SchemaBuilder::class)]
27#[UsesClass(\SqlSemantics\Core\Ast\DialectParser::class)]
28#[UsesClass(\SqlSemantics\Core\Ast\ColumnReader::class)]
29#[UsesClass(\SqlSemantics\Core\Ast\ConstraintReader::class)]
30#[UsesClass(\SqlSemantics\Core\Ast\Identifiers::class)]
31#[UsesClass(\SqlSemantics\Core\Ast\SchemaReader::class)]
32#[UsesClass(\SqlSemantics\Core\Ast\StatementList::class)]
33#[UsesClass(\SqlSemantics\Core\Ast\Tree::class)]
34#[UsesClass(\SqlSemantics\Core\Ast\TypeReader::class)]
35#[UsesClass(\SqlSemantics\Core\Binding\BoundRelation::class)]
36#[UsesClass(\SqlSemantics\Core\Binding\IdentitySequence::class)]
37#[UsesClass(\SqlSemantics\Core\Binding\Scope::class)]
38#[UsesClass(\SqlSemantics\Core\Binding\TableResolver::class)]
39#[UsesClass(\SqlSemantics\Core\Model\ColumnBinding::class)]
40#[UsesClass(\SqlSemantics\Core\Model\Expression::class)]
41#[UsesClass(\SqlSemantics\Core\Model\Join::class)]
42#[UsesClass(\SqlSemantics\Core\Model\Ordering::class)]
43#[UsesClass(\SqlSemantics\Core\Model\OutputColumn::class)]
44#[UsesClass(\SqlSemantics\Core\Model\BoundSelect::class)]
45#[UsesClass(\SqlSemantics\Core\Model\TableUse::class)]
46#[UsesClass(\SqlSemantics\Core\Schema::class)]
47#[UsesClass(\SqlSemantics\Core\Schema\ColumnDefinition::class)]
48#[UsesClass(\SqlSemantics\Core\Schema\TableConstraint::class)]
49#[UsesClass(\SqlSemantics\Core\Schema\TableDefinition::class)]
50#[UsesClass(SemanticException::class)]
51#[UsesClass(\SqlSemantics\Core\Type\TypeDescriptor::class)]
52#[UsesClass(\SqlSemantics\Core\Policy\SyntaxRules::class)]
53#[UsesClass(\SqlSemantics\Platform\PostgreSql\QueryRules::class)]
54#[UsesClass(\SqlSemantics\Platform\PostgreSql\Platform::class)]
55#[UsesClass(\SqlSemantics\Platform\PostgreSql\TypeRules::class)]
56#[UsesClass(\SqlSemantics\Platform\PostgreSql\NameRules::class)]
57#[UsesClass(\SqlSemantics\Platform\PostgreSql\SchemaRules::class)]
58#[UsesClass(\SqlSemantics\Platform\Sqlite\QueryRules::class)]
59#[UsesClass(\SqlSemantics\Platform\Sqlite\Platform::class)]
60#[UsesClass(\SqlSemantics\Platform\Sqlite\TypeRules::class)]
61#[UsesClass(\SqlSemantics\Platform\Sqlite\NameRules::class)]
62#[UsesClass(\SqlSemantics\Platform\Sqlite\SchemaRules::class)]
63#[UsesClass(\SqlSemantics\Platform\MySql\QueryRules::class)]
64#[UsesClass(\SqlSemantics\Platform\MySql\Platform::class)]
65#[UsesClass(\SqlSemantics\Platform\MySql\TypeRules::class)]
66#[UsesClass(\SqlSemantics\Platform\MySql\NameRules::class)]
67#[UsesClass(\SqlSemantics\Platform\MySql\SchemaRules::class)]
68#[Medium]
69final class TokenGroupsTest extends TestCase
70{
71    public function testParenthesesRetainsNestedGroups(): void
72    {
73        $tree = (new \SqlParser\PostgreSql\PostgreSqlParser())->parse('SELECT COALESCE((1), 2), (3)');
74        $groups = \SqlSemantics\Core\Ast\TokenGroups::parentheses($tree->tokens());
75        self::assertCount(2, $groups);
76        self::assertSame(['(', '1', ')', ',', '2'], array_column($groups[0], 'text'));
77        self::assertSame(['3'], array_column($groups[1], 'text'));
78    }
79
80    public function testNamesPreservesQuotedCommas(): void
81    {
82        $node = (new \SqlParser\PostgreSql\PostgreSqlParser())->parse('CREATE TABLE users ("a,b" INTEGER, c INTEGER, PRIMARY KEY ("a,b", c))')->find('columnList')[0];
83        $names = \SqlSemantics\Core\Ast\TokenGroups::names($node->tokens(), new \SqlSemantics\Core\Ast\Identifiers(PostgreSqlDialect::PostgreSql));
84        self::assertSame(['a,b', 'c'], $names);
85    }
86    public function testKeyNamesSeparatesPrefixLengthsAndSortDirections(): void
87    {
88        $state = (new \SqlSemantics\Facade\Schema(\SqlSemantics\Platform\MySql\Dialect::MySql))->analyze('CREATE TABLE t (id INT, label VARCHAR(30), UNIQUE KEY idx (label(10) DESC, id ASC))');
89        self::assertSame(['label', 'id'], $state->tables[0]->constraints[0]->columns);
90    }
91    public function testConstraintHeaderSeparatesQuotedNames(): void
92    {
93        $node = (new \SqlParser\PostgreSql\PostgreSqlParser())->parse('CREATE TABLE t (id INTEGER, CONSTRAINT "primary" PRIMARY KEY (id))')->find('TableConstraint')[0];
94        [$name, $tokens] = \SqlSemantics\Core\Ast\TokenGroups::constraintHeader($node->tokens(), new \SqlSemantics\Core\Ast\Identifiers(PostgreSqlDialect::PostgreSql));
95        self::assertSame('primary', $name);
96        self::assertSame(['PRIMARY', 'KEY', '(', 'id', ')'], array_column($tokens, 'text'));
97    }
98
99}
100