xref: /PHP-5.6/ext/pdo/pdo_sql_parser.c (revision 30925cd4)
1 /* Generated by re2c 0.13.5 */
2 #line 1 "ext/pdo/pdo_sql_parser.re"
3 /*
4   +----------------------------------------------------------------------+
5   | PHP Version 5                                                        |
6   +----------------------------------------------------------------------+
7   | Copyright (c) 1997-2016 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 	int len;
386 	int bindno;
387 	int qlen;		/* quoted length of value */
388 	char *quoted;	/* quoted value */
389 	int freeq;
390 	struct placeholder *next;
391 };
392 
pdo_parse_params(pdo_stmt_t * stmt,char * inquery,int inquery_len,char ** outquery,int * outquery_len TSRMLS_DC)393 PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len,
394 	char **outquery, int *outquery_len TSRMLS_DC)
395 {
396 	Scanner s;
397 	char *ptr, *newbuffer;
398 	int t;
399 	int bindno = 0;
400 	int ret = 0;
401 	int newbuffer_len;
402 	HashTable *params;
403 	struct pdo_bound_param_data *param;
404 	int query_type = PDO_PLACEHOLDER_NONE;
405 	struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
406 
407 	ptr = *outquery;
408 	s.cur = inquery;
409 	s.end = inquery + inquery_len + 1;
410 
411 	/* phase 1: look for args */
412 	while((t = scan(&s)) != PDO_PARSER_EOI) {
413 		if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
414 			if (t == PDO_PARSER_BIND) {
415 				int len = s.cur - s.tok;
416 				if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
417 					continue;
418 				}
419 				query_type |= PDO_PLACEHOLDER_NAMED;
420 			} else {
421 				query_type |= PDO_PLACEHOLDER_POSITIONAL;
422 			}
423 
424 			plc = emalloc(sizeof(*plc));
425 			memset(plc, 0, sizeof(*plc));
426 			plc->next = NULL;
427 			plc->pos = s.tok;
428 			plc->len = s.cur - s.tok;
429 			plc->bindno = bindno++;
430 
431 			if (placetail) {
432 				placetail->next = plc;
433 			} else {
434 				placeholders = plc;
435 			}
436 			placetail = plc;
437 		}
438 	}
439 
440 	if (bindno == 0) {
441 		/* nothing to do; good! */
442 		return 0;
443 	}
444 
445 	/* did the query make sense to me? */
446 	if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
447 		/* they mixed both types; punt */
448 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters" TSRMLS_CC);
449 		ret = -1;
450 		goto clean_up;
451 	}
452 
453 	if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
454 		/* query matches native syntax */
455 		ret = 0;
456 		goto clean_up;
457 	}
458 
459 	if (stmt->named_rewrite_template) {
460 		/* magic/hack.
461 		 * We we pretend that the query was positional even if
462 		 * it was named so that we fall into the
463 		 * named rewrite case below.  Not too pretty,
464 		 * but it works. */
465 		query_type = PDO_PLACEHOLDER_POSITIONAL;
466 	}
467 
468 	params = stmt->bound_params;
469 
470 	/* Do we have placeholders but no bound params */
471 	if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
472 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound" TSRMLS_CC);
473 		ret = -1;
474 		goto clean_up;
475 	}
476 
477 	if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
478 		/* extra bit of validation for instances when same params are bound more then once */
479 		if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
480 			int ok = 1;
481 			for (plc = placeholders; plc; plc = plc->next) {
482 				if (zend_hash_find(params, plc->pos, plc->len, (void**) &param) == FAILURE) {
483 					ok = 0;
484 					break;
485 				}
486 			}
487 			if (ok) {
488 				goto safe;
489 			}
490 		}
491 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens" TSRMLS_CC);
492 		ret = -1;
493 		goto clean_up;
494 	}
495 safe:
496 	/* what are we going to do ? */
497 	if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
498 		/* query generation */
499 
500 		newbuffer_len = inquery_len;
501 
502 		/* let's quote all the values */
503 		for (plc = placeholders; plc; plc = plc->next) {
504 			if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
505 				ret = zend_hash_index_find(params, plc->bindno, (void**) &param);
506 			} else {
507 				ret = zend_hash_find(params, plc->pos, plc->len, (void**) &param);
508 			}
509 			if (ret == FAILURE) {
510 				/* parameter was not defined */
511 				ret = -1;
512 				pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
513 				goto clean_up;
514 			}
515 			if (stmt->dbh->methods->quoter) {
516 				if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(param->parameter) == IS_RESOURCE) {
517 					php_stream *stm;
518 
519 					php_stream_from_zval_no_verify(stm, &param->parameter);
520 					if (stm) {
521 						size_t len;
522 						char *buf = NULL;
523 
524 						len = php_stream_copy_to_mem(stm, &buf, PHP_STREAM_COPY_ALL, 0);
525 						if (!stmt->dbh->methods->quoter(stmt->dbh, buf, len, &plc->quoted, &plc->qlen,
526 								param->param_type TSRMLS_CC)) {
527 							/* bork */
528 							ret = -1;
529 							strncpy(stmt->error_code, stmt->dbh->error_code, 6);
530 							if (buf) {
531 								efree(buf);
532 							}
533 							goto clean_up;
534 						}
535 						if (buf) {
536 							efree(buf);
537 						}
538 					} else {
539 						pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource" TSRMLS_CC);
540 						ret = -1;
541 						goto clean_up;
542 					}
543 					plc->freeq = 1;
544 				} else {
545 					zval tmp_param = *param->parameter;
546 					zval_copy_ctor(&tmp_param);
547 					switch (Z_TYPE(tmp_param)) {
548 						case IS_NULL:
549 							plc->quoted = "NULL";
550 							plc->qlen = sizeof("NULL")-1;
551 							plc->freeq = 0;
552 							break;
553 
554 						case IS_BOOL:
555 							convert_to_long(&tmp_param);
556 							/* fall through */
557 						case IS_LONG:
558 						case IS_DOUBLE:
559 							convert_to_string(&tmp_param);
560 							plc->qlen = Z_STRLEN(tmp_param);
561 							plc->quoted = estrdup(Z_STRVAL(tmp_param));
562 							plc->freeq = 1;
563 							break;
564 
565 						default:
566 							convert_to_string(&tmp_param);
567 							if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL(tmp_param),
568 									Z_STRLEN(tmp_param), &plc->quoted, &plc->qlen,
569 									param->param_type TSRMLS_CC)) {
570 								/* bork */
571 								ret = -1;
572 								strncpy(stmt->error_code, stmt->dbh->error_code, 6);
573 								goto clean_up;
574 							}
575 							plc->freeq = 1;
576 					}
577 					zval_dtor(&tmp_param);
578 				}
579 			} else {
580 				plc->quoted = Z_STRVAL_P(param->parameter);
581 				plc->qlen = Z_STRLEN_P(param->parameter);
582 			}
583 			newbuffer_len += plc->qlen;
584 		}
585 
586 rewrite:
587 		/* allocate output buffer */
588 		newbuffer = emalloc(newbuffer_len + 1);
589 		*outquery = newbuffer;
590 
591 		/* and build the query */
592 		plc = placeholders;
593 		ptr = inquery;
594 
595 		do {
596 			t = plc->pos - ptr;
597 			if (t) {
598 				memcpy(newbuffer, ptr, t);
599 				newbuffer += t;
600 			}
601 			memcpy(newbuffer, plc->quoted, plc->qlen);
602 			newbuffer += plc->qlen;
603 			ptr = plc->pos + plc->len;
604 
605 			plc = plc->next;
606 		} while (plc);
607 
608 		t = (inquery + inquery_len) - ptr;
609 		if (t) {
610 			memcpy(newbuffer, ptr, t);
611 			newbuffer += t;
612 		}
613 		*newbuffer = '\0';
614 		*outquery_len = newbuffer - *outquery;
615 
616 		ret = 1;
617 		goto clean_up;
618 
619 	} else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
620 		/* rewrite ? to :pdoX */
621 		char *name, *idxbuf;
622 		const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
623 		int bind_no = 1;
624 
625 		newbuffer_len = inquery_len;
626 
627 		if (stmt->bound_param_map == NULL) {
628 			ALLOC_HASHTABLE(stmt->bound_param_map);
629 			zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
630 		}
631 
632 		for (plc = placeholders; plc; plc = plc->next) {
633 			int skip_map = 0;
634 			char *p;
635 			name = estrndup(plc->pos, plc->len);
636 
637 			/* check if bound parameter is already available */
638 			if (!strcmp(name, "?") || zend_hash_find(stmt->bound_param_map, name, plc->len + 1, (void**) &p) == FAILURE) {
639 				spprintf(&idxbuf, 0, tmpl, bind_no++);
640 			} else {
641 				idxbuf = estrdup(p);
642 				skip_map = 1;
643 			}
644 
645 			plc->quoted = idxbuf;
646 			plc->qlen = strlen(plc->quoted);
647 			plc->freeq = 1;
648 			newbuffer_len += plc->qlen;
649 
650 			if (!skip_map && stmt->named_rewrite_template) {
651 				/* create a mapping */
652 				zend_hash_update(stmt->bound_param_map, name, plc->len + 1, idxbuf, plc->qlen + 1, NULL);
653 			}
654 
655 			/* map number to name */
656 			zend_hash_index_update(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1, NULL);
657 
658 			efree(name);
659 		}
660 
661 		goto rewrite;
662 
663 	} else {
664 		/* rewrite :name to ? */
665 
666 		newbuffer_len = inquery_len;
667 
668 		if (stmt->bound_param_map == NULL) {
669 			ALLOC_HASHTABLE(stmt->bound_param_map);
670 			zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
671 		}
672 
673 		for (plc = placeholders; plc; plc = plc->next) {
674 			char *name;
675 
676 			name = estrndup(plc->pos, plc->len);
677 			zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL);
678 			efree(name);
679 			plc->quoted = "?";
680 			plc->qlen = 1;
681 		}
682 
683 		goto rewrite;
684 	}
685 
686 clean_up:
687 
688 	while (placeholders) {
689 		plc = placeholders;
690 		placeholders = plc->next;
691 
692 		if (plc->freeq) {
693 			efree(plc->quoted);
694 		}
695 
696 		efree(plc);
697 	}
698 
699 	return ret;
700 }
701 
702 #if 0
703 int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
704 		int *outquery_len TSRMLS_DC)
705 {
706 	Scanner s;
707 	char *ptr;
708 	int t;
709 	int bindno = 0;
710 	int newbuffer_len;
711 	int padding;
712 	HashTable *params = stmt->bound_params;
713 	struct pdo_bound_param_data *param;
714 	/* allocate buffer for query with expanded binds, ptr is our writing pointer */
715 	newbuffer_len = inquery_len;
716 
717 	/* calculate the possible padding factor due to quoting */
718 	if(stmt->dbh->max_escaped_char_length) {
719 		padding = stmt->dbh->max_escaped_char_length;
720 	} else {
721 		padding = 3;
722 	}
723 	if(params) {
724 		zend_hash_internal_pointer_reset(params);
725 		while (SUCCESS == zend_hash_get_current_data(params, (void**)&param)) {
726 			if(param->parameter) {
727 				convert_to_string(param->parameter);
728 				/* accommodate a string that needs to be fully quoted
729                    bind placeholders are at least 2 characters, so
730                    the accommodate their own "'s
731                 */
732 				newbuffer_len += padding * Z_STRLEN_P(param->parameter);
733 			}
734 			zend_hash_move_forward(params);
735 		}
736 	}
737 	*outquery = (char *) emalloc(newbuffer_len + 1);
738 	*outquery_len = 0;
739 
740 	ptr = *outquery;
741 	s.cur = inquery;
742 	while((t = scan(&s)) != PDO_PARSER_EOI) {
743 		if(t == PDO_PARSER_TEXT) {
744 			memcpy(ptr, s.tok, s.cur - s.tok);
745 			ptr += (s.cur - s.tok);
746 			*outquery_len += (s.cur - s.tok);
747 		}
748 		else if(t == PDO_PARSER_BIND) {
749 			if(!params) {
750 				/* error */
751 				efree(*outquery);
752 				*outquery = NULL;
753 				return (int) (s.cur - inquery);
754 			}
755 			/* lookup bind first via hash and then index */
756 			/* stupid keys need to be null-terminated, even though we know their length */
757 			if((SUCCESS == zend_hash_find(params, s.tok, s.cur-s.tok,(void **)&param))
758 			    ||
759 			   (SUCCESS == zend_hash_index_find(params, bindno, (void **)&param)))
760 			{
761 				char *quotedstr;
762 				int quotedstrlen;
763 				/* restore the in-string key, doesn't need null-termination here */
764 				/* currently everything is a string here */
765 
766 				/* quote the bind value if necessary */
767 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
768 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
769 				{
770 					memcpy(ptr, quotedstr, quotedstrlen);
771 					ptr += quotedstrlen;
772 					*outquery_len += quotedstrlen;
773 					efree(quotedstr);
774 				} else {
775 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
776 					ptr += Z_STRLEN_P(param->parameter);
777 					*outquery_len += (Z_STRLEN_P(param->parameter));
778 				}
779 			}
780 			else {
781 				/* error and cleanup */
782 				efree(*outquery);
783 				*outquery = NULL;
784 				return (int) (s.cur - inquery);
785 			}
786 			bindno++;
787 		}
788 		else if(t == PDO_PARSER_BIND_POS) {
789 			if(!params) {
790 				/* error */
791 				efree(*outquery);
792 				*outquery = NULL;
793 				return (int) (s.cur - inquery);
794 			}
795 			/* lookup bind by index */
796 			if(SUCCESS == zend_hash_index_find(params, bindno, (void **)&param))
797 			{
798 				char *quotedstr;
799 				int quotedstrlen;
800 				/* currently everything is a string here */
801 
802 				/* quote the bind value if necessary */
803 				if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
804 					Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
805 				{
806 					memcpy(ptr, quotedstr, quotedstrlen);
807 					ptr += quotedstrlen;
808 					*outquery_len += quotedstrlen;
809 					efree(quotedstr);
810 				} else {
811 					memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
812 					ptr += Z_STRLEN_P(param->parameter);
813 					*outquery_len += (Z_STRLEN_P(param->parameter));
814 				}
815 			}
816 			else {
817 				/* error and cleanup */
818 				efree(*outquery);
819 				*outquery = NULL;
820 				return (int) (s.cur - inquery);
821 			}
822 			bindno++;
823 		}
824 	}
825 	*ptr = '\0';
826 	return 0;
827 }
828 #endif
829 
830 /*
831  * Local variables:
832  * tab-width: 4
833  * c-basic-offset: 4
834  * End:
835  * vim600: noet sw=4 ts=4 fdm=marker ft=c
836  * vim<600: noet sw=4 ts=4
837  */
838