packages/ztd-query-sqlite/src/Sql/Dml/Update/UpdateClauseParser.php
1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Sqlite\Sql\Dml\Update;
6
7use ZtdQuery\Sql\SqlTokenKind;
8use ZtdQuery\Sql\SqlTokenStream;
9
10/**
11 * Extracts UPDATE aliases and top-level filtering clauses.
12 *
13 * @visibility ZtdQuery\Platform\Sqlite
14 */
15final class UpdateClauseParser
16{
17 /**
18 * Extracts UPDATE aliases and top-level filtering clauses.
19 */
20 public function extractUpdateAlias(string $sql): ?string
21 {
22 $tokens = SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->significantTokens();
23 foreach ($tokens as $index => $token) {
24 if (!$token->isTopLevel() || !$token->isKeyword('UPDATE')) {
25 continue;
26 }
27
28 $index++;
29 if (($tokens[$index] ?? null)?->isKeyword('OR') === true) {
30 $index += 2;
31 }
32 $index = (new \ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder())->identifierEndIndex($tokens, $index);
33 if (($tokens[$index] ?? null)?->kind === SqlTokenKind::Symbol
34 && $tokens[$index]->text === '.'
35 ) {
36 $index = (new \ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder())->identifierEndIndex($tokens, $index + 1);
37 }
38 if (($tokens[$index] ?? null)?->isKeyword('AS') === true) {
39 $index++;
40 }
41
42 $alias = $tokens[$index] ?? null;
43 $set = $tokens[$index + 1] ?? null;
44 if ($alias === null
45 || $set === null
46 || !$set->isKeyword('SET')
47 || !in_array($alias->kind, [SqlTokenKind::Word, SqlTokenKind::QuotedIdentifier], true)
48 ) {
49 return null;
50 }
51
52 return (new \ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder())->unquoteIdentifier($alias->text);
53 }
54
55 return null;
56 }
57
58 /**
59 * Extracts UPDATE aliases and top-level filtering clauses.
60 */
61 public function extractUpdateFromClause(string $sql): ?string
62 {
63 return SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->topLevelClause(
64 ['FROM'],
65 [['WHERE'], ['ORDER', 'BY'], ['LIMIT'], ['RETURNING']],
66 );
67 }
68
69 /**
70 * Extract WHERE clause from a DML statement.
71 */
72 public function extractWhereClause(string $sql): ?string
73 {
74 return SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->topLevelClause(
75 ['WHERE'],
76 [['ORDER', 'BY'], ['LIMIT'], ['GROUP', 'BY'], ['HAVING'], ['RETURNING']],
77 );
78 }
79
80 /**
81 * Extract ORDER BY clause from a statement.
82 */
83 public function extractOrderByClause(string $sql): ?string
84 {
85 return SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->topLevelClause(
86 ['ORDER', 'BY'],
87 [['LIMIT'], ['RETURNING']],
88 );
89 }
90
91 /**
92 * Extract LIMIT clause from a statement.
93 */
94 public function extractLimitClause(string $sql): ?string
95 {
96 return SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->topLevelClause(['LIMIT'], [['RETURNING']]);
97 }
98
99 /**
100 * Extracts UPDATE aliases and top-level filtering clauses.
101 */
102 public function extractOnConflictUpdateWhere(string $sql): ?string
103 {
104 return SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::create())->topLevelClauseAfter(
105 ['DO', 'UPDATE', 'SET'],
106 ['WHERE'],
107 [['RETURNING']],
108 );
109 }
110}
111