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