Issues (119)

src/Parsers/LockExpressions.php (1 issue)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Parsers;
6
7
use PhpMyAdmin\SqlParser\Components\LockExpression;
8
use PhpMyAdmin\SqlParser\Parseable;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\TokensList;
11
use PhpMyAdmin\SqlParser\TokenType;
12
13
/**
14
 * Parses a reference to a LOCK expression.
15
 */
16
final class LockExpressions implements Parseable
17
{
18
    /**
19
     * @param Parser               $parser  the parser that serves as context
20
     * @param TokensList           $list    the list of tokens that are being parsed
21
     * @param array<string, mixed> $options parameters for parsing
22
     */
23 42
    public static function parse(Parser $parser, TokensList $list, array $options = []): LockExpression
24
    {
25 42
        $ret = new LockExpression();
26
27
        /**
28
         * The state of the parser.
29
         *
30
         * Below are the states of the parser.
31
         *
32
         *      0 ---------------- [ tbl_name ] -----------------> 1
33
         *      1 ---------------- [ lock_type ] ----------------> 2
34
         *      2 -------------------- [ , ] --------------------> break
35
         */
36 42
        $state = 0;
37
38 42
        $prevToken = null;
39
40 42
        for (; $list->idx < $list->count; ++$list->idx) {
41
            /**
42
             * Token parsed at this moment.
43
             */
44 42
            $token = $list->tokens[$list->idx];
45
46
            // End of statement.
47
            if (
48 42
                $token->type === TokenType::Delimiter
49 42
                || ($token->type === TokenType::Operator
50 42
                && $token->value === ',')
51
            ) {
52 42
                break;
53
            }
54
55 42
            if ($state === 0) {
56 42
                $ret->table = Expressions::parse($parser, $list, ['parseField' => 'table']);
57 42
                $state = 1;
58 36
            } elseif ($state === 1) {
59
                // parse lock type
60 36
                $ret->type = self::parseLockType($parser, $list);
61 36
                $state = 2;
62
            }
63
64 42
            $prevToken = $token;
65
        }
66
67
        // 2 is the only valid end state
68 42
        if ($state !== 2) {
69 6
            $parser->error('Unexpected end of LOCK expression.', $prevToken);
70
        }
71
72 42
        --$list->idx;
73
74 42
        return $ret;
75
    }
76
77 36
    private static function parseLockType(Parser $parser, TokensList $list): string
78
    {
79 36
        $lockType = '';
80
81
        /**
82
         * The state of the parser while parsing for lock type.
83
         *
84
         * Below are the states of the parser.
85
         *
86
         *      0 ---------------- [ READ ] -----------------> 1
87
         *      0 ------------- [ LOW_PRIORITY ] ------------> 2
88
         *      0 ---------------- [ WRITE ] ----------------> 3
89
         *      1 ---------------- [ LOCAL ] ----------------> 3
90
         *      2 ---------------- [ WRITE ] ----------------> 3
91
         */
92 36
        $state = 0;
93
94 36
        $prevToken = null;
95
96 36
        for (; $list->idx < $list->count; ++$list->idx) {
97
            /**
98
             * Token parsed at this moment.
99
             */
100 36
            $token = $list->tokens[$list->idx];
101
102
            // End of statement.
103
            if (
104 36
                $token->type === TokenType::Delimiter
105 36
                || ($token->type === TokenType::Operator
106 36
                && $token->value === ',')
107
            ) {
108 20
                --$list->idx;
109 20
                break;
110
            }
111
112
            // Skipping whitespaces and comments.
113 36
            if ($token->type === TokenType::Whitespace || $token->type === TokenType::Comment) {
114 26
                continue;
115
            }
116
117
            // We only expect keywords
118 36
            if ($token->type !== TokenType::Keyword) {
119 6
                $parser->error('Unexpected token.', $token);
120 6
                break;
121
            }
122
123 34
            if ($state === 0) {
124 34
                if ($token->keyword === 'READ') {
125 20
                    $state = 1;
126 20
                } elseif ($token->keyword === 'LOW_PRIORITY') {
127 12
                    $state = 2;
128 10
                } elseif ($token->keyword === 'WRITE') {
129 6
                    $state = 3;
130
                } else {
131 4
                    $parser->error('Unexpected keyword.', $token);
132 4
                    break;
133
                }
134
135 30
                $lockType .= $token->keyword;
136 24
            } elseif ($state === 1) {
137 16
                if ($token->keyword !== 'LOCAL') {
138 4
                    $parser->error('Unexpected keyword.', $token);
139 4
                    break;
140
                }
141
142 12
                $lockType .= ' ' . $token->keyword;
143 12
                $state = 3;
144 12
            } elseif ($state === 2) {
145 12
                if ($token->keyword !== 'WRITE') {
146 2
                    $parser->error('Unexpected keyword.', $token);
147 2
                    break;
148
                }
149
150 10
                $lockType .= ' ' . $token->keyword;
151 10
                $state = 3; // parsing over
152
            }
153
154 30
            $prevToken = $token;
155
        }
156
157
        // Only  two possible end states
158 36
        if ($state !== 1 && $state !== 3) {
0 ignored issues
show
The condition $state !== 3 is always true.
Loading history...
159 8
            $parser->error('Unexpected end of LOCK expression.', $prevToken);
160
        }
161
162 36
        return $lockType;
163
    }
164
}
165