File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / pdo / pdo_sql_parser.re
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:47:58 2012 UTC (12 years, 5 months ago) by misho
Branches: php, MAIN
CVS tags: v5_3_10, HEAD
php

    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,v 1.1.1.1 2012/02/21 23:47:58 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         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**) &param) == 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**) &param);
  193: 			} else {
  194: 				ret = zend_hash_find(params, plc->pos, plc->len, (void**) &param);
  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, &param->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**)&param)) {
  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 **)&param))  
  442: 			    ||
  443: 			   (SUCCESS == zend_hash_index_find(params, bindno, (void **)&param))) 
  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), &quotedstr, &quotedstrlen 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 **)&param)) 
  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), &quotedstr, &quotedstrlen 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:  */

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