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