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