1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* `CREATE` statement. |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace PhpMyAdmin\SqlParser\Statements; |
8
|
|
|
|
9
|
|
|
use PhpMyAdmin\SqlParser\Components\ArrayObj; |
10
|
|
|
use PhpMyAdmin\SqlParser\Components\CreateDefinition; |
11
|
|
|
use PhpMyAdmin\SqlParser\Components\DataType; |
12
|
|
|
use PhpMyAdmin\SqlParser\Components\Expression; |
13
|
|
|
use PhpMyAdmin\SqlParser\Components\OptionsArray; |
14
|
|
|
use PhpMyAdmin\SqlParser\Components\ParameterDefinition; |
15
|
|
|
use PhpMyAdmin\SqlParser\Components\PartitionDefinition; |
16
|
|
|
use PhpMyAdmin\SqlParser\Parser; |
17
|
|
|
use PhpMyAdmin\SqlParser\Statement; |
18
|
|
|
use PhpMyAdmin\SqlParser\Token; |
19
|
|
|
use PhpMyAdmin\SqlParser\TokensList; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* `CREATE` statement. |
23
|
|
|
* |
24
|
|
|
* @category Statements |
25
|
|
|
* |
26
|
|
|
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+ |
27
|
|
|
*/ |
28
|
|
|
class CreateStatement extends Statement |
29
|
|
|
{ |
30
|
|
|
/** |
31
|
|
|
* Options for `CREATE` statements. |
32
|
|
|
* |
33
|
|
|
* @var array |
34
|
|
|
*/ |
35
|
|
|
public static $OPTIONS = array( |
36
|
|
|
// CREATE TABLE |
37
|
|
|
'TEMPORARY' => 1, |
38
|
|
|
|
39
|
|
|
// CREATE VIEW |
40
|
|
|
'OR REPLACE' => array(2, 'var='), |
41
|
|
|
'ALGORITHM' => array(3, 'var='), |
42
|
|
|
// `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE` |
43
|
|
|
'DEFINER' => array(4, 'expr='), |
44
|
|
|
'SQL SECURITY' => array(5, 'var'), |
45
|
|
|
|
46
|
|
|
'DATABASE' => 6, |
47
|
|
|
'EVENT' => 6, |
48
|
|
|
'FUNCTION' => 6, |
49
|
|
|
'INDEX' => 6, |
50
|
|
|
'UNIQUE INDEX' => 6, |
51
|
|
|
'FULLTEXT INDEX' => 6, |
52
|
|
|
'SPATIAL INDEX' => 6, |
53
|
|
|
'PROCEDURE' => 6, |
54
|
|
|
'SERVER' => 6, |
55
|
|
|
'TABLE' => 6, |
56
|
|
|
'TABLESPACE' => 6, |
57
|
|
|
'TRIGGER' => 6, |
58
|
|
|
'USER' => 6, |
59
|
|
|
'VIEW' => 6, |
60
|
|
|
|
61
|
|
|
// CREATE TABLE |
62
|
|
|
'IF NOT EXISTS' => 7, |
63
|
|
|
); |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* All database options. |
67
|
|
|
* |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
public static $DB_OPTIONS = array( |
71
|
|
|
'CHARACTER SET' => array(1, 'var='), |
72
|
|
|
'CHARSET' => array(1, 'var='), |
73
|
|
|
'DEFAULT CHARACTER SET' => array(1, 'var='), |
74
|
|
|
'DEFAULT CHARSET' => array(1, 'var='), |
75
|
|
|
'DEFAULT COLLATE' => array(2, 'var='), |
76
|
|
|
'COLLATE' => array(2, 'var='), |
77
|
|
|
); |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* All table options. |
81
|
|
|
* |
82
|
|
|
* @var array |
83
|
|
|
*/ |
84
|
|
|
public static $TABLE_OPTIONS = array( |
85
|
|
|
'ENGINE' => array(1, 'var='), |
86
|
|
|
'AUTO_INCREMENT' => array(2, 'var='), |
87
|
|
|
'AVG_ROW_LENGTH' => array(3, 'var'), |
88
|
|
|
'CHARACTER SET' => array(4, 'var='), |
89
|
|
|
'CHARSET' => array(4, 'var='), |
90
|
|
|
'DEFAULT CHARACTER SET' => array(4, 'var='), |
91
|
|
|
'DEFAULT CHARSET' => array(4, 'var='), |
92
|
|
|
'CHECKSUM' => array(5, 'var'), |
93
|
|
|
'DEFAULT COLLATE' => array(6, 'var='), |
94
|
|
|
'COLLATE' => array(6, 'var='), |
95
|
|
|
'COMMENT' => array(7, 'var='), |
96
|
|
|
'CONNECTION' => array(8, 'var'), |
97
|
|
|
'DATA DIRECTORY' => array(9, 'var'), |
98
|
|
|
'DELAY_KEY_WRITE' => array(10, 'var'), |
99
|
|
|
'INDEX DIRECTORY' => array(11, 'var'), |
100
|
|
|
'INSERT_METHOD' => array(12, 'var'), |
101
|
|
|
'KEY_BLOCK_SIZE' => array(13, 'var'), |
102
|
|
|
'MAX_ROWS' => array(14, 'var'), |
103
|
|
|
'MIN_ROWS' => array(15, 'var'), |
104
|
|
|
'PACK_KEYS' => array(16, 'var'), |
105
|
|
|
'PASSWORD' => array(17, 'var'), |
106
|
|
|
'ROW_FORMAT' => array(18, 'var'), |
107
|
|
|
'TABLESPACE' => array(19, 'var'), |
108
|
|
|
'STORAGE' => array(20, 'var'), |
109
|
|
|
'UNION' => array(21, 'var'), |
110
|
|
|
); |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* All function options. |
114
|
|
|
* |
115
|
|
|
* @var array |
116
|
|
|
*/ |
117
|
|
|
public static $FUNC_OPTIONS = array( |
118
|
|
|
'COMMENT' => array(1, 'var='), |
119
|
|
|
'LANGUAGE SQL' => 2, |
120
|
|
|
'DETERMINISTIC' => 3, |
121
|
|
|
'NOT DETERMINISTIC' => 3, |
122
|
|
|
'CONTAINS SQL' => 4, |
123
|
|
|
'NO SQL' => 4, |
124
|
|
|
'READS SQL DATA' => 4, |
125
|
|
|
'MODIFIES SQL DATA' => 4, |
126
|
|
|
'SQL SECURITY DEFINER' => array(5, 'var'), |
127
|
|
|
); |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* All trigger options. |
131
|
|
|
* |
132
|
|
|
* @var array |
133
|
|
|
*/ |
134
|
|
|
public static $TRIGGER_OPTIONS = array( |
135
|
|
|
'BEFORE' => 1, |
136
|
|
|
'AFTER' => 1, |
137
|
|
|
'INSERT' => 2, |
138
|
|
|
'UPDATE' => 2, |
139
|
|
|
'DELETE' => 2, |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* The name of the entity that is created. |
144
|
|
|
* |
145
|
|
|
* Used by all `CREATE` statements. |
146
|
|
|
* |
147
|
|
|
* @var Expression |
148
|
|
|
*/ |
149
|
|
|
public $name; |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* The options of the entity (table, procedure, function, etc.). |
153
|
|
|
* |
154
|
|
|
* Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`. |
155
|
|
|
* |
156
|
|
|
* @var OptionsArray |
157
|
|
|
* |
158
|
|
|
* @see static::$TABLE_OPTIONS |
159
|
|
|
* @see static::$FUNC_OPTIONS |
160
|
|
|
* @see static::$TRIGGER_OPTIONS |
161
|
|
|
*/ |
162
|
|
|
public $entityOptions; |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* If `CREATE TABLE`, a list of columns and keys. |
166
|
|
|
* If `CREATE VIEW`, a list of columns. |
167
|
|
|
* |
168
|
|
|
* Used by `CREATE TABLE` and `CREATE VIEW`. |
169
|
|
|
* |
170
|
|
|
* @var CreateDefinition[]|ArrayObj |
171
|
|
|
*/ |
172
|
|
|
public $fields; |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* If `CREATE TABLE ... SELECT`. |
176
|
|
|
* |
177
|
|
|
* Used by `CREATE TABLE` |
178
|
|
|
* |
179
|
|
|
* @var SelectStatement |
180
|
|
|
*/ |
181
|
|
|
public $select; |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* If `CREATE TABLE ... LIKE`. |
185
|
|
|
* |
186
|
|
|
* Used by `CREATE TABLE` |
187
|
|
|
* |
188
|
|
|
* @var Expression |
189
|
|
|
*/ |
190
|
|
|
public $like; |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Expression used for partitioning. |
194
|
|
|
* |
195
|
|
|
* @var string |
196
|
|
|
*/ |
197
|
|
|
public $partitionBy; |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* The number of partitions. |
201
|
|
|
* |
202
|
|
|
* @var int |
203
|
|
|
*/ |
204
|
|
|
public $partitionsNum; |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Expression used for subpartitioning. |
208
|
|
|
* |
209
|
|
|
* @var string |
210
|
|
|
*/ |
211
|
|
|
public $subpartitionBy; |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* The number of subpartitions. |
215
|
|
|
* |
216
|
|
|
* @var int |
217
|
|
|
*/ |
218
|
|
|
public $subpartitionsNum; |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* The partition of the new table. |
222
|
|
|
* |
223
|
|
|
* @var PartitionDefinition[] |
224
|
|
|
*/ |
225
|
|
|
public $partitions; |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* If `CREATE TRIGGER` the name of the table. |
229
|
|
|
* |
230
|
|
|
* Used by `CREATE TRIGGER`. |
231
|
|
|
* |
232
|
|
|
* @var Expression |
233
|
|
|
*/ |
234
|
|
|
public $table; |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* The return data type of this routine. |
238
|
|
|
* |
239
|
|
|
* Used by `CREATE FUNCTION`. |
240
|
|
|
* |
241
|
|
|
* @var DataType |
242
|
|
|
*/ |
243
|
|
|
public $return; |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* The parameters of this routine. |
247
|
|
|
* |
248
|
|
|
* Used by `CREATE FUNCTION` and `CREATE PROCEDURE`. |
249
|
|
|
* |
250
|
|
|
* @var ParameterDefinition[] |
251
|
|
|
*/ |
252
|
|
|
public $parameters; |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* The body of this function or procedure. For views, it is the select |
256
|
|
|
* statement that gets the. |
257
|
|
|
* |
258
|
|
|
* Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`. |
259
|
|
|
* |
260
|
|
|
* @var Token[]|string |
261
|
|
|
*/ |
262
|
|
|
public $body = array(); |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @return string |
266
|
|
|
*/ |
267
|
10 |
|
public function build() |
268
|
|
|
{ |
269
|
10 |
|
$fields = ''; |
270
|
10 |
|
if (!empty($this->fields)) { |
271
|
5 |
|
if (is_array($this->fields)) { |
272
|
4 |
|
$fields = CreateDefinition::build($this->fields) . ' '; |
273
|
1 |
|
} elseif ($this->fields instanceof ArrayObj) { |
274
|
1 |
|
$fields = ArrayObj::build($this->fields); |
275
|
|
|
} |
276
|
|
|
} |
277
|
10 |
|
if ($this->options->has('DATABASE')) { |
278
|
|
|
return 'CREATE ' |
279
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
280
|
1 |
|
. Expression::build($this->name) . ' ' |
281
|
1 |
|
. OptionsArray::build($this->entityOptions); |
282
|
9 |
|
} elseif ($this->options->has('TABLE') && !is_null($this->select)) { |
283
|
|
|
return 'CREATE ' |
284
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
285
|
1 |
|
. Expression::build($this->name) . ' ' |
286
|
1 |
|
. $this->select->build(); |
287
|
8 |
|
} elseif ($this->options->has('TABLE') && !is_null($this->like)) { |
288
|
|
|
return 'CREATE ' |
289
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
290
|
1 |
|
. Expression::build($this->name) . ' LIKE ' |
291
|
1 |
|
. Expression::build($this->like); |
292
|
8 |
|
} elseif ($this->options->has('TABLE')) { |
293
|
4 |
|
$partition = ''; |
294
|
|
|
|
295
|
4 |
|
if (!empty($this->partitionBy)) { |
296
|
1 |
|
$partition .= "\nPARTITION BY " . $this->partitionBy; |
297
|
|
|
} |
298
|
4 |
|
if (!empty($this->partitionsNum)) { |
299
|
1 |
|
$partition .= "\nPARTITIONS " . $this->partitionsNum; |
300
|
|
|
} |
301
|
4 |
|
if (!empty($this->subpartitionBy)) { |
302
|
1 |
|
$partition .= "\nSUBPARTITION BY " . $this->subpartitionBy; |
303
|
|
|
} |
304
|
4 |
|
if (!empty($this->subpartitionsNum)) { |
305
|
1 |
|
$partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum; |
306
|
|
|
} |
307
|
4 |
|
if (!empty($this->partitions)) { |
308
|
1 |
|
$partition .= "\n" . PartitionDefinition::build($this->partitions); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
return 'CREATE ' |
312
|
4 |
|
. OptionsArray::build($this->options) . ' ' |
313
|
4 |
|
. Expression::build($this->name) . ' ' |
314
|
4 |
|
. $fields |
315
|
4 |
|
. OptionsArray::build($this->entityOptions) |
316
|
4 |
|
. $partition; |
317
|
4 |
|
} elseif ($this->options->has('VIEW')) { |
318
|
|
|
return 'CREATE ' |
319
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
320
|
1 |
|
. Expression::build($this->name) . ' ' |
321
|
1 |
|
. $fields . ' AS ' . TokensList::build($this->body) . ' ' |
322
|
1 |
|
. OptionsArray::build($this->entityOptions); |
323
|
3 |
|
} elseif ($this->options->has('TRIGGER')) { |
324
|
|
|
return 'CREATE ' |
325
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
326
|
1 |
|
. Expression::build($this->name) . ' ' |
327
|
1 |
|
. OptionsArray::build($this->entityOptions) . ' ' |
328
|
1 |
|
. 'ON ' . Expression::build($this->table) . ' ' |
329
|
1 |
|
. 'FOR EACH ROW ' . TokensList::build($this->body); |
330
|
2 |
|
} elseif (($this->options->has('PROCEDURE')) |
331
|
2 |
|
|| ($this->options->has('FUNCTION')) |
332
|
|
|
) { |
333
|
1 |
|
$tmp = ''; |
334
|
1 |
|
if ($this->options->has('FUNCTION')) { |
335
|
1 |
|
$tmp = 'RETURNS ' . DataType::build($this->return); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
return 'CREATE ' |
339
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
340
|
1 |
|
. Expression::build($this->name) . ' ' |
341
|
1 |
|
. ParameterDefinition::build($this->parameters) . ' ' |
342
|
1 |
|
. $tmp . ' ' . TokensList::build($this->body); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return 'CREATE ' |
346
|
1 |
|
. OptionsArray::build($this->options) . ' ' |
347
|
1 |
|
. Expression::build($this->name) . ' ' |
348
|
1 |
|
. TokensList::build($this->body); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* @param Parser $parser the instance that requests parsing |
353
|
|
|
* @param TokensList $list the list of tokens to be parsed |
354
|
|
|
*/ |
355
|
52 |
|
public function parse(Parser $parser, TokensList $list) |
356
|
|
|
{ |
357
|
52 |
|
++$list->idx; // Skipping `CREATE`. |
358
|
|
|
|
359
|
|
|
// Parsing options. |
360
|
52 |
|
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); |
361
|
52 |
|
++$list->idx; // Skipping last option. |
362
|
|
|
|
363
|
|
|
// Parsing the field name. |
364
|
52 |
|
$this->name = Expression::parse( |
365
|
|
|
$parser, |
366
|
|
|
$list, |
367
|
|
|
array( |
368
|
52 |
|
'parseField' => 'table', |
369
|
|
|
'breakOnAlias' => true, |
370
|
|
|
) |
371
|
|
|
); |
372
|
|
|
|
373
|
52 |
|
if ((!isset($this->name)) || ($this->name === '')) { |
374
|
1 |
|
$parser->error( |
375
|
1 |
|
'The name of the entity was expected.', |
376
|
1 |
|
$list->tokens[$list->idx] |
377
|
|
|
); |
378
|
|
|
} else { |
379
|
51 |
|
++$list->idx; // Skipping field. |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Token parsed at this moment. |
384
|
|
|
* |
385
|
|
|
* @var Token |
386
|
|
|
*/ |
387
|
52 |
|
$token = $list->tokens[$list->idx]; |
388
|
52 |
|
$nextidx = $list->idx + 1; |
389
|
52 |
|
while ($nextidx < $list->count && $list->tokens[$nextidx]->type == Token::TYPE_WHITESPACE) { |
390
|
28 |
|
++$nextidx; |
391
|
|
|
} |
392
|
|
|
|
393
|
52 |
|
if ($this->options->has('DATABASE')) { |
394
|
1 |
|
$this->entityOptions = OptionsArray::parse( |
395
|
|
|
$parser, |
396
|
|
|
$list, |
397
|
1 |
|
static::$DB_OPTIONS |
398
|
|
|
); |
399
|
51 |
|
} elseif ($this->options->has('TABLE') |
400
|
51 |
|
&& ($token->type == Token::TYPE_KEYWORD) |
401
|
51 |
|
&& ($token->keyword == 'SELECT') |
402
|
|
|
) { |
403
|
|
|
/* CREATE TABLE ... SELECT */ |
404
|
2 |
|
$this->select = new SelectStatement($parser, $list); |
405
|
49 |
|
} elseif ($this->options->has('TABLE') |
406
|
49 |
|
&& ($token->type == Token::TYPE_KEYWORD) && ($token->keyword == 'AS') |
407
|
49 |
|
&& ($list->tokens[$nextidx]->type == Token::TYPE_KEYWORD) |
408
|
49 |
|
&& ($list->tokens[$nextidx]->value == 'SELECT') |
409
|
|
|
) { |
410
|
|
|
/* CREATE TABLE ... AS SELECT */ |
411
|
1 |
|
$list->idx = $nextidx; |
412
|
1 |
|
$this->select = new SelectStatement($parser, $list); |
413
|
48 |
|
} elseif ($this->options->has('TABLE') |
414
|
48 |
|
&& $token->type == Token::TYPE_KEYWORD |
415
|
48 |
|
&& $token->keyword == 'LIKE' |
416
|
|
|
) { |
417
|
|
|
/* CREATE TABLE `new_tbl` LIKE 'orig_tbl' */ |
418
|
3 |
|
$list->idx = $nextidx; |
419
|
3 |
|
$this->like = Expression::parse( |
420
|
|
|
$parser, |
421
|
|
|
$list, |
422
|
|
|
array( |
423
|
3 |
|
'parseField' => 'table', |
424
|
|
|
'breakOnAlias' => true, |
425
|
|
|
) |
426
|
|
|
); |
427
|
|
|
// The 'LIKE' keyword was found, but no table_name was found next to it |
428
|
3 |
|
if ($this->like == null) { |
429
|
1 |
|
$parser->error( |
430
|
1 |
|
'A table name was expected.', |
431
|
3 |
|
$list->tokens[$list->idx] |
432
|
|
|
); |
433
|
|
|
} |
434
|
46 |
|
} elseif ($this->options->has('TABLE')) { |
435
|
26 |
|
$this->fields = CreateDefinition::parse($parser, $list); |
436
|
26 |
|
if (empty($this->fields)) { |
437
|
3 |
|
$parser->error( |
438
|
3 |
|
'At least one column definition was expected.', |
439
|
3 |
|
$list->tokens[$list->idx] |
440
|
|
|
); |
441
|
|
|
} |
442
|
26 |
|
++$list->idx; |
443
|
|
|
|
444
|
26 |
|
$this->entityOptions = OptionsArray::parse( |
445
|
|
|
$parser, |
446
|
|
|
$list, |
447
|
26 |
|
static::$TABLE_OPTIONS |
448
|
|
|
); |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* The field that is being filled (`partitionBy` or |
452
|
|
|
* `subpartitionBy`). |
453
|
|
|
* |
454
|
|
|
* @var string |
455
|
|
|
*/ |
456
|
26 |
|
$field = null; |
457
|
|
|
|
458
|
|
|
/** |
459
|
|
|
* The number of brackets. `false` means no bracket was found |
460
|
|
|
* previously. At least one bracket is required to validate the |
461
|
|
|
* expression. |
462
|
|
|
* |
463
|
|
|
* @var int|bool |
464
|
|
|
*/ |
465
|
26 |
|
$brackets = false; |
466
|
|
|
|
467
|
|
|
/* |
468
|
|
|
* Handles partitions. |
469
|
|
|
*/ |
470
|
26 |
|
for (; $list->idx < $list->count; ++$list->idx) { |
471
|
|
|
/** |
472
|
|
|
* Token parsed at this moment. |
473
|
|
|
* |
474
|
|
|
* @var Token |
475
|
|
|
*/ |
476
|
26 |
|
$token = $list->tokens[$list->idx]; |
477
|
|
|
|
478
|
|
|
// End of statement. |
479
|
26 |
|
if ($token->type === Token::TYPE_DELIMITER) { |
480
|
21 |
|
break; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
// Skipping comments. |
484
|
26 |
|
if ($token->type === Token::TYPE_COMMENT) { |
485
|
1 |
|
continue; |
486
|
|
|
} |
487
|
|
|
|
488
|
26 |
|
if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITION BY')) { |
489
|
2 |
|
$field = 'partitionBy'; |
490
|
2 |
|
$brackets = false; |
491
|
26 |
|
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITION BY')) { |
492
|
2 |
|
$field = 'subpartitionBy'; |
493
|
2 |
|
$brackets = false; |
494
|
26 |
|
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITIONS')) { |
495
|
2 |
|
$token = $list->getNextOfType(Token::TYPE_NUMBER); |
496
|
2 |
|
--$list->idx; // `getNextOfType` also advances one position. |
497
|
2 |
|
$this->partitionsNum = $token->value; |
498
|
26 |
|
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITIONS')) { |
499
|
2 |
|
$token = $list->getNextOfType(Token::TYPE_NUMBER); |
500
|
2 |
|
--$list->idx; // `getNextOfType` also advances one position. |
501
|
2 |
|
$this->subpartitionsNum = $token->value; |
502
|
26 |
|
} elseif (!empty($field)) { |
503
|
|
|
/* |
504
|
|
|
* Handling the content of `PARTITION BY` and `SUBPARTITION BY`. |
505
|
|
|
*/ |
506
|
|
|
|
507
|
|
|
// Counting brackets. |
508
|
2 |
|
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { |
509
|
|
|
// This is used instead of `++$brackets` because, |
510
|
|
|
// initially, `$brackets` is `false` cannot be |
511
|
|
|
// incremented. |
512
|
2 |
|
$brackets = $brackets + 1; |
513
|
2 |
|
} elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) { |
514
|
2 |
|
--$brackets; |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
// Building the expression used for partitioning. |
518
|
2 |
|
$this->$field .= ($token->type === Token::TYPE_WHITESPACE) ? ' ' : $token->token; |
519
|
|
|
|
520
|
|
|
// Last bracket was read, the expression ended. |
521
|
|
|
// Comparing with `0` and not `false`, because `false` means |
522
|
|
|
// that no bracket was found and at least one must is |
523
|
|
|
// required. |
524
|
2 |
|
if ($brackets === 0) { |
525
|
2 |
|
$this->$field = trim($this->$field); |
526
|
2 |
|
$field = null; |
527
|
|
|
} |
528
|
26 |
|
} elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { |
529
|
5 |
|
if (!empty($this->partitionBy)) { |
530
|
2 |
|
$this->partitions = ArrayObj::parse( |
|
|
|
|
531
|
|
|
$parser, |
532
|
|
|
$list, |
533
|
|
|
array( |
534
|
2 |
|
'type' => 'PhpMyAdmin\\SqlParser\\Components\\PartitionDefinition', |
535
|
|
|
) |
536
|
|
|
); |
537
|
|
|
} |
538
|
5 |
|
break; |
539
|
|
|
} |
540
|
|
|
} |
541
|
20 |
|
} elseif (($this->options->has('PROCEDURE')) |
542
|
20 |
|
|| ($this->options->has('FUNCTION')) |
543
|
|
|
) { |
544
|
10 |
|
$this->parameters = ParameterDefinition::parse($parser, $list); |
545
|
10 |
|
if ($this->options->has('FUNCTION')) { |
546
|
5 |
|
$prev_token = $token; |
547
|
5 |
|
$token = $list->getNextOfType(Token::TYPE_KEYWORD); |
548
|
5 |
|
if (is_null($token) || $token->keyword !== 'RETURNS') { |
549
|
2 |
|
$parser->error( |
550
|
2 |
|
'A "RETURNS" keyword was expected.', |
551
|
2 |
|
is_null($token) ? $prev_token : $token |
552
|
|
|
); |
553
|
|
|
} else { |
554
|
3 |
|
++$list->idx; |
555
|
3 |
|
$this->return = DataType::parse( |
556
|
|
|
$parser, |
557
|
|
|
$list |
558
|
|
|
); |
559
|
|
|
} |
560
|
|
|
} |
561
|
10 |
|
++$list->idx; |
562
|
|
|
|
563
|
10 |
|
$this->entityOptions = OptionsArray::parse( |
564
|
|
|
$parser, |
565
|
|
|
$list, |
566
|
10 |
|
static::$FUNC_OPTIONS |
567
|
|
|
); |
568
|
10 |
|
++$list->idx; |
569
|
|
|
|
570
|
10 |
|
for (; $list->idx < $list->count; ++$list->idx) { |
571
|
9 |
|
$token = $list->tokens[$list->idx]; |
572
|
9 |
|
$this->body[] = $token; |
573
|
|
|
} |
574
|
10 |
|
} elseif ($this->options->has('VIEW')) { |
575
|
5 |
|
$token = $list->getNext(); // Skipping whitespaces and comments. |
576
|
|
|
|
577
|
|
|
// Parsing columns list. |
578
|
5 |
|
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { |
579
|
2 |
|
--$list->idx; // getNext() also goes forward one field. |
580
|
2 |
|
$this->fields = ArrayObj::parse($parser, $list); |
581
|
2 |
|
++$list->idx; // Skipping last token from the array. |
582
|
2 |
|
$list->getNext(); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
// Parsing the `AS` keyword. |
586
|
5 |
View Code Duplication |
for (; $list->idx < $list->count; ++$list->idx) { |
587
|
5 |
|
$token = $list->tokens[$list->idx]; |
588
|
5 |
|
if ($token->type === Token::TYPE_DELIMITER) { |
589
|
5 |
|
break; |
590
|
|
|
} |
591
|
5 |
|
$this->body[] = $token; |
592
|
|
|
} |
593
|
5 |
|
} elseif ($this->options->has('TRIGGER')) { |
594
|
|
|
// Parsing the time and the event. |
595
|
1 |
|
$this->entityOptions = OptionsArray::parse( |
596
|
|
|
$parser, |
597
|
|
|
$list, |
598
|
1 |
|
static::$TRIGGER_OPTIONS |
599
|
|
|
); |
600
|
1 |
|
++$list->idx; |
601
|
|
|
|
602
|
1 |
|
$list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); |
603
|
1 |
|
++$list->idx; // Skipping `ON`. |
604
|
|
|
|
605
|
|
|
// Parsing the name of the table. |
606
|
1 |
|
$this->table = Expression::parse( |
607
|
|
|
$parser, |
608
|
|
|
$list, |
609
|
|
|
array( |
610
|
1 |
|
'parseField' => 'table', |
611
|
|
|
'breakOnAlias' => true, |
612
|
|
|
) |
613
|
|
|
); |
614
|
1 |
|
++$list->idx; |
615
|
|
|
|
616
|
1 |
|
$list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); |
617
|
1 |
|
++$list->idx; // Skipping `FOR EACH ROW`. |
618
|
|
|
|
619
|
1 |
|
for (; $list->idx < $list->count; ++$list->idx) { |
620
|
1 |
|
$token = $list->tokens[$list->idx]; |
621
|
1 |
|
$this->body[] = $token; |
622
|
|
|
} |
623
|
|
|
} else { |
624
|
4 |
View Code Duplication |
for (; $list->idx < $list->count; ++$list->idx) { |
625
|
4 |
|
$token = $list->tokens[$list->idx]; |
626
|
4 |
|
if ($token->type === Token::TYPE_DELIMITER) { |
627
|
4 |
|
break; |
628
|
|
|
} |
629
|
1 |
|
$this->body[] = $token; |
630
|
|
|
} |
631
|
|
|
} |
632
|
52 |
|
} |
633
|
|
|
} |
634
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.