xref: /PHP-7.0/ext/pdo/pdo_sql_parser.c (revision 478f119a)
1 /* Generated by re2c 0.13.5 */
2 #line 1 "ext/pdo/pdo_sql_parser.re"
3 /*
4   +----------------------------------------------------------------------+
5   | PHP Version 7                                                        |
6   +----------------------------------------------------------------------+
7   | Copyright (c) 1997-2017 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 
58 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
59 	yych = *YYCURSOR;
60 	switch (yych) {
61 	case 0x00:	goto yy2;
62 	case '"':	goto yy3;
63 	case '\'':	goto yy5;
64 	case '(':
65 	case ')':
66 	case '*':
67 	case '+':
68 	case ',':
69 	case '.':	goto yy9;
70 	case '-':	goto yy10;
71 	case '/':	goto yy11;
72 	case ':':	goto yy6;
73 	case '?':	goto yy7;
74 	default:	goto yy12;
75 	}
76 yy2:
77 	YYCURSOR = YYMARKER;
78 	goto yy4;
79 yy3:
80 	yych = *(YYMARKER = ++YYCURSOR);
81 	if (yych >= 0x01) goto yy37;
82 yy4:
83 #line 63 "ext/pdo/pdo_sql_parser.re"
84 	{ SKIP_ONE(PDO_PARSER_TEXT); }
85 #line 86 "ext/pdo/pdo_sql_parser.c"
86 yy5:
87 	yych = *(YYMARKER = ++YYCURSOR);
88 	if (yych <= 0x00) goto yy4;
89 	goto yy32;
90 yy6:
91 	yych = *++YYCURSOR;
92 	switch (yych) {
93 	case '0':
94 	case '1':
95 	case '2':
96 	case '3':
97 	case '4':
98 	case '5':
99 	case '6':
100 	case '7':
101 	case '8':
102 	case '9':
103 	case 'A':
104 	case 'B':
105 	case 'C':
106 	case 'D':
107 	case 'E':
108 	case 'F':
109 	case 'G':
110 	case 'H':
111 	case 'I':
112 	case 'J':
113 	case 'K':
114 	case 'L':
115 	case 'M':
116 	case 'N':
117 	case 'O':
118 	case 'P':
119 	case 'Q':
120 	case 'R':
121 	case 'S':
122 	case 'T':
123 	case 'U':
124 	case 'V':
125 	case 'W':
126 	case 'X':
127 	case 'Y':
128 	case 'Z':
129 	case '_':
130 	case 'a':
131 	case 'b':
132 	case 'c':
133 	case 'd':
134 	case 'e':
135 	case 'f':
136 	case 'g':
137 	case 'h':
138 	case 'i':
139 	case 'j':
140 	case 'k':
141 	case 'l':
142 	case 'm':
143 	case 'n':
144 	case 'o':
145 	case 'p':
146 	case 'q':
147 	case 'r':
148 	case 's':
149 	case 't':
150 	case 'u':
151 	case 'v':
152 	case 'w':
153 	case 'x':
154 	case 'y':
155 	case 'z':	goto yy26;
156 	case ':':	goto yy29;
157 	default:	goto yy4;
158 	}
159 yy7:
160 	++YYCURSOR;
161 	switch ((yych = *YYCURSOR)) {
162 	case '?':	goto yy23;
163 	default:	goto yy8;
164 	}
165 yy8:
166 #line 62 "ext/pdo/pdo_sql_parser.re"
167 	{ RET(PDO_PARSER_BIND_POS); }
168 #line 169 "ext/pdo/pdo_sql_parser.c"
169 yy9:
170 	yych = *++YYCURSOR;
171 	goto yy4;
172 yy10:
173 	yych = *++YYCURSOR;
174 	switch (yych) {
175 	case '-':	goto yy21;
176 	default:	goto yy4;
177 	}
178 yy11:
179 	yych = *(YYMARKER = ++YYCURSOR);
180 	switch (yych) {
181 	case '*':	goto yy15;
182 	default:	goto yy4;
183 	}
184 yy12:
185 	++YYCURSOR;
186 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
187 	yych = *YYCURSOR;
188 	switch (yych) {
189 	case 0x00:
190 	case '"':
191 	case '\'':
192 	case '(':
193 	case ')':
194 	case '*':
195 	case '+':
196 	case ',':
197 	case '-':
198 	case '.':
199 	case '/':
200 	case ':':
201 	case '?':	goto yy14;
202 	default:	goto yy12;
203 	}
204 yy14:
205 #line 65 "ext/pdo/pdo_sql_parser.re"
206 	{ RET(PDO_PARSER_TEXT); }
207 #line 208 "ext/pdo/pdo_sql_parser.c"
208 yy15:
209 	++YYCURSOR;
210 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
211 	yych = *YYCURSOR;
212 	switch (yych) {
213 	case '*':	goto yy17;
214 	default:	goto yy15;
215 	}
216 yy17:
217 	++YYCURSOR;
218 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
219 	yych = *YYCURSOR;
220 	switch (yych) {
221 	case '*':	goto yy17;
222 	case '/':	goto yy19;
223 	default:	goto yy15;
224 	}
225 yy19:
226 	++YYCURSOR;
227 yy20:
228 #line 64 "ext/pdo/pdo_sql_parser.re"
229 	{ RET(PDO_PARSER_TEXT); }
230 #line 231 "ext/pdo/pdo_sql_parser.c"
231 yy21:
232 	++YYCURSOR;
233 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
234 	yych = *YYCURSOR;
235 	switch (yych) {
236 	case '\n':
237 	case '\r':	goto yy20;
238 	default:	goto yy21;
239 	}
240 yy23:
241 	++YYCURSOR;
242 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
243 	yych = *YYCURSOR;
244 	switch (yych) {
245 	case '?':	goto yy23;
246 	default:	goto yy25;
247 	}
248 yy25:
249 #line 60 "ext/pdo/pdo_sql_parser.re"
250 	{ RET(PDO_PARSER_TEXT); }
251 #line 252 "ext/pdo/pdo_sql_parser.c"
252 yy26:
253 	++YYCURSOR;
254 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
255 	yych = *YYCURSOR;
256 	switch (yych) {
257 	case '0':
258 	case '1':
259 	case '2':
260 	case '3':
261 	case '4':
262 	case '5':
263 	case '6':
264 	case '7':
265 	case '8':
266 	case '9':
267 	case 'A':
268 	case 'B':
269 	case 'C':
270 	case 'D':
271 	case 'E':
272 	case 'F':
273 	case 'G':
274 	case 'H':
275 	case 'I':
276 	case 'J':
277 	case 'K':
278 	case 'L':
279 	case 'M':
280 	case 'N':
281 	case 'O':
282 	case 'P':
283 	case 'Q':
284 	case 'R':
285 	case 'S':
286 	case 'T':
287 	case 'U':
288 	case 'V':
289 	case 'W':
290 	case 'X':
291 	case 'Y':
292 	case 'Z':
293 	case '_':
294 	case 'a':
295 	case 'b':
296 	case 'c':
297 	case 'd':
298 	case 'e':
299 	case 'f':
300 	case 'g':
301 	case 'h':
302 	case 'i':
303 	case 'j':
304 	case 'k':
305 	case 'l':
306 	case 'm':
307 	case 'n':
308 	case 'o':
309 	case 'p':
310 	case 'q':
311 	case 'r':
312 	case 's':
313 	case 't':
314 	case 'u':
315 	case 'v':
316 	case 'w':
317 	case 'x':
318 	case 'y':
319 	case 'z':	goto yy26;
320 	default:	goto yy28;
321 	}
322 yy28:
323 #line 61 "ext/pdo/pdo_sql_parser.re"
324 	{ RET(PDO_PARSER_BIND); }
325 #line 326 "ext/pdo/pdo_sql_parser.c"
326 yy29:
327 	++YYCURSOR;
328 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
329 	yych = *YYCURSOR;
330 	switch (yych) {
331 	case ':':	goto yy29;
332 	default:	goto yy25;
333 	}
334 yy31:
335 	++YYCURSOR;
336 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
337 	yych = *YYCURSOR;
338 yy32:
339 	switch (yych) {
340 	case 0x00:	goto yy2;
341 	case '\'':	goto yy34;
342 	case '\\':	goto yy33;
343 	default:	goto yy31;
344 	}
345 yy33:
346 	++YYCURSOR;
347 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
348 	yych = *YYCURSOR;
349 	if (yych <= 0x00) goto yy2;
350 	goto yy31;
351 yy34:
352 	++YYCURSOR;
353 #line 59 "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 yy37:
361 	switch (yych) {
362 	case 0x00:	goto yy2;
363 	case '"':	goto yy39;
364 	case '\\':	goto yy38;
365 	default:	goto yy36;
366 	}
367 yy38:
368 	++YYCURSOR;
369 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
370 	yych = *YYCURSOR;
371 	if (yych <= 0x00) goto yy2;
372 	goto yy36;
373 yy39:
374 	++YYCURSOR;
375 #line 58 "ext/pdo/pdo_sql_parser.re"
376 	{ RET(PDO_PARSER_TEXT); }
377 #line 378 "ext/pdo/pdo_sql_parser.c"
378 }
379 #line 66 "ext/pdo/pdo_sql_parser.re"
380 
381 }
382 
383 struct placeholder {
384 	char *pos;
385 	size_t len;
386 	int bindno;
387 	size_t qlen;		/* quoted length of value */
388 	char *quoted;	/* quoted value */
389 	int freeq;
390 	struct placeholder *next;
391 };
392 
free_param_name(zval * el)393 static void free_param_name(zval *el) {
394 	efree(Z_PTR_P(el));
395 }
396 
pdo_parse_params(pdo_stmt_t * stmt,char * inquery,size_t inquery_len,char ** outquery,size_t * outquery_len)397 PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
398 	char **outquery, size_t *outquery_len)
399 {
400 	Scanner s;
401 	char *ptr, *newbuffer;
402 	int t;
403 	int bindno = 0;
404 	int ret = 0;
405 	size_t newbuffer_len;
406 	HashTable *params;
407 	struct pdo_bound_param_data *param;
408 	int query_type = PDO_PLACEHOLDER_NONE;
409 	struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
410 
411 	ptr = *outquery;
412 	s.cur = inquery;
413 	s.end = inquery + inquery_len + 1;
414 
415 	/* phase 1: look for args */
416 	while((t = scan(&s)) != PDO_PARSER_EOI) {
417 		if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
418 			if (t == PDO_PARSER_BIND) {
419 				int len = s.cur - s.tok;
420 				if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
421 					continue;
422 				}
423 				query_type |= PDO_PLACEHOLDER_NAMED;
424 			} else {
425 				query_type |= PDO_PLACEHOLDER_POSITIONAL;
426 			}
427 
428 			plc = emalloc(sizeof(*plc));
429 			memset(plc, 0, sizeof(*plc));
430 			plc->next = NULL;
431 			plc->pos = s.tok;
432 			plc->len = s.cur - s.tok;
433 			plc->bindno = bindno++;
434 
435 			if (placetail) {
436 				placetail->next = plc;
437 			} else {
438 				placeholders = plc;
439 			}
440 			placetail = plc;
441 		}
442 	}
443 
444 	if (bindno == 0) {
445 		/* nothing to do; good! */
446 		return 0;
447 	}
448 
449 	/* did the query make sense to me? */
450 	if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
451 		/* they mixed both types; punt */
452 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters");
453 		ret = -1;
454 		goto clean_up;
455 	}
456 
457 	if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
458 		/* query matches native syntax */
459 		ret = 0;
460 		goto clean_up;
461 	}
462 
463 	if (stmt->named_rewrite_template) {
464 		/* magic/hack.
465 		 * We we pretend that the query was positional even if
466 		 * it was named so that we fall into the
467 		 * named rewrite case below.  Not too pretty,
468 		 * but it works. */
469 		query_type = PDO_PLACEHOLDER_POSITIONAL;
470 	}
471 
472 	params = stmt->bound_params;
473 
474 	/* Do we have placeholders but no bound params */
475 	if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
476 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
477 		ret = -1;
478 		goto clean_up;
479 	}
480 
481 	if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
482 		/* extra bit of validation for instances when same params are bound more than once */
483 		if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
484 			int ok = 1;
485 			for (plc = placeholders; plc; plc = plc->next) {
486 				if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
487 					ok = 0;
488 					break;
489 				}
490 			}
491 			if (ok) {
492 				goto safe;
493 			}
494 		}
495 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
496 		ret = -1;
497 		goto clean_up;
498 	}
499 safe:
500 	/* what are we going to do ? */
501 	if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
502 		/* query generation */
503 
504 		newbuffer_len = inquery_len;
505 
506 		/* let's quote all the values */
507 		for (plc = placeholders; plc; plc = plc->next) {
508 			if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
509 				param = zend_hash_index_find_ptr(params, plc->bindno);
510 			} else {
511 				param = zend_hash_str_find_ptr(params, plc->pos, plc->len);
512 			}
513 			if (param == NULL) {
514 				/* parameter was not defined */
515 				ret = -1;
516 				pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
517 				goto clean_up;
518 			}
519 			if (stmt->dbh->methods->quoter) {
520 				zval *parameter;
521 				if (Z_ISREF(param->parameter)) {
522 					parameter = Z_REFVAL(param->parameter);
523 				} else {
524 					parameter = &param->parameter;
525 				}
526 				if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {
527 					php_stream *stm;
528 
529 					php_stream_from_zval_no_verify(stm, parameter);
530 					if (stm) {
531 						zend_string *buf;
532 
533 						buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
534 						if (!buf) {
535 							buf = ZSTR_EMPTY_ALLOC();
536 						}
537 						if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
538 								param->param_type)) {
539 							/* bork */
540 							ret = -1;
541 							strncpy(stmt->error_code, stmt->dbh->error_code, 6);
542 							if (buf) {
543 								zend_string_release(buf);
544 							}
545 							goto clean_up;
546 						}
547 						if (buf) {
548 							zend_string_release(buf);
549 						}
550 					} else {
551 						pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
552 						ret = -1;
553 						goto clean_up;
554 					}
555 					plc->freeq = 1;
556 				} else {
557 					zval tmp_param;
558 				   	ZVAL_DUP(&tmp_param, parameter);
559 					switch (Z_TYPE(tmp_param)) {
560 						case IS_NULL:
561 							plc->quoted = "NULL";
562 							plc->qlen = sizeof("NULL")-1;
563 							plc->freeq = 0;
564 							break;
565 
566 						case IS_FALSE:
567 						case IS_TRUE:
568 							convert_to_long(&tmp_param);
569 							/* fall through */
570 						case IS_LONG:
571 						case IS_DOUBLE:
572 							convert_to_string(&tmp_param);
573 							plc->qlen = Z_STRLEN(tmp_param);
574 							plc->quoted = estrdup(Z_STRVAL(tmp_param));
575 							plc->freeq = 1;
576 							break;
577 
578 						default:
579 							convert_to_string(&tmp_param);
580 							if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL(tmp_param),
581 									Z_STRLEN(tmp_param), &plc->quoted, &plc->qlen,
582 									param->param_type)) {
583 								/* bork */
584 								ret = -1;
585 								strncpy(stmt->error_code, stmt->dbh->error_code, 6);
586 								goto clean_up;
587 							}
588 							plc->freeq = 1;
589 					}
590 					zval_dtor(&tmp_param);
591 				}
592 			} else {
593 				zval *parameter;
594 				if (Z_ISREF(param->parameter)) {
595 					parameter = Z_REFVAL(param->parameter);
596 				} else {
597 					parameter = &param->parameter;
598 				}
599 				plc->quoted = Z_STRVAL_P(parameter);
600 				plc->qlen = Z_STRLEN_P(parameter);
601 			}
602 			newbuffer_len += plc->qlen;
603 		}
604 
605 rewrite:
606 		/* allocate output buffer */
607 		newbuffer = emalloc(newbuffer_len + 1);
608 		*outquery = newbuffer;
609 
610 		/* and build the query */
611 		plc = placeholders;
612 		ptr = inquery;
613 
614 		do {
615 			t = plc->pos - ptr;
616 			if (t) {
617 				memcpy(newbuffer, ptr, t);
618 				newbuffer += t;
619 			}
620 			memcpy(newbuffer, plc->quoted, plc->qlen);
621 			newbuffer += plc->qlen;
622 			ptr = plc->pos + plc->len;
623 
624 			plc = plc->next;
625 		} while (plc);
626 
627 		t = (inquery + inquery_len) - ptr;
628 		if (t) {
629 			memcpy(newbuffer, ptr, t);
630 			newbuffer += t;
631 		}
632 		*newbuffer = '\0';
633 		*outquery_len = newbuffer - *outquery;
634 
635 		ret = 1;
636 		goto clean_up;
637 
638 	} else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
639 		/* rewrite ? to :pdoX */
640 		char *name, *idxbuf;
641 		const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
642 		int bind_no = 1;
643 
644 		newbuffer_len = inquery_len;
645 
646 		if (stmt->bound_param_map == NULL) {
647 			ALLOC_HASHTABLE(stmt->bound_param_map);
648 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
649 		}
650 
651 		for (plc = placeholders; plc; plc = plc->next) {
652 			int skip_map = 0;
653 			char *p;
654 			name = estrndup(plc->pos, plc->len);
655 
656 			/* check if bound parameter is already available */
657 			if (!strcmp(name, "?") || (p = zend_hash_str_find_ptr(stmt->bound_param_map, name, plc->len)) == NULL) {
658 				spprintf(&idxbuf, 0, tmpl, bind_no++);
659 			} else {
660 				idxbuf = estrdup(p);
661 				skip_map = 1;
662 			}
663 
664 			plc->quoted = idxbuf;
665 			plc->qlen = strlen(plc->quoted);
666 			plc->freeq = 1;
667 			newbuffer_len += plc->qlen;
668 
669 			if (!skip_map && stmt->named_rewrite_template) {
670 				/* create a mapping */
671 				zend_hash_str_update_mem(stmt->bound_param_map, name, plc->len, idxbuf, plc->qlen + 1);
672 			}
673 
674 			/* map number to name */
675 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1);
676 
677 			efree(name);
678 		}
679 
680 		goto rewrite;
681 
682 	} else {
683 		/* rewrite :name to ? */
684 
685 		newbuffer_len = inquery_len;
686 
687 		if (stmt->bound_param_map == NULL) {
688 			ALLOC_HASHTABLE(stmt->bound_param_map);
689 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
690 		}
691 
692 		for (plc = placeholders; plc; plc = plc->next) {
693 			char *name;
694 			name = estrndup(plc->pos, plc->len);
695 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, name, plc->len + 1);
696 			efree(name);
697 			plc->quoted = "?";
698 			plc->qlen = 1;
699 		}
700 
701 		goto rewrite;
702 	}
703 
704 clean_up:
705 
706 	while (placeholders) {
707 		plc = placeholders;
708 		placeholders = plc->next;
709 
710 		if (plc->freeq) {
711 			efree(plc->quoted);
712 		}
713 
714 		efree(plc);
715 	}
716 
717 	return ret;
718 }
719 
720 #if 0
721 int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
722 		int *outquery_len)
723 {
724 	Scanner s;
725 	char *ptr;
726 	int t;
727 	int bindno = 0;
728 	int newbuffer_len;
729 	int padding;
730 	HashTable *params = stmt->bound_params;
731 	struct pdo_bound_param_data *param;
732 	/* allocate buffer for query with expanded binds, ptr is our writing pointer */
733 	newbuffer_len = inquery_len;
734 
735 	/* calculate the possible padding factor due to quoting */
736 	if(stmt->dbh->max_escaped_char_length) {
737 		padding = stmt->dbh->max_escaped_char_length;
738 	} else {
739 		padding = 3;
740 	}
741 	if(params) {
742 		ZEND_HASH_FOREACH_PTR(params, param) {
743 			if(param->parameter) {
744 				convert_to_string(param->parameter);
745 				/* accommodate a string that needs to be fully quoted
746                    bind placeholders are at least 2 characters, so
747                    the accommodate their own "'s
748                 */
749 				newbuffer_len += padding * Z_STRLEN_P(param->parameter);
750 			}
751 		} ZEND_HASH_FOREACH_END();
752 	}
753 	*outquery = (char *) emalloc(newbuffer_len + 1);
754 	*outquery_len = 0;
755 
756 	ptr = *outquery;
757 	s.cur = inquery;
758 	while((t = scan(&s)) != PDO_PARSER_EOI) {
759 		if(t == PDO_PARSER_TEXT) {
760 			memcpy(ptr, s.tok, s.cur - s.tok);
761 			ptr += (s.cur - s.tok);
762 			*outquery_len += (s.cur - s.tok);
763 		}
764 		else if(t == PDO_PARSER_BIND) {
765 			if(!params) {
766 				/* error */
767 				efree(*outquery);
768 				*outquery = NULL;
769 				return (int) (s.cur - inquery);
770 			}
771 			/* lookup bind first via hash and then index */
772 			/* stupid keys need to be null-terminated, even though we know their length */
773 			if((NULL != (param = zend_hash_str_find_ptr(params, s.tok, s.cur-s.tok))
774 			    ||
775 			   NULL != (params = zend_hash_index_find_ptr(params, bindno)))
776 			{
777 				char *quotedstr;
778 				int quotedstrlen;
779 				/* restore the in-string key, doesn't need null-termination here */
780 				/* currently everything is a string here */
781 
782 				/* quote the bind value if necessary */
783 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
784 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen))
785 				{
786 					memcpy(ptr, quotedstr, quotedstrlen);
787 					ptr += quotedstrlen;
788 					*outquery_len += quotedstrlen;
789 					efree(quotedstr);
790 				} else {
791 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
792 					ptr += Z_STRLEN_P(param->parameter);
793 					*outquery_len += (Z_STRLEN_P(param->parameter));
794 				}
795 			}
796 			else {
797 				/* error and cleanup */
798 				efree(*outquery);
799 				*outquery = NULL;
800 				return (int) (s.cur - inquery);
801 			}
802 			bindno++;
803 		}
804 		else if(t == PDO_PARSER_BIND_POS) {
805 			if(!params) {
806 				/* error */
807 				efree(*outquery);
808 				*outquery = NULL;
809 				return (int) (s.cur - inquery);
810 			}
811 			/* lookup bind by index */
812 			if(NULL != (params = zend_hash_index_find_ptr(params, bindno)))
813 			{
814 				char *quotedstr;
815 				int quotedstrlen;
816 				/* currently everything is a string here */
817 
818 				/* quote the bind value if necessary */
819 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
820 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen))
821 				{
822 					memcpy(ptr, quotedstr, quotedstrlen);
823 					ptr += quotedstrlen;
824 					*outquery_len += quotedstrlen;
825 					efree(quotedstr);
826 				} else {
827 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
828 					ptr += Z_STRLEN_P(param->parameter);
829 					*outquery_len += (Z_STRLEN_P(param->parameter));
830 				}
831 			}
832 			else {
833 				/* error and cleanup */
834 				efree(*outquery);
835 				*outquery = NULL;
836 				return (int) (s.cur - inquery);
837 			}
838 			bindno++;
839 		}
840 	}
841 	*ptr = '\0';
842 	return 0;
843 }
844 #endif
845 
846 /*
847  * Local variables:
848  * tab-width: 4
849  * c-basic-offset: 4
850  * End:
851  * vim600: noet sw=4 ts=4 fdm=marker ft=c
852  * vim<600: noet sw=4 ts=4
853  */
854