From: Wez Furlong Date: Fri, 8 Jul 2005 15:27:34 +0000 (+0000) Subject: Add early support for native prepared statements in pgsql. X-Git-Tag: php-5.1.0b3~91 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=529d8177fea4e759332d96ab5714273f4c006158;p=php Add early support for native prepared statements in pgsql. Note that some tests now fail; if we can't resolve this in time for the beta, the prepare code should be disabled (I'll add a flag for this later today). --- diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 index 7e334505b1..33f2709206 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -93,6 +93,9 @@ if test "$PHP_PDO_PGSQL" != "no"; then AC_CHECK_LIB(pq, PQExecParams,AC_DEFINE(HAVE_PQEXECPARAMS,1,[PostgreSQL 7.4 or later])) AC_CHECK_LIB(pq, PQresultErrorField,AC_DEFINE(HAVE_PQRESULTERRORFIELD,1,[PostgreSQL 7.4 or later])) AC_CHECK_LIB(pq, pg_encoding_to_char,AC_DEFINE(HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT,1,[Whether libpq is compiled with --enable-multibyte])) + + AC_CHECK_LIB(pq, PQprepare,AC_DEFINE(HAVE_PQPREPARE,1,[prepared statements])) + LIBS=$old_LIBS LDFLAGS=$old_LDFLAGS diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index bc33bdb733..4594da8b1a 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -132,19 +132,77 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); int scrollable; +#if HAVE_PQPREPARE + PGresult *res; + int ret; + char *nsql = NULL; + int nsql_len = 0; + ExecStatusType status; +#endif S->H = H; stmt->driver_data = S; stmt->methods = &pgsql_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL; if (scrollable) { + /* TODO: check how scrollable cursors related to prepared statements */ spprintf(&S->cursor_name, 0, "pdo_pgsql_cursor_%08x", (unsigned int) stmt); } +#if HAVE_PQPREPARE + stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; + stmt->named_rewrite_template = "$%d"; + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); + + if (ret == 1) { + /* query was re-written */ + sql = nsql; + } else if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } + + spprintf(&S->stmt_name, 0, "pdo_pgsql_stmt_%08x", (unsigned int)stmt); + res = PQprepare(H->server, S->stmt_name, sql, 0, NULL); + if (nsql) { + efree(nsql); + } + if (!res) { + pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); + return 0; + } + + /* check if the connection is using protocol version 2.0. + * if that is the reason that the prepare failed, we want to fall + * through and let PDO emulate it for us */ + status = PQresultStatus(res); + switch (status) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* it worked */ + return 1; + + case PGRES_BAD_RESPONSE: + /* server is probably too old; fall through and let + * PDO emulate it */ + efree(S->stmt_name); + S->stmt_name = NULL; + break; + + default: + /* protocol 3.0 and above; hard error */ + pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); + PQclear(res); + return 0; + } + /* fall through */ +#endif + + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; return 1; } diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index e7ad98ae2b..64767b0d26 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -49,6 +49,29 @@ static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) S->result = NULL; } +#if HAVE_PQPREPARE + if (S->stmt_name) { + pdo_pgsql_db_handle *H = S->H; + char *q = NULL; + PGresult *res; + + spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); + res = PQexec(H->server, q); + efree(q); + if (res) PQclear(res); + efree(S->stmt_name); + S->stmt_name = NULL; + } + if (S->param_lengths) { + efree(S->param_lengths); + S->param_lengths = NULL; + } + if (S->param_values) { + efree(S->param_values); + S->param_values = NULL; + } +#endif + if (S->cursor_name) { pdo_pgsql_db_handle *H = S->H; char *q = NULL; @@ -67,6 +90,7 @@ static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) S->cols = NULL; } efree(S); + stmt->driver_data = NULL; return 1; } @@ -86,6 +110,20 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) S->current_row = 0; +#if HAVE_PQPREPARE + if (S->stmt_name) { + /* using a prepared statement */ + + S->result = PQexecPrepared(H->server, S->stmt_name, + stmt->bound_params ? + zend_hash_num_elements(stmt->bound_params) : + 0, + (const char**)S->param_values, + S->param_lengths, + NULL, + 0); + } else +#endif if (S->cursor_name) { char *q = NULL; spprintf(&q, 0, "DECLARE %s CURSOR FOR %s", S->cursor_name, stmt->active_query_string); @@ -119,6 +157,44 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->stmt_name && param->is_param) { + switch (event_type) { + case PDO_PARAM_EVT_ALLOC: + /* decode name from $1, $2 into 0, 1 etc. */ + if (param->name) { + param->paramno = atoi(param->name + 1); + } + break; + + case PDO_PARAM_EVT_EXEC_PRE: + if (!S->param_values) { + S->param_values = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(char*)); + S->param_lengths = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(int)); + S->param_formats = ecalloc( + zend_hash_num_elements(stmt->bound_params), + sizeof(int)); + + } + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || + Z_TYPE_P(param->parameter) == IS_NULL) { + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + } else { + convert_to_string(param->parameter); + S->param_values[param->paramno] = Z_STRVAL_P(param->parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(param->parameter); + S->param_formats[param->paramno] = 1; + } + + break; + } + } return 1; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 565638f705..43633f4f63 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_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 | @@ -55,6 +55,12 @@ typedef struct { int current_row; pdo_pgsql_column *cols; char *cursor_name; +#if HAVE_PQPREPARE + char *stmt_name; + char **param_values; + int *param_lengths; + int *param_formats; +#endif } pdo_pgsql_stmt; typedef struct {