# mini hack
install: $(all_targets) $(install_targets) install-pdo-headers
+$(top_srcdir)/ext/pdo/pdo_sql_parser.c: $(top_srcdir)/ext/pdo/pdo_sql_parser.re
+ re2c -b $(top_srcdir)/ext/pdo/pdo_sql_parser.re > $@
+$(srcdir)/pdo_sql_parser.c: $(srcdir)/pdo_sql_parser.re
+ re2c -b $(srcdir)/pdo_sql_parser.re > $@
[ --enable-pdo Enable PHP Data Objects support])
if test "$PHP_PDO" != "no"; then
- PHP_NEW_EXTENSION(pdo, pdo.c pdo_dbh.c pdo_stmt.c, $ext_shared)
+ PHP_NEW_EXTENSION(pdo, pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c, $ext_shared)
PHP_ADD_MAKEFILE_FRAGMENT
fi
}
stmt = ecalloc(1, sizeof(*stmt));
-
+ /* uncoditionally keep this for later reference */
+ stmt->query_string = estrndup(statement, statement_len);
+ stmt->query_stringlen = statement_len;
if (dbh->methods->preparer(dbh, statement, statement_len, stmt TSRMLS_CC)) {
/* prepared; create a statement object for PHP land to access it */
Z_TYPE_P(return_value) = IS_OBJECT;
stmt->dbh = dbh;
return;
}
-
efree(stmt);
-
RETURN_FALSE;
}
--- /dev/null
+#include "php.h"
+#include "php_pdo_driver.h"
+
+#define TEXT 1
+#define BIND 2
+#define EOI 3
+
+#define RET(i) {s->cur = cursor; return i; }
+
+#define YYCTYPE char
+#define YYCURSOR cursor
+#define YYLIMIT s->lim
+#define YYMARKER s->ptr
+#define YYFILL(n)
+
+typedef struct Scanner {
+ char *lim, *ptr, *cur, *tok;
+} Scanner;
+
+static int scan(Scanner *s)
+{
+ char *cursor = s->cur;
+ std:
+ s->tok = cursor;
+ /*!re2c
+ BINDCHR = [:][a-zA-Z0-9]+;
+ SPECIALS = [:"];
+ ESC = [\\]["];
+ EOF = [\000];
+ ANYNOEOF = [\001-\377];
+ */
+
+ /*!re2c
+ (["] (ESC|ANYNOEOF\[\\"])* ["]) { RET(TEXT); }
+ BINDCHR { RET(BIND); }
+ SPECIALS { RET(TEXT); }
+ (ANYNOEOF\SPECIALS)+ { RET(TEXT); }
+ EOF { RET(EOI); }
+ */
+}
+
+int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
+ int *outquery_len)
+{
+ Scanner s;
+ char *ptr;
+ int t;
+ int bindno = 0;
+ int newbuffer_len;
+ HashTable *params = stmt->bound_params;
+ struct pdo_bound_param_data *param;
+
+ /* allocate buffer for query with expanded binds, ptr is our writing pointer */
+ newbuffer_len = inquery_len;
+ if(params) {
+ zend_hash_internal_pointer_reset(params);
+ while (SUCCESS == zend_hash_get_current_data(params, (void**)¶m)) {
+ if(param->parameter) {
+ convert_to_string(param->parameter);
+ /* accomodate a string that needs to be fully quoted
+ bind placeholders are at least 2 characters, so
+ the accomodate their own "'s
+ */
+ newbuffer_len += 2 * Z_STRLEN_P(param->parameter);
+ }
+ zend_hash_move_forward(params);
+ }
+ }
+ *outquery = (char *) emalloc(newbuffer_len + 1);
+ *outquery_len = 0;
+
+ ptr = *outquery;
+ s.cur = inquery;
+ s.lim = inquery + inquery_len;
+ while((t = scan(&s)) != EOI) {
+ if(t == TEXT) {
+ memcpy(ptr, s.tok, s.cur - s.tok);
+ ptr += (s.cur - s.tok);
+ *outquery_len += (s.cur - s.tok);
+ }
+ else if(t == BIND) {
+ if(!params) {
+ /* error */
+ efree(*outquery);
+ return 0;
+ }
+ /* lookup bind first via hash and then index */
+ if((SUCCESS == zend_hash_find(params, s.tok+1, s.cur-s.tok,(void **)¶m))
+ ||
+ (SUCCESS == zend_hash_index_find(params, bindno, (void **)¶m)))
+ {
+ char *quotedstr;
+ int quotedstrlen;
+ /* currently everything is a string here */
+
+ /* add leading quote */
+ *ptr = '"';
+ ptr++;
+ (*outquery_len)++;
+
+ /* quote the bind value if necessary */
+ if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
+ Z_STRLEN_P(param->parameter), "edstr, "edstrlen TSRMLS_CC))
+ {
+ memcpy(ptr, quotedstr, quotedstrlen);
+ ptr += quotedstrlen;
+ *outquery_len += quotedstrlen;
+ efree(quotedstr);
+ } else {
+ memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
+ ptr += Z_STRLEN_P(param->parameter);
+ *outquery_len += (Z_STRLEN_P(param->parameter));
+ }
+ /* add trailing quote */
+ *ptr = '"';
+ ptr++;
+ (*outquery_len)++;
+ }
+ else {
+ /* error and cleanup */
+ efree(*outquery);
+ return 0;
+ }
+ bindno++;
+ }
+ }
+ *ptr = '\0';
+ return 1;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
#include "php_pdo.h"
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
+#include "php_pdo_sql_parser.h"
#include "zend_exceptions.h"
/* {{{ content from zend_arg_defs.c:
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
zval *input_params = NULL;
int ret = 1;
+ char *original_query;
+ int orginal_querylen;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &input_params)) {
RETURN_FALSE;
}
param.param_type = PDO_PARAM_STR;
-#if 0
- if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp),
- "edstr, "edstrlen TSRMLS_DC)) {
- int refcount = (*tmp)->refcount;
- zval_dtor(*tmp);
- ZVAL_STRINGL(*tmp, quotedstr, quotedstrlen, 0);
- (*tmp)->refcount = refcount;
- }
-#endif
param.parameter = *tmp;
if (!really_register_bound_param(¶m, stmt, 1 TSRMLS_CC)) {
}
if (stmt->dbh->emulate_prepare) {
- /* XXX: here we need to:
- * - walk stmt->bound_params, quoting each zval value
- * (without modifying the zval)
- * - substitute these values according to name/position
- * - stash that into stmt->query_string
- *
- * When the executer() is called, it will use that query string */
-
+ /* handle the emulated parameter binding,
+ * stmt->active_query_string holds the query with binds expanded and
+ * quoted.
+ */
+ if(pdo_parse_params(stmt, stmt->query_string, stmt->query_stringlen,
+ &stmt->active_query_string, &stmt->active_query_stringlen) == 0) {
+ // parse error in handling the query
+ RETURN_FALSE;
+ }
} else if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE TSRMLS_CC)) {
RETURN_FALSE;
}
-
if (stmt->methods->executer(stmt TSRMLS_CC)) {
+ efree(stmt->active_query_string);
+ stmt->active_query_string = NULL;
if (!stmt->executed) {
/* this is the first execute */
RETURN_BOOL(ret);
}
+ efree(stmt->active_query_string);
+ stmt->active_query_string = NULL;
RETURN_FALSE;
}
/* }}} */
#ifndef PHP_PDO_H
#define PHP_PDO_H
+#include "php.h"
extern zend_module_entry pdo_module_entry;
#define phpext_pdo_ptr &pdo_module_entry
/* not always meaningful */
long row_count;
- /* used only when PDO is emulating prepare for the driver */
+ /* used to hold the statement's current query */
char *query_string;
+ int query_stringlen;
+
+ /* the copy of the query with expanded binds ONLY for emulated-prepare drivers */
+ char *active_query_string;
+ int active_query_stringlen;
};
/* call this in MINIT to register your PDO driver */
--- /dev/null
+#ifndef PHP_PDO_MYSQL_SQL_PARSER_H
+#define PHP_PDO_MYSQL_SQL_PARSER_H
+#include "php.h"
+int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery,
+ int *outquery_len);
+
+#endif