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: ! 19: /* $Id: pdo_sql_parser.re 321634 2012-01-01 13:15:04Z felipe $ */ ! 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: */