xref: /PHP-7.1/ext/pdo/pdo_sql_parser.c (revision b65abecf)
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 = &param->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 					zval tmp_param;
559 				   	ZVAL_DUP(&tmp_param, parameter);
560 					switch (Z_TYPE(tmp_param)) {
561 						case IS_NULL:
562 							plc->quoted = "NULL";
563 							plc->qlen = sizeof("NULL")-1;
564 							plc->freeq = 0;
565 							break;
566 
567 						case IS_FALSE:
568 						case IS_TRUE:
569 							convert_to_long(&tmp_param);
570 							/* fall through */
571 						case IS_LONG:
572 						case IS_DOUBLE:
573 							convert_to_string(&tmp_param);
574 							plc->qlen = Z_STRLEN(tmp_param);
575 							plc->quoted = estrdup(Z_STRVAL(tmp_param));
576 							plc->freeq = 1;
577 							break;
578 
579 						default:
580 							convert_to_string(&tmp_param);
581 							if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL(tmp_param),
582 									Z_STRLEN(tmp_param), &plc->quoted, &plc->qlen,
583 									param->param_type)) {
584 								/* bork */
585 								ret = -1;
586 								strncpy(stmt->error_code, stmt->dbh->error_code, 6);
587 								goto clean_up;
588 							}
589 							plc->freeq = 1;
590 					}
591 					zval_dtor(&tmp_param);
592 				}
593 			} else {
594 				zval *parameter;
595 				if (Z_ISREF(param->parameter)) {
596 					parameter = Z_REFVAL(param->parameter);
597 				} else {
598 					parameter = &param->parameter;
599 				}
600 				plc->quoted = Z_STRVAL_P(parameter);
601 				plc->qlen = Z_STRLEN_P(parameter);
602 			}
603 			newbuffer_len += plc->qlen;
604 		}
605 
606 rewrite:
607 		/* allocate output buffer */
608 		newbuffer = emalloc(newbuffer_len + 1);
609 		*outquery = newbuffer;
610 
611 		/* and build the query */
612 		plc = placeholders;
613 		ptr = inquery;
614 
615 		do {
616 			t = plc->pos - ptr;
617 			if (t) {
618 				memcpy(newbuffer, ptr, t);
619 				newbuffer += t;
620 			}
621 			memcpy(newbuffer, plc->quoted, plc->qlen);
622 			newbuffer += plc->qlen;
623 			ptr = plc->pos + plc->len;
624 
625 			plc = plc->next;
626 		} while (plc);
627 
628 		t = (inquery + inquery_len) - ptr;
629 		if (t) {
630 			memcpy(newbuffer, ptr, t);
631 			newbuffer += t;
632 		}
633 		*newbuffer = '\0';
634 		*outquery_len = newbuffer - *outquery;
635 
636 		ret = 1;
637 		goto clean_up;
638 
639 	} else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
640 		/* rewrite ? to :pdoX */
641 		char *name, *idxbuf;
642 		const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
643 		int bind_no = 1;
644 
645 		newbuffer_len = inquery_len;
646 
647 		if (stmt->bound_param_map == NULL) {
648 			ALLOC_HASHTABLE(stmt->bound_param_map);
649 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
650 		}
651 
652 		for (plc = placeholders; plc; plc = plc->next) {
653 			int skip_map = 0;
654 			char *p;
655 			name = estrndup(plc->pos, plc->len);
656 
657 			/* check if bound parameter is already available */
658 			if (!strcmp(name, "?") || (p = zend_hash_str_find_ptr(stmt->bound_param_map, name, plc->len)) == NULL) {
659 				spprintf(&idxbuf, 0, tmpl, bind_no++);
660 			} else {
661 				idxbuf = estrdup(p);
662 				skip_map = 1;
663 			}
664 
665 			plc->quoted = idxbuf;
666 			plc->qlen = strlen(plc->quoted);
667 			plc->freeq = 1;
668 			newbuffer_len += plc->qlen;
669 
670 			if (!skip_map && stmt->named_rewrite_template) {
671 				/* create a mapping */
672 				zend_hash_str_update_mem(stmt->bound_param_map, name, plc->len, idxbuf, plc->qlen + 1);
673 			}
674 
675 			/* map number to name */
676 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1);
677 
678 			efree(name);
679 		}
680 
681 		goto rewrite;
682 
683 	} else {
684 		/* rewrite :name to ? */
685 
686 		newbuffer_len = inquery_len;
687 
688 		if (stmt->bound_param_map == NULL) {
689 			ALLOC_HASHTABLE(stmt->bound_param_map);
690 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
691 		}
692 
693 		for (plc = placeholders; plc; plc = plc->next) {
694 			char *name;
695 			name = estrndup(plc->pos, plc->len);
696 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, name, plc->len + 1);
697 			efree(name);
698 			plc->quoted = "?";
699 			plc->qlen = 1;
700 		}
701 
702 		goto rewrite;
703 	}
704 
705 clean_up:
706 
707 	while (placeholders) {
708 		plc = placeholders;
709 		placeholders = plc->next;
710 
711 		if (plc->freeq) {
712 			efree(plc->quoted);
713 		}
714 
715 		efree(plc);
716 	}
717 
718 	return ret;
719 }
720 
721 #if 0
722 int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
723 		int *outquery_len)
724 {
725 	Scanner s;
726 	char *ptr;
727 	int t;
728 	int bindno = 0;
729 	int newbuffer_len;
730 	int padding;
731 	HashTable *params = stmt->bound_params;
732 	struct pdo_bound_param_data *param;
733 	/* allocate buffer for query with expanded binds, ptr is our writing pointer */
734 	newbuffer_len = inquery_len;
735 
736 	/* calculate the possible padding factor due to quoting */
737 	if(stmt->dbh->max_escaped_char_length) {
738 		padding = stmt->dbh->max_escaped_char_length;
739 	} else {
740 		padding = 3;
741 	}
742 	if(params) {
743 		ZEND_HASH_FOREACH_PTR(params, param) {
744 			if(param->parameter) {
745 				convert_to_string(param->parameter);
746 				/* accommodate a string that needs to be fully quoted
747                    bind placeholders are at least 2 characters, so
748                    the accommodate their own "'s
749                 */
750 				newbuffer_len += padding * Z_STRLEN_P(param->parameter);
751 			}
752 		} ZEND_HASH_FOREACH_END();
753 	}
754 	*outquery = (char *) emalloc(newbuffer_len + 1);
755 	*outquery_len = 0;
756 
757 	ptr = *outquery;
758 	s.cur = inquery;
759 	while((t = scan(&s)) != PDO_PARSER_EOI) {
760 		if(t == PDO_PARSER_TEXT) {
761 			memcpy(ptr, s.tok, s.cur - s.tok);
762 			ptr += (s.cur - s.tok);
763 			*outquery_len += (s.cur - s.tok);
764 		}
765 		else if(t == PDO_PARSER_BIND) {
766 			if(!params) {
767 				/* error */
768 				efree(*outquery);
769 				*outquery = NULL;
770 				return (int) (s.cur - inquery);
771 			}
772 			/* lookup bind first via hash and then index */
773 			/* stupid keys need to be null-terminated, even though we know their length */
774 			if((NULL != (param = zend_hash_str_find_ptr(params, s.tok, s.cur-s.tok))
775 			    ||
776 			   NULL != (params = zend_hash_index_find_ptr(params, bindno)))
777 			{
778 				char *quotedstr;
779 				int quotedstrlen;
780 				/* restore the in-string key, doesn't need null-termination here */
781 				/* currently everything is a string here */
782 
783 				/* quote the bind value if necessary */
784 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
785 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen))
786 				{
787 					memcpy(ptr, quotedstr, quotedstrlen);
788 					ptr += quotedstrlen;
789 					*outquery_len += quotedstrlen;
790 					efree(quotedstr);
791 				} else {
792 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
793 					ptr += Z_STRLEN_P(param->parameter);
794 					*outquery_len += (Z_STRLEN_P(param->parameter));
795 				}
796 			}
797 			else {
798 				/* error and cleanup */
799 				efree(*outquery);
800 				*outquery = NULL;
801 				return (int) (s.cur - inquery);
802 			}
803 			bindno++;
804 		}
805 		else if(t == PDO_PARSER_BIND_POS) {
806 			if(!params) {
807 				/* error */
808 				efree(*outquery);
809 				*outquery = NULL;
810 				return (int) (s.cur - inquery);
811 			}
812 			/* lookup bind by index */
813 			if(NULL != (params = zend_hash_index_find_ptr(params, bindno)))
814 			{
815 				char *quotedstr;
816 				int quotedstrlen;
817 				/* currently everything is a string here */
818 
819 				/* quote the bind value if necessary */
820 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
821 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen))
822 				{
823 					memcpy(ptr, quotedstr, quotedstrlen);
824 					ptr += quotedstrlen;
825 					*outquery_len += quotedstrlen;
826 					efree(quotedstr);
827 				} else {
828 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
829 					ptr += Z_STRLEN_P(param->parameter);
830 					*outquery_len += (Z_STRLEN_P(param->parameter));
831 				}
832 			}
833 			else {
834 				/* error and cleanup */
835 				efree(*outquery);
836 				*outquery = NULL;
837 				return (int) (s.cur - inquery);
838 			}
839 			bindno++;
840 		}
841 	}
842 	*ptr = '\0';
843 	return 0;
844 }
845 #endif
846 
847 /*
848  * Local variables:
849  * tab-width: 4
850  * c-basic-offset: 4
851  * End:
852  * vim600: noet sw=4 ts=4 fdm=marker ft=c
853  * vim<600: noet sw=4 ts=4
854  */
855