]> granicus.if.org Git - php/commitdiff
Enable native mysql 4.1.x prepared statement support
authorWez Furlong <wez@php.net>
Sun, 3 Jul 2005 02:20:08 +0000 (02:20 +0000)
committerWez Furlong <wez@php.net>
Sun, 3 Jul 2005 02:20:08 +0000 (02:20 +0000)
# the hardest part was installing 4.1.x on a gentoo box over a 56k modem

ext/pdo_mysql/config.m4
ext/pdo_mysql/mysql_driver.c
ext/pdo_mysql/mysql_statement.c
ext/pdo_mysql/pdo_mysql.c
ext/pdo_mysql/php_pdo_mysql.h
ext/pdo_mysql/php_pdo_mysql_int.h

index c38e970f2d0f3c925c58e1530ed20e27c9eca2b5..cbfe32f0164e2a758be19b20357f76f35b37cd72 100755 (executable)
@@ -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
index a0f7ac07c1239a28130187b5cde3359460a7b74e..3779d3d514ff568bfffc4352413fe6b740a0df08 100755 (executable)
@@ -13,6 +13,7 @@
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: George Schlossnagle <george@omniti.com>                      |
+  |         Wez Furlong <wez@php.net>                                    |
   +----------------------------------------------------------------------+
 */
 
@@ -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;
 }
index a88eb15b8d094f0f60bafd0b0f97b7bed51c805e..31b26f4ed84d8b9f3b7a16379c840e11e8b5aa62 100755 (executable)
@@ -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 <george@omniti.com>                      |
+  |         Wez Furlong <wez@php.net>                                    |
   +----------------------------------------------------------------------+
 */
 
@@ -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
 };
 
 /*
index d42c13f72da1d88174022d6e7a42e4316bdbae07..136b2dbe357d0bc15016ca2f23e6c5fe3306b91f 100755 (executable)
@@ -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        |
index 1fed28bd5e1d081ef7fd23f3605b5aaef00129c8..0c71d47500f7fc63fc10bec721dbed03172a6d15 100755 (executable)
@@ -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        |
index 9a2c473a1392d3cb921c02d632a1da49b8a8b89f..4f5a091c989dc9bcd5462c21ce95a2a15decf42d 100755 (executable)
@@ -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 <george@omniti.com>                      |
+  |         Wez Furlong <wez@php.net>                                    |
   +----------------------------------------------------------------------+
 */
 
@@ -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;