Test Failed
Pull Request — master (#291)
by William
12:25
created

InsertStatement::parse()   F

Complexity

Conditions 25
Paths 10

Size

Total Lines 128

Duplication

Lines 31
Ratio 24.22 %

Code Coverage

Tests 63
CRAP Score 25.0182

Importance

Changes 0
Metric Value
dl 31
loc 128
ccs 63
cts 65
cp 0.9692
rs 3.3333
c 0
b 0
f 0
cc 25
nc 10
nop 2
crap 25.0182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * `INSERT` statement.
5
 */
6
7
namespace PhpMyAdmin\SqlParser\Statements;
8
9
use PhpMyAdmin\SqlParser\Components\ArrayObj;
10
use PhpMyAdmin\SqlParser\Components\Array2d;
11
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
12
use PhpMyAdmin\SqlParser\Components\OptionsArray;
13
use PhpMyAdmin\SqlParser\Components\SetOperation;
14
use PhpMyAdmin\SqlParser\Parser;
15
use PhpMyAdmin\SqlParser\Statement;
16
use PhpMyAdmin\SqlParser\Token;
17
use PhpMyAdmin\SqlParser\TokensList;
18
19
/**
20
 * `INSERT` statement.
21
 *
22
 * INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
23
 *     [INTO] tbl_name
24
 *     [PARTITION (partition_name,...)]
25
 *     [(col_name,...)]
26
 *     {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
27
 *     [ ON DUPLICATE KEY UPDATE
28
 *       col_name=expr
29
 *         [, col_name=expr] ... ]
30
 *
31
 * or
32
 *
33
 * INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
34
 *     [INTO] tbl_name
35
 *     [PARTITION (partition_name,...)]
36
 *     SET col_name={expr | DEFAULT}, ...
37
 *     [ ON DUPLICATE KEY UPDATE
38
 *       col_name=expr
39
 *         [, col_name=expr] ... ]
40
 *
41
 * or
42
 *
43
 * INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
44
 *     [INTO] tbl_name
45
 *     [PARTITION (partition_name,...)]
46
 *     [(col_name,...)]
47
 *     SELECT ...
48
 *     [ ON DUPLICATE KEY UPDATE
49
 *       col_name=expr
50
 *         [, col_name=expr] ... ]
51
 *
52
 * @category   Statements
53
 *
54
 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
55
 */
56
class InsertStatement extends Statement
57
{
58
    /**
59
     * Options for `INSERT` statements.
60
     *
61
     * @var array
62
     */
63
    public static $OPTIONS = array(
64
        'LOW_PRIORITY' => 1,
65
        'DELAYED' => 2,
66
        'HIGH_PRIORITY' => 3,
67
        'IGNORE' => 4
68
    );
69
70
    /**
71
     * Tables used as target for this statement.
72
     *
73
     * @var IntoKeyword
74
     */
75
    public $into;
76
77
    /**
78
     * Values to be inserted.
79
     *
80
     * @var ArrayObj[]|null
81
     */
82
    public $values;
83
84
    /**
85
     * If SET clause is present
86
     * holds the SetOperation.
87
     *
88
     * @var SetOperation[]
89
     */
90
    public $set;
91
92
    /**
93
     * If SELECT clause is present
94
     * holds the SelectStatement.
95
     *
96
     * @var SelectStatement
97
     */
98
    public $select;
99
100
    /**
101
     * If ON DUPLICATE KEY UPDATE clause is present
102
     * holds the SetOperation.
103
     *
104
     * @var SetOperation[]
105
     */
106
    public $onDuplicateSet;
107
108
    /**
109
     * @return string
110
     */
111 1
    public function build()
112
    {
113 1
        $ret = 'INSERT ' . $this->options;
114 1
        $ret = trim($ret) . ' INTO ' . $this->into;
115
116 1 View Code Duplication
        if (! is_null($this->values) && count($this->values) > 0) {
117 1
            $ret .= ' VALUES ' . Array2d::build($this->values);
118 1
        } elseif (! is_null($this->set) && count($this->set) > 0) {
119 1
            $ret .= ' SET ' . SetOperation::build($this->set);
120 1
        } elseif (! is_null($this->select) && strlen($this->select) > 0) {
121 1
            $ret .= ' ' . $this->select->build();
122
        }
123
124 1
        if (! is_null($this->onDuplicateSet) && count($this->onDuplicateSet) > 0) {
125 1
            $ret .= ' ON DUPLICATE KEY UPDATE ' . SetOperation::build($this->onDuplicateSet);
126
        }
127
128 1
        return $ret;
129
    }
130
131
    /**
132
     * @param Parser     $parser the instance that requests parsing
133
     * @param TokensList $list   the list of tokens to be parsed
134
     */
135 15
    public function parse(Parser $parser, TokensList $list)
136
    {
137 15
        ++$list->idx; // Skipping `INSERT`.
138
139
        // parse any options if provided
140 15
        $this->options = OptionsArray::parse(
141 15
            $parser,
142 15
            $list,
143 15
            static::$OPTIONS
144
        );
145 15
        ++$list->idx;
146
147
        /**
148
         * The state of the parser.
149
         *
150
         * Below are the states of the parser.
151
         *
152
         *      0 ---------------------------------[ INTO ]----------------------------------> 1
153
         *
154
         *      1 -------------------------[ VALUES/VALUE/SET/SELECT ]-----------------------> 2
155
         *
156
         *      2 -------------------------[ ON DUPLICATE KEY UPDATE ]-----------------------> 3
157
         *
158
         * @var int
159
         */
160 15
        $state = 0;
161
162
        /**
163
         * For keeping track of semi-states on encountering
164
         * ON DUPLICATE KEY UPDATE ...
165
         */
166 15
        $miniState = 0;
167
168 15
        for (; $list->idx < $list->count; ++$list->idx) {
169
            /**
170
             * Token parsed at this moment.
171
             *
172
             * @var Token
173
             */
174 15
            $token = $list->tokens[$list->idx];
175
176
            // End of statement.
177 15
            if ($token->type === Token::TYPE_DELIMITER) {
178 13
                break;
179
            }
180
181
            // Skipping whitespaces and comments.
182 14
            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
183 10
                continue;
184
            }
185
186 14
            if ($state === 0) {
187 14
                if ($token->type === Token::TYPE_KEYWORD
188 14
                    && $token->keyword !== 'INTO'
189
                ) {
190
                    $parser->error('Unexpected keyword.', $token);
191
                    break;
192
                }
193
194 14
                ++$list->idx;
195 14
                $this->into = IntoKeyword::parse(
196 14
                    $parser,
197 14
                    $list,
198 14
                    array('fromInsert' => true)
199
                );
200
201 14
                $state = 1;
202 14 View Code Duplication
            } elseif ($state === 1) {
203 14
                if ($token->type === Token::TYPE_KEYWORD) {
204 13
                    if ($token->keyword === 'VALUE'
205 13
                        || $token->keyword === 'VALUES'
206
                    ) {
207 8
                        ++$list->idx; // skip VALUES
208
209 8
                        $this->values = Array2d::parse($parser, $list);
210 6
                    } elseif ($token->keyword === 'SET') {
211 3
                        ++$list->idx; // skip SET
212
213 3
                        $this->set = SetOperation::parse($parser, $list);
214 4
                    } elseif ($token->keyword === 'SELECT') {
215 3
                        $this->select = new SelectStatement($parser, $list);
216
                    } else {
217 1
                        $parser->error(
218 1
                            'Unexpected keyword.',
219
                            $token
220
                        );
221 1
                        break;
222
                    }
223 12
                    $state = 2;
224 12
                    $miniState = 1;
225
                } else {
226 1
                    $parser->error(
227 1
                        'Unexpected token.',
228
                        $token
229
                    );
230 13
                    break;
231
                }
232 5
            } elseif ($state === 2) {
233 5
                $lastCount = $miniState;
234
235 5
                if ($miniState === 1 && $token->keyword === 'ON') {
236 5
                    ++$miniState;
237 5
                } elseif ($miniState === 2 && $token->keyword === 'DUPLICATE') {
238 5
                    ++$miniState;
239 5
                } elseif ($miniState === 3 && $token->keyword === 'KEY') {
240 5
                    ++$miniState;
241 5
                } elseif ($miniState === 4 && $token->keyword === 'UPDATE') {
242 4
                    ++$miniState;
243
                }
244
245 5
                if ($lastCount === $miniState) {
246 1
                    $parser->error(
247 1
                        'Unexpected token.',
248
                        $token
249
                    );
250 1
                    break;
251
                }
252
253 5
                if ($miniState === 5) {
254 4
                    ++$list->idx;
255 4
                    $this->onDuplicateSet = SetOperation::parse($parser, $list);
256 4
                    $state = 3;
257
                }
258
            }
259
        }
260
261 15
        --$list->idx;
262 15
    }
263
}
264