File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / pdo / pdo_sql_parser.re
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:51 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    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: pdo_sql_parser.re,v 1.1.1.5 2014/06/15 20:03:51 misho Exp $ */
   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: 
   39: typedef struct Scanner {
   40: 	char 	*ptr, *cur, *tok, *end;
   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	= ([:]{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: 
   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: {
   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**) &param) == 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: 	}
  181: safe:
  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**) &param);
  192: 			} else {
  193: 				ret = zend_hash_find(params, plc->pos, plc->len, (void**) &param);
  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, &param->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: 
  272: rewrite:
  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: 
  372: clean_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
  389: int 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**)&param)) {
  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 **)&param))  
  444: 			    ||
  445: 			   (SUCCESS == zend_hash_index_find(params, bindno, (void **)&param))) 
  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), &quotedstr, &quotedstrlen 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 **)&param)) 
  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), &quotedstr, &quotedstrlen 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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>