]> granicus.if.org Git - php/commitdiff
Add early support for native prepared statements in pgsql.
authorWez Furlong <wez@php.net>
Fri, 8 Jul 2005 15:27:34 +0000 (15:27 +0000)
committerWez Furlong <wez@php.net>
Fri, 8 Jul 2005 15:27:34 +0000 (15:27 +0000)
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).

ext/pdo_pgsql/config.m4
ext/pdo_pgsql/pgsql_driver.c
ext/pdo_pgsql/pgsql_statement.c
ext/pdo_pgsql/php_pdo_pgsql_int.h

index 7e334505b11bf948acf334144da9b1a8f83ccdc2..33f270920632ec246ad60f5be744a40868882ae5 100644 (file)
@@ -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
 
index bc33bdb733188b4b855ef0c23cc0225310794006..4594da8b1ada4f59c16b2bf2ddeb2409f49fce6e 100644 (file)
@@ -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;
 }
 
index e7ad98ae2b3d16db4a7de2f8879ef26ee8cc30da..64767b0d266bef666ba499df0fba88411193c0fa 100644 (file)
@@ -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;
 }
 
index 565638f70588f37d637e73093874f9f534d8116b..43633f4f639d890b1025af66c32d25008e6ba9d1 100644 (file)
@@ -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 {