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