1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* php-sql-creator.php. |
5
|
|
|
* |
6
|
|
|
* A pure PHP SQL creator, which generates SQL from the output of SQLParser. |
7
|
|
|
* |
8
|
|
|
* Copyright (c) 2012, André Rothe <[email protected], [email protected]> |
9
|
|
|
* |
10
|
|
|
* All rights reserved. |
11
|
|
|
* |
12
|
|
|
* Redistribution and use in source and binary forms, with or without modification, |
13
|
|
|
* are permitted provided that the following conditions are met: |
14
|
|
|
* |
15
|
|
|
* * Redistributions of source code must retain the above copyright notice, |
16
|
|
|
* this list of conditions and the following disclaimer. |
17
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice, |
18
|
|
|
* this list of conditions and the following disclaimer in the documentation |
19
|
|
|
* and/or other materials provided with the distribution. |
20
|
|
|
* |
21
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
22
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
23
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
24
|
|
|
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
25
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
26
|
|
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
27
|
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
29
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
30
|
|
|
* DAMAGE. |
31
|
|
|
*/ |
32
|
|
|
namespace SQLParser; |
33
|
|
|
|
34
|
|
|
class PHPSQLCreator |
35
|
|
|
{ |
36
|
|
|
public function __construct($parsed = false) |
37
|
|
|
{ |
38
|
|
|
if ($parsed) { |
39
|
|
|
$this->create($parsed); |
40
|
|
|
} |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
public function create($parsed) |
44
|
|
|
{ |
45
|
|
|
$k = key($parsed); |
46
|
|
|
switch ($k) { |
47
|
|
|
|
48
|
|
|
case 'UNION': |
49
|
|
|
case 'UNION ALL': |
50
|
|
|
throw new UnsupportedFeatureException($k); |
51
|
|
|
break; |
|
|
|
|
52
|
|
|
case 'SELECT': |
53
|
|
|
$this->created = $this->processSelectStatement($parsed); |
|
|
|
|
54
|
|
|
break; |
55
|
|
|
case 'INSERT': |
56
|
|
|
$this->created = $this->processInsertStatement($parsed); |
57
|
|
|
break; |
58
|
|
|
case 'DELETE': |
59
|
|
|
$this->created = $this->processDeleteStatement($parsed); |
60
|
|
|
break; |
61
|
|
|
case 'UPDATE': |
62
|
|
|
$this->created = $this->processUpdateStatement($parsed); |
63
|
|
|
break; |
64
|
|
|
default: |
65
|
|
|
throw new UnsupportedFeatureException($k); |
66
|
|
|
break; |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
return $this->created; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
protected function processSelectStatement($parsed) |
73
|
|
|
{ |
74
|
|
|
$sql = $this->processSELECT($parsed['SELECT']).' '.$this->processFROM($parsed['FROM']); |
75
|
|
|
if (isset($parsed['WHERE'])) { |
76
|
|
|
$sql .= ' '.$this->processWHERE($parsed['WHERE']); |
77
|
|
|
} |
78
|
|
|
if (isset($parsed['GROUP'])) { |
79
|
|
|
$sql .= ' '.$this->processGROUP($parsed['GROUP']); |
80
|
|
|
} |
81
|
|
|
if (isset($parsed['ORDER'])) { |
82
|
|
|
$sql .= ' '.$this->processORDER($parsed['ORDER']); |
83
|
|
|
} |
84
|
|
|
if (isset($parsed['LIMIT'])) { |
85
|
|
|
$sql .= ' '.$this->processLIMIT($parsed['LIMIT']); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return $sql; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
protected function processInsertStatement($parsed) |
92
|
|
|
{ |
93
|
|
|
return $this->processINSERT($parsed['INSERT']).' '.$this->processVALUES($parsed['VALUES']); |
94
|
|
|
# TODO: subquery? |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
protected function processDeleteStatement($parsed) |
98
|
|
|
{ |
99
|
|
|
return $this->processDELETE($parsed['DELETE']).' '.$this->processFROM($parsed['FROM']).' ' |
100
|
|
|
.$this->processWHERE($parsed['WHERE']); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
protected function processUpdateStatement($parsed) |
104
|
|
|
{ |
105
|
|
|
$sql = $this->processUPDATE($parsed['UPDATE']).' '.$this->processSET($parsed['SET']); |
106
|
|
|
if (isset($parsed['WHERE'])) { |
107
|
|
|
$sql .= ' '.$this->processWHERE($parsed['WHERE']); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $sql; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
protected function processDELETE($parsed) |
114
|
|
|
{ |
115
|
|
|
$sql = 'DELETE'; |
116
|
|
|
foreach ($parsed['TABLES'] as $k => $v) { |
117
|
|
|
$sql .= $v.','; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return substr($sql, 0, -1); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
View Code Duplication |
protected function processSELECT($parsed) |
|
|
|
|
124
|
|
|
{ |
125
|
|
|
$sql = ''; |
126
|
|
|
foreach ($parsed as $k => $v) { |
127
|
|
|
$len = strlen($sql); |
128
|
|
|
$sql .= $this->processColRef($v); |
129
|
|
|
$sql .= $this->processSelectExpression($v); |
130
|
|
|
$sql .= $this->processFunction($v); |
131
|
|
|
$sql .= $this->processConstant($v); |
132
|
|
|
|
133
|
|
|
if ($len == strlen($sql)) { |
134
|
|
|
throw new UnableToCreateSQLException('SELECT', $k, $v, 'expr_type'); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
$sql .= ','; |
138
|
|
|
} |
139
|
|
|
$sql = substr($sql, 0, -1); |
140
|
|
|
|
141
|
|
|
return 'SELECT '.$sql; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
View Code Duplication |
protected function processFROM($parsed) |
|
|
|
|
145
|
|
|
{ |
146
|
|
|
$sql = ''; |
147
|
|
|
foreach ($parsed as $k => $v) { |
148
|
|
|
$len = strlen($sql); |
149
|
|
|
$sql .= $this->processTable($v, $k); |
150
|
|
|
$sql .= $this->processTableExpression($v, $k); |
151
|
|
|
$sql .= $this->processSubquery($v, $k); |
152
|
|
|
|
153
|
|
|
if ($len == strlen($sql)) { |
154
|
|
|
throw new UnableToCreateSQLException('FROM', $k, $v, 'expr_type'); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
$sql .= ' '; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
return 'FROM '.substr($sql, 0, -1); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
protected function processORDER($parsed) |
164
|
|
|
{ |
165
|
|
|
$sql = ''; |
166
|
|
|
foreach ($parsed as $k => $v) { |
167
|
|
|
$len = strlen($sql); |
168
|
|
|
$sql .= $this->processOrderByAlias($v); |
169
|
|
|
$sql .= $this->processColRef($v); |
170
|
|
|
|
171
|
|
|
if ($len == strlen($sql)) { |
172
|
|
|
throw new UnableToCreateSQLException('ORDER', $k, $v, 'expr_type'); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$sql .= ','; |
176
|
|
|
} |
177
|
|
|
$sql = substr($sql, 0, -1); |
178
|
|
|
|
179
|
|
|
return 'ORDER BY '.$sql; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
protected function processLIMIT($parsed) |
183
|
|
|
{ |
184
|
|
|
$sql = ($parsed['offset'] ? $parsed['offset'].', ' : '').$parsed['rowcount']; |
185
|
|
|
if ($sql === '') { |
186
|
|
|
throw new UnableToCreateSQLException('LIMIT', 'rowcount', $parsed, 'rowcount'); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return 'LIMIT '.$sql; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
View Code Duplication |
protected function processGROUP($parsed) |
|
|
|
|
193
|
|
|
{ |
194
|
|
|
$sql = ''; |
195
|
|
|
foreach ($parsed as $k => $v) { |
196
|
|
|
$len = strlen($sql); |
197
|
|
|
$sql .= $this->processColRef($v); |
198
|
|
|
|
199
|
|
|
if ($len == strlen($sql)) { |
200
|
|
|
throw new UnableToCreateSQLException('GROUP', $k, $v, 'expr_type'); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
$sql .= ','; |
204
|
|
|
} |
205
|
|
|
$sql = substr($sql, 0, -1); |
206
|
|
|
|
207
|
|
|
return 'GROUP BY '.$sql; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
protected function processRecord($parsed) |
211
|
|
|
{ |
212
|
|
|
if ($parsed['expr_type'] !== ExpressionType::RECORD) { |
213
|
|
|
return ''; |
214
|
|
|
} |
215
|
|
|
$sql = ''; |
216
|
|
View Code Duplication |
foreach ($parsed['data'] as $k => $v) { |
|
|
|
|
217
|
|
|
$len = strlen($sql); |
218
|
|
|
$sql .= $this->processConstant($v); |
219
|
|
|
$sql .= $this->processFunction($v); |
220
|
|
|
$sql .= $this->processOperator($v); |
221
|
|
|
|
222
|
|
|
if ($len == strlen($sql)) { |
223
|
|
|
throw new UnableToCreateSQLException(ExpressionType::RECORD, $k, $v, 'expr_type'); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
$sql .= ','; |
227
|
|
|
} |
228
|
|
|
$sql = substr($sql, 0, -1); |
229
|
|
|
|
230
|
|
|
return '('.$sql.')'; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
View Code Duplication |
protected function processVALUES($parsed) |
|
|
|
|
234
|
|
|
{ |
235
|
|
|
$sql = ''; |
236
|
|
|
foreach ($parsed as $k => $v) { |
237
|
|
|
$len = strlen($sql); |
238
|
|
|
$sql .= $this->processRecord($v); |
239
|
|
|
|
240
|
|
|
if ($len == strlen($sql)) { |
241
|
|
|
throw new UnableToCreateSQLException('VALUES', $k, $v, 'expr_type'); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$sql .= ','; |
245
|
|
|
} |
246
|
|
|
$sql = substr($sql, 0, -1); |
247
|
|
|
|
248
|
|
|
return 'VALUES '.$sql; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
protected function processINSERT($parsed) |
252
|
|
|
{ |
253
|
|
|
$sql = 'INSERT INTO '.$parsed['table']; |
254
|
|
|
|
255
|
|
|
if ($parsed['columns'] === false) { |
256
|
|
|
return $sql; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$columns = ''; |
260
|
|
|
foreach ($parsed['columns'] as $k => $v) { |
261
|
|
|
$len = strlen($columns); |
262
|
|
|
$columns .= $this->processColRef($v); |
263
|
|
|
|
264
|
|
|
if ($len == strlen($columns)) { |
265
|
|
|
throw new UnableToCreateSQLException('INSERT[columns]', $k, $v, 'expr_type'); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$columns .= ','; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
if ($columns !== '') { |
272
|
|
|
$columns = ' ('.substr($columns, 0, -1).')'; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
$sql .= $columns; |
276
|
|
|
|
277
|
|
|
return $sql; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
protected function processUPDATE($parsed) |
281
|
|
|
{ |
282
|
|
|
return 'UPDATE '.$parsed[0]['table']; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
protected function processSetExpression($parsed) |
286
|
|
|
{ |
287
|
|
|
if ($parsed['expr_type'] !== ExpressionType::EXPRESSION) { |
288
|
|
|
return ''; |
289
|
|
|
} |
290
|
|
|
$sql = ''; |
291
|
|
|
foreach ($parsed['sub_tree'] as $k => $v) { |
292
|
|
|
$len = strlen($sql); |
293
|
|
|
$sql .= $this->processColRef($v); |
294
|
|
|
$sql .= $this->processConstant($v); |
295
|
|
|
$sql .= $this->processOperator($v); |
296
|
|
|
$sql .= $this->processFunction($v); |
297
|
|
|
|
298
|
|
|
if ($len == strlen($sql)) { |
299
|
|
|
throw new UnableToCreateSQLException('SET expression subtree', $k, $v, 'expr_type'); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
$sql .= ' '; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
$sql = substr($sql, 0, -1); |
306
|
|
|
|
307
|
|
|
return $sql; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
View Code Duplication |
protected function processSET($parsed) |
|
|
|
|
311
|
|
|
{ |
312
|
|
|
$sql = ''; |
313
|
|
|
foreach ($parsed as $k => $v) { |
314
|
|
|
$len = strlen($sql); |
315
|
|
|
$sql .= $this->processSetExpression($v); |
316
|
|
|
|
317
|
|
|
if ($len == strlen($sql)) { |
318
|
|
|
throw new UnableToCreateSQLException('SET', $k, $v, 'expr_type'); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
$sql .= ','; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
return 'SET '.substr($sql, 0, -1); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
protected function processWHERE($parsed) |
328
|
|
|
{ |
329
|
|
|
$sql = 'WHERE '; |
330
|
|
|
foreach ($parsed as $k => $v) { |
331
|
|
|
$len = strlen($sql); |
332
|
|
|
|
333
|
|
|
$sql .= $this->processOperator($v); |
334
|
|
|
$sql .= $this->processConstant($v); |
335
|
|
|
$sql .= $this->processColRef($v); |
336
|
|
|
$sql .= $this->processSubquery($v); |
337
|
|
|
$sql .= $this->processInList($v); |
338
|
|
|
$sql .= $this->processFunction($v); |
339
|
|
|
$sql .= $this->processWhereExpression($v); |
340
|
|
|
$sql .= $this->processWhereBracketExpression($v); |
341
|
|
|
|
342
|
|
|
if (strlen($sql) == $len) { |
343
|
|
|
throw new UnableToCreateSQLException('WHERE', $k, $v, 'expr_type'); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
$sql .= ' '; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
return substr($sql, 0, -1); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
View Code Duplication |
protected function processWhereExpression($parsed) |
|
|
|
|
353
|
|
|
{ |
354
|
|
|
if ($parsed['expr_type'] !== ExpressionType::EXPRESSION) { |
355
|
|
|
return ''; |
356
|
|
|
} |
357
|
|
|
$sql = ''; |
358
|
|
|
foreach ($parsed['sub_tree'] as $k => $v) { |
359
|
|
|
$len = strlen($sql); |
360
|
|
|
$sql .= $this->processColRef($v); |
361
|
|
|
$sql .= $this->processConstant($v); |
362
|
|
|
$sql .= $this->processOperator($v); |
363
|
|
|
$sql .= $this->processInList($v); |
364
|
|
|
$sql .= $this->processFunction($v); |
365
|
|
|
$sql .= $this->processWhereExpression($v); |
366
|
|
|
$sql .= $this->processWhereBracketExpression($v); |
367
|
|
|
|
368
|
|
|
if ($len == strlen($sql)) { |
369
|
|
|
throw new UnableToCreateSQLException('WHERE expression subtree', $k, $v, 'expr_type'); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
$sql .= ' '; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
$sql = substr($sql, 0, -1); |
376
|
|
|
|
377
|
|
|
return $sql; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
View Code Duplication |
protected function processWhereBracketExpression($parsed) |
|
|
|
|
381
|
|
|
{ |
382
|
|
|
if ($parsed['expr_type'] !== ExpressionType::BRACKET_EXPRESSION) { |
383
|
|
|
return ''; |
384
|
|
|
} |
385
|
|
|
$sql = ''; |
386
|
|
|
foreach ($parsed['sub_tree'] as $k => $v) { |
387
|
|
|
$len = strlen($sql); |
388
|
|
|
$sql .= $this->processColRef($v); |
389
|
|
|
$sql .= $this->processConstant($v); |
390
|
|
|
$sql .= $this->processOperator($v); |
391
|
|
|
$sql .= $this->processInList($v); |
392
|
|
|
$sql .= $this->processFunction($v); |
393
|
|
|
$sql .= $this->processWhereExpression($v); |
394
|
|
|
$sql .= $this->processWhereBracketExpression($v); |
395
|
|
|
|
396
|
|
|
if ($len == strlen($sql)) { |
397
|
|
|
throw new UnableToCreateSQLException('WHERE expression subtree', $k, $v, 'expr_type'); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
$sql .= ' '; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
$sql = '('.substr($sql, 0, -1).')'; |
404
|
|
|
|
405
|
|
|
return $sql; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
protected function processOrderByAlias($parsed) |
409
|
|
|
{ |
410
|
|
|
if ($parsed['expr_type'] !== ExpressionType::ALIAS) { |
411
|
|
|
return ''; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
return $parsed['base_expr'].$this->processDirection($parsed['direction']); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
protected function processLimitRowCount($key, $value) |
418
|
|
|
{ |
419
|
|
|
if ($key != 'rowcount') { |
420
|
|
|
return ''; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
return $value; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
protected function processLimitOffset($key, $value) |
427
|
|
|
{ |
428
|
|
|
if ($key !== 'offset') { |
429
|
|
|
return ''; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
return $value; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
protected function processFunction($parsed) |
436
|
|
|
{ |
437
|
|
|
if (($parsed['expr_type'] !== ExpressionType::AGGREGATE_FUNCTION) |
438
|
|
|
&& ($parsed['expr_type'] !== ExpressionType::SIMPLE_FUNCTION)) { |
439
|
|
|
return ''; |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
if ($parsed['sub_tree'] === false) { |
443
|
|
|
return $parsed['base_expr'].'()'; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
$sql = ''; |
447
|
|
|
foreach ($parsed['sub_tree'] as $k => $v) { |
448
|
|
|
$len = strlen($sql); |
449
|
|
|
$sql .= $this->processFunction($v); |
450
|
|
|
$sql .= $this->processConstant($v); |
451
|
|
|
$sql .= $this->processColRef($v); |
452
|
|
|
$sql .= $this->processReserved($v); |
453
|
|
|
|
454
|
|
|
if ($len == strlen($sql)) { |
455
|
|
|
throw new UnableToCreateSQLException('function subtree', $k, $v, 'expr_type'); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
$sql .= ($this->isReserved($v) ? ' ' : ','); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
return $parsed['base_expr'].'('.substr($sql, 0, -1).')'; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
protected function processSelectExpression($parsed) |
465
|
|
|
{ |
466
|
|
|
if ($parsed['expr_type'] !== ExpressionType::EXPRESSION) { |
467
|
|
|
return ''; |
468
|
|
|
} |
469
|
|
|
$sql = $this->processSubTree($parsed, ' '); |
470
|
|
|
$sql .= $this->processAlias($parsed['alias']); |
471
|
|
|
|
472
|
|
|
return $sql; |
473
|
|
|
} |
474
|
|
|
|
475
|
|
View Code Duplication |
protected function processSelectBracketExpression($parsed) |
|
|
|
|
476
|
|
|
{ |
477
|
|
|
if ($parsed['expr_type'] !== ExpressionType::BRACKET_EXPRESSION) { |
478
|
|
|
return ''; |
479
|
|
|
} |
480
|
|
|
$sql = $this->processSubTree($parsed, ' '); |
481
|
|
|
$sql = '('.$sql.')'; |
482
|
|
|
|
483
|
|
|
return $sql; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
protected function processSubTree($parsed, $delim = ' ') |
487
|
|
|
{ |
488
|
|
|
if ($parsed['sub_tree'] === '') { |
489
|
|
|
return ''; |
490
|
|
|
} |
491
|
|
|
$sql = ''; |
492
|
|
|
foreach ($parsed['sub_tree'] as $k => $v) { |
493
|
|
|
$len = strlen($sql); |
494
|
|
|
$sql .= $this->processFunction($v); |
495
|
|
|
$sql .= $this->processOperator($v); |
496
|
|
|
$sql .= $this->processConstant($v); |
497
|
|
|
$sql .= $this->processSubQuery($v); |
498
|
|
|
$sql .= $this->processSelectBracketExpression($v); |
499
|
|
|
|
500
|
|
|
if ($len == strlen($sql)) { |
501
|
|
|
throw new UnableToCreateSQLException('expression subtree', $k, $v, 'expr_type'); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
$sql .= $delim; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
return substr($sql, 0, -1); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
protected function processRefClause($parsed) |
511
|
|
|
{ |
512
|
|
|
if ($parsed === false) { |
513
|
|
|
return ''; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
$sql = ''; |
517
|
|
View Code Duplication |
foreach ($parsed as $k => $v) { |
|
|
|
|
518
|
|
|
$len = strlen($sql); |
519
|
|
|
$sql .= $this->processColRef($v); |
520
|
|
|
$sql .= $this->processOperator($v); |
521
|
|
|
$sql .= $this->processConstant($v); |
522
|
|
|
|
523
|
|
|
if ($len == strlen($sql)) { |
524
|
|
|
throw new UnableToCreateSQLException('expression ref_clause', $k, $v, 'expr_type'); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
$sql .= ' '; |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
return '('.substr($sql, 0, -1).')'; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
protected function processAlias($parsed) |
534
|
|
|
{ |
535
|
|
|
if ($parsed === false) { |
536
|
|
|
return ''; |
537
|
|
|
} |
538
|
|
|
$sql = ''; |
539
|
|
|
if ($parsed['as']) { |
540
|
|
|
$sql .= ' as'; |
541
|
|
|
} |
542
|
|
|
$sql .= ' '.$parsed['name']; |
543
|
|
|
|
544
|
|
|
return $sql; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
protected function processJoin($parsed) |
548
|
|
|
{ |
549
|
|
|
if ($parsed === 'CROSS') { |
550
|
|
|
return ','; |
551
|
|
|
} |
552
|
|
|
if ($parsed === 'JOIN') { |
553
|
|
|
return 'INNER JOIN'; |
554
|
|
|
} |
555
|
|
|
if ($parsed === 'LEFT') { |
556
|
|
|
return 'LEFT JOIN'; |
557
|
|
|
} |
558
|
|
|
if ($parsed === 'RIGHT') { |
559
|
|
|
return 'RIGHT JOIN'; |
560
|
|
|
} |
561
|
|
|
// TODO: add more |
562
|
|
|
throw new UnsupportedFeatureException($parsed); |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
protected function processRefType($parsed) |
566
|
|
|
{ |
567
|
|
|
if ($parsed === false) { |
568
|
|
|
return ''; |
569
|
|
|
} |
570
|
|
|
if ($parsed === 'ON') { |
571
|
|
|
return ' ON '; |
572
|
|
|
} |
573
|
|
|
if ($parsed === 'USING') { |
574
|
|
|
return ' USING '; |
575
|
|
|
} |
576
|
|
|
// TODO: add more |
577
|
|
|
throw new UnsupportedFeatureException($parsed); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
protected function processTable($parsed, $index) |
581
|
|
|
{ |
582
|
|
|
if ($parsed['expr_type'] !== ExpressionType::TABLE) { |
583
|
|
|
return ''; |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
$sql = $parsed['table']; |
587
|
|
|
$sql .= $this->processAlias($parsed['alias']); |
588
|
|
|
|
589
|
|
|
if ($index !== 0) { |
590
|
|
|
$sql = $this->processJoin($parsed['join_type']).' '.$sql; |
591
|
|
|
$sql .= $this->processRefType($parsed['ref_type']); |
592
|
|
|
$sql .= $this->processRefClause($parsed['ref_clause']); |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
return $sql; |
596
|
|
|
} |
597
|
|
|
|
598
|
|
View Code Duplication |
protected function processTableExpression($parsed, $index) |
|
|
|
|
599
|
|
|
{ |
600
|
|
|
if ($parsed['expr_type'] !== ExpressionType::TABLE_EXPRESSION) { |
601
|
|
|
return ''; |
602
|
|
|
} |
603
|
|
|
$sql = substr($this->processFROM($parsed['sub_tree']), 5); // remove FROM keyword |
604
|
|
|
$sql = '('.$sql.')'; |
605
|
|
|
$sql .= $this->processAlias($parsed['alias']); |
606
|
|
|
|
607
|
|
|
if ($index !== 0) { |
608
|
|
|
$sql = $this->processJoin($parsed['join_type']).' '.$sql; |
609
|
|
|
$sql .= $this->processRefType($parsed['ref_type']); |
610
|
|
|
$sql .= $this->processRefClause($parsed['ref_clause']); |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
return $sql; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
View Code Duplication |
protected function processSubQuery($parsed, $index = 0) |
|
|
|
|
617
|
|
|
{ |
618
|
|
|
if ($parsed['expr_type'] !== ExpressionType::SUBQUERY) { |
619
|
|
|
return ''; |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
$sql = $this->processSelectStatement($parsed['sub_tree']); |
623
|
|
|
$sql = '('.$sql.')'; |
624
|
|
|
|
625
|
|
|
if (isset($parsed['alias'])) { |
626
|
|
|
$sql .= $this->processAlias($parsed['alias']); |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
if ($index !== 0) { |
630
|
|
|
$sql = $this->processJoin($parsed['join_type']).' '.$sql; |
631
|
|
|
$sql .= $this->processRefType($parsed['ref_type']); |
632
|
|
|
$sql .= $this->processRefClause($parsed['ref_clause']); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
return $sql; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
protected function processOperator($parsed) |
639
|
|
|
{ |
640
|
|
|
if ($parsed['expr_type'] !== ExpressionType::OPERATOR) { |
641
|
|
|
return ''; |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
return $parsed['base_expr']; |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
protected function processColRef($parsed) |
648
|
|
|
{ |
649
|
|
|
if ($parsed['expr_type'] !== ExpressionType::COLREF) { |
650
|
|
|
return ''; |
651
|
|
|
} |
652
|
|
|
$sql = $parsed['base_expr']; |
653
|
|
|
if (isset($parsed['alias'])) { |
654
|
|
|
$sql .= $this->processAlias($parsed['alias']); |
655
|
|
|
} |
656
|
|
|
if (isset($parsed['direction'])) { |
657
|
|
|
$sql .= $this->processDirection($parsed['direction']); |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
return $sql; |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
protected function processDirection($parsed) |
664
|
|
|
{ |
665
|
|
|
$sql = ($parsed ? ' '.$parsed : ''); |
666
|
|
|
|
667
|
|
|
return $sql; |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
protected function processReserved($parsed) |
671
|
|
|
{ |
672
|
|
|
if (!$this->isReserved($parsed)) { |
673
|
|
|
return ''; |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
return $parsed['base_expr']; |
677
|
|
|
} |
678
|
|
|
|
679
|
|
|
protected function isReserved($parsed) |
680
|
|
|
{ |
681
|
|
|
return ($parsed['expr_type'] === ExpressionType::RESERVED); |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
protected function processConstant($parsed) |
685
|
|
|
{ |
686
|
|
|
if ($parsed['expr_type'] !== ExpressionType::CONSTANT) { |
687
|
|
|
return ''; |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
return $parsed['base_expr']; |
691
|
|
|
} |
692
|
|
|
|
693
|
|
View Code Duplication |
protected function processInList($parsed) |
|
|
|
|
694
|
|
|
{ |
695
|
|
|
if ($parsed['expr_type'] !== ExpressionType::IN_LIST) { |
696
|
|
|
return ''; |
697
|
|
|
} |
698
|
|
|
$sql = $this->processSubTree($parsed, ','); |
699
|
|
|
|
700
|
|
|
return '('.$sql.')'; |
701
|
|
|
} |
702
|
|
|
} // END CLASS |
703
|
|
|
|
704
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.