1 /* Generated by re2c 0.16 */
2 #line 1 "ext/pdo/pdo_sql_parser.re"
3 /*
4 +----------------------------------------------------------------------+
5 | PHP Version 7 |
6 +----------------------------------------------------------------------+
7 | Copyright (c) 1997-2018 The PHP Group |
8 +----------------------------------------------------------------------+
9 | This source file is subject to version 3.01 of the PHP license, |
10 | that is bundled with this package in the file LICENSE, and is |
11 | available through the world-wide-web at the following url: |
12 | http://www.php.net/license/3_01.txt |
13 | If you did not receive a copy of the PHP license and are unable to |
14 | obtain it through the world-wide-web, please send a note to |
15 | license@php.net so we can mail you a copy immediately. |
16 +----------------------------------------------------------------------+
17 | Author: George Schlossnagle <george@omniti.com> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id$ */
22
23 #include "php.h"
24 #include "php_pdo_driver.h"
25 #include "php_pdo_int.h"
26
27 #define PDO_PARSER_TEXT 1
28 #define PDO_PARSER_BIND 2
29 #define PDO_PARSER_BIND_POS 3
30 #define PDO_PARSER_EOI 4
31
32 #define RET(i) {s->cur = cursor; return i; }
33 #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
34
35 #define YYCTYPE unsigned char
36 #define YYCURSOR cursor
37 #define YYLIMIT s->end
38 #define YYMARKER s->ptr
39 #define YYFILL(n) { RET(PDO_PARSER_EOI); }
40
41 typedef struct Scanner {
42 char *ptr, *cur, *tok, *end;
43 } Scanner;
44
scan(Scanner * s)45 static int scan(Scanner *s)
46 {
47 char *cursor = s->cur;
48
49 s->tok = cursor;
50 #line 55 "ext/pdo/pdo_sql_parser.re"
51
52
53
54 #line 55 "ext/pdo/pdo_sql_parser.c"
55 {
56 YYCTYPE yych;
57 if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
58 yych = *YYCURSOR;
59 switch (yych) {
60 case 0x00: goto yy2;
61 case '"': goto yy6;
62 case '\'': goto yy8;
63 case '(':
64 case ')':
65 case '*':
66 case '+':
67 case ',':
68 case '.': goto yy9;
69 case '-': goto yy10;
70 case '/': goto yy11;
71 case ':': goto yy12;
72 case '?': goto yy13;
73 default: goto yy3;
74 }
75 yy2:
76 YYCURSOR = YYMARKER;
77 goto yy7;
78 yy3:
79 ++YYCURSOR;
80 if (YYLIMIT <= YYCURSOR) YYFILL(1);
81 yych = *YYCURSOR;
82 switch (yych) {
83 case 0x00:
84 case '"':
85 case '\'':
86 case '(':
87 case ')':
88 case '*':
89 case '+':
90 case ',':
91 case '-':
92 case '.':
93 case '/':
94 case ':':
95 case '?': goto yy5;
96 default: goto yy3;
97 }
98 yy5:
99 #line 65 "ext/pdo/pdo_sql_parser.re"
100 { RET(PDO_PARSER_TEXT); }
101 #line 102 "ext/pdo/pdo_sql_parser.c"
102 yy6:
103 yych = *(YYMARKER = ++YYCURSOR);
104 if (yych >= 0x01) goto yy16;
105 yy7:
106 #line 63 "ext/pdo/pdo_sql_parser.re"
107 { SKIP_ONE(PDO_PARSER_TEXT); }
108 #line 109 "ext/pdo/pdo_sql_parser.c"
109 yy8:
110 yych = *(YYMARKER = ++YYCURSOR);
111 if (yych <= 0x00) goto yy7;
112 goto yy21;
113 yy9:
114 yych = *++YYCURSOR;
115 goto yy7;
116 yy10:
117 yych = *++YYCURSOR;
118 switch (yych) {
119 case '-': goto yy25;
120 default: goto yy7;
121 }
122 yy11:
123 yych = *(YYMARKER = ++YYCURSOR);
124 switch (yych) {
125 case '*': goto yy28;
126 default: goto yy7;
127 }
128 yy12:
129 yych = *++YYCURSOR;
130 switch (yych) {
131 case '0':
132 case '1':
133 case '2':
134 case '3':
135 case '4':
136 case '5':
137 case '6':
138 case '7':
139 case '8':
140 case '9':
141 case 'A':
142 case 'B':
143 case 'C':
144 case 'D':
145 case 'E':
146 case 'F':
147 case 'G':
148 case 'H':
149 case 'I':
150 case 'J':
151 case 'K':
152 case 'L':
153 case 'M':
154 case 'N':
155 case 'O':
156 case 'P':
157 case 'Q':
158 case 'R':
159 case 'S':
160 case 'T':
161 case 'U':
162 case 'V':
163 case 'W':
164 case 'X':
165 case 'Y':
166 case 'Z':
167 case '_':
168 case 'a':
169 case 'b':
170 case 'c':
171 case 'd':
172 case 'e':
173 case 'f':
174 case 'g':
175 case 'h':
176 case 'i':
177 case 'j':
178 case 'k':
179 case 'l':
180 case 'm':
181 case 'n':
182 case 'o':
183 case 'p':
184 case 'q':
185 case 'r':
186 case 's':
187 case 't':
188 case 'u':
189 case 'v':
190 case 'w':
191 case 'x':
192 case 'y':
193 case 'z': goto yy30;
194 case ':': goto yy33;
195 default: goto yy7;
196 }
197 yy13:
198 ++YYCURSOR;
199 switch ((yych = *YYCURSOR)) {
200 case '?': goto yy36;
201 default: goto yy14;
202 }
203 yy14:
204 #line 62 "ext/pdo/pdo_sql_parser.re"
205 { RET(PDO_PARSER_BIND_POS); }
206 #line 207 "ext/pdo/pdo_sql_parser.c"
207 yy15:
208 ++YYCURSOR;
209 if (YYLIMIT <= YYCURSOR) YYFILL(1);
210 yych = *YYCURSOR;
211 yy16:
212 switch (yych) {
213 case 0x00: goto yy2;
214 case '"': goto yy17;
215 case '\\': goto yy19;
216 default: goto yy15;
217 }
218 yy17:
219 ++YYCURSOR;
220 #line 58 "ext/pdo/pdo_sql_parser.re"
221 { RET(PDO_PARSER_TEXT); }
222 #line 223 "ext/pdo/pdo_sql_parser.c"
223 yy19:
224 ++YYCURSOR;
225 if (YYLIMIT <= YYCURSOR) YYFILL(1);
226 yych = *YYCURSOR;
227 if (yych <= 0x00) goto yy2;
228 goto yy15;
229 yy20:
230 ++YYCURSOR;
231 if (YYLIMIT <= YYCURSOR) YYFILL(1);
232 yych = *YYCURSOR;
233 yy21:
234 switch (yych) {
235 case 0x00: goto yy2;
236 case '\'': goto yy22;
237 case '\\': goto yy24;
238 default: goto yy20;
239 }
240 yy22:
241 ++YYCURSOR;
242 #line 59 "ext/pdo/pdo_sql_parser.re"
243 { RET(PDO_PARSER_TEXT); }
244 #line 245 "ext/pdo/pdo_sql_parser.c"
245 yy24:
246 ++YYCURSOR;
247 if (YYLIMIT <= YYCURSOR) YYFILL(1);
248 yych = *YYCURSOR;
249 if (yych <= 0x00) goto yy2;
250 goto yy20;
251 yy25:
252 ++YYCURSOR;
253 if (YYLIMIT <= YYCURSOR) YYFILL(1);
254 yych = *YYCURSOR;
255 switch (yych) {
256 case '\n':
257 case '\r': goto yy27;
258 default: goto yy25;
259 }
260 yy27:
261 #line 64 "ext/pdo/pdo_sql_parser.re"
262 { RET(PDO_PARSER_TEXT); }
263 #line 264 "ext/pdo/pdo_sql_parser.c"
264 yy28:
265 ++YYCURSOR;
266 if (YYLIMIT <= YYCURSOR) YYFILL(1);
267 yych = *YYCURSOR;
268 switch (yych) {
269 case '*': goto yy38;
270 default: goto yy28;
271 }
272 yy30:
273 ++YYCURSOR;
274 if (YYLIMIT <= YYCURSOR) YYFILL(1);
275 yych = *YYCURSOR;
276 switch (yych) {
277 case '0':
278 case '1':
279 case '2':
280 case '3':
281 case '4':
282 case '5':
283 case '6':
284 case '7':
285 case '8':
286 case '9':
287 case 'A':
288 case 'B':
289 case 'C':
290 case 'D':
291 case 'E':
292 case 'F':
293 case 'G':
294 case 'H':
295 case 'I':
296 case 'J':
297 case 'K':
298 case 'L':
299 case 'M':
300 case 'N':
301 case 'O':
302 case 'P':
303 case 'Q':
304 case 'R':
305 case 'S':
306 case 'T':
307 case 'U':
308 case 'V':
309 case 'W':
310 case 'X':
311 case 'Y':
312 case 'Z':
313 case '_':
314 case 'a':
315 case 'b':
316 case 'c':
317 case 'd':
318 case 'e':
319 case 'f':
320 case 'g':
321 case 'h':
322 case 'i':
323 case 'j':
324 case 'k':
325 case 'l':
326 case 'm':
327 case 'n':
328 case 'o':
329 case 'p':
330 case 'q':
331 case 'r':
332 case 's':
333 case 't':
334 case 'u':
335 case 'v':
336 case 'w':
337 case 'x':
338 case 'y':
339 case 'z': goto yy30;
340 default: goto yy32;
341 }
342 yy32:
343 #line 61 "ext/pdo/pdo_sql_parser.re"
344 { RET(PDO_PARSER_BIND); }
345 #line 346 "ext/pdo/pdo_sql_parser.c"
346 yy33:
347 ++YYCURSOR;
348 if (YYLIMIT <= YYCURSOR) YYFILL(1);
349 yych = *YYCURSOR;
350 switch (yych) {
351 case ':': goto yy33;
352 default: goto yy35;
353 }
354 yy35:
355 #line 60 "ext/pdo/pdo_sql_parser.re"
356 { RET(PDO_PARSER_TEXT); }
357 #line 358 "ext/pdo/pdo_sql_parser.c"
358 yy36:
359 ++YYCURSOR;
360 if (YYLIMIT <= YYCURSOR) YYFILL(1);
361 yych = *YYCURSOR;
362 switch (yych) {
363 case '?': goto yy36;
364 default: goto yy35;
365 }
366 yy38:
367 ++YYCURSOR;
368 if (YYLIMIT <= YYCURSOR) YYFILL(1);
369 yych = *YYCURSOR;
370 switch (yych) {
371 case '*': goto yy38;
372 case '/': goto yy40;
373 default: goto yy28;
374 }
375 yy40:
376 ++YYCURSOR;
377 yych = *YYCURSOR;
378 goto yy27;
379 }
380 #line 66 "ext/pdo/pdo_sql_parser.re"
381
382 }
383
384 struct placeholder {
385 char *pos;
386 size_t len;
387 int bindno;
388 size_t qlen; /* quoted length of value */
389 char *quoted; /* quoted value */
390 int freeq;
391 struct placeholder *next;
392 };
393
free_param_name(zval * el)394 static void free_param_name(zval *el) {
395 efree(Z_PTR_P(el));
396 }
397
pdo_parse_params(pdo_stmt_t * stmt,char * inquery,size_t inquery_len,char ** outquery,size_t * outquery_len)398 PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
399 char **outquery, size_t *outquery_len)
400 {
401 Scanner s;
402 char *ptr, *newbuffer;
403 int t;
404 uint32_t bindno = 0;
405 int ret = 0;
406 size_t newbuffer_len;
407 HashTable *params;
408 struct pdo_bound_param_data *param;
409 int query_type = PDO_PLACEHOLDER_NONE;
410 struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
411
412 ptr = *outquery;
413 s.cur = inquery;
414 s.end = inquery + inquery_len + 1;
415
416 /* phase 1: look for args */
417 while((t = scan(&s)) != PDO_PARSER_EOI) {
418 if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
419 if (t == PDO_PARSER_BIND) {
420 int len = s.cur - s.tok;
421 if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
422 continue;
423 }
424 query_type |= PDO_PLACEHOLDER_NAMED;
425 } else {
426 query_type |= PDO_PLACEHOLDER_POSITIONAL;
427 }
428
429 plc = emalloc(sizeof(*plc));
430 memset(plc, 0, sizeof(*plc));
431 plc->next = NULL;
432 plc->pos = s.tok;
433 plc->len = s.cur - s.tok;
434 plc->bindno = bindno++;
435
436 if (placetail) {
437 placetail->next = plc;
438 } else {
439 placeholders = plc;
440 }
441 placetail = plc;
442 }
443 }
444
445 if (bindno == 0) {
446 /* nothing to do; good! */
447 return 0;
448 }
449
450 /* did the query make sense to me? */
451 if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
452 /* they mixed both types; punt */
453 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters");
454 ret = -1;
455 goto clean_up;
456 }
457
458 if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
459 /* query matches native syntax */
460 ret = 0;
461 goto clean_up;
462 }
463
464 if (stmt->named_rewrite_template) {
465 /* magic/hack.
466 * We we pretend that the query was positional even if
467 * it was named so that we fall into the
468 * named rewrite case below. Not too pretty,
469 * but it works. */
470 query_type = PDO_PLACEHOLDER_POSITIONAL;
471 }
472
473 params = stmt->bound_params;
474
475 /* Do we have placeholders but no bound params */
476 if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
477 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
478 ret = -1;
479 goto clean_up;
480 }
481
482 if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
483 /* extra bit of validation for instances when same params are bound more than once */
484 if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
485 int ok = 1;
486 for (plc = placeholders; plc; plc = plc->next) {
487 if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
488 ok = 0;
489 break;
490 }
491 }
492 if (ok) {
493 goto safe;
494 }
495 }
496 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
497 ret = -1;
498 goto clean_up;
499 }
500 safe:
501 /* what are we going to do ? */
502 if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
503 /* query generation */
504
505 newbuffer_len = inquery_len;
506
507 /* let's quote all the values */
508 for (plc = placeholders; plc; plc = plc->next) {
509 if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
510 param = zend_hash_index_find_ptr(params, plc->bindno);
511 } else {
512 param = zend_hash_str_find_ptr(params, plc->pos, plc->len);
513 }
514 if (param == NULL) {
515 /* parameter was not defined */
516 ret = -1;
517 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
518 goto clean_up;
519 }
520 if (stmt->dbh->methods->quoter) {
521 zval *parameter;
522 if (Z_ISREF(param->parameter)) {
523 parameter = Z_REFVAL(param->parameter);
524 } else {
525 parameter = ¶m->parameter;
526 }
527 if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {
528 php_stream *stm;
529
530 php_stream_from_zval_no_verify(stm, parameter);
531 if (stm) {
532 zend_string *buf;
533
534 buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
535 if (!buf) {
536 buf = ZSTR_EMPTY_ALLOC();
537 }
538 if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
539 param->param_type)) {
540 /* bork */
541 ret = -1;
542 strncpy(stmt->error_code, stmt->dbh->error_code, 6);
543 if (buf) {
544 zend_string_release(buf);
545 }
546 goto clean_up;
547 }
548 if (buf) {
549 zend_string_release(buf);
550 }
551 } else {
552 pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
553 ret = -1;
554 goto clean_up;
555 }
556 plc->freeq = 1;
557 } else {
558 enum pdo_param_type param_type = param->param_type;
559 zend_string *buf = NULL;
560
561 /* assume all types are nullable */
562 if (Z_TYPE_P(parameter) == IS_NULL) {
563 param_type = PDO_PARAM_NULL;
564 }
565
566 switch (param_type) {
567 case PDO_PARAM_BOOL:
568 plc->quoted = zend_is_true(parameter) ? "1" : "0";
569 plc->qlen = sizeof("1")-1;
570 plc->freeq = 0;
571 break;
572
573 case PDO_PARAM_INT:
574 buf = zend_long_to_str(zval_get_long(parameter));
575
576 plc->qlen = ZSTR_LEN(buf);
577 plc->quoted = estrdup(ZSTR_VAL(buf));
578 plc->freeq = 1;
579 break;
580
581 case PDO_PARAM_NULL:
582 plc->quoted = "NULL";
583 plc->qlen = sizeof("NULL")-1;
584 plc->freeq = 0;
585 break;
586
587 default:
588 buf = zval_get_string(parameter);
589 if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf),
590 ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
591 param_type)) {
592 /* bork */
593 ret = -1;
594 strncpy(stmt->error_code, stmt->dbh->error_code, 6);
595 if (buf) {
596 zend_string_release(buf);
597 }
598 goto clean_up;
599 }
600 plc->freeq = 1;
601 }
602
603 if (buf) {
604 zend_string_release(buf);
605 }
606 }
607 } else {
608 zval *parameter;
609 if (Z_ISREF(param->parameter)) {
610 parameter = Z_REFVAL(param->parameter);
611 } else {
612 parameter = ¶m->parameter;
613 }
614 plc->quoted = Z_STRVAL_P(parameter);
615 plc->qlen = Z_STRLEN_P(parameter);
616 }
617 newbuffer_len += plc->qlen;
618 }
619
620 rewrite:
621 /* allocate output buffer */
622 newbuffer = emalloc(newbuffer_len + 1);
623 *outquery = newbuffer;
624
625 /* and build the query */
626 plc = placeholders;
627 ptr = inquery;
628
629 do {
630 t = plc->pos - ptr;
631 if (t) {
632 memcpy(newbuffer, ptr, t);
633 newbuffer += t;
634 }
635 memcpy(newbuffer, plc->quoted, plc->qlen);
636 newbuffer += plc->qlen;
637 ptr = plc->pos + plc->len;
638
639 plc = plc->next;
640 } while (plc);
641
642 t = (inquery + inquery_len) - ptr;
643 if (t) {
644 memcpy(newbuffer, ptr, t);
645 newbuffer += t;
646 }
647 *newbuffer = '\0';
648 *outquery_len = newbuffer - *outquery;
649
650 ret = 1;
651 goto clean_up;
652
653 } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
654 /* rewrite ? to :pdoX */
655 char *name, *idxbuf;
656 const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
657 int bind_no = 1;
658
659 newbuffer_len = inquery_len;
660
661 if (stmt->bound_param_map == NULL) {
662 ALLOC_HASHTABLE(stmt->bound_param_map);
663 zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
664 }
665
666 for (plc = placeholders; plc; plc = plc->next) {
667 int skip_map = 0;
668 char *p;
669 name = estrndup(plc->pos, plc->len);
670
671 /* check if bound parameter is already available */
672 if (!strcmp(name, "?") || (p = zend_hash_str_find_ptr(stmt->bound_param_map, name, plc->len)) == NULL) {
673 spprintf(&idxbuf, 0, tmpl, bind_no++);
674 } else {
675 idxbuf = estrdup(p);
676 skip_map = 1;
677 }
678
679 plc->quoted = idxbuf;
680 plc->qlen = strlen(plc->quoted);
681 plc->freeq = 1;
682 newbuffer_len += plc->qlen;
683
684 if (!skip_map && stmt->named_rewrite_template) {
685 /* create a mapping */
686 zend_hash_str_update_mem(stmt->bound_param_map, name, plc->len, idxbuf, plc->qlen + 1);
687 }
688
689 /* map number to name */
690 zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1);
691
692 efree(name);
693 }
694
695 goto rewrite;
696
697 } else {
698 /* rewrite :name to ? */
699
700 newbuffer_len = inquery_len;
701
702 if (stmt->bound_param_map == NULL) {
703 ALLOC_HASHTABLE(stmt->bound_param_map);
704 zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
705 }
706
707 for (plc = placeholders; plc; plc = plc->next) {
708 char *name;
709 name = estrndup(plc->pos, plc->len);
710 zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, name, plc->len + 1);
711 efree(name);
712 plc->quoted = "?";
713 plc->qlen = 1;
714 }
715
716 goto rewrite;
717 }
718
719 clean_up:
720
721 while (placeholders) {
722 plc = placeholders;
723 placeholders = plc->next;
724
725 if (plc->freeq) {
726 efree(plc->quoted);
727 }
728
729 efree(plc);
730 }
731
732 return ret;
733 }
734
735 /*
736 * Local variables:
737 * tab-width: 4
738 * c-basic-offset: 4
739 * End:
740 * vim600: noet sw=4 ts=4 fdm=marker ft=c
741 * vim<600: noet sw=4 ts=4
742 */
743