From: Wez Furlong Date: Sun, 13 Feb 2005 06:29:35 +0000 (+0000) Subject: implement mapping of :name to ? parameters for drivers that only support ? X-Git-Tag: RELEASE_0_2_3~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0da6a84edfdcbd264d9ad0f562fe4c2566ee9e62;p=php implement mapping of :name to ? parameters for drivers that only support ? placeholders. The current restriction is that you may not use the same named parameter more than one in a given query, as there is a danger of scary things happen with the zval if it gets bound multiple times. --- diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 68d6785362..97e7616f5d 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -259,10 +259,25 @@ rewrite: } else { /* rewrite :name to ? */ + + newbuffer_len = inquery_len; + + if (stmt->bound_param_map == NULL) { + ALLOC_HASHTABLE(stmt->bound_param_map); + zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0); + } + + for (plc = placeholders; plc; plc = plc->next) { + char *name; + + name = estrndup(plc->pos, plc->len); + zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL); + efree(name); + plc->quoted = "?"; + plc->qlen = 1; + } - /* HARD!. We need to remember the mapping and bind those positions. */ - pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "cannot map :name to ? in this version" TSRMLS_CC); - ret = -1; + goto rewrite; } clean_up: diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 2b165acaf8..b70eda3d24 100755 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -73,6 +73,37 @@ static PHP_FUNCTION(dbstmt_constructor) /* {{{ */ } /* }}} */ +static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param TSRMLS_DC) +{ + if (stmt->bound_param_map) { + /* rewriting :name to ? style. + * We need to fixup the parameter numbers on the parameters. + * If we find that a given named parameter has been used twice, + * we will raise an error, as we can't be sure that it is safe + * to bind multiple parameters onto the same zval in the underlying + * driver */ + char **name; + int position = 0; + zend_hash_internal_pointer_reset(stmt->bound_param_map); + while (SUCCESS == zend_hash_get_current_data(stmt->bound_param_map, (void**)&name)) { + if (strcmp(name, param->name)) { + position++; + zend_hash_move_forward(stmt->bound_param_map); + continue; + } + if (param->paramno >= 0) { + pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "PDO refuses to handle repeating the same :named parameter for multiple positions with this driver, as it might be unsafe to do so. Consider using a separate name for each parameter instead" TSRMLS_CC); + return -1; + } + param->paramno = position; + return 1; + } + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC); + return 0; + } + return 1; +} + /* trigger callback hook for parameters */ static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type TSRMLS_DC) { @@ -83,14 +114,13 @@ static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_typ if (!stmt->methods->param_hook) { return 1; } - + ht = stmt->bound_params; iterate: if (ht) { zend_hash_internal_pointer_reset(ht); while (SUCCESS == zend_hash_get_current_data(ht, (void**)¶m)) { - if (!stmt->methods->param_hook(stmt, param, event_type TSRMLS_CC)) { ret = 0; break; @@ -141,6 +171,7 @@ int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC) } } +#if 0 /* update the column index on named bound parameters */ if (stmt->bound_params) { struct pdo_bound_param_data *param; @@ -150,6 +181,7 @@ int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC) param->paramno = col; } } +#endif if (stmt->bound_columns) { struct pdo_bound_param_data *param; @@ -253,6 +285,10 @@ static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_s zend_hash_index_update(hash, param->paramno, param, sizeof(*param), (void**)&pparam); } + if (!rewrite_name_to_position(stmt, pparam TSRMLS_CC)) { + return 0; + } + /* tell the driver we just created a parameter */ if (stmt->methods->param_hook) { return stmt->methods->param_hook(stmt, pparam, PDO_PARAM_EVT_ALLOC TSRMLS_CC); @@ -1232,6 +1268,10 @@ static void free_statement(pdo_stmt_t *stmt TSRMLS_DC) zend_hash_destroy(stmt->bound_params); FREE_HASHTABLE(stmt->bound_params); } + if (stmt->bound_param_map) { + zend_hash_destroy(stmt->bound_param_map); + FREE_HASHTABLE(stmt->bound_param_map); + } if (stmt->bound_columns) { zend_hash_destroy(stmt->bound_columns); FREE_HASHTABLE(stmt->bound_columns); diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 465be2e518..fea279afc7 100755 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -35,7 +35,7 @@ struct pdo_bound_param_data; # define FALSE 0 #endif -#define PDO_DRIVER_API 20050206 +#define PDO_DRIVER_API 20050213 enum pdo_param_type { PDO_PARAM_NULL, @@ -502,6 +502,8 @@ struct _pdo_stmt_t { /* keep track of bound input parameters. Some drivers support * input/output parameters, but you can't rely on that working */ HashTable *bound_params; + /* When rewriting from named to positional, this maps positions to names */ + HashTable *bound_param_map; /* keep track of PHP variables bound to named (or positional) columns * in the result set */ HashTable *bound_columns;