Back to index

php5  5.3.10
mysqlnd.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2006-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Georg Richter <georg@mysql.com>                             |
00016   |          Andrey Hristov <andrey@mysql.com>                           |
00017   |          Ulf Wendel <uwendel@mysql.com>                              |
00018   +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: mysqlnd.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 #include "php.h"
00023 #include "mysqlnd.h"
00024 #include "mysqlnd_wireprotocol.h"
00025 #include "mysqlnd_priv.h"
00026 #include "mysqlnd_result.h"
00027 #include "mysqlnd_statistics.h"
00028 #include "mysqlnd_charset.h"
00029 #include "mysqlnd_debug.h"
00030 /* for php_get_current_user() */
00031 #include "ext/standard/basic_functions.h"
00032 
00033 /*
00034   TODO :
00035   - Don't bind so tightly the metadata with the result set. This means
00036        that the metadata reading should not expect a MYSQLND_RES pointer, it
00037        does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
00038        For normal statements we will then just assign it to a member of
00039        MYSQLND_RES. For PS statements, it will stay as part of the statement
00040        (MYSQLND_STMT) between prepare and execute. At execute the new metadata
00041        will be sent by the server, so we will discard the old one and then
00042        finally attach it to the result set. This will make the code more clean,
00043        as a prepared statement won't have anymore stmt->result != NULL, as it
00044        is now, just to have where to store the metadata.
00045 
00046   - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
00047        terminated by a string with ptr being NULL. Thus, multi-part messages can be
00048        sent to the network like writev() and this can save at least for
00049        mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
00050        code in few other places cleaner.
00051 */
00052 
00053 extern MYSQLND_CHARSET *mysqlnd_charsets;
00054 
00055 
00056 
00057 PHPAPI const char * const mysqlnd_old_passwd  = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
00058 "Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
00059 "store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
00060 "flag from your my.cnf file";
00061 
00062 PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
00063 PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
00064 PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
00065 
00066 PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
00067 static zend_bool mysqlnd_library_initted = FALSE;
00068 
00069 static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods;
00070 
00071 /* {{{ mysqlnd_library_end */
00072 PHPAPI void mysqlnd_library_end(TSRMLS_D)
00073 {
00074        if (mysqlnd_library_initted == TRUE) {
00075               mysqlnd_stats_end(mysqlnd_global_stats);
00076               mysqlnd_global_stats = NULL;
00077               mysqlnd_library_initted = FALSE;
00078        }
00079 }
00080 /* }}} */
00081 
00082 
00083 /* {{{ mysqlnd_conn::free_options */
00084 static void
00085 MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND * conn TSRMLS_DC)
00086 {
00087        zend_bool pers = conn->persistent;
00088 
00089        if (conn->options.charset_name) {
00090               mnd_pefree(conn->options.charset_name, pers);
00091               conn->options.charset_name = NULL;
00092        }
00093        if (conn->options.num_commands) {
00094               unsigned int i;
00095               for (i = 0; i < conn->options.num_commands; i++) {
00096                      /* allocated with pestrdup */
00097                      mnd_pefree(conn->options.init_commands[i], pers);
00098               }
00099               mnd_pefree(conn->options.init_commands, pers);
00100               conn->options.init_commands = NULL;
00101        }
00102        if (conn->options.cfg_file) {
00103               mnd_pefree(conn->options.cfg_file, pers);
00104               conn->options.cfg_file = NULL;
00105        }
00106        if (conn->options.cfg_section) {
00107               mnd_pefree(conn->options.cfg_section, pers);
00108               conn->options.cfg_section = NULL;
00109        }
00110 }
00111 /* }}} */
00112 
00113 
00114 /* {{{ mysqlnd_conn::free_contents */
00115 static void
00116 MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC)
00117 {
00118        zend_bool pers = conn->persistent;
00119 
00120        DBG_ENTER("mysqlnd_conn::free_contents");
00121 
00122        mysqlnd_local_infile_default(conn);
00123        if (conn->current_result) {
00124               conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
00125               conn->current_result = NULL;
00126        }
00127 
00128        if (conn->net) {
00129               conn->net->m.free_contents(conn->net TSRMLS_CC);
00130        }
00131 
00132        DBG_INF("Freeing memory of members");
00133 
00134        if (conn->host) {
00135               DBG_INF("Freeing host");
00136               mnd_pefree(conn->host, pers);
00137               conn->host = NULL;
00138        }
00139        if (conn->user) {
00140               DBG_INF("Freeing user");
00141               mnd_pefree(conn->user, pers);
00142               conn->user = NULL;
00143        }
00144        if (conn->passwd) {
00145               DBG_INF("Freeing passwd");
00146               mnd_pefree(conn->passwd, pers);
00147               conn->passwd = NULL;
00148        }
00149        if (conn->connect_or_select_db) {
00150               DBG_INF("Freeing connect_or_select_db");
00151               mnd_pefree(conn->connect_or_select_db, pers);
00152               conn->connect_or_select_db = NULL;
00153        }
00154        if (conn->unix_socket) {
00155               DBG_INF("Freeing unix_socket");
00156               mnd_pefree(conn->unix_socket, pers);
00157               conn->unix_socket = NULL;
00158        }
00159        DBG_INF_FMT("scheme=%s", conn->scheme);
00160        if (conn->scheme) {
00161               DBG_INF("Freeing scheme");
00162               mnd_pefree(conn->scheme, pers);
00163               conn->scheme = NULL;
00164        }
00165        if (conn->server_version) {
00166               DBG_INF("Freeing server_version");
00167               mnd_pefree(conn->server_version, pers);
00168               conn->server_version = NULL;
00169        }
00170        if (conn->host_info) {
00171               DBG_INF("Freeing host_info");
00172               mnd_pefree(conn->host_info, pers);
00173               conn->host_info = NULL;
00174        }
00175        if (conn->scramble) {
00176               DBG_INF("Freeing scramble");
00177               mnd_pefree(conn->scramble, pers);
00178               conn->scramble = NULL;
00179        }
00180        if (conn->last_message) {
00181               mnd_pefree(conn->last_message, pers);
00182               conn->last_message = NULL;
00183        }
00184        conn->charset = NULL;
00185        conn->greet_charset = NULL;
00186 
00187        DBG_VOID_RETURN;
00188 }
00189 /* }}} */
00190 
00191 
00192 /* {{{ mysqlnd_conn::dtor */
00193 static void
00194 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
00195 {
00196        DBG_ENTER("mysqlnd_conn::dtor");
00197        DBG_INF_FMT("conn=%llu", conn->thread_id);
00198 
00199        conn->m->free_contents(conn TSRMLS_CC);
00200        conn->m->free_options(conn TSRMLS_CC);
00201 
00202        if (conn->net) {
00203               DBG_INF("Freeing net");
00204               mysqlnd_net_free(conn->net TSRMLS_CC);
00205               conn->net = NULL;
00206        }
00207 
00208        if (conn->protocol) {
00209               DBG_INF("Freeing protocol");
00210               mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
00211               conn->protocol = NULL;
00212        }
00213 
00214        if (conn->stats) {
00215               mysqlnd_stats_end(conn->stats);
00216        }
00217 
00218        mnd_pefree(conn, conn->persistent);
00219 
00220        DBG_VOID_RETURN;
00221 }
00222 /* }}} */
00223 
00224 
00225 /* {{{ mysqlnd_conn::simple_command_handle_response */
00226 static enum_func_status
00227 MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND * conn, enum mysqlnd_packet_type ok_packet,
00228                                                                                                           zend_bool silent, enum php_mysqlnd_server_command command,
00229                                                                                                           zend_bool ignore_upsert_status TSRMLS_DC)
00230 {
00231        enum_func_status ret = FAIL;
00232 
00233        DBG_ENTER("mysqlnd_conn::simple_command_handle_response");
00234        DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
00235 
00236        switch (ok_packet) {
00237               case PROT_OK_PACKET:{
00238                      MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
00239                      if (!ok_response) {
00240                             SET_OOM_ERROR(conn->error_info);
00241                             break;
00242                      }
00243                      if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
00244                             if (!silent) {
00245                                    DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
00246                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u",
00247                                                                 mysqlnd_command_to_text[command], getpid());
00248                             }
00249                      } else {
00250                             DBG_INF_FMT("OK from server");
00251                             if (0xFF == ok_response->field_count) {
00252                                    /* The server signalled error. Set the error */
00253                                    SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
00254                                    ret = FAIL;
00255                                    /*
00256                                      Cover a protocol design error: error packet does not
00257                                      contain the server status. Therefore, the client has no way
00258                                      to find out whether there are more result sets of
00259                                      a multiple-result-set statement pending. Luckily, in 5.0 an
00260                                      error always aborts execution of a statement, wherever it is
00261                                      a multi-statement or a stored procedure, so it should be
00262                                      safe to unconditionally turn off the flag here.
00263                                    */
00264                                    conn->upsert_status.server_status &= ~SERVER_MORE_RESULTS_EXISTS;
00265                                    SET_ERROR_AFF_ROWS(conn);
00266                             } else {
00267                                    SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
00268                                                                ok_response->message, ok_response->message_len,
00269                                                                conn->persistent);
00270 
00271                                    if (!ignore_upsert_status) {
00272                                           conn->upsert_status.warning_count = ok_response->warning_count;
00273                                           conn->upsert_status.server_status = ok_response->server_status;
00274                                           conn->upsert_status.affected_rows = ok_response->affected_rows;
00275                                           conn->upsert_status.last_insert_id = ok_response->last_insert_id;
00276                                    }
00277                             }
00278                      }
00279                      PACKET_FREE(ok_response);
00280                      break;
00281               }
00282               case PROT_EOF_PACKET:{
00283                      MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
00284                      if (!ok_response) {
00285                             SET_OOM_ERROR(conn->error_info);
00286                             break;
00287                      }
00288                      if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
00289                             SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
00290                                                          "Malformed packet");
00291                             if (!silent) {
00292                                    DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
00293                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
00294                                                                 mysqlnd_command_to_text[command], getpid());
00295                             }
00296                      } else if (0xFF == ok_response->field_count) {
00297                             /* The server signalled error. Set the error */
00298                             SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
00299                             SET_ERROR_AFF_ROWS(conn);
00300                      } else if (0xFE != ok_response->field_count) {
00301                             SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
00302                             if (!silent) {
00303                                    DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
00304                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
00305                                                                ok_response->field_count);
00306                             }
00307                      } else {
00308                             DBG_INF_FMT("OK from server");
00309                      }
00310                      PACKET_FREE(ok_response);
00311                      break;
00312               }
00313               default:
00314                      SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
00315                      php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
00316                      break;
00317        }
00318        DBG_INF(ret == PASS ? "PASS":"FAIL");
00319        DBG_RETURN(ret);
00320 }
00321 /* }}} */
00322 
00323 
00324 /* {{{ mysqlnd_conn::simple_command */
00325 static enum_func_status
00326 MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND * conn, enum php_mysqlnd_server_command command,
00327                         const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
00328                         zend_bool ignore_upsert_status TSRMLS_DC)
00329 {
00330        enum_func_status ret = PASS;
00331        MYSQLND_PACKET_COMMAND * cmd_packet;
00332 
00333        DBG_ENTER("mysqlnd_conn::simple_command");
00334        DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent);
00335 
00336        switch (CONN_GET_STATE(conn)) {
00337               case CONN_READY:
00338                      break;
00339               case CONN_QUIT_SENT:
00340                      SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
00341                      DBG_ERR("Server is gone");
00342                      DBG_RETURN(FAIL);
00343               default:
00344                      SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
00345                      DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
00346                      DBG_RETURN(FAIL);
00347        }
00348 
00349        /* clean UPSERT info */
00350        if (!ignore_upsert_status) {
00351               memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
00352        }
00353        SET_ERROR_AFF_ROWS(conn);
00354        SET_EMPTY_ERROR(conn->error_info);
00355 
00356        cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC);
00357        if (!cmd_packet) {
00358               SET_OOM_ERROR(conn->error_info);
00359               DBG_RETURN(FAIL);
00360        }
00361 
00362        cmd_packet->command = command;
00363        if (arg && arg_len) {
00364               cmd_packet->argument = arg;
00365               cmd_packet->arg_len  = arg_len;
00366        }
00367 
00368        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
00369 
00370        if (! PACKET_WRITE(cmd_packet, conn)) {
00371               if (!silent) {
00372                      DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
00373                      php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
00374               }
00375               DBG_ERR("Server is gone");
00376               ret = FAIL;
00377        } else if (ok_packet != PROT_LAST) {
00378               ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
00379        }
00380 
00381        PACKET_FREE(cmd_packet);
00382        DBG_INF(ret == PASS ? "PASS":"FAIL");
00383        DBG_RETURN(ret);
00384 }
00385 /* }}} */
00386 
00387 
00388 /* {{{ mysqlnd_conn::set_server_option */
00389 static enum_func_status
00390 MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
00391 {
00392        enum_func_status ret;
00393        char buffer[2];
00394        DBG_ENTER("mysqlnd_conn::set_server_option");
00395 
00396        int2store(buffer, (unsigned int) option);
00397        ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
00398        DBG_RETURN(ret);
00399 }
00400 /* }}} */
00401 
00402 
00403 /* {{{ mysqlnd_conn::restart_psession */
00404 static enum_func_status
00405 MYSQLND_METHOD(mysqlnd_conn, restart_psession)(MYSQLND * conn TSRMLS_DC)
00406 {
00407        DBG_ENTER("mysqlnd_conn::restart_psession");
00408        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
00409        /* Free here what should not be seen by the next script */
00410        if (conn->last_message) {
00411               mnd_pefree(conn->last_message, conn->persistent);
00412               conn->last_message = NULL;
00413        }
00414        DBG_RETURN(PASS);
00415 }
00416 /* }}} */
00417 
00418 
00419 /* {{{ mysqlnd_conn::end_psession */
00420 static enum_func_status
00421 MYSQLND_METHOD(mysqlnd_conn, end_psession)(MYSQLND * conn TSRMLS_DC)
00422 {
00423        DBG_ENTER("mysqlnd_conn::end_psession");
00424        DBG_RETURN(PASS);
00425 }
00426 /* }}} */
00427 
00428 
00429 #define MYSQLND_ASSEBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL
00430 /* {{{ mysqlnd_connect_run_authentication */
00431 static enum_func_status
00432 mysqlnd_connect_run_authentication(
00433                      MYSQLND * conn,
00434                      const char * const user,
00435                      const char * const passwd,
00436                      const char * const db,
00437                      size_t db_len,
00438                      const MYSQLND_PACKET_GREET * const greet_packet,
00439                      const MYSQLND_OPTIONS * const options,
00440                      unsigned long mysql_flags
00441                      TSRMLS_DC)
00442 {
00443        const MYSQLND_CHARSET * charset = NULL;
00444        enum_func_status ret = FAIL;
00445        MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
00446        MYSQLND_PACKET_OK * ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
00447 
00448        DBG_ENTER("mysqlnd_connect_run_authentication");
00449 
00450        if (!auth_packet || !ok_packet) {
00451               SET_OOM_ERROR(conn->error_info);
00452               goto err;
00453        }
00454 
00455 #ifdef MYSQLND_SSL_SUPPORTED
00456        if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) {
00457               auth_packet->send_half_packet = TRUE;
00458        }
00459 #endif
00460        auth_packet->user           = user;
00461        auth_packet->password       = passwd;
00462 
00463        if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
00464               auth_packet->charset_no     = charset->nr;
00465        } else {
00466 #if MYSQLND_UNICODE
00467               auth_packet->charset_no     = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
00468 #else
00469               auth_packet->charset_no     = greet_packet->charset_no;
00470 #endif
00471        }
00472        auth_packet->db                    = db;
00473        auth_packet->db_len         = db_len;
00474        auth_packet->max_packet_size= MYSQLND_ASSEBLED_PACKET_MAX_SIZE;
00475        auth_packet->client_flags= mysql_flags;
00476 
00477        conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent);
00478        if (!conn->scramble) {
00479               SET_OOM_ERROR(conn->error_info);
00480               goto err;
00481        }
00482        memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH);
00483 
00484        if (!PACKET_WRITE(auth_packet, conn)) {
00485               CONN_SET_STATE(conn, CONN_QUIT_SENT);
00486               SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
00487               goto err;
00488        }
00489 
00490 #ifdef MYSQLND_SSL_SUPPORTED
00491        if (auth_packet->send_half_packet) {
00492               zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE;
00493               DBG_INF("Switching to SSL");
00494 
00495               conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
00496 
00497               if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) {
00498                      goto err;
00499               }
00500 
00501               auth_packet->send_half_packet = FALSE;
00502               if (!PACKET_WRITE(auth_packet, conn)) {
00503                      CONN_SET_STATE(conn, CONN_QUIT_SENT);
00504                      SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
00505                      goto err;
00506               }
00507        }
00508 #endif
00509 
00510 
00511        if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) {
00512               if (ok_packet->field_count == 0xFE) {
00513                      /* old authentication with new server  !*/
00514                      DBG_ERR(mysqlnd_old_passwd);
00515                      SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
00516               } else if (ok_packet->field_count == 0xFF) {
00517                      if (ok_packet->sqlstate[0]) {
00518                             strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate));
00519                             DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error);
00520                      }
00521                      conn->error_info.error_no = ok_packet->error_no;
00522                      strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error));
00523               }
00524               goto err;
00525        }
00526 
00527        SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
00528                                    ok_packet->message, ok_packet->message_len,
00529                                    conn->persistent);
00530        conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
00531        ret = PASS;
00532 err:
00533        PACKET_FREE(auth_packet);
00534        PACKET_FREE(ok_packet);
00535        DBG_RETURN(ret);
00536 }
00537 /* }}} */
00538 
00539 
00540 #define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
00541                             CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
00542                             CLIENT_MULTI_RESULTS)
00543 
00544 
00545 
00546 /* {{{ mysqlnd_conn::connect */
00547 static enum_func_status
00548 MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn,
00549                                            const char *host, const char *user,
00550                                            const char *passwd, unsigned int passwd_len,
00551                                            const char *db, unsigned int db_len,
00552                                            unsigned int port,
00553                                            const char * socket_or_pipe,
00554                                            unsigned int mysql_flags
00555                                            TSRMLS_DC)
00556 {
00557        char *errstr = NULL;
00558        int errcode = 0, host_len;
00559        zend_bool unix_socket = FALSE;
00560        zend_bool reconnect = FALSE;
00561        zend_bool saved_compression = FALSE;
00562 
00563        MYSQLND_PACKET_GREET * greet_packet = NULL;
00564 
00565        DBG_ENTER("mysqlnd_conn::connect");
00566 
00567        DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
00568                             host?host:"", user?user:"", db?db:"", port, mysql_flags,
00569                             conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
00570 
00571        if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
00572               DBG_INF("Connecting on a connected handle.");
00573 
00574               if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
00575                      MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
00576                      reconnect = TRUE;
00577                      conn->m->send_close(conn TSRMLS_CC);
00578               }
00579 
00580               conn->m->free_contents(conn TSRMLS_CC);
00581               MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
00582               if (conn->persistent) {
00583                      MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
00584               }
00585               /* Now reconnect using the same handle */
00586               if (conn->net->compressed) {
00587                      /*
00588                        we need to save the state. As we will re-connect, net->compressed should be off, or
00589                        we will look for a compression header as part of the greet message, but there will
00590                        be none.
00591                      */
00592                      saved_compression = TRUE;
00593                      conn->net->compressed = FALSE;
00594               }
00595        }
00596 
00597        if (!host || !host[0]) {
00598               host = "localhost";
00599        }
00600        if (!user) {
00601               DBG_INF_FMT("no user given, using empty string");
00602               user = "";
00603        }
00604        if (!passwd) {
00605               DBG_INF_FMT("no password given, using empty string");
00606               passwd = "";
00607               passwd_len = 0;
00608        }
00609        if (!db) {
00610               DBG_INF_FMT("no db given, using empty string");
00611               db = "";
00612               db_len = 0;
00613        }
00614 
00615        host_len = strlen(host);
00616        {
00617               char * transport = NULL;
00618               int transport_len;
00619 #ifndef PHP_WIN32
00620               if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
00621                      DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
00622                      if (!socket_or_pipe) {
00623                             socket_or_pipe = "/tmp/mysql.sock";
00624                      }
00625                      transport_len = spprintf(&transport, 0, "unix://%s", socket_or_pipe);
00626                      unix_socket = TRUE;
00627               } else
00628 #endif
00629               {
00630                      if (!port) {
00631                             port = 3306;
00632                      }
00633                      transport_len = spprintf(&transport, 0, "tcp://%s:%u", host, port);
00634               }
00635               if (!transport) {
00636                      SET_OOM_ERROR(conn->error_info);
00637                      goto err; /* OOM */
00638               }
00639               DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
00640               conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
00641               conn->scheme_len = transport_len;
00642               efree(transport); /* allocated by spprintf */
00643               transport = NULL;
00644               if (!conn->scheme) {
00645                      goto err; /* OOM */
00646               }
00647        }
00648 
00649        greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
00650        if (!greet_packet) {
00651               SET_OOM_ERROR(conn->error_info);
00652               goto err; /* OOM */
00653        }
00654 
00655        if (FAIL == conn->net->m.connect(conn->net, conn->scheme, conn->scheme_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) {
00656               goto err;
00657        }
00658 
00659        DBG_INF_FMT("stream=%p", conn->net->stream);
00660 
00661        if (FAIL == PACKET_READ(greet_packet, conn)) {
00662               DBG_ERR("Error while reading greeting packet");
00663               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
00664               goto err;
00665        } else if (greet_packet->error_no) {
00666               DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
00667               SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
00668               goto err;
00669        } else if (greet_packet->pre41) {
00670               DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
00671               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
00672                                           " is not supported. Server is %-.32s", greet_packet->server_version);
00673               SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
00674                                            "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
00675               goto err;
00676        }
00677 
00678        conn->thread_id                    = greet_packet->thread_id;
00679        conn->protocol_version      = greet_packet->protocol_version;
00680        conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
00681 
00682        conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
00683        /* we allow load data local infile by default */
00684        mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS;
00685        mysql_flags |= MYSQLND_CAPABILITIES;
00686 
00687        if (db) {
00688               mysql_flags |= CLIENT_CONNECT_WITH_DB;
00689        }
00690 
00691        if (PG(open_basedir) && strlen(PG(open_basedir))) {
00692               mysql_flags ^= CLIENT_LOCAL_FILES;
00693        }
00694 
00695 #ifndef MYSQLND_COMPRESSION_ENABLED
00696        if (mysql_flags & CLIENT_COMPRESS) {
00697               mysql_flags &= ~CLIENT_COMPRESS;
00698        }
00699 #else
00700        if (conn->net->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
00701               mysql_flags |= CLIENT_COMPRESS;
00702        }
00703 #endif
00704 #ifndef MYSQLND_SSL_SUPPORTED
00705        if (mysql_flags & CLIENT_SSL) {
00706               mysql_flags &= ~CLIENT_SSL;
00707        }
00708 #else
00709        if (conn->net->options.ssl_key || conn->net->options.ssl_cert ||
00710               conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher)
00711        {
00712               mysql_flags |= CLIENT_SSL;
00713        }
00714 #endif
00715 
00716        if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, greet_packet, &conn->options, mysql_flags TSRMLS_CC)) {
00717               goto err;
00718        }
00719 
00720        {
00721               CONN_SET_STATE(conn, CONN_READY);
00722 
00723               if (saved_compression) {
00724                      conn->net->compressed = TRUE;
00725               }
00726               /*
00727                 If a connect on a existing handle is performed and mysql_flags is
00728                 passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
00729                 which we set based on saved_compression.
00730               */
00731               conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
00732 
00733               conn->user                         = mnd_pestrdup(user, conn->persistent);
00734               conn->user_len                     = strlen(conn->user);
00735               conn->passwd                = mnd_pestrndup(passwd, passwd_len, conn->persistent);
00736               conn->passwd_len            = passwd_len;
00737               conn->port                         = port;
00738               conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
00739               conn->connect_or_select_db_len = db_len;
00740 
00741               if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
00742                      SET_OOM_ERROR(conn->error_info);
00743                      goto err; /* OOM */
00744               }
00745 
00746               if (!unix_socket) {
00747                      conn->host = mnd_pestrdup(host, conn->persistent);
00748                      if (!conn->host) {
00749                             SET_OOM_ERROR(conn->error_info);
00750                             goto err; /* OOM */
00751                      }
00752                      conn->host_len = strlen(conn->host);
00753                      {
00754                             char *p;
00755                             spprintf(&p, 0, "%s via TCP/IP", conn->host);
00756                             if (!p) {
00757                                    SET_OOM_ERROR(conn->error_info);
00758                                    goto err; /* OOM */
00759                             }
00760                             conn->host_info =  mnd_pestrdup(p, conn->persistent);
00761                             efree(p); /* allocated by spprintf */
00762                             if (!conn->host_info) {
00763                                    SET_OOM_ERROR(conn->error_info);
00764                                    goto err; /* OOM */
00765                             }
00766                      }
00767               } else {
00768                      conn->unix_socket    = mnd_pestrdup(socket_or_pipe, conn->persistent);
00769                      conn->host_info             = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
00770                      if (!conn->unix_socket || !conn->host_info) {
00771                             SET_OOM_ERROR(conn->error_info);
00772                             goto err; /* OOM */
00773                      }
00774                      conn->unix_socket_len = strlen(conn->unix_socket);
00775               }
00776               conn->client_flag           = mysql_flags;
00777               conn->max_packet_size       = MYSQLND_ASSEBLED_PACKET_MAX_SIZE;
00778               /* todo: check if charset is available */
00779               conn->server_capabilities = greet_packet->server_capabilities;
00780               conn->upsert_status.warning_count = 0;
00781               conn->upsert_status.server_status = greet_packet->server_status;
00782               conn->upsert_status.affected_rows = 0;
00783 
00784               SET_EMPTY_ERROR(conn->error_info);
00785 
00786               mysqlnd_local_infile_default(conn);
00787 
00788 #if MYSQLND_UNICODE
00789               {
00790                      unsigned int as_unicode = 1;
00791                      conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC);
00792                      DBG_INF("unicode set");
00793               }
00794 #endif
00795               if (conn->options.init_commands) {
00796                      unsigned int current_command = 0;
00797                      for (; current_command < conn->options.num_commands; ++current_command) {
00798                             const char * const command = conn->options.init_commands[current_command];
00799                             if (command) {
00800                                    MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
00801                                    if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
00802                                           MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
00803                                           goto err;
00804                                    }
00805                                    if (conn->last_query_type == QUERY_SELECT) {
00806                                           MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC);
00807                                           if (result) {
00808                                                  result->m.free_result(result, TRUE TSRMLS_CC);
00809                                           }
00810                                    }
00811                             }
00812                      }
00813               }
00814 
00815 
00816               MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
00817               if (reconnect) {
00818                      MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
00819               }
00820               if (conn->persistent) {
00821                      MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
00822               }
00823 
00824               DBG_INF_FMT("connection_id=%llu", conn->thread_id);
00825 
00826               PACKET_FREE(greet_packet);
00827 
00828               DBG_RETURN(PASS);
00829        }
00830 err:
00831        PACKET_FREE(greet_packet);
00832 
00833        if (errstr) {
00834               DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme);
00835               SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
00836               php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme);
00837               /* no mnd_ since we don't allocate it */
00838               efree(errstr);
00839        }
00840        conn->m->free_contents(conn TSRMLS_CC);
00841        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
00842 
00843        DBG_RETURN(FAIL);
00844 }
00845 /* }}} */
00846 
00847 
00848 /* {{{ mysqlnd_connect */
00849 PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
00850                                            const char *host, const char *user,
00851                                            const char *passwd, unsigned int passwd_len,
00852                                            const char *db, unsigned int db_len,
00853                                            unsigned int port,
00854                                            const char *socket_or_pipe,
00855                                            unsigned int mysql_flags
00856                                            TSRMLS_DC)
00857 {
00858        enum_func_status ret = FAIL;
00859        zend_bool self_alloced = FALSE;
00860 
00861        DBG_ENTER("mysqlnd_connect");
00862        DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
00863 
00864        if (!conn) {
00865               self_alloced = TRUE;
00866               if (!(conn = mysqlnd_init(FALSE))) {
00867                      /* OOM */
00868                      DBG_RETURN(NULL);
00869               }
00870        }
00871 
00872        ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
00873 
00874        if (ret == FAIL) {
00875               if (self_alloced) {
00876                      /*
00877                        We have alloced, thus there are no references to this
00878                        object - we are free to kill it!
00879                      */
00880                      conn->m->dtor(conn TSRMLS_CC);
00881               }
00882               DBG_RETURN(NULL);
00883        }
00884        DBG_RETURN(conn);
00885 }
00886 /* }}} */
00887 
00888 
00889 /* {{{ mysqlnd_conn::change_user */
00890 static enum_func_status
00891 MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
00892                                                                         const char *user,
00893                                                                         const char *passwd,
00894                                                                         const char *db,
00895                                                                         zend_bool silent TSRMLS_DC)
00896 {
00897        size_t user_len;
00898        enum_func_status ret = FAIL;
00899        MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp;
00900        char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ + 2];
00901        char *p = buffer;
00902        const MYSQLND_CHARSET * old_cs = conn->charset;
00903 
00904        DBG_ENTER("mysqlnd_conn::change_user");
00905        DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
00906                             conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
00907 
00908        SET_ERROR_AFF_ROWS(conn);
00909 
00910        if (!user) {
00911               user = "";
00912        }
00913        if (!passwd) {
00914               passwd = "";
00915        }
00916        if (!db) {
00917               db = "";
00918        }
00919 
00920        /* 1. user ASCIIZ */
00921        user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN);
00922        memcpy(p, user, user_len);
00923        p += user_len;
00924        *p++ = '\0';
00925 
00926        /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */
00927        if (passwd[0]) {
00928               *p++ = SCRAMBLE_LENGTH;
00929               php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd);
00930               p += SCRAMBLE_LENGTH;
00931        } else {
00932               *p++ = '\0';
00933        }
00934 
00935        /* 3. db ASCIIZ */
00936        if (db[0]) {
00937               size_t db_len = MIN(strlen(db), MYSQLND_MAX_ALLOWED_DB_LEN);
00938               memcpy(p, db, db_len);
00939               p += db_len;
00940        }
00941        *p++ = '\0';
00942 
00943        /*
00944          4. request the current charset, or it will be reset to the system one.
00945          5.0 doesn't support it. Support added in 5.1.23 by fixing the following bug : 
00946          Bug #30472 libmysql doesn't reset charset, insert_id after succ. mysql_change_user() call
00947        */
00948        if (mysqlnd_get_server_version(conn) >= 50123) {
00949               int2store(p, conn->charset->nr);
00950               p+=2;
00951        }
00952 
00953        if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer, p - buffer,
00954                                                                   PROT_LAST /* we will handle the OK packet*/,
00955                                                                   silent, TRUE TSRMLS_CC)) {
00956               DBG_RETURN(FAIL);
00957        }
00958 
00959        chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
00960        if (!chg_user_resp) {
00961               SET_OOM_ERROR(conn->error_info);
00962               goto end;
00963        }
00964        ret = PACKET_READ(chg_user_resp, conn);
00965        conn->error_info = chg_user_resp->error_info;
00966 
00967        if (conn->error_info.error_no) {
00968               ret = FAIL;
00969               /*
00970                 COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
00971                 bug#25371 mysql_change_user() triggers "packets out of sync"
00972                 When it gets fixed, there should be one more check here
00973               */
00974               if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) {
00975                      MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
00976                      if (redundant_error_packet) {
00977                             PACKET_READ(redundant_error_packet, conn);
00978                             PACKET_FREE(redundant_error_packet);
00979                             DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
00980                      } else {
00981                             SET_OOM_ERROR(conn->error_info);
00982                      }
00983               }
00984        }
00985        if (ret == PASS) {
00986               char * tmp = NULL;
00987               /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
00988               tmp = mnd_pestrndup(user, user_len, conn->persistent);
00989               if (conn->user) {
00990                      mnd_pefree(conn->user, conn->persistent);
00991               }
00992               conn->user = tmp;
00993 
00994               tmp = mnd_pestrdup(passwd, conn->persistent);
00995               if (conn->passwd) {
00996                      mnd_pefree(conn->passwd, conn->persistent);
00997               }
00998               conn->passwd = tmp;
00999 
01000               if (conn->last_message) {
01001                      mnd_pefree(conn->last_message, conn->persistent);
01002                      conn->last_message = NULL;
01003               }
01004               memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
01005               /* set charset for old servers */
01006               if (mysqlnd_get_server_version(conn) < 50123) {
01007                      ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
01008               }
01009        } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
01010               /* old authentication with new server  !*/
01011               DBG_ERR(mysqlnd_old_passwd);
01012               SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
01013        }
01014 end:
01015        PACKET_FREE(chg_user_resp);
01016 
01017        /*
01018          Here we should close all statements. Unbuffered queries should not be a
01019          problem as we won't allow sending COM_CHANGE_USER.
01020        */
01021        DBG_INF(ret == PASS? "PASS":"FAIL");
01022        DBG_RETURN(ret);
01023 }
01024 /* }}} */
01025 
01026 
01027 /* {{{ mysqlnd_conn::query */
01028 /*
01029   If conn->error_info.error_no is not zero, then we had an error.
01030   Still the result from the query is PASS
01031 */
01032 static enum_func_status
01033 MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
01034 {
01035        enum_func_status ret = FAIL;
01036        DBG_ENTER("mysqlnd_conn::query");
01037        DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
01038 
01039        if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
01040               PASS == conn->m->reap_query(conn TSRMLS_CC))
01041        {
01042               ret = PASS;
01043               if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
01044                      MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
01045               }
01046        }
01047        DBG_RETURN(ret);
01048 }
01049 /* }}} */
01050 
01051 
01052 /* {{{ mysqlnd_conn::send_query */
01053 static enum_func_status
01054 MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
01055 {
01056        enum_func_status ret;
01057        DBG_ENTER("mysqlnd_conn::send_query");
01058        DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
01059 
01060        ret = conn->m->simple_command(conn, COM_QUERY, query, query_len,
01061                                                          PROT_LAST /* we will handle the OK packet*/,
01062                                                          FALSE, FALSE TSRMLS_CC);
01063        if (PASS == ret) {
01064               CONN_SET_STATE(conn, CONN_QUERY_SENT);
01065        }
01066        DBG_RETURN(ret);
01067 }
01068 /* }}} */
01069 
01070 
01071 /* {{{ mysqlnd_conn::reap_query */
01072 static enum_func_status
01073 MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC)
01074 {
01075        enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
01076        DBG_ENTER("mysqlnd_conn::reap_query");
01077        DBG_INF_FMT("conn=%llu", conn->thread_id);
01078 
01079        if (state <= CONN_READY || state == CONN_QUIT_SENT) {
01080               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
01081               DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
01082               DBG_RETURN(FAIL);
01083        }
01084        /*
01085          Here read the result set. We don't do it in simple_command because it need
01086          information from the ok packet. We will fetch it ourselves.
01087        */
01088        DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC));
01089 }
01090 /* }}} */
01091 
01092 
01093 #include "php_network.h"
01094 
01095 MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
01096 {
01097        int cnt = 0;
01098        MYSQLND **p = conn_array, **p_p;
01099        MYSQLND **ret = NULL;
01100 
01101        while (*p) {
01102               if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
01103                      cnt++;
01104               }
01105               p++;
01106        }
01107        if (cnt) {
01108               MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
01109               p_p = p = conn_array;
01110               while (*p) {
01111                      if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
01112                             *ret_p = *p;
01113                             *p = NULL;
01114                             ret_p++;
01115                      } else {
01116                             *p_p = *p;
01117                             p_p++;
01118                      }
01119                      p++;
01120               }
01121               *ret_p = NULL;
01122        }
01123        return ret;
01124 }
01125 
01126 
01127 /* {{{ stream_select mysqlnd_stream_array_to_fd_set functions */
01128 static int mysqlnd_stream_array_to_fd_set(MYSQLND **conn_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
01129 {
01130        php_socket_t this_fd;
01131        int cnt = 0;
01132        MYSQLND **p = conn_array;
01133 
01134        while (*p) {
01135               /* get the fd.
01136                * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
01137                * when casting.  It is only used here so that the buffered data warning
01138                * is not displayed.
01139                * */
01140               if (SUCCESS == php_stream_cast((*p)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
01141                                                                       (void*)&this_fd, 1) && this_fd >= 0) {
01142 
01143                      PHP_SAFE_FD_SET(this_fd, fds);
01144 
01145                      if (this_fd > *max_fd) {
01146                             *max_fd = this_fd;
01147                      }
01148                      cnt++;
01149               }
01150               p++;
01151        }
01152        return cnt ? 1 : 0;
01153 }
01154 
01155 static int mysqlnd_stream_array_from_fd_set(MYSQLND **conn_array, fd_set *fds TSRMLS_DC)
01156 {
01157        php_socket_t this_fd;
01158        int ret = 0;
01159        zend_bool disproportion = FALSE;
01160 
01161 
01162        MYSQLND **fwd = conn_array, **bckwd = conn_array;
01163 
01164        while (*fwd) {
01165               if (SUCCESS == php_stream_cast((*fwd)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
01166                                                                       (void*)&this_fd, 1) && this_fd >= 0) {
01167                      if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
01168                             if (disproportion) {
01169                                    *bckwd = *fwd;
01170                             }
01171                             bckwd++;
01172                             fwd++;
01173                             ret++;
01174                             continue;
01175                      }
01176               }
01177               disproportion = TRUE;
01178               fwd++;
01179        }
01180        *bckwd = NULL;/* NULL-terminate the list */
01181 
01182        return ret;
01183 }
01184 /* }}} */
01185 
01186 #ifndef PHP_WIN32
01187 #define php_select(m, r, w, e, t)  select(m, r, w, e, t)
01188 #else
01189 #include "win32/select.h"
01190 #endif
01191 
01192 /* {{{ _mysqlnd_poll */
01193 PHPAPI enum_func_status
01194 _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC)
01195 {
01196 
01197        struct timeval       tv;
01198        struct timeval *tv_p = NULL;
01199        fd_set               rfds, wfds, efds;
01200        php_socket_t  max_fd = 0;
01201        int                         retval, sets = 0;
01202        int                         set_count, max_set_count = 0;
01203        DBG_ENTER("mysqlnd_poll");
01204 
01205        if (sec < 0 || usec < 0) {
01206               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
01207               DBG_RETURN(FAIL);
01208        }
01209 
01210        *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
01211 
01212        FD_ZERO(&rfds);
01213        FD_ZERO(&wfds);
01214        FD_ZERO(&efds);
01215 
01216        if (r_array != NULL) {
01217               set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
01218               if (set_count > max_set_count) {
01219                      max_set_count = set_count;
01220               }
01221               sets += set_count;
01222        }
01223 
01224        if (e_array != NULL) {
01225               set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
01226               if (set_count > max_set_count) {
01227                      max_set_count = set_count;
01228               }
01229               sets += set_count;
01230        }
01231 
01232        if (!sets) {
01233               php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
01234               DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
01235               DBG_RETURN(FAIL);
01236        }
01237 
01238        PHP_SAFE_MAX_FD(max_fd, max_set_count);
01239 
01240        /* Solaris + BSD do not like microsecond values which are >= 1 sec */
01241        if (usec > 999999) {
01242               tv.tv_sec = sec + (usec / 1000000);
01243               tv.tv_usec = usec % 1000000;
01244        } else {
01245               tv.tv_sec = sec;
01246               tv.tv_usec = usec;
01247        }
01248 
01249        tv_p = &tv;
01250 
01251        retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
01252 
01253        if (retval == -1) {
01254               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
01255                                           errno, strerror(errno), max_fd);
01256               DBG_RETURN(FAIL);
01257        }
01258 
01259        if (r_array != NULL) {
01260               mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
01261        }
01262        if (e_array != NULL) {
01263               mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
01264        }
01265 
01266        *desc_num = retval;
01267 
01268        DBG_RETURN(PASS);
01269 }
01270 /* }}} */
01271 
01272 
01273 /*
01274   COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
01275   - There is no result set header - status from the command, which
01276     impacts us to allocate big chunk of memory for reading the metadata.
01277   - The EOF packet is consumed by the metadata packet reader.
01278 */
01279 
01280 /* {{{ mysqlnd_conn::list_fields */
01281 MYSQLND_RES *
01282 MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND * conn, const char *table, const char *achtung_wild TSRMLS_DC)
01283 {
01284        /* db + \0 + wild + \0 (for wild) */
01285        char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
01286        size_t table_len, wild_len;
01287        MYSQLND_RES *result = NULL;
01288        DBG_ENTER("mysqlnd_conn::list_fields");
01289        DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
01290 
01291        p = buff;
01292        if (table && (table_len = strlen(table))) {
01293               size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
01294               memcpy(p, table, to_copy);
01295               p += to_copy;
01296               *p++ = '\0';
01297        }
01298 
01299        if (achtung_wild && (wild_len = strlen(achtung_wild))) {
01300               size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
01301               memcpy(p, achtung_wild, to_copy);
01302               p += to_copy;
01303               *p++ = '\0';
01304        }
01305 
01306        if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
01307                                                                   PROT_LAST /* we will handle the OK packet*/,
01308                                                                   FALSE, TRUE TSRMLS_CC)) {
01309               DBG_RETURN(NULL);
01310        }
01311 
01312        /*
01313           Prepare for the worst case.
01314           MyISAM goes to 2500 BIT columns, double it for safety.
01315        */
01316        result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
01317        if (!result) {
01318               DBG_RETURN(NULL);
01319        }
01320 
01321        if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
01322               DBG_ERR("Error ocurred while reading metadata");
01323               result->m.free_result(result, TRUE TSRMLS_CC);
01324               DBG_RETURN(NULL);
01325        }
01326 
01327        result->type = MYSQLND_RES_NORMAL;
01328        result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
01329        result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
01330        if (!result->unbuf) {
01331               /* OOM */
01332               SET_OOM_ERROR(conn->error_info);
01333               result->m.free_result(result, TRUE TSRMLS_CC);
01334               DBG_RETURN(NULL);
01335        }
01336        result->unbuf->eof_reached = TRUE;
01337 
01338        DBG_RETURN(result);
01339 }
01340 /* }}} */
01341 
01342 
01343 /* {{{ mysqlnd_conn::list_method */
01344 MYSQLND_RES *
01345 MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
01346 {
01347        char *show_query = NULL;
01348        size_t show_query_len;
01349        MYSQLND_RES *result = NULL;
01350 
01351        DBG_ENTER("mysqlnd_conn::list_method");
01352        DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
01353 
01354        if (par1) {
01355               if (achtung_wild) {
01356                      show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild);
01357               } else {
01358                      show_query_len = spprintf(&show_query, 0, query, par1);
01359               }
01360        } else {
01361               if (achtung_wild) {
01362                      show_query_len = spprintf(&show_query, 0, query, achtung_wild);
01363               } else {
01364                      show_query_len = strlen(show_query = (char *)query);
01365               }
01366        }
01367 
01368        if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
01369               result = conn->m->store_result(conn TSRMLS_CC);
01370        }
01371        if (show_query != query) {
01372               efree(show_query); /* allocated by spprintf */
01373        }
01374        DBG_RETURN(result);
01375 }
01376 /* }}} */
01377 
01378 
01379 /* {{{ mysqlnd_conn::errno */
01380 static unsigned int
01381 MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn TSRMLS_DC)
01382 {
01383        return conn->error_info.error_no;
01384 }
01385 /* }}} */
01386 
01387 
01388 /* {{{ mysqlnd_conn::error */
01389 static const char *
01390 MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn TSRMLS_DC)
01391 {
01392        return conn->error_info.error;
01393 }
01394 /* }}} */
01395 
01396 
01397 /* {{{ mysqlnd_conn::sqlstate */
01398 static const char *
01399 MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn TSRMLS_DC)
01400 {
01401        return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;
01402 }
01403 /* }}} */
01404 
01405 
01406 /* {{{ mysqlnd_old_escape_string */
01407 PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
01408 {
01409        DBG_ENTER("mysqlnd_old_escape_string");
01410        DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
01411 }
01412 /* }}} */
01413 
01414 /* {{{ mysqlnd_conn::ssl_set */
01415 static enum_func_status
01416 MYSQLND_METHOD(mysqlnd_conn, ssl_set)(MYSQLND * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
01417 {
01418        return (PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
01419               PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
01420               PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
01421               PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
01422               PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
01423 }
01424 /* }}} */
01425 
01426 
01427 /* {{{ mysqlnd_conn::escape_string */
01428 static ulong
01429 MYSQLND_METHOD(mysqlnd_conn, escape_string)(MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
01430 {
01431        DBG_ENTER("mysqlnd_conn::escape_string");
01432        DBG_INF_FMT("conn=%llu", conn->thread_id);
01433        if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
01434               DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
01435        }
01436        DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
01437 }
01438 /* }}} */
01439 
01440 
01441 /* {{{ mysqlnd_conn::dump_debug_info */
01442 static enum_func_status
01443 MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC)
01444 {
01445        DBG_ENTER("mysqlnd_conn::dump_debug_info");
01446        DBG_INF_FMT("conn=%llu", conn->thread_id);
01447        DBG_RETURN(conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC));
01448 }
01449 /* }}} */
01450 
01451 
01452 /* {{{ mysqlnd_conn::select_db */
01453 static enum_func_status
01454 MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
01455 {
01456        enum_func_status ret;
01457 
01458        DBG_ENTER("mysqlnd_conn::select_db");
01459        DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
01460 
01461        ret = conn->m->simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
01462        /*
01463          The server sends 0 but libmysql doesn't read it and has established
01464          a protocol of giving back -1. Thus we have to follow it :(
01465        */
01466        SET_ERROR_AFF_ROWS(conn);
01467        if (ret == PASS) {
01468               if (conn->connect_or_select_db) {
01469                      mnd_pefree(conn->connect_or_select_db, conn->persistent);
01470               }
01471               conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
01472               conn->connect_or_select_db_len = db_len;
01473               if (!conn->connect_or_select_db) {
01474                      /* OOM */
01475                      SET_OOM_ERROR(conn->error_info);
01476                      ret = FAIL;
01477               }
01478        }
01479        DBG_RETURN(ret);
01480 }
01481 /* }}} */
01482 
01483 
01484 /* {{{ mysqlnd_conn::ping */
01485 static enum_func_status
01486 MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC)
01487 {
01488        enum_func_status ret;
01489 
01490        DBG_ENTER("mysqlnd_conn::ping");
01491        DBG_INF_FMT("conn=%llu", conn->thread_id);
01492 
01493        ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
01494        /*
01495          The server sends 0 but libmysql doesn't read it and has established
01496          a protocol of giving back -1. Thus we have to follow it :(
01497        */
01498        SET_ERROR_AFF_ROWS(conn);
01499 
01500        DBG_INF_FMT("ret=%u", ret);
01501        DBG_RETURN(ret);
01502 }
01503 /* }}} */
01504 
01505 
01506 /* {{{ mysqlnd_conn::statistic */
01507 static enum_func_status
01508 MYSQLND_METHOD(mysqlnd_conn, statistic)(MYSQLND * conn, char **message, unsigned int * message_len TSRMLS_DC)
01509 {
01510        enum_func_status ret;
01511        MYSQLND_PACKET_STATS * stats_header;
01512 
01513        DBG_ENTER("mysqlnd_conn::statistic");
01514        DBG_INF_FMT("conn=%llu", conn->thread_id);
01515 
01516        ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
01517        if (FAIL == ret) {
01518               DBG_RETURN(FAIL);
01519        }
01520        stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
01521        if (!stats_header) {
01522               SET_OOM_ERROR(conn->error_info);
01523               DBG_RETURN(FAIL);
01524        }
01525 
01526        if (FAIL == (ret = PACKET_READ(stats_header, conn))) {
01527               DBG_RETURN(FAIL);
01528        }
01529        /* will be freed by Zend, thus don't use the mnd_ allocator */
01530        *message = estrndup(stats_header->message, stats_header->message_len); 
01531        *message_len = stats_header->message_len;
01532        PACKET_FREE(stats_header);
01533 
01534        DBG_INF(*message);
01535        DBG_RETURN(PASS);
01536 }
01537 /* }}} */
01538 
01539 
01540 /* {{{ mysqlnd_conn::kill */
01541 static enum_func_status
01542 MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND * conn, unsigned int pid TSRMLS_DC)
01543 {
01544        enum_func_status ret;
01545        char buff[4];
01546 
01547        DBG_ENTER("mysqlnd_conn::kill");
01548        DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
01549 
01550        int4store(buff, pid);
01551 
01552        /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
01553        if (pid != conn->thread_id) {
01554               ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
01555               /*
01556                 The server sends 0 but libmysql doesn't read it and has established
01557                 a protocol of giving back -1. Thus we have to follow it :(
01558               */
01559               SET_ERROR_AFF_ROWS(conn);
01560        } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
01561               CONN_SET_STATE(conn, CONN_QUIT_SENT);
01562        }
01563        DBG_RETURN(ret);
01564 }
01565 /* }}} */
01566 
01567 
01568 /* {{{ mysqlnd_conn::set_charset */
01569 static enum_func_status
01570 MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC)
01571 {
01572        enum_func_status ret = PASS;
01573        char * query;
01574        size_t query_len;
01575        const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
01576 
01577        DBG_ENTER("mysqlnd_conn::set_charset");
01578        DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
01579 
01580        if (!charset) {
01581               SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
01582                                            "Invalid characterset or character set not supported");
01583               DBG_RETURN(FAIL);
01584        }
01585 
01586        query_len = spprintf(&query, 0, "SET NAMES %s", csname);
01587 
01588        if (FAIL == conn->m->query(conn, query, query_len TSRMLS_CC)) {
01589               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
01590        } else if (conn->error_info.error_no) {
01591               ret = FAIL;
01592        } else {
01593               conn->charset = charset;
01594        }
01595        efree(query); /* allocated by spprintf */
01596 
01597        DBG_INF(ret == PASS? "PASS":"FAIL");
01598        DBG_RETURN(ret);
01599 }
01600 /* }}} */
01601 
01602 
01603 /* {{{ mysqlnd_conn::refresh */
01604 static enum_func_status
01605 MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, uint8_t options TSRMLS_DC)
01606 {
01607        zend_uchar bits[1];
01608        DBG_ENTER("mysqlnd_conn::refresh");
01609        DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
01610 
01611        int1store(bits, options);
01612 
01613        DBG_RETURN(conn->m->simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
01614 }
01615 /* }}} */
01616 
01617 
01618 /* {{{ mysqlnd_conn::shutdown */
01619 static enum_func_status
01620 MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, uint8_t level TSRMLS_DC)
01621 {
01622        zend_uchar bits[1];
01623        DBG_ENTER("mysqlnd_conn::shutdown");
01624        DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
01625 
01626        int1store(bits, level);
01627 
01628        DBG_RETURN(conn->m->simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
01629 }
01630 /* }}} */
01631 
01632 
01633 /* {{{ mysqlnd_send_close */
01634 static enum_func_status
01635 MYSQLND_METHOD(mysqlnd_conn, send_close)(MYSQLND * const conn TSRMLS_DC)
01636 {
01637        enum_func_status ret = PASS;
01638 
01639        DBG_ENTER("mysqlnd_send_close");
01640        DBG_INF_FMT("conn=%llu conn->net->stream->abstract=%p",
01641                             conn->thread_id, conn->net->stream? conn->net->stream->abstract:NULL);
01642 
01643        switch (CONN_GET_STATE(conn)) {
01644               case CONN_READY:
01645                      DBG_INF("Connection clean, sending COM_QUIT");
01646                      if (conn->net->stream) {
01647                             ret =  conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
01648                      }
01649                      /* Do nothing */
01650                      break;
01651               case CONN_SENDING_LOAD_DATA:
01652                      /*
01653                        Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
01654                        will crash (assert) a debug server.
01655                      */
01656               case CONN_NEXT_RESULT_PENDING:
01657               case CONN_QUERY_SENT:
01658               case CONN_FETCHING_DATA:
01659                      MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
01660                      DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
01661                      /*
01662                        Do nothing, the connection will be brutally closed
01663                        and the server will catch it and free close from its side.
01664                      */
01665               case CONN_ALLOCED:
01666                      /*
01667                        Allocated but not connected or there was failure when trying
01668                        to connect with pre-allocated connect.
01669 
01670                        Fall-through
01671                      */
01672               case CONN_QUIT_SENT:
01673                      /* The user has killed its own connection */
01674                      break;
01675        }
01676        /*
01677          We hold one reference, and every other object which needs the
01678          connection does increase it by 1.
01679        */
01680        CONN_SET_STATE(conn, CONN_QUIT_SENT);
01681 
01682        DBG_RETURN(ret);
01683 }
01684 /* }}} */
01685 
01686 
01687 /* {{{ mysqlnd_conn::close */
01688 static enum_func_status
01689 MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC)
01690 {
01691        enum_func_status ret = PASS;
01692        static enum_mysqlnd_collected_stats
01693        close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
01694               STAT_CLOSE_EXPLICIT,
01695               STAT_CLOSE_IMPLICIT,
01696               STAT_CLOSE_DISCONNECT
01697        };
01698        enum_mysqlnd_collected_stats statistic = close_type_to_stat_map[close_type];
01699 
01700        DBG_ENTER("mysqlnd_conn::close");
01701        DBG_INF_FMT("conn=%llu", conn->thread_id);
01702 
01703        if (conn->state >= CONN_READY) {
01704               MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
01705               MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
01706               if (conn->persistent) {
01707                      MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
01708               }
01709        }
01710 
01711        /*
01712          Close now, free_reference will try,
01713          if we are last, but that's not a problem.
01714        */
01715        ret = conn->m->send_close(conn TSRMLS_CC);
01716 
01717        ret = conn->m->free_reference(conn TSRMLS_CC);
01718 
01719        DBG_RETURN(ret);
01720 }
01721 /* }}} */
01722 
01723 
01724 /* {{{ mysqlnd_conn::get_reference */
01725 static MYSQLND *
01726 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn TSRMLS_DC)
01727 {
01728        DBG_ENTER("mysqlnd_conn::get_reference");
01729        ++conn->refcount;
01730        DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
01731        DBG_RETURN(conn);
01732 }
01733 /* }}} */
01734 
01735 
01736 /* {{{ mysqlnd_conn::free_reference */
01737 static enum_func_status
01738 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC)
01739 {
01740        enum_func_status ret = PASS;
01741        DBG_ENTER("mysqlnd_conn::free_reference");
01742        DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
01743        if (!(--conn->refcount)) {
01744               /*
01745                 No multithreading issues as we don't share the connection :)
01746                 This will free the object too, of course because references has
01747                 reached zero.
01748               */
01749               ret = conn->m->send_close(conn TSRMLS_CC);
01750               conn->m->dtor(conn TSRMLS_CC);
01751        }
01752        DBG_RETURN(ret);
01753 }
01754 /* }}} */
01755 
01756 
01757 /* {{{ mysqlnd_conn::get_state */
01758 static enum mysqlnd_connection_state
01759 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
01760 {
01761        DBG_ENTER("mysqlnd_conn::get_state");
01762        DBG_RETURN(conn->state);
01763 }
01764 /* }}} */
01765 
01766 
01767 /* {{{ mysqlnd_conn::set_state */
01768 static void
01769 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
01770 {
01771        DBG_ENTER("mysqlnd_conn::set_state");
01772        DBG_INF_FMT("New state=%u", new_state);
01773        conn->state = new_state;
01774        DBG_VOID_RETURN;
01775 }
01776 /* }}} */
01777 
01778 
01779 /* {{{ mysqlnd_conn::field_count */
01780 static unsigned int
01781 MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn TSRMLS_DC)
01782 {
01783        return conn->field_count;
01784 }
01785 /* }}} */
01786 
01787 
01788 /* {{{ mysqlnd_conn::insert_id */
01789 static uint64_t
01790 MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn TSRMLS_DC)
01791 {
01792        return conn->upsert_status.last_insert_id;
01793 }
01794 /* }}} */
01795 
01796 
01797 /* {{{ mysqlnd_conn::affected_rows */
01798 static uint64_t
01799 MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn TSRMLS_DC)
01800 {
01801        return conn->upsert_status.affected_rows;
01802 }
01803 /* }}} */
01804 
01805 
01806 /* {{{ mysqlnd_conn::warning_count */
01807 static unsigned int
01808 MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn TSRMLS_DC)
01809 {
01810        return conn->upsert_status.warning_count;
01811 }
01812 /* }}} */
01813 
01814 
01815 /* {{{ mysqlnd_conn::info */
01816 static const char *
01817 MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn TSRMLS_DC)
01818 {
01819        return conn->last_message;
01820 }
01821 /* }}} */
01822 
01823 #if !defined(MYSQLND_USE_OPTIMISATIONS) || MYSQLND_USE_OPTIMISATIONS == 0
01824 /* {{{ mysqlnd_get_client_info */
01825 PHPAPI const char * mysqlnd_get_client_info()
01826 {
01827        return MYSQLND_VERSION;
01828 }
01829 /* }}} */
01830 
01831 
01832 /* {{{ mysqlnd_get_client_version */
01833 PHPAPI unsigned int mysqlnd_get_client_version()
01834 {
01835        return MYSQLND_VERSION_ID;
01836 }
01837 /* }}} */
01838 #endif
01839 
01840 /* {{{ mysqlnd_conn::get_server_info */
01841 static const char *
01842 MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn TSRMLS_DC)
01843 {
01844        return conn->server_version;
01845 }
01846 /* }}} */
01847 
01848 
01849 /* {{{ mysqlnd_conn::get_host_info */
01850 static const char *
01851 MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn TSRMLS_DC)
01852 {
01853        return conn->host_info;
01854 }
01855 /* }}} */
01856 
01857 
01858 /* {{{ mysqlnd_conn::get_proto_info */
01859 static unsigned int
01860 MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn TSRMLS_DC)
01861 {
01862        return conn->protocol_version;
01863 }
01864 /* }}} */
01865 
01866 
01867 /* {{{ mysqlnd_conn::charset_name */
01868 static const char *
01869 MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn TSRMLS_DC)
01870 {
01871        return conn->charset->name;
01872 }
01873 /* }}} */
01874 
01875 
01876 /* {{{ mysqlnd_conn::thread_id */
01877 static uint64_t
01878 MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn TSRMLS_DC)
01879 {
01880        return conn->thread_id;
01881 }
01882 /* }}} */
01883 
01884 
01885 /* {{{ mysqlnd_conn::get_server_version */
01886 static unsigned long
01887 MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn TSRMLS_DC)
01888 {
01889        long major, minor, patch;
01890        char *p;
01891 
01892        if (!(p = conn->server_version)) {
01893               return 0;
01894        }
01895 
01896        major = strtol(p, &p, 10);
01897        p += 1; /* consume the dot */
01898        minor = strtol(p, &p, 10);
01899        p += 1; /* consume the dot */
01900        patch = strtol(p, &p, 10);
01901 
01902        return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
01903 }
01904 /* }}} */
01905 
01906 
01907 /* {{{ mysqlnd_conn::more_results */
01908 static zend_bool
01909 MYSQLND_METHOD(mysqlnd_conn, more_results)(const MYSQLND * const conn TSRMLS_DC)
01910 {
01911        DBG_ENTER("mysqlnd_conn::more_results");
01912        /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
01913        DBG_RETURN(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
01914 }
01915 /* }}} */
01916 
01917 
01918 /* {{{ mysqlnd_conn::next_result */
01919 static enum_func_status
01920 MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
01921 {
01922        enum_func_status ret;
01923 
01924        DBG_ENTER("mysqlnd_conn::next_result");
01925        DBG_INF_FMT("conn=%llu", conn->thread_id);
01926 
01927        if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
01928               DBG_RETURN(FAIL);
01929        }
01930 
01931        SET_EMPTY_ERROR(conn->error_info);
01932        SET_ERROR_AFF_ROWS(conn);
01933        /*
01934          We are sure that there is a result set, since conn->state is set accordingly
01935          in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
01936        */
01937        if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
01938               /*
01939                 There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
01940                 So there are no more results and we should just return FALSE, error_no has been set
01941               */
01942               if (!conn->error_info.error_no) {
01943                      DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
01944                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
01945                      CONN_SET_STATE(conn, CONN_QUIT_SENT);
01946               } else {
01947                      DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info.error_no, conn->error_info.error);
01948               }
01949        }
01950        if (ret == PASS && conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
01951               MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
01952        }
01953 
01954        DBG_RETURN(ret);
01955 }
01956 /* }}} */
01957 
01958 
01959 /* {{{ mysqlnd_field_type_name */
01960 PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
01961 {
01962        switch(field_type) {
01963               case FIELD_TYPE_STRING:
01964               case FIELD_TYPE_VAR_STRING:
01965                      return "string";
01966               case FIELD_TYPE_TINY:
01967               case FIELD_TYPE_SHORT:
01968               case FIELD_TYPE_LONG:
01969               case FIELD_TYPE_LONGLONG:
01970               case FIELD_TYPE_INT24:
01971                      return "int";
01972               case FIELD_TYPE_FLOAT:
01973               case FIELD_TYPE_DOUBLE:
01974               case FIELD_TYPE_DECIMAL:
01975               case FIELD_TYPE_NEWDECIMAL:
01976                      return "real";
01977               case FIELD_TYPE_TIMESTAMP:
01978                      return "timestamp";
01979               case FIELD_TYPE_YEAR:
01980                      return "year";
01981               case FIELD_TYPE_DATE:
01982               case FIELD_TYPE_NEWDATE:
01983                      return "date";
01984               case FIELD_TYPE_TIME:
01985                      return "time";
01986               case FIELD_TYPE_SET:
01987                      return "set";
01988               case FIELD_TYPE_ENUM:
01989                      return "enum";
01990               case FIELD_TYPE_GEOMETRY:
01991                      return "geometry";
01992               case FIELD_TYPE_DATETIME:
01993                      return "datetime";
01994               case FIELD_TYPE_TINY_BLOB:
01995               case FIELD_TYPE_MEDIUM_BLOB:
01996               case FIELD_TYPE_LONG_BLOB:
01997               case FIELD_TYPE_BLOB:
01998                      return "blob";
01999               case FIELD_TYPE_NULL:
02000                      return "null";
02001               case FIELD_TYPE_BIT:
02002                      return "bit";
02003               default:
02004                      return "unknown";
02005        }
02006 }
02007 /* }}} */
02008 
02009 
02010 /* {{{ mysqlnd_conn::set_client_option */
02011 static enum_func_status
02012 MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,
02013                                                                                     enum mysqlnd_option option,
02014                                                                                     const char * const value
02015                                                                                     TSRMLS_DC)
02016 {
02017        enum_func_status ret = PASS;
02018        DBG_ENTER("mysqlnd_conn::set_client_option");
02019        DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
02020        switch (option) {
02021               case MYSQL_OPT_COMPRESS:
02022 #ifdef WHEN_SUPPORTED_BY_MYSQLI
02023               case MYSQL_OPT_READ_TIMEOUT:
02024               case MYSQL_OPT_WRITE_TIMEOUT:
02025 #endif
02026               case MYSQLND_OPT_SSL_KEY:
02027               case MYSQLND_OPT_SSL_CERT:
02028               case MYSQLND_OPT_SSL_CA:
02029               case MYSQLND_OPT_SSL_CAPATH:
02030               case MYSQLND_OPT_SSL_CIPHER:
02031               case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
02032               case MYSQL_OPT_CONNECT_TIMEOUT:
02033               case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
02034               case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
02035                      ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC);
02036                      break;
02037 #if MYSQLND_UNICODE
02038               case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
02039                      conn->options.numeric_and_datetime_as_unicode = *(unsigned int*) value;
02040                      break;
02041 #endif
02042 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
02043               case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
02044                      DBG_INF("MYSQLND_OPT_INT_AND_FLOAT_NATIVE");
02045                      conn->options.int_and_float_native = *(unsigned int*) value;
02046                      break;
02047 #endif
02048               case MYSQL_OPT_LOCAL_INFILE:
02049                      DBG_INF("MYSQL_OPT_LOCAL_INFILE");
02050                      if (!value || (*(unsigned int*) value) ? 1 : 0) {
02051                             conn->options.flags |= CLIENT_LOCAL_FILES;
02052                      } else {
02053                             conn->options.flags &= ~CLIENT_LOCAL_FILES;
02054                      }
02055                      break;
02056               case MYSQL_INIT_COMMAND:
02057               {
02058                      char ** new_init_commands;
02059                      char * new_command;
02060                      DBG_INF("MYSQL_INIT_COMMAND");
02061                      DBG_INF_FMT("command=%s", value);
02062                      /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
02063                      /* Don't assign to conn->options.init_commands because in case of OOM we will lose the pointer and leak */
02064                      new_init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent);
02065                      if (!new_init_commands) {
02066                             goto oom;
02067                      }
02068                      conn->options.init_commands = new_init_commands;
02069                      new_command = mnd_pestrdup(value, conn->persistent);
02070                      if (!new_command) {
02071                             goto oom;
02072                      }
02073                      conn->options.init_commands[conn->options.num_commands] = new_command;
02074                      ++conn->options.num_commands;
02075                      break;
02076               }
02077               case MYSQL_READ_DEFAULT_FILE:
02078               case MYSQL_READ_DEFAULT_GROUP:
02079 #ifdef WHEN_SUPPORTED_BY_MYSQLI
02080               case MYSQL_SET_CLIENT_IP:
02081               case MYSQL_REPORT_DATA_TRUNCATION:
02082 #endif
02083                      /* currently not supported. Todo!! */
02084                      break;
02085               case MYSQL_SET_CHARSET_NAME:
02086               {
02087                      char * new_charset_name = mnd_pestrdup(value, conn->persistent);
02088                      DBG_INF("MYSQL_SET_CHARSET_NAME");
02089                      if (!new_charset_name) {
02090                             goto oom;
02091                      }
02092                      if (conn->options.charset_name) {
02093                             mnd_pefree(conn->options.charset_name, conn->persistent);
02094                      }
02095                      conn->options.charset_name = new_charset_name;
02096                      DBG_INF_FMT("charset=%s", conn->options.charset_name);
02097                      break;
02098               }
02099               case MYSQL_OPT_NAMED_PIPE:
02100                      conn->options.protocol = MYSQL_PROTOCOL_PIPE;
02101                      break;
02102               case MYSQL_OPT_PROTOCOL:
02103                      if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
02104                             conn->options.protocol = *(unsigned int*) value;
02105                      }
02106                      break;
02107 #ifdef WHEN_SUPPORTED_BY_MYSQLI
02108               case MYSQL_SET_CHARSET_DIR:
02109               case MYSQL_OPT_RECONNECT:
02110                      /* we don't need external character sets, all character sets are
02111                         compiled in. For compatibility we just ignore this setting.
02112                         Same for protocol, we don't support old protocol */
02113               case MYSQL_OPT_USE_REMOTE_CONNECTION:
02114               case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
02115               case MYSQL_OPT_GUESS_CONNECTION:
02116                      /* todo: throw an error, we don't support embedded */
02117                      break;
02118 #endif
02119 
02120 #ifdef WHEN_SUPPORTED_BY_MYSQLI
02121               case MYSQL_SHARED_MEMORY_BASE_NAME:
02122               case MYSQL_OPT_USE_RESULT:
02123               case MYSQL_SECURE_AUTH:
02124                      /* not sure, todo ? */
02125 #endif
02126               default:
02127                      ret = FAIL;
02128        }
02129        DBG_RETURN(ret);
02130 oom:
02131        SET_OOM_ERROR(conn->error_info);
02132        DBG_RETURN(FAIL);
02133 }
02134 /* }}} */
02135 
02136 
02137 /* {{{ mysqlnd_conn::use_result */
02138 static MYSQLND_RES *
02139 MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
02140 {
02141        MYSQLND_RES * result;
02142 
02143        DBG_ENTER("mysqlnd_conn::use_result");
02144        DBG_INF_FMT("conn=%llu", conn->thread_id);
02145 
02146        if (!conn->current_result) {
02147               DBG_RETURN(NULL);
02148        }
02149 
02150        /* Nothing to store for UPSERT/LOAD DATA */
02151        if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
02152               SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
02153                                            mysqlnd_out_of_sync);
02154               DBG_ERR("Command out of sync");
02155               DBG_RETURN(NULL);
02156        }
02157 
02158        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
02159 
02160        conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
02161        result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
02162 
02163        if (!result) {
02164               conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
02165        }
02166        conn->current_result = NULL;
02167 
02168        DBG_RETURN(result);
02169 }
02170 /* }}} */
02171 
02172 
02173 /* {{{ mysqlnd_conn::store_result */
02174 static MYSQLND_RES *
02175 MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
02176 {
02177        MYSQLND_RES *result;
02178 
02179        DBG_ENTER("mysqlnd_conn::store_result");
02180        DBG_INF_FMT("conn=%llu", conn->thread_id);
02181 
02182        if (!conn->current_result) {
02183               DBG_RETURN(NULL);
02184        }
02185 
02186        /* Nothing to store for UPSERT/LOAD DATA*/
02187        if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
02188               SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
02189                                            mysqlnd_out_of_sync);
02190               DBG_ERR("Command out of sync");
02191               DBG_RETURN(NULL);
02192        }
02193 
02194        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
02195 
02196        result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC);
02197        if (!result) {
02198               conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
02199        }
02200        conn->current_result = NULL;
02201        DBG_RETURN(result);
02202 }
02203 /* }}} */
02204 
02205 
02206 /* {{{ mysqlnd_conn::get_connection_stats */
02207 static void
02208 MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
02209                                                                                        zval *return_value
02210                                                                                        TSRMLS_DC ZEND_FILE_LINE_DC)
02211 {
02212        DBG_ENTER("mysqlnd_conn::get_connection_stats");
02213        DBG_INF_FMT("conn=%llu", conn->thread_id);
02214        mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
02215        DBG_VOID_RETURN;
02216 }
02217 /* }}} */
02218 
02219 
02220 /* {{{ mysqlnd_conn::set_autocommit */
02221 static enum_func_status
02222 MYSQLND_METHOD(mysqlnd_conn, set_autocommit)(MYSQLND * conn, unsigned int mode TSRMLS_DC)
02223 {
02224        enum_func_status ret;
02225        DBG_ENTER("mysqlnd_conn::set_autocommit");
02226        ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
02227        DBG_RETURN(ret);
02228 }
02229 /* }}} */
02230 
02231 
02232 /* {{{ mysqlnd_conn::tx_commit */
02233 static enum_func_status
02234 MYSQLND_METHOD(mysqlnd_conn, tx_commit)(MYSQLND * conn TSRMLS_DC)
02235 {
02236        enum_func_status ret;
02237        DBG_ENTER("mysqlnd_conn::tx_commit");
02238        ret = conn->m->query(conn, "COMMIT", sizeof("COMMIT") - 1 TSRMLS_CC);
02239        DBG_RETURN(ret);
02240 }
02241 /* }}} */
02242 
02243 
02244 /* {{{ mysqlnd_conn::tx_rollback */
02245 static enum_func_status
02246 MYSQLND_METHOD(mysqlnd_conn, tx_rollback)(MYSQLND * conn TSRMLS_DC)
02247 {
02248        enum_func_status ret;
02249        DBG_ENTER("mysqlnd_conn::tx_rollback");
02250        ret = conn->m->query(conn, "ROLLBACK", sizeof("ROLLBACK") - 1 TSRMLS_CC);
02251        DBG_RETURN(ret);
02252 }
02253 /* }}} */
02254 
02255 
02256 
02257 MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC);
02258 static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC);
02259 
02260 static
02261 MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
02262        MYSQLND_METHOD(mysqlnd_conn, init),
02263        MYSQLND_METHOD(mysqlnd_conn, connect),
02264 
02265        MYSQLND_METHOD(mysqlnd_conn, escape_string),
02266        MYSQLND_METHOD(mysqlnd_conn, set_charset),
02267        MYSQLND_METHOD(mysqlnd_conn, query),
02268        MYSQLND_METHOD(mysqlnd_conn, send_query),
02269        MYSQLND_METHOD(mysqlnd_conn, reap_query),
02270        MYSQLND_METHOD(mysqlnd_conn, use_result),
02271        MYSQLND_METHOD(mysqlnd_conn, store_result),
02272        MYSQLND_METHOD(mysqlnd_conn, next_result),
02273        MYSQLND_METHOD(mysqlnd_conn, more_results),
02274 
02275        _mysqlnd_stmt_init,
02276 
02277        MYSQLND_METHOD(mysqlnd_conn, shutdown),
02278        MYSQLND_METHOD(mysqlnd_conn, refresh),
02279 
02280        MYSQLND_METHOD(mysqlnd_conn, ping),
02281        MYSQLND_METHOD(mysqlnd_conn, kill),
02282        MYSQLND_METHOD(mysqlnd_conn, select_db),
02283        MYSQLND_METHOD(mysqlnd_conn, dump_debug_info),
02284        MYSQLND_METHOD(mysqlnd_conn, change_user),
02285 
02286        MYSQLND_METHOD(mysqlnd_conn, errno),
02287        MYSQLND_METHOD(mysqlnd_conn, error),
02288        MYSQLND_METHOD(mysqlnd_conn, sqlstate),
02289        MYSQLND_METHOD(mysqlnd_conn, thread_id),
02290 
02291        MYSQLND_METHOD(mysqlnd_conn, get_connection_stats),
02292 
02293        MYSQLND_METHOD(mysqlnd_conn, get_server_version),
02294        MYSQLND_METHOD(mysqlnd_conn, get_server_info),
02295        MYSQLND_METHOD(mysqlnd_conn, statistic),
02296        MYSQLND_METHOD(mysqlnd_conn, get_host_info),
02297        MYSQLND_METHOD(mysqlnd_conn, get_proto_info),
02298        MYSQLND_METHOD(mysqlnd_conn, info),
02299        MYSQLND_METHOD(mysqlnd_conn, charset_name),
02300        MYSQLND_METHOD(mysqlnd_conn, list_fields),
02301        MYSQLND_METHOD(mysqlnd_conn, list_method),
02302 
02303        MYSQLND_METHOD(mysqlnd_conn, insert_id),
02304        MYSQLND_METHOD(mysqlnd_conn, affected_rows),
02305        MYSQLND_METHOD(mysqlnd_conn, warning_count),
02306        MYSQLND_METHOD(mysqlnd_conn, field_count),
02307 
02308        MYSQLND_METHOD(mysqlnd_conn, set_server_option),
02309        MYSQLND_METHOD(mysqlnd_conn, set_client_option),
02310        MYSQLND_METHOD(mysqlnd_conn, free_contents),
02311        MYSQLND_METHOD(mysqlnd_conn, free_options),
02312        MYSQLND_METHOD(mysqlnd_conn, close),
02313 
02314        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
02315 
02316        mysqlnd_query_read_result_set_header,
02317 
02318        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference),
02319        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference),
02320        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state),
02321        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state),
02322 
02323        MYSQLND_METHOD(mysqlnd_conn, simple_command),
02324        MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response),
02325        MYSQLND_METHOD(mysqlnd_conn, restart_psession),
02326        MYSQLND_METHOD(mysqlnd_conn, end_psession),
02327        MYSQLND_METHOD(mysqlnd_conn, send_close),
02328 
02329        MYSQLND_METHOD(mysqlnd_conn, ssl_set),
02330        mysqlnd_result_init
02331 #ifdef AUTOCOMMIT_TX_COMMIT_ROLLBACK
02332        ,MYSQLND_METHOD(mysqlnd_conn, set_autocommit),
02333        MYSQLND_METHOD(mysqlnd_conn, tx_commit),
02334        MYSQLND_METHOD(mysqlnd_conn, tx_rollback)
02335 #endif
02336 MYSQLND_CLASS_METHODS_END;
02337 
02338 
02339 /* {{{ mysqlnd_conn::init */
02340 static enum_func_status
02341 MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC)
02342 {
02343        DBG_ENTER("mysqlnd_conn::init");
02344        mysqlnd_stats_init(&conn->stats, STAT_LAST);
02345        SET_ERROR_AFF_ROWS(conn);
02346 
02347        conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC);
02348        conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
02349 
02350        DBG_RETURN(conn->net && conn->protocol? PASS:FAIL);
02351 }
02352 /* }}} */
02353 
02354 
02355 /* {{{ mysqlnd_init */
02356 PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC)
02357 {
02358        size_t alloc_size = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
02359        MYSQLND *ret;
02360 
02361        DBG_ENTER("mysqlnd_init");
02362        DBG_INF_FMT("persistent=%u", persistent);
02363        ret = mnd_pecalloc(1, alloc_size, persistent);
02364        if (!ret) {
02365               DBG_RETURN(NULL);
02366        }
02367 
02368        ret->persistent = persistent;
02369        ret->m = mysqlnd_conn_methods;
02370        CONN_SET_STATE(ret, CONN_ALLOCED);
02371        ret->m->get_reference(ret TSRMLS_CC);
02372 
02373        if (PASS != ret->m->init(ret TSRMLS_CC)) {
02374               ret->m->dtor(ret TSRMLS_CC);
02375               ret = NULL;
02376        }
02377 
02378        DBG_RETURN(ret);
02379 }
02380 /* }}} */
02381 
02382 
02383 /* {{{ mysqlnd_library_init */
02384 PHPAPI void mysqlnd_library_init(TSRMLS_D)
02385 {
02386        if (mysqlnd_library_initted == FALSE) {
02387               mysqlnd_library_initted = TRUE;
02388               mysqlnd_conn_methods = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn);
02389               _mysqlnd_init_ps_subsystem();
02390               /* Should be calloc, as mnd_calloc will reference LOCK_access*/
02391               mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST);
02392        }
02393 }
02394 /* }}} */
02395 
02396 /* {{{ mysqlnd_conn_get_methods */
02397 PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods()
02398 {
02399        return mysqlnd_conn_methods;
02400 }
02401 /* }}} */
02402 
02403 /* {{{ mysqlnd_conn_set_methods */
02404 PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
02405 {
02406        mysqlnd_conn_methods = methods;
02407 }
02408 /* }}} */
02409 
02410 
02411 static unsigned int mysqlnd_plugins_counter = 0;
02412 
02413 /* {{{ mysqlnd_plugin_register */
02414 PHPAPI unsigned int mysqlnd_plugin_register()
02415 {
02416        return mysqlnd_plugins_counter++;
02417 }
02418 /* }}} */
02419 
02420 
02421 /* {{{ mysqlnd_plugin_count */
02422 PHPAPI unsigned int mysqlnd_plugin_count()
02423 {
02424        return mysqlnd_plugins_counter;
02425 }
02426 /* }}} */
02427 
02428 
02429 /* {{{ _mysqlnd_plugin_get_plugin_connection_data */
02430 PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id TSRMLS_DC)
02431 {
02432        DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
02433        DBG_INF_FMT("plugin_id=%u", plugin_id);
02434        if (!conn || plugin_id >= mysqlnd_plugin_count()) {
02435               return NULL;
02436        }
02437        DBG_RETURN((void *)((char *)conn + sizeof(MYSQLND) + plugin_id * sizeof(void *)));
02438 }
02439 /* }}} */
02440 
02441 /*
02442  * Local variables:
02443  * tab-width: 4
02444  * c-basic-offset: 4
02445  * End:
02446  * vim600: noet sw=4 ts=4 fdm=marker
02447  * vim<600: noet sw=4 ts=4
02448  */