From: Wez Furlong Date: Sun, 3 Jul 2005 02:20:08 +0000 (+0000) Subject: Enable native mysql 4.1.x prepared statement support X-Git-Tag: php-5.1.0b3~215 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d7e5dfb2def27dd3e9ec03b31bac1f6b705c365a;p=php Enable native mysql 4.1.x prepared statement support # the hardest part was installing 4.1.x on a gentoo box over a 56k modem --- diff --git a/ext/pdo_mysql/config.m4 b/ext/pdo_mysql/config.m4 index c38e970f2d..cbfe32f016 100755 --- a/ext/pdo_mysql/config.m4 +++ b/ext/pdo_mysql/config.m4 @@ -58,7 +58,7 @@ Note that the MySQL client library is not bundled anymore!]) _SAVE_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $PDO_MYSQL_LIBS" - AC_CHECK_FUNCS([mysql_commit mysql_stmt_prepare mysql_next_result]) + AC_CHECK_FUNCS([mysql_commit mysql_stmt_prepare mysql_next_result mysql_sqlstate]) LDFLAGS=$_SAVE_LDFLAGS PHP_CHECK_PDO_INCLUDES diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index a0f7ac07c1..3779d3d514 100755 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: George Schlossnagle | + | Wez Furlong | +----------------------------------------------------------------------+ */ @@ -46,9 +47,10 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; pdo_error_type *pdo_err; pdo_mysql_error_info *einfo; + pdo_mysql_stmt *S = NULL; if (stmt) { - pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; + S = (pdo_mysql_stmt*)stmt->driver_data; pdo_err = &stmt->error_code; einfo = &S->einfo; } else { @@ -69,14 +71,30 @@ int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int lin if (2014 != einfo->errcode) { einfo->errmsg = pestrdup(mysql_error(H->server), dbh->is_persistent); } else { - einfo->errmsg = pestrdup("Cannot execute queries, while other unbuffered queries are active. To enable query buffering set PDO_MYSQL_ATTR_USE_BUFFERED_QUERY attribute.", dbh->is_persistent); + einfo->errmsg = pestrdup( + "Cannot execute queries while other unbuffered queries are active. " + "Consider using PDOStatement::fetchAll(). Alternatively, if your code " + "is only ever going to run against mysql, you may enable query " + "buffering by setting the PDO_MYSQL_ATTR_USE_BUFFERED_QUERY attribute.", + dbh->is_persistent); } } else { /* no error */ strcpy(*pdo_err, PDO_ERR_NONE); return 0; } +#if HAVE_MYSQL_SQLSTATE +# if HAVE_MYSQL_STMT_PREPARE + if (S && S->stmt) { + strcpy(*pdo_err, mysql_stmt_sqlstate(S->stmt)); + } else +# endif + { + strcpy(*pdo_err, mysql_sqlstate(H->server)); + } +#else strcpy(*pdo_err, pdo_mysql_get_sqlstate(einfo->errcode)); +#endif if (!dbh->methods) { zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "SQLSTATE[%s] [%d] %s", @@ -131,13 +149,67 @@ static int mysql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; pdo_mysql_stmt *S = ecalloc(1, sizeof(pdo_mysql_stmt)); - +#if HAVE_MYSQL_STMT_PREPARE + char *nsql = NULL; + int nsql_len = 0; + int ret; +#endif + S->H = H; - S->result = NULL; - stmt->driver_data = S; stmt->methods = &mysql_stmt_methods; + + /* TODO: add runtime check to determine if the server we are talking to supports + * prepared statements; if it doesn't, we should set stmt->supports_placeholders + * to PDO_PLACEHOLDER_NONE, and have the rest of the code look at S->stmt to + * determine if we're using real prepared statements or the PDO emulated version */ +#if HAVE_MYSQL_STMT_PREPARE + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); + + if (ret == 1) { + /* query was rewritten */ + sql = nsql; + sql_len = nsql_len; + } else if (ret == -1) { + /* failed to parse */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } + + S->stmt = mysql_stmt_init(H->server); + if (!S->stmt) { + pdo_mysql_error(dbh); + if (nsql) { + efree(nsql); + } + return 0; + } + + if (mysql_stmt_prepare(S->stmt, sql, sql_len)) { + /* TODO: might need to pull statement specific info here? */ + pdo_mysql_error(dbh); + if (nsql) { + efree(nsql); + } + return 0; + } + + S->num_params = mysql_stmt_param_count(S->stmt); + + if (S->num_params) { + S->params = ecalloc(S->num_params, sizeof(MYSQL_BIND)); + S->in_null = ecalloc(S->num_params, sizeof(my_bool)); + S->in_length = ecalloc(S->num_params, sizeof(unsigned long)); + } + + dbh->alloc_own_columns = 1; + + return 1; + +#else stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; +#endif return 1; } diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index a88eb15b8d..31b26f4ed8 100755 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2004 The PHP Group | + | Copyright (c) 1997-2005 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: George Schlossnagle | + | Wez Furlong | +----------------------------------------------------------------------+ */ @@ -44,6 +45,22 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) efree(S->einfo.errmsg); S->einfo.errmsg = NULL; } +#if HAVE_MYSQL_STMT_PREPARE + if (S->stmt) { + mysql_stmt_close(S->stmt); + S->stmt = NULL; + } + if (S->params) { + efree(S->params); + efree(S->in_null); + efree(S->in_length); + } + if (S->bound_result) { + efree(S->bound_result); + efree(S->out_null); + efree(S->out_length); + } +#endif efree(S); return 1; } @@ -53,7 +70,52 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; pdo_mysql_db_handle *H = S->H; my_ulonglong row_count; +#if HAVE_MYSQL_STMT_PREPARE + int i; + if (S->stmt) { + /* (re)bind the parameters */ + if (mysql_stmt_bind_param(S->stmt, S->params)) { + pdo_mysql_error_stmt(stmt); + return 0; + } + + if (mysql_stmt_execute(S->stmt)) { + pdo_mysql_error_stmt(stmt); + return 0; + } + + if (!stmt->executed) { + /* figure out the result set format, if any */ + S->result = mysql_stmt_result_metadata(S->stmt); + if (S->result) { + S->fields = mysql_fetch_fields(S->result); + stmt->column_count = (int)mysql_num_fields(S->result); + + S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND)); + S->out_null = ecalloc(stmt->column_count, sizeof(my_bool)); + S->out_length = ecalloc(stmt->column_count, sizeof(unsigned long)); + + /* summon memory to hold the row */ + for (i = 0; i < stmt->column_count; i++) { + S->bound_result[i].buffer_length = S->fields[i].length; + S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length); + S->bound_result[i].is_null = &S->out_null[i]; + S->bound_result[i].length = &S->out_length[i]; + S->bound_result[i].buffer_type = MYSQL_TYPE_STRING; + } + + if (mysql_stmt_bind_result(S->stmt, S->bound_result)) { + pdo_mysql_error_stmt(stmt); + return 0; + } + } + } + + stmt->row_count = mysql_stmt_affected_rows(S->stmt); + return 1; + } +#endif /* ensure that we free any previous unfetched results */ if (S->result) { mysql_free_result(S->result); @@ -93,13 +155,12 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) return 1; } -#if HAVE_MYSQL_NEXT_RESULT static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) { +#if HAVE_MYSQL_NEXT_RESULT pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; pdo_mysql_db_handle *H = S->H; my_ulonglong row_count; - int debug=0; int ret; /* ensure that we free any previous unfetched results */ @@ -129,13 +190,79 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) S->fields = mysql_fetch_fields(S->result); return 1; } -} +#else + strcpy(stmt->error_code, "HYC00"); + return 0; #endif +} static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC) { +#if HAVE_MYSQL_STMT_PREPARE + pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; + MYSQL_BIND *b; + + if (S->stmt && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_ALLOC: + /* sanity check parameter number range */ + if (param->paramno < 0 || param->paramno >= S->num_params) { + strcpy(stmt->error_code, "HY093"); + return 0; + } + b = &S->params[param->paramno]; + param->driver_data = b; + b->is_null = &S->in_null[param->paramno]; + b->length = &S->in_length[param->paramno]; + return 1; + + case PDO_PARAM_EVT_EXEC_PRE: + b = (MYSQL_BIND*)param->driver_data; + + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(param->parameter) == IS_NULL) { + *b->is_null = 1; + b->buffer_type = MYSQL_TYPE_STRING; + b->buffer = NULL; + b->buffer_length = 0; + *b->length = 0; + return 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_LOB: + case PDO_PARAM_STMT: + return 0; + default: + ; + } + + switch (Z_TYPE_P(param->parameter)) { + case IS_STRING: + b->buffer_type = MYSQL_TYPE_STRING; + b->buffer = Z_STRVAL_P(param->parameter); + b->buffer_length = Z_STRLEN_P(param->parameter); + *b->length = Z_STRLEN_P(param->parameter); + return 1; + + case IS_LONG: + b->buffer_type = MYSQL_TYPE_LONG; + b->buffer = &Z_LVAL_P(param->parameter); + return 1; + + case IS_DOUBLE: + b->buffer_type = MYSQL_TYPE_DOUBLE; + b->buffer = &Z_DVAL_P(param->parameter); + return 1; + + default: + return 0; + } + } + } +#endif return 1; } @@ -143,6 +270,29 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; +#if HAVE_MYSQL_STMT_PREPARE + int ret; + + if (S->stmt) { + ret = mysql_stmt_fetch(S->stmt); + +#ifdef MYSQL_DATA_TRUNCATED + if (ret == MYSQL_DATA_TRUNCATED) { + ret = 0; + } +#endif + + if (ret) { + if (ret != MYSQL_NO_DATA) { + pdo_mysql_error_stmt(stmt); + } + return 0; + } + + return 1; + } +#endif + if (!S->result) { return 0; } @@ -177,13 +327,13 @@ static int pdo_mysql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) if (cols[0].name) { return 1; } - for(i=0; i < stmt->column_count; i++) { + for (i=0; i < stmt->column_count; i++) { int namelen; namelen = strlen(S->fields[i].name); cols[i].precision = S->fields[i].decimals; cols[i].maxlen = S->fields[i].length; cols[i].namelen = namelen; - cols[i].name = estrndup(S->fields[i].name, namelen + 1); + cols[i].name = estrndup(S->fields[i].name, namelen); cols[i].param_type = PDO_PARAM_STR; } return 1; @@ -193,13 +343,31 @@ static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsig { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; - if (S->current_data == NULL || !S->result) { - return 0; +#if HAVE_MYSQL_STMT_PREPARE + if (!S->stmt) { +#endif + if (S->current_data == NULL || !S->result) { + return 0; + } +#if HAVE_MYSQL_STMT_PREPARE } +#endif if (colno >= stmt->column_count) { /* error invalid column */ return 0; } +#if HAVE_MYSQL_STMT_PREPARE + if (S->stmt) { + if (S->out_null[colno]) { + *ptr = NULL; + *len = 0; + return 1; + } + *ptr = S->bound_result[colno].buffer; + *len = S->out_length[colno]; + return 1; + } +#endif *ptr = S->current_data[colno]; *len = S->current_lengths[colno]; return 1; @@ -297,9 +465,7 @@ struct pdo_stmt_methods mysql_stmt_methods = { NULL, /* set_attr */ NULL, /* get_attr */ pdo_mysql_stmt_col_meta, -#if HAVE_MYSQL_NEXT_RESULT pdo_mysql_stmt_next_rowset -#endif }; /* diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index d42c13f72d..136b2dbe35 100755 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2004 The PHP Group | + | Copyright (c) 1997-2005 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/pdo_mysql/php_pdo_mysql.h b/ext/pdo_mysql/php_pdo_mysql.h index 1fed28bd5e..0c71d47500 100755 --- a/ext/pdo_mysql/php_pdo_mysql.h +++ b/ext/pdo_mysql/php_pdo_mysql.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2004 The PHP Group | + | Copyright (c) 1997-2005 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h index 9a2c473a13..4f5a091c98 100755 --- a/ext/pdo_mysql/php_pdo_mysql_int.h +++ b/ext/pdo_mysql/php_pdo_mysql_int.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2004 The PHP Group | + | Copyright (c) 1997-2005 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: George Schlossnagle | + | Wez Furlong | +----------------------------------------------------------------------+ */ @@ -52,14 +53,20 @@ typedef struct { MYSQL_ROW current_data; long *current_lengths; pdo_mysql_error_info einfo; -} pdo_mysql_stmt; +#if HAVE_MYSQL_STMT_PREPARE + MYSQL_STMT *stmt; + + int num_params; + MYSQL_BIND *params; + my_bool *in_null; + unsigned long *in_length; + + MYSQL_BIND *bound_result; + my_bool *out_null; + unsigned long *out_length; -typedef struct { - char *repr; - long repr_len; - int mysql_type; - void *thing; /* for LOBS, REFCURSORS etc. */ -} pdo_mysql_bound_param; +#endif +} pdo_mysql_stmt; extern pdo_driver_t pdo_mysql_driver;