1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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$ */ 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 s->end 36#define YYMARKER s->ptr 37#define YYFILL(n) { RET(PDO_PARSER_EOI); } 38 39typedef struct Scanner { 40 char *ptr, *cur, *tok, *end; 41} Scanner; 42 43static 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 ANYNOEOF = [\001-\377]; 55 */ 56 57 /*!re2c 58 (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); } 59 (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); } 60 MULTICHAR{2,} { RET(PDO_PARSER_TEXT); } 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 69struct 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 79PDO_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; 95 s.end = inquery + inquery_len + 1; 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 } 181safe: 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 269rewrite: 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 369clean_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 386int 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); 411 /* accomodate a string that needs to be fully quoted 412 bind placeholders are at least 2 characters, so 413 the accomodate their own "'s 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 */ 521