1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2014 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 = ([:]{2,}|[?]{2,}); 54 ANYNOEOF = [\001-\377]; 55 */ 56 57 /*!re2c 58 (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); } 59 (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); } 60 MULTICHAR { 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 zval tmp_param = *param->parameter; 232 zval_copy_ctor(&tmp_param); 233 switch (Z_TYPE(tmp_param)) { 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: 241 convert_to_long(&tmp_param); 242 /* fall through */ 243 case IS_LONG: 244 case IS_DOUBLE: 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; 249 break; 250 251 default: 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, 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 } 263 zval_dtor(&tmp_param); 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 272rewrite: 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 372clean_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 389int 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); 414 /* accommodate a string that needs to be fully quoted 415 bind placeholders are at least 2 characters, so 416 the accommodate their own "'s 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 */ 524