Annotation of embedaddon/php/ext/pdo/pdo_sql_parser.re, revision 1.1.1.5
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 {
1.1.1.5 ! misho 231: zval tmp_param = *param->parameter;
! 232: zval_copy_ctor(&tmp_param);
! 233: switch (Z_TYPE(tmp_param)) {
1.1 misho 234: case IS_NULL:
235: plc->quoted = "NULL";
236: plc->qlen = sizeof("NULL")-1;
237: plc->freeq = 0;
238: break;
239:
240: case IS_BOOL:
1.1.1.5 ! misho 241: convert_to_long(&tmp_param);
! 242: /* fall through */
1.1 misho 243: case IS_LONG:
244: case IS_DOUBLE:
1.1.1.5 ! misho 245: convert_to_string(&tmp_param);
! 246: plc->qlen = Z_STRLEN(tmp_param);
! 247: plc->quoted = estrdup(Z_STRVAL(tmp_param));
! 248: plc->freeq = 1;
1.1 misho 249: break;
250:
251: default:
1.1.1.5 ! misho 252: convert_to_string(&tmp_param);
! 253: if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL(tmp_param),
! 254: Z_STRLEN(tmp_param), &plc->quoted, &plc->qlen,
1.1 misho 255: param->param_type TSRMLS_CC)) {
256: /* bork */
257: ret = -1;
258: strncpy(stmt->error_code, stmt->dbh->error_code, 6);
259: goto clean_up;
260: }
261: plc->freeq = 1;
262: }
1.1.1.5 ! misho 263: zval_dtor(&tmp_param);
1.1 misho 264: }
265: } else {
266: plc->quoted = Z_STRVAL_P(param->parameter);
267: plc->qlen = Z_STRLEN_P(param->parameter);
268: }
269: newbuffer_len += plc->qlen;
270: }
271:
272: rewrite:
273: /* allocate output buffer */
274: newbuffer = emalloc(newbuffer_len + 1);
275: *outquery = newbuffer;
276:
277: /* and build the query */
278: plc = placeholders;
279: ptr = inquery;
280:
281: do {
282: t = plc->pos - ptr;
283: if (t) {
284: memcpy(newbuffer, ptr, t);
285: newbuffer += t;
286: }
287: memcpy(newbuffer, plc->quoted, plc->qlen);
288: newbuffer += plc->qlen;
289: ptr = plc->pos + plc->len;
290:
291: plc = plc->next;
292: } while (plc);
293:
294: t = (inquery + inquery_len) - ptr;
295: if (t) {
296: memcpy(newbuffer, ptr, t);
297: newbuffer += t;
298: }
299: *newbuffer = '\0';
300: *outquery_len = newbuffer - *outquery;
301:
302: ret = 1;
303: goto clean_up;
304:
305: } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
306: /* rewrite ? to :pdoX */
307: char *name, *idxbuf;
308: const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
309: int bind_no = 1;
310:
311: newbuffer_len = inquery_len;
312:
313: if (stmt->bound_param_map == NULL) {
314: ALLOC_HASHTABLE(stmt->bound_param_map);
315: zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
316: }
317:
318: for (plc = placeholders; plc; plc = plc->next) {
319: int skip_map = 0;
320: char *p;
321: name = estrndup(plc->pos, plc->len);
322:
323: /* check if bound parameter is already available */
324: if (!strcmp(name, "?") || zend_hash_find(stmt->bound_param_map, name, plc->len + 1, (void**) &p) == FAILURE) {
325: spprintf(&idxbuf, 0, tmpl, bind_no++);
326: } else {
327: idxbuf = estrdup(p);
328: skip_map = 1;
329: }
330:
331: plc->quoted = idxbuf;
332: plc->qlen = strlen(plc->quoted);
333: plc->freeq = 1;
334: newbuffer_len += plc->qlen;
335:
336: if (!skip_map && stmt->named_rewrite_template) {
337: /* create a mapping */
338: zend_hash_update(stmt->bound_param_map, name, plc->len + 1, idxbuf, plc->qlen + 1, NULL);
339: }
340:
341: /* map number to name */
342: zend_hash_index_update(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1, NULL);
343:
344: efree(name);
345: }
346:
347: goto rewrite;
348:
349: } else {
350: /* rewrite :name to ? */
351:
352: newbuffer_len = inquery_len;
353:
354: if (stmt->bound_param_map == NULL) {
355: ALLOC_HASHTABLE(stmt->bound_param_map);
356: zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
357: }
358:
359: for (plc = placeholders; plc; plc = plc->next) {
360: char *name;
361:
362: name = estrndup(plc->pos, plc->len);
363: zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL);
364: efree(name);
365: plc->quoted = "?";
366: plc->qlen = 1;
367: }
368:
369: goto rewrite;
370: }
371:
372: clean_up:
373:
374: while (placeholders) {
375: plc = placeholders;
376: placeholders = plc->next;
377:
378: if (plc->freeq) {
379: efree(plc->quoted);
380: }
381:
382: efree(plc);
383: }
384:
385: return ret;
386: }
387:
388: #if 0
389: int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
390: int *outquery_len TSRMLS_DC)
391: {
392: Scanner s;
393: char *ptr;
394: int t;
395: int bindno = 0;
396: int newbuffer_len;
397: int padding;
398: HashTable *params = stmt->bound_params;
399: struct pdo_bound_param_data *param;
400: /* allocate buffer for query with expanded binds, ptr is our writing pointer */
401: newbuffer_len = inquery_len;
402:
403: /* calculate the possible padding factor due to quoting */
404: if(stmt->dbh->max_escaped_char_length) {
405: padding = stmt->dbh->max_escaped_char_length;
406: } else {
407: padding = 3;
408: }
409: if(params) {
410: zend_hash_internal_pointer_reset(params);
411: while (SUCCESS == zend_hash_get_current_data(params, (void**)¶m)) {
412: if(param->parameter) {
413: convert_to_string(param->parameter);
1.1.1.3 misho 414: /* accommodate a string that needs to be fully quoted
1.1 misho 415: bind placeholders are at least 2 characters, so
1.1.1.3 misho 416: the accommodate their own "'s
1.1 misho 417: */
418: newbuffer_len += padding * Z_STRLEN_P(param->parameter);
419: }
420: zend_hash_move_forward(params);
421: }
422: }
423: *outquery = (char *) emalloc(newbuffer_len + 1);
424: *outquery_len = 0;
425:
426: ptr = *outquery;
427: s.cur = inquery;
428: while((t = scan(&s)) != PDO_PARSER_EOI) {
429: if(t == PDO_PARSER_TEXT) {
430: memcpy(ptr, s.tok, s.cur - s.tok);
431: ptr += (s.cur - s.tok);
432: *outquery_len += (s.cur - s.tok);
433: }
434: else if(t == PDO_PARSER_BIND) {
435: if(!params) {
436: /* error */
437: efree(*outquery);
438: *outquery = NULL;
439: return (int) (s.cur - inquery);
440: }
441: /* lookup bind first via hash and then index */
442: /* stupid keys need to be null-terminated, even though we know their length */
443: if((SUCCESS == zend_hash_find(params, s.tok, s.cur-s.tok,(void **)¶m))
444: ||
445: (SUCCESS == zend_hash_index_find(params, bindno, (void **)¶m)))
446: {
447: char *quotedstr;
448: int quotedstrlen;
449: /* restore the in-string key, doesn't need null-termination here */
450: /* currently everything is a string here */
451:
452: /* quote the bind value if necessary */
453: if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
454: Z_STRLEN_P(param->parameter), "edstr, "edstrlen TSRMLS_CC))
455: {
456: memcpy(ptr, quotedstr, quotedstrlen);
457: ptr += quotedstrlen;
458: *outquery_len += quotedstrlen;
459: efree(quotedstr);
460: } else {
461: memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
462: ptr += Z_STRLEN_P(param->parameter);
463: *outquery_len += (Z_STRLEN_P(param->parameter));
464: }
465: }
466: else {
467: /* error and cleanup */
468: efree(*outquery);
469: *outquery = NULL;
470: return (int) (s.cur - inquery);
471: }
472: bindno++;
473: }
474: else if(t == PDO_PARSER_BIND_POS) {
475: if(!params) {
476: /* error */
477: efree(*outquery);
478: *outquery = NULL;
479: return (int) (s.cur - inquery);
480: }
481: /* lookup bind by index */
482: if(SUCCESS == zend_hash_index_find(params, bindno, (void **)¶m))
483: {
484: char *quotedstr;
485: int quotedstrlen;
486: /* currently everything is a string here */
487:
488: /* quote the bind value if necessary */
489: if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
490: Z_STRLEN_P(param->parameter), "edstr, "edstrlen TSRMLS_CC))
491: {
492: memcpy(ptr, quotedstr, quotedstrlen);
493: ptr += quotedstrlen;
494: *outquery_len += quotedstrlen;
495: efree(quotedstr);
496: } else {
497: memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
498: ptr += Z_STRLEN_P(param->parameter);
499: *outquery_len += (Z_STRLEN_P(param->parameter));
500: }
501: }
502: else {
503: /* error and cleanup */
504: efree(*outquery);
505: *outquery = NULL;
506: return (int) (s.cur - inquery);
507: }
508: bindno++;
509: }
510: }
511: *ptr = '\0';
512: return 0;
513: }
514: #endif
515:
516: /*
517: * Local variables:
518: * tab-width: 4
519: * c-basic-offset: 4
520: * End:
521: * vim600: noet sw=4 ts=4 fdm=marker ft=c
522: * vim<600: noet sw=4 ts=4
523: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>