packages/ztd-query-postgres/src/Sql/Dml/Update/UpdateClauseParser.php
1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Postgres\Sql\Dml\Update;
6
7use ZtdQuery\Sql\SqlTokenStream;
8
9/**
10 * Extracts PostgreSQL UPDATE aliases and assignments.
11 *
12 * @visibility root
13 */
14final class UpdateClauseParser
15{
16 /**
17 * Extract table alias from UPDATE statement.
18 */
19 public function extractUpdateAlias(string $sql): ?string
20 {
21 $sql = \ZtdQuery\Platform\Postgres\Sql\PostgreSqlLexicalMasker::maskComments($sql);
22 if (preg_match('/UPDATE\s+(?:ONLY\s+)?(?:"[^"]+"|[a-zA-Z_]\w*(?:\."[^"]+"|\.(?:[a-zA-Z_]\w*))?)\s+(?:AS\s+)?("[^"]+"|[a-zA-Z_]\w*)\s+SET\b/i', $sql, $m) === 1) {
23 return (new \ZtdQuery\Platform\Postgres\Sql\Lexing\IdentifierDecoder())->unquoteIdentifier($m[1]);
24 }
25
26 return null;
27 }
28
29 /**
30 * Extract SET assignments from UPDATE statement.
31 *
32 * @return array<string, string> column => value expression
33 */
34 public function extractUpdateSets(string $sql): array
35 {
36 $setClause = SqlTokenStream::tokenize($sql, \ZtdQuery\Platform\Postgres\Sql\PgSqlLexerProfile::create())->topLevelClause(
37 ['SET'],
38 [['FROM'], ['WHERE'], ['RETURNING']],
39 );
40 if ($setClause === null) {
41 return [];
42 }
43
44 $assignments = SqlTokenStream::tokenize($setClause, \ZtdQuery\Platform\Postgres\Sql\PgSqlLexerProfile::create())->splitTopLevel();
45 $result = [];
46
47 foreach ($assignments as $assignment) {
48 $assignment = trim($assignment);
49 if (preg_match('/^("[^"]+"|[a-zA-Z_]\w*)\s*=\s*(.+)$/s', $assignment, $parts) === 1) {
50 $colName = (new \ZtdQuery\Platform\Postgres\Sql\Lexing\IdentifierDecoder())->unquoteIdentifier($parts[1]);
51 $result[$colName] = trim($parts[2]);
52 }
53 }
54
55 return $result;
56 }
57}
58