Annotation of embedaddon/php/ext/pdo/pdo_sql_parser.re, revision 1.1.1.4
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.3 misho 5: | Copyright (c) 1997-2013 The PHP Group |
1.1 misho 6: +----------------------------------------------------------------------+
7: | This source file is subject to version 3.01 of the PHP license, |
8: | that is bundled with this package in the file LICENSE, and is |
9: | available through the world-wide-web at the following url: |
10: | http://www.php.net/license/3_01.txt |
11: | If you did not receive a copy of the PHP license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@php.net so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
15: | Author: George Schlossnagle <george@omniti.com> |
16: +----------------------------------------------------------------------+
17: */
18:
1.1.1.2 misho 19: /* $Id$ */
1.1 misho 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
1.1.1.3 misho 35: #define YYLIMIT s->end
1.1 misho 36: #define YYMARKER s->ptr
1.1.1.3 misho 37: #define YYFILL(n) { RET(PDO_PARSER_EOI); }
1.1 misho 38:
39: typedef struct Scanner {
1.1.1.3 misho 40: char *ptr, *cur, *tok, *end;
1.1 misho 41: } Scanner;
42:
43: static int scan(Scanner *s)
44: {
45: char *cursor = s->cur;
46:
47: s->tok = cursor;
48: /*!re2c
49: BINDCHR = [:][a-zA-Z0-9_]+;
50: QUESTION = [?];
51: COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
52: SPECIALS = [:?"'];
1.1.1.4 ! misho 53: MULTICHAR = ([:]{2,}|[?]{2,});
1.1 misho 54: ANYNOEOF = [\001-\377];
55: */
56:
57: /*!re2c
58: (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
59: (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
1.1.1.4 ! misho 60: MULTICHAR { RET(PDO_PARSER_TEXT); }
1.1 misho 61: BINDCHR { RET(PDO_PARSER_BIND); }
62: QUESTION { RET(PDO_PARSER_BIND_POS); }
63: SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
64: COMMENTS { RET(PDO_PARSER_TEXT); }
65: (ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); }
66: */
67: }
68:
69: struct placeholder {
70: char *pos;
71: int len;
72: int bindno;
73: int qlen; /* quoted length of value */
74: char *quoted; /* quoted value */
75: int freeq;
76: struct placeholder *next;
77: };
78:
79: PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len,
80: char **outquery, int *outquery_len TSRMLS_DC)
81: {
82: Scanner s;
83: char *ptr, *newbuffer;
84: int t;
85: int bindno = 0;
86: int ret = 0;
87: int newbuffer_len;
88: HashTable *params;
89: struct pdo_bound_param_data *param;
90: int query_type = PDO_PLACEHOLDER_NONE;
91: struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
92:
93: ptr = *outquery;
94: s.cur = inquery;
1.1.1.3 misho 95: s.end = inquery + inquery_len + 1;
1.1 misho 96:
97: /* phase 1: look for args */
98: while((t = scan(&s)) != PDO_PARSER_EOI) {
99: if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
100: if (t == PDO_PARSER_BIND) {
101: int len = s.cur - s.tok;
102: if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
103: continue;
104: }
105: query_type |= PDO_PLACEHOLDER_NAMED;
106: } else {
107: query_type |= PDO_PLACEHOLDER_POSITIONAL;
108: }
109:
110: plc = emalloc(sizeof(*plc));
111: memset(plc, 0, sizeof(*plc));
112: plc->next = NULL;
113: plc->pos = s.tok;
114: plc->len = s.cur - s.tok;
115: plc->bindno = bindno++;
116:
117: if (placetail) {
118: placetail->next = plc;
119: } else {
120: placeholders = plc;
121: }
122: placetail = plc;
123: }
124: }
125:
126: if (bindno == 0) {
127: /* nothing to do; good! */
128: return 0;
129: }
130:
131: /* did the query make sense to me? */
132: if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
133: /* they mixed both types; punt */
134: pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters" TSRMLS_CC);
135: ret = -1;
136: goto clean_up;
137: }
138:
139: if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
140: /* query matches native syntax */
141: ret = 0;
142: goto clean_up;
143: }
144:
145: if (stmt->named_rewrite_template) {
146: /* magic/hack.
147: * We we pretend that the query was positional even if
148: * it was named so that we fall into the
149: * named rewrite case below. Not too pretty,
150: * but it works. */
151: query_type = PDO_PLACEHOLDER_POSITIONAL;
152: }
153:
154: params = stmt->bound_params;
155:
156: /* Do we have placeholders but no bound params */
157: if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
158: pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound" TSRMLS_CC);
159: ret = -1;
160: goto clean_up;
161: }
162:
163: if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
164: /* extra bit of validation for instances when same params are bound more then once */
165: if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
166: int ok = 1;
167: for (plc = placeholders; plc; plc = plc->next) {
168: if (zend_hash_find(params, plc->pos, plc->len, (void**) ¶m) == FAILURE) {
169: ok = 0;
170: break;
171: }
172: }
173: if (ok) {
174: goto safe;
175: }
176: }
177: pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens" TSRMLS_CC);
178: ret = -1;
179: goto clean_up;
180: }
181: safe:
182: /* what are we going to do ? */
183: if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
184: /* query generation */
185:
186: newbuffer_len = inquery_len;
187:
188: /* let's quote all the values */
189: for (plc = placeholders; plc; plc = plc->next) {
190: if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
191: ret = zend_hash_index_find(params, plc->bindno, (void**) ¶m);
192: } else {
193: ret = zend_hash_find(params, plc->pos, plc->len, (void**) ¶m);
194: }
195: if (ret == FAILURE) {
196: /* parameter was not defined */
197: ret = -1;
198: pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
199: goto clean_up;
200: }
201: if (stmt->dbh->methods->quoter) {
202: if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(param->parameter) == IS_RESOURCE) {
203: php_stream *stm;
204:
205: php_stream_from_zval_no_verify(stm, ¶m->parameter);
206: if (stm) {
207: size_t len;
208: char *buf = NULL;
209:
210: len = php_stream_copy_to_mem(stm, &buf, PHP_STREAM_COPY_ALL, 0);
211: if (!stmt->dbh->methods->quoter(stmt->dbh, buf, len, &plc->quoted, &plc->qlen,
212: param->param_type TSRMLS_CC)) {
213: /* bork */
214: ret = -1;
215: strncpy(stmt->error_code, stmt->dbh->error_code, 6);
216: if (buf) {
217: efree(buf);
218: }
219: goto clean_up;
220: }
221: if (buf) {
222: efree(buf);
223: }
224: } else {
225: pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource" TSRMLS_CC);
226: ret = -1;
227: goto clean_up;
228: }
229: plc->freeq = 1;
230: } else {
231: switch (Z_TYPE_P(param->parameter)) {
232: case IS_NULL:
233: plc->quoted = "NULL";
234: plc->qlen = sizeof("NULL")-1;
235: plc->freeq = 0;
236: break;
237:
238: case IS_BOOL:
239: convert_to_long(param->parameter);
240:
241: case IS_LONG:
242: case IS_DOUBLE:
243: convert_to_string(param->parameter);
244: plc->qlen = Z_STRLEN_P(param->parameter);
245: plc->quoted = Z_STRVAL_P(param->parameter);
246: plc->freeq = 0;
247: break;
248:
249: default:
250: convert_to_string(param->parameter);
251: if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
252: Z_STRLEN_P(param->parameter), &plc->quoted, &plc->qlen,
253: param->param_type TSRMLS_CC)) {
254: /* bork */
255: ret = -1;
256: strncpy(stmt->error_code, stmt->dbh->error_code, 6);
257: goto clean_up;
258: }
259: plc->freeq = 1;
260: }
261: }
262: } else {
263: plc->quoted = Z_STRVAL_P(param->parameter);
264: plc->qlen = Z_STRLEN_P(param->parameter);
265: }
266: newbuffer_len += plc->qlen;
267: }
268:
269: rewrite:
270: /* allocate output buffer */
271: newbuffer = emalloc(newbuffer_len + 1);
272: *outquery = newbuffer;
273:
274: /* and build the query */
275: plc = placeholders;
276: ptr = inquery;
277:
278: do {
279: t = plc->pos - ptr;
280: if (t) {
281: memcpy(newbuffer, ptr, t);
282: newbuffer += t;
283: }
284: memcpy(newbuffer, plc->quoted, plc->qlen);
285: newbuffer += plc->qlen;
286: ptr = plc->pos + plc->len;
287:
288: plc = plc->next;
289: } while (plc);
290:
291: t = (inquery + inquery_len) - ptr;
292: if (t) {
293: memcpy(newbuffer, ptr, t);
294: newbuffer += t;
295: }
296: *newbuffer = '\0';
297: *outquery_len = newbuffer - *outquery;
298:
299: ret = 1;
300: goto clean_up;
301:
302: } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
303: /* rewrite ? to :pdoX */
304: char *name, *idxbuf;
305: const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
306: int bind_no = 1;
307:
308: newbuffer_len = inquery_len;
309:
310: if (stmt->bound_param_map == NULL) {
311: ALLOC_HASHTABLE(stmt->bound_param_map);
312: zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
313: }
314:
315: for (plc = placeholders; plc; plc = plc->next) {
316: int skip_map = 0;
317: char *p;
318: name = estrndup(plc->pos, plc->len);
319:
320: /* check if bound parameter is already available */
321: if (!strcmp(name, "?") || zend_hash_find(stmt->bound_param_map, name, plc->len + 1, (void**) &p) == FAILURE) {
322: spprintf(&idxbuf, 0, tmpl, bind_no++);
323: } else {
324: idxbuf = estrdup(p);
325: skip_map = 1;
326: }
327:
328: plc->quoted = idxbuf;
329: plc->qlen = strlen(plc->quoted);
330: plc->freeq = 1;
331: newbuffer_len += plc->qlen;
332:
333: if (!skip_map && stmt->named_rewrite_template) {
334: /* create a mapping */
335: zend_hash_update(stmt->bound_param_map, name, plc->len + 1, idxbuf, plc->qlen + 1, NULL);
336: }
337:
338: /* map number to name */
339: zend_hash_index_update(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1, NULL);
340:
341: efree(name);
342: }
343:
344: goto rewrite;
345:
346: } else {
347: /* rewrite :name to ? */
348:
349: newbuffer_len = inquery_len;
350:
351: if (stmt->bound_param_map == NULL) {
352: ALLOC_HASHTABLE(stmt->bound_param_map);
353: zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
354: }
355:
356: for (plc = placeholders; plc; plc = plc->next) {
357: char *name;
358:
359: name = estrndup(plc->pos, plc->len);
360: zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL);
361: efree(name);
362: plc->quoted = "?";
363: plc->qlen = 1;
364: }
365:
366: goto rewrite;
367: }
368:
369: clean_up:
370:
371: while (placeholders) {
372: plc = placeholders;
373: placeholders = plc->next;
374:
375: if (plc->freeq) {
376: efree(plc->quoted);
377: }
378:
379: efree(plc);
380: }
381:
382: return ret;
383: }
384:
385: #if 0
386: int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
387: int *outquery_len TSRMLS_DC)
388: {
389: Scanner s;
390: char *ptr;
391: int t;
392: int bindno = 0;
393: int newbuffer_len;
394: int padding;
395: HashTable *params = stmt->bound_params;
396: struct pdo_bound_param_data *param;
397: /* allocate buffer for query with expanded binds, ptr is our writing pointer */
398: newbuffer_len = inquery_len;
399:
400: /* calculate the possible padding factor due to quoting */
401: if(stmt->dbh->max_escaped_char_length) {
402: padding = stmt->dbh->max_escaped_char_length;
403: } else {
404: padding = 3;
405: }
406: if(params) {
407: zend_hash_internal_pointer_reset(params);
408: while (SUCCESS == zend_hash_get_current_data(params, (void**)¶m)) {
409: if(param->parameter) {
410: convert_to_string(param->parameter);
1.1.1.3 misho 411: /* accommodate a string that needs to be fully quoted
1.1 misho 412: bind placeholders are at least 2 characters, so
1.1.1.3 misho 413: the accommodate their own "'s
1.1 misho 414: */
415: newbuffer_len += padding * Z_STRLEN_P(param->parameter);
416: }
417: zend_hash_move_forward(params);
418: }
419: }
420: *outquery = (char *) emalloc(newbuffer_len + 1);
421: *outquery_len = 0;
422:
423: ptr = *outquery;
424: s.cur = inquery;
425: while((t = scan(&s)) != PDO_PARSER_EOI) {
426: if(t == PDO_PARSER_TEXT) {
427: memcpy(ptr, s.tok, s.cur - s.tok);
428: ptr += (s.cur - s.tok);
429: *outquery_len += (s.cur - s.tok);
430: }
431: else if(t == PDO_PARSER_BIND) {
432: if(!params) {
433: /* error */
434: efree(*outquery);
435: *outquery = NULL;
436: return (int) (s.cur - inquery);
437: }
438: /* lookup bind first via hash and then index */
439: /* stupid keys need to be null-terminated, even though we know their length */
440: if((SUCCESS == zend_hash_find(params, s.tok, s.cur-s.tok,(void **)¶m))
441: ||
442: (SUCCESS == zend_hash_index_find(params, bindno, (void **)¶m)))
443: {
444: char *quotedstr;
445: int quotedstrlen;
446: /* restore the in-string key, doesn't need null-termination here */
447: /* currently everything is a string here */
448:
449: /* quote the bind value if necessary */
450: if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
451: Z_STRLEN_P(param->parameter), "edstr, "edstrlen TSRMLS_CC))
452: {
453: memcpy(ptr, quotedstr, quotedstrlen);
454: ptr += quotedstrlen;
455: *outquery_len += quotedstrlen;
456: efree(quotedstr);
457: } else {
458: memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
459: ptr += Z_STRLEN_P(param->parameter);
460: *outquery_len += (Z_STRLEN_P(param->parameter));
461: }
462: }
463: else {
464: /* error and cleanup */
465: efree(*outquery);
466: *outquery = NULL;
467: return (int) (s.cur - inquery);
468: }
469: bindno++;
470: }
471: else if(t == PDO_PARSER_BIND_POS) {
472: if(!params) {
473: /* error */
474: efree(*outquery);
475: *outquery = NULL;
476: return (int) (s.cur - inquery);
477: }
478: /* lookup bind by index */
479: if(SUCCESS == zend_hash_index_find(params, bindno, (void **)¶m))
480: {
481: char *quotedstr;
482: int quotedstrlen;
483: /* currently everything is a string here */
484:
485: /* quote the bind value if necessary */
486: if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
487: Z_STRLEN_P(param->parameter), "edstr, "edstrlen TSRMLS_CC))
488: {
489: memcpy(ptr, quotedstr, quotedstrlen);
490: ptr += quotedstrlen;
491: *outquery_len += quotedstrlen;
492: efree(quotedstr);
493: } else {
494: memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
495: ptr += Z_STRLEN_P(param->parameter);
496: *outquery_len += (Z_STRLEN_P(param->parameter));
497: }
498: }
499: else {
500: /* error and cleanup */
501: efree(*outquery);
502: *outquery = NULL;
503: return (int) (s.cur - inquery);
504: }
505: bindno++;
506: }
507: }
508: *ptr = '\0';
509: return 0;
510: }
511: #endif
512:
513: /*
514: * Local variables:
515: * tab-width: 4
516: * c-basic-offset: 4
517: * End:
518: * vim600: noet sw=4 ts=4 fdm=marker ft=c
519: * vim<600: noet sw=4 ts=4
520: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>