diff options
Diffstat (limited to 'ext/mysqlnd')
30 files changed, 3668 insertions, 2233 deletions
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 index f93177663..4a23e101a 100644 --- a/ext/mysqlnd/config9.m4 +++ b/ext/mysqlnd/config9.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config9.m4 292495 2009-12-22 19:30:09Z johannes $ +dnl $Id: config9.m4 298023 2010-04-15 11:01:30Z andrey $ dnl config.m4 for mysqlnd driver @@ -36,6 +36,7 @@ if test "$PHP_MYSQLND_ENABLED" = "yes"; then MYSQLND_LIBS="$MYSQLND_LIBS -lz" fi fi + AC_DEFINE([MYSQLND_SSL_SUPPORTED], 1, [Enable SSL support]) fi if test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 31fb47ae8..d0f2c1c20 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd.c 300635 2010-06-21 15:32:26Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -30,9 +30,6 @@ /* for php_get_current_user() */ #include "ext/standard/basic_functions.h" -/* the server doesn't support 4byte utf8, but let's make it forward compatible */ -#define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte */ -#define MYSQLND_MAX_ALLOWED_DB_LEN 256 /* 64 char * 4byte */ /* TODO : - Don't bind so tightly the metadata with the result set. This means @@ -57,15 +54,18 @@ extern MYSQLND_CHARSET *mysqlnd_charsets; -PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using old authentication"; +PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. " +"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will " +"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 " +"flag from your my.cnf file"; + PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away"; PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now"; +PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory"; PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL; static zend_bool mysqlnd_library_initted = FALSE; -static enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC); - static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods; /* {{{ mysqlnd_library_end */ @@ -82,7 +82,7 @@ PHPAPI void mysqlnd_library_end(TSRMLS_D) /* {{{ mysqlnd_conn::free_options */ static void -MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND * conn TSRMLS_DC) { zend_bool pers = conn->persistent; @@ -107,32 +107,13 @@ MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND *conn TSRMLS_DC) mnd_pefree(conn->options.cfg_section, pers); conn->options.cfg_section = NULL; } - if (conn->options.ssl_key) { - mnd_pefree(conn->options.ssl_key, pers); - conn->options.ssl_key = NULL; - } - if (conn->options.ssl_cert) { - mnd_pefree(conn->options.ssl_cert, pers); - conn->options.ssl_cert = NULL; - } - if (conn->options.ssl_ca) { - mnd_pefree(conn->options.ssl_ca, pers); - conn->options.ssl_ca = NULL; - } - if (conn->options.ssl_capath) { - mnd_pefree(conn->options.ssl_capath, pers); - conn->options.ssl_capath = NULL; - } - if (conn->options.ssl_cipher) { - mnd_pefree(conn->options.ssl_cipher, pers); - conn->options.ssl_cipher = NULL; - } } +/* }}} */ /* {{{ mysqlnd_conn::free_contents */ static void -MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC) { zend_bool pers = conn->persistent; @@ -140,12 +121,13 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) mysqlnd_local_infile_default(conn); if (conn->current_result) { - conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC); - mnd_efree(conn->current_result); + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); conn->current_result = NULL; } - conn->net->m.free_contents(conn->net TSRMLS_CC); + if (conn->net) { + conn->net->m.free_contents(conn->net TSRMLS_CC); + } DBG_INF("Freeing memory of members"); @@ -208,7 +190,7 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC) /* {{{ mysqlnd_conn::dtor */ static void -MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::dtor"); DBG_INF_FMT("conn=%llu", conn->thread_id); @@ -241,22 +223,26 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC) /* {{{ mysqlnd_conn::simple_command_handle_response */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum mysqlnd_packet_type ok_packet, - zend_bool silent, enum php_mysqlnd_server_command command, - zend_bool ignore_upsert_status TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND * conn, enum mysqlnd_packet_type ok_packet, + zend_bool silent, enum php_mysqlnd_server_command command, + zend_bool ignore_upsert_status TSRMLS_DC) { - enum_func_status ret; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_conn::simple_command_handle_response"); - DBG_INF_FMT("silent=%d packet=%d command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); + DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]); switch (ok_packet) { case PROT_OK_PACKET:{ MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); + if (!ok_response) { + SET_OOM_ERROR(conn->error_info); + break; + } if (FAIL == (ret = PACKET_READ(ok_response, conn))) { if (!silent) { DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%d", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u", mysqlnd_command_to_text[command], getpid()); } } else { @@ -294,25 +280,27 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum } case PROT_EOF_PACKET:{ MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC); + if (!ok_response) { + SET_OOM_ERROR(conn->error_info); + break; + } if (FAIL == (ret = PACKET_READ(ok_response, conn))) { SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d", - mysqlnd_command_to_text[command], getpid()); + mysqlnd_command_to_text[command], getpid()); } } else if (0xFF == ok_response->field_count) { /* The server signalled error. Set the error */ SET_CLIENT_ERROR(conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error); SET_ERROR_AFF_ROWS(conn); } else if (0xFE != ok_response->field_count) { - SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, - "Malformed packet"); + SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); if (!silent) { DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "EOF packet expected, field count wasn't 0xFE but 0x%2X", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count); } } else { @@ -322,11 +310,8 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum break; } default: - ret = FAIL; - SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, - "Malformed packet"); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %d passed to the function", - ok_packet); + SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet"); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet); break; } DBG_INF(ret == PASS ? "PASS":"FAIL"); @@ -337,7 +322,7 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response)(MYSQLND *conn, enum /* {{{ mysqlnd_conn::simple_command */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_server_command command, +MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND * conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC) { @@ -345,7 +330,7 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser MYSQLND_PACKET_COMMAND * cmd_packet; DBG_ENTER("mysqlnd_conn::simple_command"); - DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent); + DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent); switch (CONN_GET_STATE(conn)) { case CONN_READY: @@ -355,9 +340,8 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser DBG_ERR("Server is gone"); DBG_RETURN(FAIL); default: - SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - mysqlnd_out_of_sync); - DBG_ERR("Command out of sync"); + SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn)); DBG_RETURN(FAIL); } @@ -369,6 +353,11 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser SET_EMPTY_ERROR(conn->error_info); cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC); + if (!cmd_packet) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } + cmd_packet->command = command; if (arg && arg_len) { cmd_packet->argument = arg; @@ -411,7 +400,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn, enum_mysql /* {{{ _mysqlnd_restart_psession */ -PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC) +PHPAPI void _mysqlnd_restart_psession(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("_mysqlnd_restart_psession"); conn->m->restart_psession(conn TSRMLS_CC); @@ -421,7 +410,7 @@ PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC) /* {{{ _mysqlnd_end_psession */ -PHPAPI void _mysqlnd_end_psession(MYSQLND *conn TSRMLS_DC) +PHPAPI void _mysqlnd_end_psession(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("_mysqlnd_end_psession"); conn->m->end_psession(conn TSRMLS_CC); @@ -455,9 +444,126 @@ MYSQLND_METHOD(mysqlnd_conn, end_psession)(MYSQLND * conn TSRMLS_DC) /* }}} */ +#define MYSQLND_ASSEBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL +/* {{{ mysqlnd_connect_run_authentication */ +static enum_func_status +mysqlnd_connect_run_authentication( + MYSQLND * conn, + const char * const user, + const char * const passwd, + const char * const db, + size_t db_len, + const MYSQLND_PACKET_GREET * const greet_packet, + const MYSQLND_OPTIONS * const options, + unsigned long mysql_flags + TSRMLS_DC) +{ + const MYSQLND_CHARSET * charset = NULL; + enum_func_status ret = FAIL; + MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC); + MYSQLND_PACKET_OK * ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); + + DBG_ENTER("mysqlnd_connect_run_authentication"); + + if (!auth_packet || !ok_packet) { + SET_OOM_ERROR(conn->error_info); + goto err; + } + +#ifdef MYSQLND_SSL_SUPPORTED + if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) { + auth_packet->send_half_packet = TRUE; + } +#endif + auth_packet->user = user; + auth_packet->password = passwd; + + if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) { + auth_packet->charset_no = charset->nr; + } else { +#if PHP_MAJOR_VERSION >= 6 + auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ +#else + auth_packet->charset_no = greet_packet->charset_no; +#endif + } + auth_packet->db = db; + auth_packet->db_len = db_len; + auth_packet->max_packet_size= MYSQLND_ASSEBLED_PACKET_MAX_SIZE; + auth_packet->client_flags= mysql_flags; + + conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); + if (!conn->scramble) { + SET_OOM_ERROR(conn->error_info); + goto err; + } + memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH); + + if (!PACKET_WRITE(auth_packet, conn)) { + CONN_SET_STATE(conn, CONN_QUIT_SENT); + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + goto err; + } + +#ifdef MYSQLND_SSL_SUPPORTED + if (auth_packet->send_half_packet) { + zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE; + DBG_INF("Switching to SSL"); + + conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC); + + if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) { + goto err; + } + + auth_packet->send_half_packet = FALSE; + if (!PACKET_WRITE(auth_packet, conn)) { + CONN_SET_STATE(conn, CONN_QUIT_SENT); + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + goto err; + } + } +#endif + + + if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) { + if (ok_packet->field_count == 0xFE) { + /* old authentication with new server !*/ + DBG_ERR(mysqlnd_old_passwd); + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd); + } else if (ok_packet->field_count == 0xFF) { + if (ok_packet->sqlstate[0]) { + strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate)); + DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error); + } + conn->error_info.error_no = ok_packet->error_no; + strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error)); + } + goto err; + } + + SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, + ok_packet->message, ok_packet->message_len, + conn->persistent); + conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no); + ret = PASS; +err: + PACKET_FREE(auth_packet); + PACKET_FREE(ok_packet); + DBG_RETURN(ret); +} +/* }}} */ + + +#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \ + CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \ + CLIENT_MULTI_RESULTS) + + + /* {{{ mysqlnd_conn::connect */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, +MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn, const char *host, const char *user, const char *passwd, unsigned int passwd_len, const char *db, unsigned int db_len, @@ -466,32 +572,27 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, unsigned int mysql_flags TSRMLS_DC) { - char *transport = NULL, *errstr = NULL; - int transport_len, errcode = 0, host_len; - zend_bool self_alloced = FALSE; + char *errstr = NULL; + int errcode = 0, host_len; zend_bool unix_socket = FALSE; - const MYSQLND_CHARSET * charset; zend_bool reconnect = FALSE; zend_bool saved_compression = FALSE; MYSQLND_PACKET_GREET * greet_packet = NULL; - MYSQLND_PACKET_AUTH * auth_packet = NULL; - MYSQLND_PACKET_OK * ok_packet = NULL; DBG_ENTER("mysqlnd_conn::connect"); - DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d", + DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags, conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1); if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) { - DBG_INF_FMT("state=%d", CONN_GET_STATE(conn)); DBG_INF("Connecting on a connected handle."); if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) { MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT); reconnect = TRUE; - mysqlnd_send_close(conn TSRMLS_CC); + conn->m->send_close(conn TSRMLS_CC); } conn->m->free_contents(conn TSRMLS_CC); @@ -514,50 +615,63 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, if (!host || !host[0]) { host = "localhost"; } - if (!user || !user[0]) { - user = php_get_current_user(); + if (!user) { + DBG_INF_FMT("no user given, using empty string"); + user = ""; } if (!passwd) { + DBG_INF_FMT("no password given, using empty string"); passwd = ""; passwd_len = 0; } if (!db) { + DBG_INF_FMT("no db given, using empty string"); db = ""; db_len = 0; } - if (!port && !socket) { - port = 3306; - } + host_len = strlen(host); + { + char * transport = NULL; + int transport_len; #ifndef PHP_WIN32 - if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) { - if (!socket) { - socket = "/tmp/mysql.sock"; - } - transport_len = spprintf(&transport, 0, "unix://%s", socket); - unix_socket = TRUE; - } else + if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) { + DBG_INF_FMT("socket=%s", socket? socket:"n/a"); + if (!socket) { + socket = "/tmp/mysql.sock"; + } + transport_len = spprintf(&transport, 0, "unix://%s", socket); + unix_socket = TRUE; + } else #endif - { - transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port); + { + if (!port) { + port = 3306; + } + transport_len = spprintf(&transport, 0, "tcp://%s:%u", host, port); + } + if (!transport) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } + DBG_INF_FMT("transport=%s", transport); + conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent); + conn->scheme_len = transport_len; + efree(transport); /* allocated by spprintf */ + transport = NULL; + if (!conn->scheme) { + goto err; /* OOM */ + } } - DBG_INF_FMT("transport=%s", transport); - greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC); - auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC); - ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); - - if (conn->persistent) { - conn->scheme = pestrndup(transport, transport_len, 1); - mnd_efree(transport); - } else { - conn->scheme = transport; + if (!greet_packet) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ } - conn->scheme_len = transport_len; - DBG_INF(conn->scheme); - if (FAIL == conn->net->m.connect(conn->net, conn->scheme, transport_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) { - goto err; + + if (FAIL == conn->net->m.connect(conn->net, conn->scheme, conn->scheme_len, conn->persistent, &errstr, &errcode TSRMLS_CC)) { + goto err; } DBG_INF_FMT("stream=%p", conn->net->stream); @@ -567,7 +681,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid()); goto err; } else if (greet_packet->error_no) { - DBG_ERR_FMT("errorno=%d error=%s", greet_packet->error_no, greet_packet->error); + DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error); SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error); goto err; } else if (greet_packet->pre41) { @@ -581,64 +695,46 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, conn->thread_id = greet_packet->thread_id; conn->protocol_version = greet_packet->protocol_version; - conn->server_version = pestrdup(greet_packet->server_version, conn->persistent); + conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent); conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no); /* we allow load data local infile by default */ - mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS; + mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS; + mysql_flags |= MYSQLND_CAPABILITIES; + + if (db) { + mysql_flags |= CLIENT_CONNECT_WITH_DB; + } + + if (PG(open_basedir) && strlen(PG(open_basedir))) { + mysql_flags ^= CLIENT_LOCAL_FILES; + } + #ifndef MYSQLND_COMPRESSION_ENABLED if (mysql_flags & CLIENT_COMPRESS) { mysql_flags &= ~CLIENT_COMPRESS; } #endif - - auth_packet->user = user; - auth_packet->password = passwd; - - if (conn->options.charset_name && - (charset = mysqlnd_find_charset_name(conn->options.charset_name))) - { - auth_packet->charset_no = charset->nr; - } else { -#if PHP_MAJOR_VERSION >= 6 - auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */ +#ifndef MYSQLND_SSL_SUPPORTED + if (mysql_flags & CLIENT_SSL) { + mysql_flags &= ~CLIENT_SSL; + } #else - auth_packet->charset_no = greet_packet->charset_no; -#endif + if (conn->net->options.ssl_key || conn->net->options.ssl_cert || + conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher) + { + mysql_flags |= CLIENT_SSL; } - auth_packet->db = db; - auth_packet->db_len = db_len; - auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL; - auth_packet->client_flags= mysql_flags; +#endif - conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); - memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH); - if (!PACKET_WRITE(auth_packet, conn)) { - CONN_SET_STATE(conn, CONN_QUIT_SENT); - SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, greet_packet, &conn->options, mysql_flags TSRMLS_CC)) { goto err; } - if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) { - if (ok_packet->field_count == 0xFE) { - /* old authentication with new server !*/ - DBG_ERR(mysqlnd_old_passwd); - SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd); - } else if (ok_packet->field_count == 0xFF) { - if (ok_packet->sqlstate[0]) { - if (!self_alloced) { - strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate)); - } - DBG_ERR_FMT("ERROR:%d [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error); - } - if (!self_alloced) { - conn->error_info.error_no = ok_packet->error_no; - strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error)); - } - } - } else { + { CONN_SET_STATE(conn, CONN_READY); - if (!self_alloced && saved_compression) { + + if (saved_compression) { conn->net->compressed = TRUE; } /* @@ -648,108 +744,113 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, */ conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE; - conn->user = pestrdup(user, conn->persistent); + conn->user = mnd_pestrdup(user, conn->persistent); conn->user_len = strlen(conn->user); - conn->passwd = pestrndup(passwd, passwd_len, conn->persistent); + conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent); conn->passwd_len = passwd_len; conn->port = port; - conn->connect_or_select_db = pestrndup(db, db_len, conn->persistent); + conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent); conn->connect_or_select_db_len = db_len; - if (!unix_socket) { - char *p; + if (!conn->user || !conn->passwd || !conn->connect_or_select_db) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } - conn->host = pestrdup(host, conn->persistent); + if (!unix_socket) { + conn->host = mnd_pestrdup(host, conn->persistent); + if (!conn->host) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } conn->host_len = strlen(conn->host); - spprintf(&p, 0, "%s via TCP/IP", conn->host); - if (conn->persistent) { - conn->host_info = pestrdup(p, 1); - mnd_efree(p); - } else { - conn->host_info = p; + { + char *p; + spprintf(&p, 0, "%s via TCP/IP", conn->host); + if (!p) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } + conn->host_info = mnd_pestrdup(p, conn->persistent); + efree(p); /* allocated by spprintf */ + if (!conn->host_info) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } } } else { - conn->unix_socket = pestrdup(socket, conn->persistent); + conn->unix_socket = mnd_pestrdup(socket, conn->persistent); + conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent); + if (!conn->unix_socket || !conn->host_info) { + SET_OOM_ERROR(conn->error_info); + goto err; /* OOM */ + } conn->unix_socket_len = strlen(conn->unix_socket); - conn->host_info = pestrdup("Localhost via UNIX socket", conn->persistent); } - conn->client_flag = auth_packet->client_flags; - conn->max_packet_size = auth_packet->max_packet_size; + conn->client_flag = mysql_flags; + conn->max_packet_size = MYSQLND_ASSEBLED_PACKET_MAX_SIZE; /* todo: check if charset is available */ - conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no); conn->server_capabilities = greet_packet->server_capabilities; conn->upsert_status.warning_count = 0; conn->upsert_status.server_status = greet_packet->server_status; conn->upsert_status.affected_rows = 0; - SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, - ok_packet->message, ok_packet->message_len, - conn->persistent); SET_EMPTY_ERROR(conn->error_info); mysqlnd_local_infile_default(conn); - { - unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ - conn->m->set_client_option(conn, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC); - } - MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1); - if (reconnect) { - MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT); - } - if (conn->persistent) { - MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1); - } - - DBG_INF_FMT("connection_id=%llu", conn->thread_id); #if PHP_MAJOR_VERSION >= 6 { unsigned int as_unicode = 1; - conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, - (char *)&as_unicode TSRMLS_CC); + conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC); DBG_INF("unicode set"); } #endif - if (conn->options.init_commands) { - int current_command = 0; + unsigned int current_command = 0; for (; current_command < conn->options.num_commands; ++current_command) { const char * const command = conn->options.init_commands[current_command]; - MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT); - if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) { - MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT); - goto err; - } - if (conn->last_query_type == QUERY_SELECT) { - MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); - result->m.free_result(result, TRUE TSRMLS_CC); + if (command) { + MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT); + if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) { + MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT); + goto err; + } + if (conn->last_query_type == QUERY_SELECT) { + MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + } } } } + + MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1); + if (reconnect) { + MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT); + } + if (conn->persistent) { + MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1); + } + + DBG_INF_FMT("connection_id=%llu", conn->thread_id); + PACKET_FREE(greet_packet); - PACKET_FREE(auth_packet); - PACKET_FREE(ok_packet); DBG_RETURN(PASS); } err: PACKET_FREE(greet_packet); - PACKET_FREE(auth_packet); - PACKET_FREE(ok_packet); if (errstr) { - DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + DBG_ERR_FMT("[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); /* no mnd_ since we don't allocate it */ efree(errstr); } - if (conn->scheme) { - /* no mnd_ since we don't allocate it */ - pefree(conn->scheme, conn->persistent); - conn->scheme = NULL; - } MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE); @@ -768,15 +869,18 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, unsigned int mysql_flags TSRMLS_DC) { - enum_func_status ret; + enum_func_status ret = FAIL; zend_bool self_alloced = FALSE; DBG_ENTER("mysqlnd_connect"); - DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d", host?host:"", user?user:"", db?db:"", port, mysql_flags); + DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags); if (!conn) { - conn = mysqlnd_init(FALSE); self_alloced = TRUE; + if (!(conn = mysqlnd_init(FALSE))) { + /* OOM */ + DBG_RETURN(NULL); + } } ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket, mysql_flags TSRMLS_CC); @@ -805,7 +909,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, Still the result from the query is PASS */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; DBG_ENTER("mysqlnd_conn::query"); @@ -833,7 +937,7 @@ MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND *conn, const char *query, unsigned i /* {{{ mysqlnd_conn::send_query */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC) { enum_func_status ret; DBG_ENTER("mysqlnd_conn::send_query"); @@ -858,7 +962,7 @@ MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC) if (state <= CONN_READY || state == CONN_QUIT_SENT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed"); - DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%d", state); + DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state); DBG_RETURN(FAIL); } DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC)); @@ -1055,10 +1159,10 @@ _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long s /* {{{ mysqlnd_conn::list_fields */ MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND * conn, const char *table, const char *achtung_wild TSRMLS_DC) { /* db + \0 + wild + \0 (for wild) */ - char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 4 * 2 + 1 + 1], *p; + char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p; size_t table_len, wild_len; MYSQLND_RES *result = NULL; DBG_ENTER("mysqlnd_conn::list_fields"); @@ -1066,14 +1170,16 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons p = buff; if (table && (table_len = strlen(table))) { - memcpy(p, table, MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); - p += table_len; + size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, table, to_copy); + p += to_copy; *p++ = '\0'; } if (achtung_wild && (wild_len = strlen(achtung_wild))) { - memcpy(p, achtung_wild, MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4)); - p += wild_len; + size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, achtung_wild, to_copy); + p += to_copy; *p++ = '\0'; } @@ -1082,12 +1188,15 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons FALSE, TRUE TSRMLS_CC)) { DBG_RETURN(NULL); } + /* Prepare for the worst case. MyISAM goes to 2500 BIT columns, double it for safety. */ - result = mysqlnd_result_init(5000 TSRMLS_CC); - + result = conn->m->result_init(5000, conn->persistent TSRMLS_CC); + if (!result) { + DBG_RETURN(NULL); + } if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) { DBG_ERR("Error ocurred while reading metadata"); @@ -1098,6 +1207,12 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->unbuf) { + /* OOM */ + SET_OOM_ERROR(conn->error_info); + result->m.free_result(result, TRUE TSRMLS_CC); + DBG_RETURN(NULL); + } result->unbuf->eof_reached = TRUE; DBG_RETURN(result); @@ -1107,14 +1222,14 @@ MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, cons /* {{{ mysqlnd_conn::list_method */ MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC) { char *show_query = NULL; size_t show_query_len; MYSQLND_RES *result = NULL; DBG_ENTER("mysqlnd_conn::list_method"); - DBG_INF_FMT("conn=%llu query=%s wild=%d", conn->thread_id, query, achtung_wild); + DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild); if (par1) { if (achtung_wild) { @@ -1134,7 +1249,7 @@ MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query, cons result = conn->m->store_result(conn TSRMLS_CC); } if (show_query != query) { - mnd_efree(show_query); + efree(show_query); /* allocated by spprintf */ } DBG_RETURN(result); } @@ -1177,6 +1292,18 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size } /* }}} */ +/* {{{ mysqlnd_conn::ssl_set */ +void +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) +{ + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC); +} +/* }}} */ + /* {{{ mysqlnd_conn::escape_string */ static ulong @@ -1220,10 +1347,15 @@ MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const SET_ERROR_AFF_ROWS(conn); if (ret == PASS) { if (conn->connect_or_select_db) { - pefree(conn->connect_or_select_db, conn->persistent); + mnd_pefree(conn->connect_or_select_db, conn->persistent); } - conn->connect_or_select_db = pestrndup(db, db_len, conn->persistent); + conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent); conn->connect_or_select_db_len = db_len; + if (!conn->connect_or_select_db) { + /* OOM */ + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + } } DBG_RETURN(ret); } @@ -1246,7 +1378,7 @@ MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) */ SET_ERROR_AFF_ROWS(conn); - DBG_INF_FMT("ret=%d", ret); + DBG_INF_FMT("ret=%u", ret); DBG_RETURN(ret); } /* }}} */ @@ -1254,7 +1386,7 @@ MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC) /* {{{ mysqlnd_conn::stat */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND * conn, char **message, unsigned int * message_len TSRMLS_DC) { enum_func_status ret; MYSQLND_PACKET_STATS * stats_header; @@ -1267,13 +1399,17 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * DBG_RETURN(FAIL); } stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC); + if (!stats_header) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } + if (FAIL == (ret = PACKET_READ(stats_header, conn))) { DBG_RETURN(FAIL); } - *message = stats_header->message; + /* will be freed by Zend, thus don't use the mnd_ allocator */ + *message = estrndup(stats_header->message, stats_header->message_len); *message_len = stats_header->message_len; - /* Ownership transfer */ - stats_header->message = NULL; PACKET_FREE(stats_header); DBG_INF(*message); @@ -1284,7 +1420,7 @@ MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * /* {{{ mysqlnd_conn::kill */ static enum_func_status -MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND * conn, unsigned int pid TSRMLS_DC) { enum_func_status ret; char buff[4]; @@ -1316,7 +1452,7 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC) { enum_func_status ret = PASS; - char *query; + char * query; size_t query_len; const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname); @@ -1338,7 +1474,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * con } else { conn->charset = charset; } - mnd_efree(query); + efree(query); /* allocated by spprintf */ DBG_INF(ret == PASS? "PASS":"FAIL"); DBG_RETURN(ret); @@ -1378,7 +1514,7 @@ MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, uint8_t level TSRML /* {{{ mysqlnd_send_close */ static enum_func_status -mysqlnd_send_close(MYSQLND * conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, send_close)(MYSQLND * const conn TSRMLS_DC) { enum_func_status ret = PASS; @@ -1458,7 +1594,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type c Close now, free_reference will try, if we are last, but that's not a problem. */ - ret = mysqlnd_send_close(conn TSRMLS_CC); + ret = conn->m->send_close(conn TSRMLS_CC); ret = conn->m->free_reference(conn TSRMLS_CC); @@ -1492,7 +1628,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS This will free the object too, of course because references has reached zero. */ - ret = mysqlnd_send_close(conn TSRMLS_CC); + ret = conn->m->send_close(conn TSRMLS_CC); conn->m->dtor(conn TSRMLS_CC); } DBG_RETURN(ret); @@ -1515,7 +1651,7 @@ static void MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::set_state"); - DBG_INF_FMT("New state=%d", new_state); + DBG_INF_FMT("New state=%u", new_state); conn->state = new_state; DBG_VOID_RETURN; } @@ -1686,11 +1822,11 @@ MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC) So there are no more results and we should just return FALSE, error_no has been set */ if (!conn->error_info.error_no) { - DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__); + DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid()); CONN_SET_STATE(conn, CONN_QUIT_SENT); } else { - DBG_INF_FMT("Error from the server : (%d) %s", conn->error_info.error_no, conn->error_info.error); + DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info.error_no, conn->error_info.error); } } @@ -1761,18 +1897,21 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, /* User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3 Stack space is not that expensive, so use a bit more to be protected against - stack overrungs. + buffer overflows. */ size_t user_len; - enum_func_status ret; + enum_func_status ret = FAIL; MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp; - char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1]; + char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ ]; char *p = buffer; + const MYSQLND_CHARSET * old_cs = conn->charset; DBG_ENTER("mysqlnd_conn::change_user"); - DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%d", + DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u", conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 ); + SET_ERROR_AFF_ROWS(conn); + if (!user) { user = ""; } @@ -1784,7 +1923,7 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, } /* 1. user ASCIIZ */ - user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_DB_LEN); + user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN); memcpy(p, user, user_len); p += user_len; *p++ = '\0'; @@ -1800,12 +1939,22 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, /* 3. db ASCIIZ */ if (db[0]) { - size_t db_len = strlen(db); - memcpy(p, db, MIN(db_len, MYSQLND_MAX_ALLOWED_DB_LEN)); + size_t db_len = MIN(strlen(db), MYSQLND_MAX_ALLOWED_DB_LEN); + memcpy(p, db, db_len); p += db_len; } *p++ = '\0'; + /* + 4. request the current charset, or it will be reset to the system one. + 5.0 doesn't support it. Support added in 5.1.23 by fixing the following bug : + Bug #30472 libmysql doesn't reset charset, insert_id after succ. mysql_change_user() call + */ + if (mysqlnd_get_server_version(conn) >= 50123) { + int2store(p, conn->charset->nr); + p+=2; + } + if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer, p - buffer, PROT_LAST /* we will handle the OK packet*/, silent, TRUE TSRMLS_CC)) { @@ -1813,9 +1962,12 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, } chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC); + if (!chg_user_resp) { + SET_OOM_ERROR(conn->error_info); + goto end; + } ret = PACKET_READ(chg_user_resp, conn); conn->error_info = chg_user_resp->error_info; - PACKET_FREE(chg_user_resp); if (conn->error_info.error_no) { ret = FAIL; @@ -1824,29 +1976,44 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn, bug#25371 mysql_change_user() triggers "packets out of sync" When it gets fixed, there should be one more check here */ - if (mysqlnd_get_server_version(conn) > 50113L && - mysqlnd_get_server_version(conn) < 50118L) - { + if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) { MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC); - PACKET_READ(redundant_error_packet, conn); - PACKET_FREE(redundant_error_packet); - DBG_INF_FMT("Server is %d, buggy, sends two ERR messages", mysqlnd_get_server_version(conn)); + if (redundant_error_packet) { + PACKET_READ(redundant_error_packet, conn); + PACKET_FREE(redundant_error_packet); + DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn)); + } else { + SET_OOM_ERROR(conn->error_info); + } } } if (ret == PASS) { - mnd_pefree(conn->user, conn->persistent); - conn->user = pestrndup(user, user_len, conn->persistent); - mnd_pefree(conn->passwd, conn->persistent); - conn->passwd = pestrdup(passwd, conn->persistent); + if (conn->user) { + mnd_pefree(conn->user, conn->persistent); + } + conn->user = mnd_pestrndup(user, user_len, conn->persistent); + + if (conn->passwd) { + mnd_pefree(conn->passwd, conn->persistent); + } + conn->passwd = mnd_pestrdup(passwd, conn->persistent); + if (conn->last_message) { mnd_pefree(conn->last_message, conn->persistent); conn->last_message = NULL; } - conn->charset = conn->greet_charset; memset(&conn->upsert_status, 0, sizeof(conn->upsert_status)); + /* set charset for old servers */ + if (mysqlnd_get_server_version(conn) < 50123) { + ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC); + } + } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) { + /* old authentication with new server !*/ + DBG_ERR(mysqlnd_old_passwd); + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd); } - - SET_ERROR_AFF_ROWS(conn); +end: + PACKET_FREE(chg_user_resp); /* Here we should close all statements. Unbuffered queries should not be a @@ -1865,8 +2032,9 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, const char * const value TSRMLS_DC) { + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_conn::set_client_option"); - DBG_INF_FMT("conn=%llu option=%d", conn->thread_id, option); + DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option); switch (option) { #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_COMPRESS: @@ -1875,10 +2043,16 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, case MYSQL_OPT_READ_TIMEOUT: case MYSQL_OPT_WRITE_TIMEOUT: #endif + case MYSQLND_OPT_SSL_KEY: + case MYSQLND_OPT_SSL_CERT: + case MYSQLND_OPT_SSL_CA: + case MYSQLND_OPT_SSL_CAPATH: + case MYSQLND_OPT_SSL_CIPHER: + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: case MYSQL_OPT_CONNECT_TIMEOUT: case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: case MYSQLND_OPT_NET_READ_BUFFER_SIZE: - conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC); + ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC); break; #if PHP_MAJOR_VERSION >= 6 case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE: @@ -1900,28 +2074,48 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, } break; case MYSQL_INIT_COMMAND: + { + char ** new_init_commands; + char * new_command; DBG_INF("MYSQL_INIT_COMMAND"); DBG_INF_FMT("command=%s", value); /* when num_commands is 0, then realloc will be effectively a malloc call, internally */ - conn->options.init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), - conn->persistent); - conn->options.init_commands[conn->options.num_commands] = pestrdup(value, conn->persistent); + /* Don't assign to conn->options.init_commands because in case of OOM we will lose the pointer and leak */ + new_init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent); + if (!new_init_commands) { + goto oom; + } + conn->options.init_commands = new_init_commands; + new_command = mnd_pestrdup(value, conn->persistent); + if (!new_command) { + goto oom; + } + conn->options.init_commands[conn->options.num_commands] = new_command; ++conn->options.num_commands; break; + } case MYSQL_READ_DEFAULT_FILE: case MYSQL_READ_DEFAULT_GROUP: #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CLIENT_IP: case MYSQL_REPORT_DATA_TRUNCATION: - case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: #endif /* currently not supported. Todo!! */ break; case MYSQL_SET_CHARSET_NAME: + { + char * new_charset_name = mnd_pestrdup(value, conn->persistent); DBG_INF("MYSQL_SET_CHARSET_NAME"); - conn->options.charset_name = pestrdup(value, conn->persistent); + if (!new_charset_name) { + goto oom; + } + if (conn->options.charset_name) { + mnd_pefree(conn->options.charset_name, conn->persistent); + } + conn->options.charset_name = new_charset_name; DBG_INF_FMT("charset=%s", conn->options.charset_name); break; + } #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CHARSET_DIR: case MYSQL_OPT_RECONNECT: @@ -1944,9 +2138,12 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, /* not sure, todo ? */ #endif default: - DBG_RETURN(FAIL); + ret = FAIL; } - DBG_RETURN(PASS); + DBG_RETURN(ret); +oom: + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); } /* }}} */ @@ -1955,7 +2152,7 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) { - MYSQLND_RES *result; + MYSQLND_RES * result; DBG_ENTER("mysqlnd_conn::use_result"); DBG_INF_FMT("conn=%llu", conn->thread_id); @@ -1974,11 +2171,14 @@ MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS); - result = conn->current_result; - conn->current_result = NULL; - result->conn = conn->m->get_reference(conn TSRMLS_CC); + conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC); + result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC); - result = result->m.use_result(result, FALSE TSRMLS_CC); + if (!result) { + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); + } + conn->current_result = NULL; + DBG_RETURN(result); } /* }}} */ @@ -2007,10 +2207,11 @@ MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result; + result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC); + if (!result) { + conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); + } conn->current_result = NULL; - - result = result->m.store_result(result, conn, FALSE TSRMLS_CC); DBG_RETURN(result); } /* }}} */ @@ -2031,7 +2232,7 @@ MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn, MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC); -static void MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC); +static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC); static MYSQLND_CLASS_METHODS_START(mysqlnd_conn) @@ -2099,22 +2300,26 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn) MYSQLND_METHOD(mysqlnd_conn, simple_command), MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response), MYSQLND_METHOD(mysqlnd_conn, restart_psession), - MYSQLND_METHOD(mysqlnd_conn, end_psession) + MYSQLND_METHOD(mysqlnd_conn, end_psession), + MYSQLND_METHOD(mysqlnd_conn, send_close), + + MYSQLND_METHOD(mysqlnd_conn, ssl_set), + mysqlnd_result_init MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_conn::init */ -static void +static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::init"); - conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC); - conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC); mysqlnd_stats_init(&conn->stats, STAT_LAST); - SET_ERROR_AFF_ROWS(conn); - DBG_VOID_RETURN; + conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC); + conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC); + + DBG_RETURN(conn->net && conn->protocol? PASS:FAIL); } /* }}} */ @@ -2126,14 +2331,20 @@ PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC) MYSQLND *ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_init"); - DBG_INF_FMT("persistent=%d", persistent); + DBG_INF_FMT("persistent=%u", persistent); + if (!ret) { + DBG_RETURN(NULL); + } ret->persistent = persistent; ret->m = mysqlnd_conn_methods; CONN_SET_STATE(ret, CONN_ALLOCED); ret->m->get_reference(ret TSRMLS_CC); - ret->m->init(ret TSRMLS_CC); + if (PASS != ret->m->init(ret TSRMLS_CC)) { + ret->m->dtor(ret TSRMLS_CC); + ret = NULL; + } DBG_RETURN(ret); } diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 1b51c4f14..2db4c6dc4 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -17,12 +17,12 @@ | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.h 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd.h 300533 2010-06-17 16:56:48Z pajoye $ */ #ifndef MYSQLND_H #define MYSQLND_H -#define MYSQLND_VERSION "mysqlnd 5.0.7-dev - 091210 - $Revision: 294543 $" +#define MYSQLND_VERSION "mysqlnd 5.0.7-dev - 091210 - $Revision: 300533 $" #define MYSQLND_VERSION_ID 50007 /* This forces inlining of some accessor functions */ @@ -137,60 +137,6 @@ PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQ #define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC) #define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row) TSRMLS_CC) -/*****************************************************************************************************/ -#if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1 - -/* Errors */ -#define mysqlnd_errno(conn) (conn)->error_info.error_no -#define mysqlnd_error(conn) (conn)->error_info.error -#define mysqlnd_sqlstate(conn) ((conn)->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) - -/* Charset */ -#define mysqlnd_character_set_name(conn) (conn)->charset->name - -/* Simple metadata */ -#define mysqlnd_field_count(conn) (conn)->field_count -#define mysqlnd_insert_id(conn) (conn)->upsert_status.last_insert_id -#define mysqlnd_affected_rows(conn) (conn)->upsert_status.affected_rows -#define mysqlnd_warning_count(conn) (conn)->upsert_status.warning_count -#define mysqlnd_info(conn) (conn)->last_message -#define mysqlnd_get_server_info(conn) (conn)->server_version -#define mysqlnd_get_host_info(conn) (conn)->host_info -#define mysqlnd_get_proto_info(conn) (conn)->protocol_version -#define mysqlnd_thread_id(conn) (conn)->thread_id - -#define mysqlnd_num_rows(result) ((result)->stored_data? (result)->stored_data->row_count:0) -#define mysqlnd_num_fields(result) (result)->field_count - -#define mysqlnd_fetch_lengths(result) ((result)->m.fetch_lengths? (result)->m.fetch_lengths((result) TSRMLS_CC) : NULL) - -#define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs)) -#define mysqlnd_field_tell(result) ((result)->meta? (result)->meta->current_field:0) -#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result) TSRMLS_CC) -#define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr) TSRMLS_CC) -#define mysqlnd_fetch_fields(result) (result)->m.fetch_fields((result) TSRMLS_CC) - -/* mysqlnd metadata */ -#define mysqlnd_get_client_info() MYSQLND_VERSION -#define mysqlnd_get_client_version() MYSQLND_VERSION_ID - -/* PS */ -#define mysqlnd_stmt_insert_id(stmt) (stmt)->upsert_status.last_insert_id -#define mysqlnd_stmt_affected_rows(stmt) (stmt)->upsert_status.affected_rows -#define mysqlnd_stmt_num_rows(stmt) (stmt)->result? mysqlnd_num_rows((stmt)->result):0 -#define mysqlnd_stmt_param_count(stmt) (stmt)->param_count -#define mysqlnd_stmt_field_count(stmt) (stmt)->field_count -#define mysqlnd_stmt_warning_count(stmt) (stmt)->upsert_status.warning_count -#define mysqlnd_stmt_errno(stmt) (stmt)->error_info.error_no -#define mysqlnd_stmt_error(stmt) (stmt)->error_info.error -#define mysqlnd_stmt_sqlstate(stmt) ((stmt)->error_info.sqlstate[0] ? (stmt)->error_info.sqlstate:MYSQLND_SQLSTATE_NULL) - - - -/*****************************************************************************************************/ -#else /* Using plain functions */ -/*****************************************************************************************************/ - /* Errors */ #define mysqlnd_errno(conn) (conn)->m->get_error_no((conn) TSRMLS_CC) #define mysqlnd_error(conn) (conn)->m->get_error_str((conn) TSRMLS_CC) @@ -226,6 +172,8 @@ PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS PHPAPI const char * mysqlnd_get_client_info(); PHPAPI unsigned int mysqlnd_get_client_version(); +#define mysqlnd_ssl_set(conn, key, cert, ca, capath, cipher) (conn)->m->ssl_set((conn), (key), (cert), (ca), (capath), (cipher) TSRMLS_CC) + /* PS */ #define mysqlnd_stmt_insert_id(stmt) (stmt)->m->get_last_insert_id((stmt) TSRMLS_CC) #define mysqlnd_stmt_affected_rows(stmt) (stmt)->m->get_affected_rows((stmt) TSRMLS_CC) @@ -233,15 +181,16 @@ PHPAPI unsigned int mysqlnd_get_client_version(); #define mysqlnd_stmt_param_count(stmt) (stmt)->m->get_param_count((stmt) TSRMLS_CC) #define mysqlnd_stmt_field_count(stmt) (stmt)->m->get_field_count((stmt) TSRMLS_CC) #define mysqlnd_stmt_warning_count(stmt) (stmt)->m->get_warning_count((stmt) TSRMLS_CC) +#define mysqlnd_stmt_server_status(stmt) (stmt)->m->get_server_status((stmt) TSRMLS_CC) #define mysqlnd_stmt_errno(stmt) (stmt)->m->get_error_no((stmt) TSRMLS_CC) #define mysqlnd_stmt_error(stmt) (stmt)->m->get_error_str((stmt) TSRMLS_CC) #define mysqlnd_stmt_sqlstate(stmt) (stmt)->m->get_sqlstate((stmt) TSRMLS_CC) -#endif /* MYSQLND_USE_OPTIMISATIONS */ -/*****************************************************************************************************/ PHPAPI void mysqlnd_efree_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC); PHPAPI void mysqlnd_efree_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC); +PHPAPI void mysqlnd_free_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC); +PHPAPI void mysqlnd_free_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC); PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type); @@ -288,16 +237,18 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size #define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row) TSRMLS_CC) #define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC) #define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt) TSRMLS_CC) -#define mysqlnd_stmt_send_long_data(s,p,d,l) (s)->m->send_long_data((s), (p), (d), (l) TSRMLS_CC) -#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_parameters((stmt), (bind) TSRMLS_CC) -#define mysqlnd_stmt_bind_one_param(s,n,z,t) (s)->m->bind_one_parameter((s), (n), (z), (t) TSRMLS_CC) -#define mysqlnd_stmt_refresh_bind_param(s) (s)->m->refresh_bind_param((s) TSRMLS_CC) -#define mysqlnd_stmt_set_param_bind_dtor(s,d) (s)->m->set_param_bind_dtor((s), (d) TSRMLS_CC) -#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC) -#define mysqlnd_stmt_bind_one_result(s,no) (s)->m->bind_one_result((s), (no) TSRMLS_CC) -#define mysqlnd_stmt_set_result_bind_dtor(s,d) (s)->m->set_result_bind_dtor((s), (d) TSRMLS_CC) -#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt)) -#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt) TSRMLS_CC) +#define mysqlnd_stmt_send_long_data(stmt,p,d,l) (stmt)->m->send_long_data((stmt), (p), (d), (l) TSRMLS_CC) +#define mysqlnd_stmt_alloc_param_bind(stmt) (stmt)->m->alloc_parameter_bind((stmt) TSRMLS_CC) +#define mysqlnd_stmt_free_param_bind(stmt,bind) (stmt)->m->free_parameter_bind((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_parameters((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_one_param(stmt,n,z,t) (stmt)->m->bind_one_parameter((stmt), (n), (z), (t) TSRMLS_CC) +#define mysqlnd_stmt_refresh_bind_param(s) (s)->m->refresh_bind_param((s) TSRMLS_CC) +#define mysqlnd_stmt_alloc_result_bind(stmt) (stmt)->m->alloc_result_bind((stmt) TSRMLS_CC) +#define mysqlnd_stmt_free_result_bind(stmt,bind) (stmt)->m->free_result_bind((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC) +#define mysqlnd_stmt_bind_one_result(s,no) (s)->m->bind_one_result((s), (no) TSRMLS_CC) +#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt)) +#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt) TSRMLS_CC) #define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt) TSRMLS_CC) #define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit) TSRMLS_CC) @@ -326,9 +277,16 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqlnd) long net_read_buffer_size; long log_mask; long net_read_timeout; + long mempool_default_size; + long debug_emalloc_fail_threshold; + long debug_ecalloc_fail_threshold; + long debug_erealloc_fail_threshold; + long debug_malloc_fail_threshold; + long debug_calloc_fail_threshold; + long debug_realloc_fail_threshold; ZEND_END_MODULE_GLOBALS(mysqlnd) -ZEND_EXTERN_MODULE_GLOBALS(mysqlnd); +PHPAPI ZEND_DECLARE_MODULE_GLOBALS(mysqlnd); #ifdef ZTS #define MYSQLND_G(v) TSRMG(mysqlnd_globals_id, zend_mysqlnd_globals *, v) diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c deleted file mode 100644 index e54f80c69..000000000 --- a/ext/mysqlnd/mysqlnd_alloc.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 6 | - +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Georg Richter <georg@mysql.com> | - | Andrey Hristov <andrey@mysql.com> | - | Ulf Wendel <uwendel@mysql.com> | - +----------------------------------------------------------------------+ - -*/ - -/* $Id: mysqlnd_alloc.c 272370 2008-12-31 11:15:49Z sebastian $ */ -#include "php.h" -#include "mysqlnd.h" -#include "mysqlnd_priv.h" -#include "mysqlnd_palloc.h" - - -#define MYSQLND_SILENT -#define MYSQLND_DONT_DUMP_STATS - -#define MYSQLND_ZVALS_MAX_CACHE 5000 - - -#if A0 -/* Caching zval allocator */ -zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache); -void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache); -MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(); -MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache); -void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache); -#endif - - -/* - The cache line is a big contiguous array of zval pointers. - Because the CPU cache will cache starting from an address, and not - before it, then we have to organize our structure according to this. - Thus, if 'last_added' is valid pointer (not NULL) then last_added is - increased. When zval is cached, if there is room, last_added is decreased - and then the zval pointer will be assigned to it. This means that some - positions may become hot points and stay in the cache. - Imagine we have 5 pointers in a line - 1. last_added = list_item->ptr_line + cache->max_items; - 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL - 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL - 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL - 0x0 - 0x0 - 0x0 - 0x0 - 0x0 - --- - empty_position, always 0x0 <-- last_added - - 5. free_zval -> if (free_items++ != max_items) {// we can add more - *(--last_added) = zval_ptr; - } - (memory addresses increase downwards) - 0x0 - 0x0 - 0x0 - 0x0 - 0xA <-- last_added - --- - 0x0 - - 6. free_zval -> if (free_items++ != max_items) {// we can add more - *(--last_added) = zval_ptr; - } - 0x0 - 0x0 - 0x0 - 0xB <-- last_added - 0xA - --- - 0x0 - - 7. free_zval -> if (free_items++ != max_items) {// we can add more - *(--last_added) = zval_ptr; - } - 0x0 - 0x0 - 0xC <-- last_added - 0xB - 0xA - --- - 0x0 - - 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; - 0x0 - 0x0 - 0x0 - 0xB <-- last_added - 0xA - --- - 0x0 - - 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; - 0x0 - 0x0 - 0x0 - 0x0 - 0xA <-- last_added - --- - 0x0 - -10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL; - 0x0 - 0x0 - 0x0 - 0x0 - 0x0 - --- - 0x0 <-- last_added - -*/ - - -zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache) -{ - zval *ret = NULL; - -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_get_zval %p] *last_added=%p free_items=%d ", cache, cache? cache->free_list->last_added:NULL, cache->free_items); -#endif - - if (cache) { - if ((ret = *cache->free_list->last_added)) { - *cache->free_list->last_added++ = NULL; - - --cache->free_items; - ++cache->get_hits; - } else { - ++cache->get_misses; - } - } - if (!ret) { - ALLOC_ZVAL(ret); - } - INIT_PZVAL(ret); - -#ifndef MYSQLND_SILENT - php_printf("ret=%p\n", ret); -#endif - return ret; -} - -static -void mysqlnd_alloc_cache_prealloc(MYSQLND_ZVAL_CACHE * const cache, unsigned int count) -{ - zval *data; - cache->free_items = count; - while (count--) { - MAKE_STD_ZVAL(data); - ZVAL_NULL(data); -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_prealloc %p] items=%d data=%p\n", cache, cache->free_items, data); -#endif - - *(--cache->free_list->last_added) = data; - } -} - -void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache) -{ - if (!cache || Z_REFCOUNT_PP(zv) > 1 || cache->max_items == cache->free_items) { -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_zval_ptr_dtor %p]1 last_added-1=%p *zv=%p\n", cache->free_list->last_added, *zv); -#endif - /* We can't cache zval's with refcount > 1 */ - zval_ptr_dtor(zv); - if (cache) { - if (cache->max_items == cache->free_items) { - ++cache->put_full_misses; - } else { - ++cache->put_refcount_misses; - } - } - } else { - /* refcount is 1 and there is place. Go, cache it! */ - ++cache->free_items; - zval_dtor(*zv); - ZVAL_NULL(*zv); - *(--cache->free_list->last_added) = *zv; - ++cache->put_hits; - } -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_zval_ptr_dtor %p] free_items=%d\n", cache, cache->free_items); -#endif -} - - -MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(void) -{ - MYSQLND_ZVAL_CACHE *ret = ecalloc(1, sizeof(MYSQLND_ZVAL_CACHE)); - -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_init_cache %p]\n", ret); -#endif - - ret->max_items = MYSQLND_ZVALS_MAX_CACHE; - ret->free_items = 0; - ret->references = 1; - - /* Let's have always one, so we don't need to do a check in get_zval */ - ret->free_list = ecalloc(1, sizeof(struct st_mysqlnd_zval_list)); - - /* One more for empty position of last_added */ - ret->free_list->ptr_line = ecalloc(ret->max_items + 1, sizeof(zval *)); - ret->free_list->last_added = ret->free_list->ptr_line + ret->max_items; - - mysqlnd_alloc_cache_prealloc(ret, (ret->max_items / 100) * 100); - - return ret; -} - - -MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache) -{ - if (cache) { - cache->references++; - } - return cache; -} - - -static -void mysqlnd_alloc_free_cache(MYSQLND_ZVAL_CACHE *cache) -{ -#ifndef MYSQLND_SILENT - uint i = 0; - php_printf("[mysqlnd_alloc_free_cache %p]\n", cache); -#endif - - while (*cache->free_list->last_added) { -#ifndef MYSQLND_SILENT - php_printf("\t[free_item %d %p]\n", i++, *cache->free_list->last_added); -#endif - zval_ptr_dtor(cache->free_list->last_added); - cache->free_list->last_added++; - } -#ifndef MYSQLND_DONT_DUMP_STATS - php_printf("CACHE STATS:\n\tGET\n\t\tHITS:%lu\n\t\tMISSES=%lu\n\t\tHIT RATIO=%1.3f\n\t" - "PUT\n\t\tHITS:%lu\n\t\tFULL_MISS=%lu\n\t\tREFC_MISS=%lu\n\t\tHIT RATIO=%1.3f\n\n", - cache->get_hits, cache->get_misses, (1.0*cache->get_hits/(cache->get_hits + cache->get_misses)), - cache->put_hits, cache->put_full_misses, cache->put_refcount_misses, - (1.0 * cache->put_hits / (cache->put_hits + cache->put_full_misses + cache->put_refcount_misses))); -#endif - efree(cache->free_list->ptr_line); - efree(cache->free_list); - efree(cache); -} - - - -void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache) -{ -#ifndef MYSQLND_SILENT - php_printf("[mysqlnd_alloc_free_cache_reference %p] refs=%d\n", *cache, (*cache)->references); -#endif - if (*cache && --(*cache)->references == 0) { - mysqlnd_alloc_free_cache(*cache); - } - *cache = NULL; -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/ext/mysqlnd/mysqlnd_block_alloc.c b/ext/mysqlnd/mysqlnd_block_alloc.c index 8c7f7d951..1288e7690 100644 --- a/ext/mysqlnd/mysqlnd_block_alloc.c +++ b/ext/mysqlnd/mysqlnd_block_alloc.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_block_alloc.c 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_block_alloc.c 298917 2010-05-03 17:07:18Z andrey $ */ #include "php.h" #include "mysqlnd.h" @@ -29,23 +29,7 @@ /* {{{ mysqlnd_mempool_free_chunk */ static void -mysqlnd_mempool_free_contents(MYSQLND_MEMORY_POOL * pool TSRMLS_DC) -{ - unsigned int i; - DBG_ENTER("mysqlnd_mempool_dtor"); - for (i = 0; i < pool->free_chunk_list_elements; i++) { - MYSQLND_MEMORY_POOL_CHUNK * chunk = pool->free_chunk_list[i]; - chunk->free_chunk(chunk, FALSE TSRMLS_CC); - } - - DBG_VOID_RETURN; -} -/* }}} */ - - -/* {{{ mysqlnd_mempool_free_chunk */ -static void -mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, zend_bool cache_it TSRMLS_DC) +mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk TSRMLS_DC) { MYSQLND_MEMORY_POOL * pool = chunk->pool; DBG_ENTER("mysqlnd_mempool_free_chunk"); @@ -62,20 +46,14 @@ mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, zend_bool cache_it } else { mnd_free(chunk->ptr); } - if (cache_it && pool->free_chunk_list_elements < MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE) { - chunk->ptr = NULL; - pool->free_chunk_list[pool->free_chunk_list_elements++] = chunk; - } else { - /* We did not cache it -> free it */ - mnd_free(chunk); - } + mnd_free(chunk); DBG_VOID_RETURN; } /* }}} */ /* {{{ mysqlnd_mempool_resize_chunk */ -static void +static enum_func_status mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size TSRMLS_DC) { DBG_ENTER("mysqlnd_mempool_resize_chunk"); @@ -90,6 +68,9 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz if ((chunk->size + pool->free_size) < size) { zend_uchar *new_ptr; new_ptr = mnd_malloc(size); + if (!new_ptr) { + DBG_RETURN(FAIL); + } memcpy(new_ptr, chunk->ptr, chunk->size); chunk->ptr = new_ptr; pool->free_size += chunk->size; @@ -107,17 +88,25 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz } else { zend_uchar *new_ptr; new_ptr = mnd_malloc(size); + if (!new_ptr) { + DBG_RETURN(FAIL); + } memcpy(new_ptr, chunk->ptr, chunk->size); chunk->ptr = new_ptr; chunk->size = size; - chunk->pool = NULL; /* now we have no pool memory */ - pool->refcount--; + chunk->pool = NULL; /* now we have non-pool memory */ + pool->refcount--; } } } else { - chunk->ptr = mnd_realloc(chunk->ptr, size); + zend_uchar *new_ptr = mnd_realloc(chunk->ptr, size); + if (!new_ptr) { + DBG_RETURN(FAIL); + } + chunk->ptr = new_ptr; + } - DBG_VOID_RETURN; + DBG_RETURN(PASS); } /* }}} */ @@ -129,30 +118,31 @@ MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool MYSQLND_MEMORY_POOL_CHUNK *chunk = NULL; DBG_ENTER("mysqlnd_mempool_get_chunk"); - if (pool->free_chunk_list_elements) { - chunk = pool->free_chunk_list[--pool->free_chunk_list_elements]; - } else { - chunk = mnd_malloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK)); - } - - chunk->free_chunk = mysqlnd_mempool_free_chunk; - chunk->resize_chunk = mysqlnd_mempool_resize_chunk; - chunk->size = size; - /* - Should not go over MYSQLND_MAX_PACKET_SIZE, since we - expect non-arena memory in mysqlnd_wireprotocol.c . We - realloc the non-arena memory. - */ - chunk->pool = pool; - if (size > pool->free_size) { - chunk->ptr = mnd_malloc(size); - chunk->from_pool = FALSE; - } else { - chunk->from_pool = TRUE; - ++pool->refcount; - chunk->ptr = pool->arena + (pool->arena_size - pool->free_size); - /* Last step, update free_size */ - pool->free_size -= size; + chunk = mnd_malloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK)); + if (chunk) { + chunk->free_chunk = mysqlnd_mempool_free_chunk; + chunk->resize_chunk = mysqlnd_mempool_resize_chunk; + chunk->size = size; + /* + Should not go over MYSQLND_MAX_PACKET_SIZE, since we + expect non-arena memory in mysqlnd_wireprotocol.c . We + realloc the non-arena memory. + */ + chunk->pool = pool; + if (size > pool->free_size) { + chunk->from_pool = FALSE; + chunk->ptr = mnd_malloc(size); + if (!chunk->ptr) { + chunk->free_chunk(chunk TSRMLS_CC); + chunk = NULL; + } + } else { + chunk->from_pool = TRUE; + ++pool->refcount; + chunk->ptr = pool->arena + (pool->arena_size - pool->free_size); + /* Last step, update free_size */ + pool->free_size -= size; + } } DBG_RETURN(chunk); } @@ -166,13 +156,17 @@ mysqlnd_mempool_create(size_t arena_size TSRMLS_DC) /* We calloc, because we free(). We don't mnd_calloc() for a reason. */ MYSQLND_MEMORY_POOL * ret = mnd_calloc(1, sizeof(MYSQLND_MEMORY_POOL)); DBG_ENTER("mysqlnd_mempool_create"); - - ret->free_size = ret->arena_size = arena_size; - ret->refcount = 0; - /* OOM ? */ - ret->arena = mnd_malloc(ret->arena_size); - ret->get_chunk = mysqlnd_mempool_get_chunk; - + if (ret) { + ret->get_chunk = mysqlnd_mempool_get_chunk; + ret->free_size = ret->arena_size = arena_size ? arena_size : 0; + ret->refcount = 0; + /* OOM ? */ + ret->arena = mnd_malloc(ret->arena_size); + if (!ret->arena) { + mysqlnd_mempool_destroy(ret TSRMLS_CC); + ret = NULL; + } + } DBG_RETURN(ret); } /* }}} */ @@ -184,7 +178,6 @@ mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool TSRMLS_DC) { DBG_ENTER("mysqlnd_mempool_destroy"); /* mnd_free will reference LOCK_access and might crash, depending on the caller...*/ - mysqlnd_mempool_free_contents(pool TSRMLS_CC); mnd_free(pool->arena); mnd_free(pool); DBG_VOID_RETURN; diff --git a/ext/mysqlnd/mysqlnd_block_alloc.h b/ext/mysqlnd/mysqlnd_block_alloc.h index 0607d4209..1b7ccfe80 100644 --- a/ext/mysqlnd/mysqlnd_block_alloc.h +++ b/ext/mysqlnd/mysqlnd_block_alloc.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_block_alloc.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_block_alloc.h 298217 2010-04-20 13:50:34Z felipe $ */ #ifndef MYSQLND_BLOCK_ALLOC_H #define MYSQLND_BLOCK_ALLOC_H diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c index 7ec9bfbc2..d8a2c5ac0 100644 --- a/ext/mysqlnd/mysqlnd_charset.c +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -663,7 +663,7 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const name /* {{{ mysqlnd_cset_escape_quotes */ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr, - const char *escapestr, size_t escapestr_len TSRMLS_DC) + const char * escapestr, size_t escapestr_len TSRMLS_DC) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; @@ -717,7 +717,7 @@ PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char /* {{{ mysqlnd_cset_escape_slashes */ PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr, - const char *escapestr, size_t escapestr_len TSRMLS_DC) + const char * escapestr, size_t escapestr_len TSRMLS_DC) { const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; diff --git a/ext/mysqlnd/mysqlnd_charset.h b/ext/mysqlnd/mysqlnd_charset.h index 46cf03f1e..c5f0d7cb1 100644 --- a/ext/mysqlnd/mysqlnd_charset.h +++ b/ext/mysqlnd/mysqlnd_charset.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2010 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c index 2c2764faf..575efacac 100644 --- a/ext/mysqlnd/mysqlnd_debug.c +++ b/ext/mysqlnd/mysqlnd_debug.c @@ -1,8 +1,8 @@ -;/* +/* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_debug.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_debug.c 300635 2010-06-21 15:32:26Z andrey $ */ #include "php.h" #include "mysqlnd.h" @@ -28,7 +28,6 @@ #include "mysqlnd_statistics.h" #include "zend_builtin_functions.h" - static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace"; #ifdef ZTS @@ -59,6 +58,8 @@ static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc"; static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc"; static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc"; static const char mysqlnd_free_name[] = "_mysqlnd_free"; +static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup"; +static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup"; const char * mysqlnd_debug_std_no_trace_funcs[] = { @@ -74,6 +75,7 @@ const char * mysqlnd_debug_std_no_trace_funcs[] = mysqlnd_calloc_name, mysqlnd_realloc_name, mysqlnd_free_name, + mysqlnd_pestrndup_name, mysqlnd_read_header_name, mysqlnd_read_body_name, NULL /* must be always last */ @@ -113,10 +115,8 @@ MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self, line_buffer[6], level_buffer[7]; MYSQLND_ZTS(self); - if (!self->stream) { - if (FAIL == self->m->open(self, FALSE)) { - return FAIL; - } + if (!self->stream && FAIL == self->m->open(self, FALSE)) { + return FAIL; } if (level == -1) { @@ -185,10 +185,10 @@ MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self, pipe_buffer, type? type:"", message); ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; - efree(message_line); + efree(message_line); /* allocated by spprintf */ if (flags & MYSQLND_DEBUG_FLUSH) { self->m->close(self); - self->m->open(self, TRUE); + self->m->open(self, TRUE); } return ret; } @@ -213,10 +213,8 @@ MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self, line_buffer[6], level_buffer[7]; MYSQLND_ZTS(self); - if (!self->stream) { - if (FAIL == self->m->open(self, FALSE)) { - return FAIL; - } + if (!self->stream && FAIL == self->m->open(self, FALSE)) { + return FAIL; } if (level == -1) { @@ -291,11 +289,11 @@ MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self, efree(buffer); ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; - efree(message_line); + efree(message_line); /* allocated by spprintf */ if (flags & MYSQLND_DEBUG_FLUSH) { self->m->close(self); - self->m->open(self, TRUE); + self->m->open(self, TRUE); } return ret; } @@ -312,7 +310,7 @@ MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { return FALSE; } - if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { + if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) { return FALSE; } @@ -321,7 +319,7 @@ MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, while (*p) { if (*p == func_name) { zend_stack_push(&self->call_stack, "", sizeof("")); - return FALSE; + return FALSE; } p++; } @@ -343,14 +341,13 @@ MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, /* {{{ mysqlnd_res_meta::func_leave */ static enum_func_status -MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, - const char * const file) +MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file) { char *func_name; if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { return PASS; } - if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { + if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) { return PASS; } @@ -359,7 +356,7 @@ MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int lin if (func_name[0] == '\0') { ; /* don't log that function */ } else if (!zend_hash_num_elements(&self->not_filtered_functions) || - 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) + 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) { self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name); } @@ -493,7 +490,7 @@ MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const } else { #if 0 php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Expected list of functions for '%c' found none", mode[i]); + "Expected list of functions for '%c' found none", mode[i]); #endif } state = PARSER_WAIT_COLON; @@ -582,7 +579,7 @@ MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const state = PARSER_WAIT_COLON; } else if (state == PARSER_WAIT_COLON) { #if 0 - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]); #endif } break; @@ -653,24 +650,40 @@ PHPAPI void _mysqlnd_debug(const char * mode TSRMLS_DC) #define __zend_filename "/unknown/unknown" #define __zend_lineno 0 #endif - + +#define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s)) +#define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p)) +#define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p)) /* {{{ _mysqlnd_emalloc */ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = &MYSQLND_G(debug_emalloc_fail_threshold); DBG_ENTER(mysqlnd_emalloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); - ret = emalloc(size); - DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); - DBG_INF_FMT("size=%lu ptr=%p", size, ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = emalloc(REAL_SIZE(size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif + + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + + if (ret && collect_memory_statistics) { + *(size_t *) ret = size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -679,26 +692,33 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D) void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = persistent? &MYSQLND_G(debug_malloc_fail_threshold):&MYSQLND_G(debug_emalloc_fail_threshold); DBG_ENTER(mysqlnd_pemalloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - if (persistent == FALSE) { - DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); - } - ret = pemalloc(size, persistent); - DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent); - - if (persistent == FALSE) { - DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = pemalloc(REAL_SIZE(size), persistent); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; } +#endif + + DBG_INF_FMT("size=%lu ptr=%p persistent=%u", size, ret, persistent); - if (MYSQLND_G(collect_memory_statistics)) { + if (ret && collect_memory_statistics) { enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT; - enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT; + *(size_t *) ret = size; MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -707,18 +727,31 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = &MYSQLND_G(debug_ecalloc_fail_threshold); DBG_ENTER(mysqlnd_ecalloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); - ret = ecalloc(nmemb, size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = ecalloc(nmemb, REAL_SIZE(size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); - DBG_INF_FMT("size=%lu ptr=%p", size, ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (ret && collect_memory_statistics) { + *(size_t *) ret = size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -727,26 +760,33 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = persistent? &MYSQLND_G(debug_calloc_fail_threshold):&MYSQLND_G(debug_ecalloc_fail_threshold); DBG_ENTER(mysqlnd_pecalloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - if (persistent == FALSE) { - DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); - } - ret = pecalloc(nmemb, size, persistent); - DBG_INF_FMT("size=%lu ptr=%p", size, ret); - - if (persistent == FALSE) { - DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = pecalloc(nmemb, REAL_SIZE(size), persistent); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; } +#endif + + DBG_INF_FMT("size=%lu ptr=%p", size, ret); - if (MYSQLND_G(collect_memory_statistics)) { + if (ret && collect_memory_statistics) { enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT; - enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT; + *(size_t *) ret = size; MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -755,19 +795,31 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0; + long * threshold = &MYSQLND_G(debug_erealloc_fail_threshold); DBG_ENTER(mysqlnd_erealloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size); - DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + DBG_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size); - ret = erealloc(ptr, new_size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif - DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); - DBG_INF_FMT("new_ptr=%p", ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size); + DBG_INF_FMT("new_ptr=%p", (char*)ret); + if (ret && collect_memory_statistics) { + *(size_t *) ret = new_size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -776,27 +828,34 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0; + long * threshold = persistent? &MYSQLND_G(debug_realloc_fail_threshold):&MYSQLND_G(debug_erealloc_fail_threshold); DBG_ENTER(mysqlnd_perealloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent); - if (persistent == FALSE) { - DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); - } + DBG_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent); - ret = perealloc(ptr, new_size, persistent); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif - DBG_INF_FMT("new_ptr=%p", ret); + DBG_INF_FMT("new_ptr=%p", (char*)ret); - if (persistent == FALSE) { - DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); - } - MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT); - if (MYSQLND_G(collect_memory_statistics)) { + if (ret && collect_memory_statistics) { enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT; - enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT; + enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT; + *(size_t *) ret = new_size; MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -804,15 +863,23 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL /* {{{ _mysqlnd_efree */ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) { + size_t free_amount = 0; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); DBG_ENTER(mysqlnd_efree_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); DBG_INF_FMT("ptr=%p", ptr); - DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); - MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT); - efree(ptr); + if (ptr) { + if (collect_memory_statistics) { + free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t)); + DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount); + } + efree(REAL_PTR(ptr)); + } - DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); + if (collect_memory_statistics) { + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount); + } DBG_VOID_RETURN; } /* }}} */ @@ -821,20 +888,23 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) /* {{{ _mysqlnd_pefree */ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D) { + size_t free_amount = 0; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); DBG_ENTER(mysqlnd_pefree_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent); - if (persistent == FALSE) { - DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); - } - - pefree(ptr, persistent); + DBG_INF_FMT("ptr=%p persistent=%u", ptr, persistent); - if (persistent == FALSE) { - DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); + if (ptr) { + if (collect_memory_statistics) { + free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t)); + DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount); + } + pefree(REAL_PTR(ptr), persistent); } - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT); + + if (collect_memory_statistics) { + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1, + persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount); } DBG_VOID_RETURN; } @@ -844,16 +914,29 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D) void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = &MYSQLND_G(debug_malloc_fail_threshold); DBG_ENTER(mysqlnd_malloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - ret = malloc(size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = malloc(REAL_SIZE(size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif - DBG_INF_FMT("size=%lu ptr=%p", size, ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (ret && collect_memory_statistics) { + *(size_t *) ret = size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -862,16 +945,29 @@ void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D) void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = &MYSQLND_G(debug_calloc_fail_threshold); DBG_ENTER(mysqlnd_calloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); - ret = calloc(nmemb, size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = calloc(nmemb, REAL_SIZE(size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif - DBG_INF_FMT("size=%lu ptr=%p", size, ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size); + DBG_INF_FMT("size=%lu ptr=%p", size, ret); + if (ret && collect_memory_statistics) { + *(size_t *) ret = size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -880,19 +976,32 @@ void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D) { void *ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + long * threshold = &MYSQLND_G(debug_realloc_fail_threshold); DBG_ENTER(mysqlnd_realloc_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr); DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC)); - ret = realloc(ptr, new_size); +#ifdef PHP_DEBUG + /* -1 is also "true" */ + if (*threshold) { +#endif + ret = realloc(REAL_PTR(ptr), REAL_SIZE(new_size)); +#ifdef PHP_DEBUG + --*threshold; + } else if (*threshold == 0) { + ret = NULL; + } +#endif - DBG_INF_FMT("new_ptr=%p", ret); + DBG_INF_FMT("new_ptr=%p", (char*)ret); - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size); + if (ret && collect_memory_statistics) { + *(size_t *) ret = new_size; + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size); } - DBG_RETURN(ret); + DBG_RETURN(FAKE_PTR(ret)); } /* }}} */ @@ -900,19 +1009,241 @@ void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D) /* {{{ _mysqlnd_free */ void _mysqlnd_free(void *ptr MYSQLND_MEM_D) { + size_t free_amount = 0; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); DBG_ENTER(mysqlnd_free_name); DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); DBG_INF_FMT("ptr=%p", ptr); - free(ptr); + if (ptr) { + if (collect_memory_statistics) { + free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t)); + DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount); + } + free(REAL_PTR(ptr)); + } - if (MYSQLND_G(collect_memory_statistics)) { - MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT); + if (collect_memory_statistics) { + MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount); } DBG_VOID_RETURN; } /* }}} */ +#define SMART_STR_START_SIZE 2048 +#define SMART_STR_PREALLOC 512 +#include "ext/standard/php_smart_str.h" + + +/* {{{ _mysqlnd_pestrndup */ +char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D) +{ + char * ret; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + DBG_ENTER(mysqlnd_pestrndup_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p", ptr); + + ret = pemalloc(REAL_SIZE(length) + 1, persistent); + { + size_t l = length; + char * p = (char *) ptr; + char * dest = (char *) FAKE_PTR(ret); + while (*p && l--) { + *dest++ = *p++; + } + *dest = '\0'; + } + + if (collect_memory_statistics) { + *(size_t *) ret = length; + MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT); + } + + DBG_RETURN(FAKE_PTR(ret)); +} +/* }}} */ + + +/* {{{ _mysqlnd_pestrdup */ +char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D) +{ + char * ret; + smart_str tmp_str = {0, 0, 0}; + const char * p = ptr; + zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics); + DBG_ENTER(mysqlnd_pestrdup_name); + DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + DBG_INF_FMT("ptr=%p", ptr); + do { + smart_str_appendc(&tmp_str, *p); + } while (*p++); + + ret = pemalloc(tmp_str.len + sizeof(size_t), persistent); + memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len); + + if (ret && collect_memory_statistics) { + *(size_t *) ret = tmp_str.len; + MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT); + } + smart_str_free(&tmp_str); + + DBG_RETURN(FAKE_PTR(ret)); +} +/* }}} */ + +#if MYSQLND_DEBUG_MEMORY + +/* {{{ mysqlnd_zend_mm_emalloc */ +static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D) +{ + return emalloc(size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_pemalloc */ +static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) +{ + return pemalloc(size, persistent); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_ecalloc */ +static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) +{ + return ecalloc(nmemb, size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_pecalloc */ +static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D) +{ + return pecalloc(nmemb, size, persistent); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_erealloc */ +static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ + return erealloc(ptr, new_size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_perealloc */ +static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D) +{ + return perealloc(ptr, new_size, persistent); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_efree */ +static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D) +{ + efree(ptr); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_pefree */ +static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D) +{ + pefree(ptr, persistent); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_malloc */ +static void * mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D) +{ + return malloc(size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_calloc */ +static void * mysqlnd_zend_mm_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D) +{ + return calloc(nmemb, size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_realloc */ +static void * mysqlnd_zend_mm_realloc(void * ptr, size_t new_size MYSQLND_MEM_D) +{ + return realloc(ptr, new_size); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_free */ +static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D) +{ + free(ptr); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_pestrndup */ +static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D) +{ + return pestrndup(ptr, length, persistent); +} +/* }}} */ + + +/* {{{ mysqlnd_zend_mm_pestrdup */ +static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D) +{ + return pestrdup(ptr, persistent); +} +/* }}} */ + +#endif + + +#define MYSQLND_DEBUG_MEMORY 1 + +PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator = +{ +#if MYSQLND_DEBUG_MEMORY + _mysqlnd_emalloc, + _mysqlnd_pemalloc, + _mysqlnd_ecalloc, + _mysqlnd_pecalloc, + _mysqlnd_erealloc, + _mysqlnd_perealloc, + _mysqlnd_efree, + _mysqlnd_pefree, + _mysqlnd_malloc, + _mysqlnd_calloc, + _mysqlnd_realloc, + _mysqlnd_free, + _mysqlnd_pestrndup, + _mysqlnd_pestrdup +#else + mysqlnd_zend_mm_emalloc, + mysqlnd_zend_mm_pemalloc, + mysqlnd_zend_mm_ecalloc, + mysqlnd_zend_mm_pecalloc, + mysqlnd_zend_mm_erealloc, + mysqlnd_zend_mm_perealloc, + mysqlnd_zend_mm_efree, + mysqlnd_zend_mm_pefree, + mysqlnd_zend_mm_malloc, + mysqlnd_zend_mm_calloc, + mysqlnd_zend_mm_realloc, + mysqlnd_zend_mm_free, + mysqlnd_zend_mm_pestrndup, + mysqlnd_zend_mm_pestrdup +#endif +}; @@ -963,11 +1294,11 @@ void _mysqlnd_free(void *ptr MYSQLND_MEM_D) zval copy; \ int use_copy; \ zend_make_printable_zval(*tmp, ©, &use_copy); \ - TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ - zval_dtor(©); \ + TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ + zval_dtor(©); \ } else { \ - TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \ - } \ + TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \ + } \ } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h index be46cf0cb..f79a8969b 100644 --- a/ext/mysqlnd/mysqlnd_debug.h +++ b/ext/mysqlnd/mysqlnd_debug.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,15 +18,13 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_debug.h 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_debug.h 299755 2010-05-25 21:54:21Z andrey $ */ #ifndef MYSQLND_DEBUG_H #define MYSQLND_DEBUG_H #include "zend_stack.h" -#define MYSQLND_DEBUG_MEMORY 1 - struct st_mysqlnd_debug_methods { enum_func_status (*open)(MYSQLND_DEBUG *self, zend_bool reopen); @@ -42,6 +40,7 @@ struct st_mysqlnd_debug_methods enum_func_status (*free_handle)(MYSQLND_DEBUG *self); }; + struct st_mysqlnd_debug { php_stream *stream; @@ -62,22 +61,6 @@ PHPAPI extern const char * mysqlnd_debug_std_no_trace_funcs[]; PHPAPI MYSQLND_DEBUG * mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC); -#define MYSQLND_MEM_D TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC - - -PHPAPI void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D); -PHPAPI void _mysqlnd_efree(void *ptr MYSQLND_MEM_D); -PHPAPI void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D); -PHPAPI void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D); -PHPAPI void _mysqlnd_free(void *ptr MYSQLND_MEM_D); - PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC); #if defined(__GNUC__) @@ -127,37 +110,60 @@ static inline void DBG_ENTER(const char * const func_name) {} #endif -#if MYSQLND_DEBUG_MEMORY +#define MYSQLND_MEM_D TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC +#define MYSQLND_MEM_C TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC -#define mnd_emalloc(size) _mysqlnd_emalloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_pemalloc(size, pers) _mysqlnd_pemalloc((size), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_ecalloc(nmemb, size) _mysqlnd_ecalloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_pecalloc(nmemb, size, p) _mysqlnd_pecalloc((nmemb), (size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_erealloc(ptr, new_size) _mysqlnd_erealloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_perealloc(ptr, new_size, p) _mysqlnd_perealloc((ptr), (new_size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_efree(ptr) _mysqlnd_efree((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_pefree(ptr, pers) _mysqlnd_pefree((ptr), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_malloc(size) _mysqlnd_malloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_calloc(nmemb, size) _mysqlnd_calloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_realloc(ptr, new_size) _mysqlnd_realloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) -#define mnd_free(ptr) _mysqlnd_free((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +struct st_mysqlnd_allocator_methods +{ + void * (*m_emalloc)(size_t size MYSQLND_MEM_D); + void * (*m_pemalloc)(size_t size, zend_bool persistent MYSQLND_MEM_D); + void * (*m_ecalloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D); + void * (*m_pecalloc)(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D); + void * (*m_erealloc)(void *ptr, size_t new_size MYSQLND_MEM_D); + void * (*m_perealloc)(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D); + void (*m_efree)(void *ptr MYSQLND_MEM_D); + void (*m_pefree)(void *ptr, zend_bool persistent MYSQLND_MEM_D); + void * (*m_malloc)(size_t size MYSQLND_MEM_D); + void * (*m_calloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D); + void * (*m_realloc)(void *ptr, size_t new_size MYSQLND_MEM_D); + void (*m_free)(void *ptr MYSQLND_MEM_D); + char * (*m_pestrndup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D); + char * (*m_pestrdup)(const char * const ptr, zend_bool persistent MYSQLND_MEM_D); +}; -#else +PHPAPI extern struct st_mysqlnd_allocator_methods mysqlnd_allocator; -#define mnd_emalloc(size) emalloc((size)) -#define mnd_pemalloc(size, pers) pemalloc((size), (pers)) -#define mnd_ecalloc(nmemb, size) ecalloc((nmemb), (size)) -#define mnd_pecalloc(nmemb, size, p) pecalloc((nmemb), (size), (p)) -#define mnd_erealloc(ptr, new_size) erealloc((ptr), (new_size)) -#define mnd_perealloc(ptr, new_size, p) perealloc((ptr), (new_size), (p)) -#define mnd_efree(ptr) efree((ptr)) -#define mnd_pefree(ptr, pers) pefree((ptr), (pers)) -#define mnd_malloc(size) malloc((size)) -#define mnd_calloc(nmemb, size) calloc((nmemb), (size)) -#define mnd_realloc(ptr, new_size) realloc((ptr), (new_size)) -#define mnd_free(ptr) free((ptr)) -#endif +PHPAPI void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D); +PHPAPI void _mysqlnd_efree(void *ptr MYSQLND_MEM_D); +PHPAPI void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D); +PHPAPI void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D); +PHPAPI void _mysqlnd_free(void *ptr MYSQLND_MEM_D); +PHPAPI char * _mysqlnd_pestrndup(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D); +PHPAPI char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D); + + +#define mnd_emalloc(size) mysqlnd_allocator.m_emalloc((size) MYSQLND_MEM_C) +#define mnd_pemalloc(size, pers) mysqlnd_allocator.m_pemalloc((size), (pers) MYSQLND_MEM_C) +#define mnd_ecalloc(nmemb, size) mysqlnd_allocator.m_ecalloc((nmemb), (size) MYSQLND_MEM_C) +#define mnd_pecalloc(nmemb, size, p) mysqlnd_allocator.m_pecalloc((nmemb), (size), (p) MYSQLND_MEM_C) +#define mnd_erealloc(ptr, new_size) mysqlnd_allocator.m_erealloc((ptr), (new_size) MYSQLND_MEM_C) +#define mnd_perealloc(ptr, new_size, p) mysqlnd_allocator.m_perealloc((ptr), (new_size), (p) MYSQLND_MEM_C) +#define mnd_efree(ptr) mysqlnd_allocator.m_efree((ptr) MYSQLND_MEM_C) +#define mnd_pefree(ptr, pers) mysqlnd_allocator.m_pefree((ptr), (pers) MYSQLND_MEM_C) +#define mnd_malloc(size) mysqlnd_allocator.m_malloc((size) MYSQLND_MEM_C) +#define mnd_calloc(nmemb, size) mysqlnd_allocator.m_calloc((nmemb), (size) MYSQLND_MEM_C) +#define mnd_realloc(ptr, new_size) mysqlnd_allocator.m_realloc((ptr), (new_size) MYSQLND_MEM_C) +#define mnd_free(ptr) mysqlnd_allocator.m_free((ptr) MYSQLND_MEM_C) +#define mnd_pestrndup(ptr, size, pers) mysqlnd_allocator.m_pestrndup((ptr), (size), (pers) MYSQLND_MEM_C) +#define mnd_pestrdup(ptr, pers) mysqlnd_allocator.m_pestrdup((ptr), (pers) MYSQLND_MEM_C) #endif /* MYSQLND_DEBUG_H */ diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 281319c73..dbc938b12 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_enum_n_def.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_enum_n_def.h 298650 2010-04-27 11:02:51Z andrey $ */ #ifndef MYSQLND_ENUM_N_DEF_H #define MYSQLND_ENUM_N_DEF_H @@ -39,6 +39,8 @@ #define MYSQLND_SQLSTATE_LENGTH 5 #define MYSQLND_SQLSTATE_NULL "00000" +#define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte . MySQL supports now only 16 char, but let it be forward compatible */ +#define MYSQLND_MAX_ALLOWED_DB_LEN 1024 /* 256 char * 4byte. MySQL supports now only 64 char in the tables, but on the FS could be different. Forward compatible. */ #define MYSQLND_NET_CMD_BUFFER_MIN_SIZE 4096 #define MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR "4096" @@ -90,6 +92,8 @@ #define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */ #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */ +#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) + typedef enum mysqlnd_extension { MYSQLND_MYSQL = 0, @@ -156,6 +160,12 @@ typedef enum mysqlnd_option #endif MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202, MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203, + MYSQLND_OPT_SSL_KEY = 204, + MYSQLND_OPT_SSL_CERT = 205, + MYSQLND_OPT_SSL_CA = 206, + MYSQLND_OPT_SSL_CAPATH = 207, + MYSQLND_OPT_SSL_CIPHER = 208, + MYSQLND_OPT_SSL_PASSPHRASE = 209 } enum_mysqlnd_option; @@ -259,11 +269,11 @@ typedef enum mysqlnd_server_option /* - /-----> CONN_CLOSE <---------------\ - | ^ \ - | | \ + /-----> CONN_CLOSE <---------------\ + | ^ \ + | | \ CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA - ^ | + ^ | \-------------------------------------/ */ typedef enum mysqlnd_connection_state @@ -388,19 +398,25 @@ typedef enum mysqlnd_collected_stats STAT_STMT_CLOSE_EXPLICIT, STAT_STMT_CLOSE_IMPLICIT, STAT_MEM_EMALLOC_COUNT, - STAT_MEM_EMALLOC_AMMOUNT, + STAT_MEM_EMALLOC_AMOUNT, STAT_MEM_ECALLOC_COUNT, - STAT_MEM_ECALLOC_AMMOUNT, + STAT_MEM_ECALLOC_AMOUNT, STAT_MEM_EREALLOC_COUNT, - STAT_MEM_EREALLOC_AMMOUNT, + STAT_MEM_EREALLOC_AMOUNT, STAT_MEM_EFREE_COUNT, + STAT_MEM_EFREE_AMOUNT, STAT_MEM_MALLOC_COUNT, - STAT_MEM_MALLOC_AMMOUNT, + STAT_MEM_MALLOC_AMOUNT, STAT_MEM_CALLOC_COUNT, - STAT_MEM_CALLOC_AMMOUNT, + STAT_MEM_CALLOC_AMOUNT, STAT_MEM_REALLOC_COUNT, - STAT_MEM_REALLOC_AMMOUNT, + STAT_MEM_REALLOC_AMOUNT, STAT_MEM_FREE_COUNT, + STAT_MEM_FREE_AMOUNT, + STAT_MEM_ESTRNDUP_COUNT, + STAT_MEM_STRNDUP_COUNT, + STAT_MEM_ESTRDUP_COUNT, + STAT_MEM_STRDUP_COUNT, STAT_TEXT_TYPE_FETCHED_NULL, STAT_TEXT_TYPE_FETCHED_BIT, STAT_TEXT_TYPE_FETCHED_INT8, @@ -534,15 +550,15 @@ enum php_mysqlnd_server_command #define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1 -#define MYSQLND_REFRESH_GRANT 1 /* Refresh grant tables */ -#define MYSQLND_REFRESH_LOG 2 /* Start on new log file */ -#define MYSQLND_REFRESH_TABLES 4 /* close all tables */ -#define MYSQLND_REFRESH_HOSTS 8 /* Flush host cache */ -#define MYSQLND_REFRESH_STATUS 16 /* Flush status variables */ -#define MYSQLND_REFRESH_THREADS 32 /* Flush thread cache */ -#define MYSQLND_REFRESH_SLAVE 64 /* Reset master info and restart slave */ -#define MYSQLND_REFRESH_MASTER 128 /* Remove all bin logs in the index */ -#define MYSQLND_REFRESH_BACKUP_LOG 0x200000L +#define MYSQLND_REFRESH_GRANT 1 /* Refresh grant tables */ +#define MYSQLND_REFRESH_LOG 2 /* Start on new log file */ +#define MYSQLND_REFRESH_TABLES 4 /* close all tables */ +#define MYSQLND_REFRESH_HOSTS 8 /* Flush host cache */ +#define MYSQLND_REFRESH_STATUS 16 /* Flush status variables */ +#define MYSQLND_REFRESH_THREADS 32 /* Flush thread cache */ +#define MYSQLND_REFRESH_SLAVE 64 /* Reset master info and restart slave */ +#define MYSQLND_REFRESH_MASTER 128 /* Remove all bin logs in the index */ +#define MYSQLND_REFRESH_BACKUP_LOG 0x200000L #endif /* MYSQLND_ENUM_N_DEF_H */ diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h index 78f48c97b..12c61b0d6 100644 --- a/ext/mysqlnd/mysqlnd_libmysql_compat.h +++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -79,6 +79,7 @@ #define mysql_set_server_option(r,o) mysqlnd_set_server_option((r), (o)) #define mysql_set_character_set(r,a) mysqlnd_set_character_set((r), (a)) #define mysql_sqlstate(r) mysqlnd_sqlstate((r)) +#define mysql_ssl_set(c,key,cert,ca,capath,cipher) mysqlnd_ssl_set((c), (key), (cert), (ca), (capath), (cipher)) #define mysql_stmt_affected_rows(s) mysqlnd_stmt_affected_rows((s)) #define mysql_stmt_field_count(s) mysqlnd_stmt_field_count((s)) #define mysql_stmt_param_count(s) mysqlnd_stmt_param_count((s)) @@ -121,14 +122,14 @@ #define mysql_warning_count(r) mysqlnd_warning_count((r)) #define mysql_eof(r) (((r)->unbuf && (r)->unbuf->eof_reached) || (r)->stored_data) -#define REFRESH_GRANT MYSQLND_REFRESH_GRANT -#define REFRESH_LOG MYSQLND_REFRESH_LOG -#define REFRESH_TABLES MYSQLND_REFRESH_TABLES -#define REFRESH_HOSTS MYSQLND_REFRESH_HOSTS -#define REFRESH_STATUS MYSQLND_REFRESH_STATUS -#define REFRESH_THREADS MYSQLND_REFRESH_THREADS -#define REFRESH_SLAVE MYSQLND_REFRESH_SLAVE -#define REFRESH_MASTER MYSQLND_REFRESH_MASTER -#define REFRESH_BACKUP_LOG MYSQLND_REFRESH_BACKUP_LOG +#define REFRESH_GRANT MYSQLND_REFRESH_GRANT +#define REFRESH_LOG MYSQLND_REFRESH_LOG +#define REFRESH_TABLES MYSQLND_REFRESH_TABLES +#define REFRESH_HOSTS MYSQLND_REFRESH_HOSTS +#define REFRESH_STATUS MYSQLND_REFRESH_STATUS +#define REFRESH_THREADS MYSQLND_REFRESH_THREADS +#define REFRESH_SLAVE MYSQLND_REFRESH_SLAVE +#define REFRESH_MASTER MYSQLND_REFRESH_MASTER +#define REFRESH_BACKUP_LOG MYSQLND_REFRESH_BACKUP_LOG #endif /* MYSQLND_LIBMYSQL_COMPAT_H */ diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c index 77670ec1f..1885cbc6d 100644 --- a/ext/mysqlnd/mysqlnd_loaddata.c +++ b/ext/mysqlnd/mysqlnd_loaddata.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -37,7 +37,7 @@ if (c) {\ #define FREE_CALLBACK_ARGS(a, b, c)\ if (a) {\ - for (i=b; i < c; i++) {\ + for (i = b; i < c; i++) {\ zval_ptr_dtor(a[i]);\ mnd_efree(a[i]);\ }\ @@ -48,12 +48,17 @@ if (a) {\ static int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS_DC) { - MYSQLND_INFILE_INFO *info; - php_stream_context *context = NULL; + MYSQLND_INFILE_INFO *info; + php_stream_context *context = NULL; DBG_ENTER("mysqlnd_local_infile_init"); - *ptr = info = ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); + info = ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); + if (!info) { + DBG_RETURN(1); + } + + *ptr = info; /* check open_basedir */ if (PG(open_basedir)) { @@ -85,7 +90,7 @@ int mysqlnd_local_infile_read(void *ptr, char *buf, unsigned int buf_len TSRMLS_ MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; int count; - DBG_ENTER("mysqlnd_local_infile_read"); + DBG_ENTER("mysqlnd_local_infile_read"); count = (int)php_stream_read(info->fd, buf, buf_len); @@ -171,7 +176,7 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w char *buf; char empty_packet[MYSQLND_HEADER_SIZE]; enum_func_status result = FAIL; - unsigned int buflen = 4096; + unsigned int buflen = 4096; void *info = NULL; int bufsize; size_t ret; @@ -199,16 +204,14 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w /* error occured */ strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); conn->error_info.error_no = - infile.local_infile_error(info, conn->error_info.error, - sizeof(conn->error_info.error) TSRMLS_CC); + infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); /* write empty packet to server */ ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC); goto infile_error; } /* read data */ - while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, - buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { + while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { if ((ret = conn->net->m.send(conn, buf, bufsize TSRMLS_CC)) < 0) { DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); @@ -227,8 +230,8 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w *is_warning = TRUE; DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); - conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error, - sizeof(conn->error_info.error) TSRMLS_CC); + conn->error_info.error_no = + infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC); goto infile_error; } diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c index 8f4c46e9d..207fa53f0 100644 --- a/ext/mysqlnd/mysqlnd_net.c +++ b/ext/mysqlnd/mysqlnd_net.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -102,7 +102,11 @@ MYSQLND_METHOD(mysqlnd_net, network_write)(MYSQLND * const conn, const zend_ucha static enum_func_status MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const scheme, size_t scheme_len, zend_bool persistent, char **errstr, int * errcode TSRMLS_DC) { +#if PHP_API_VERSION < 20100412 unsigned int streams_options = ENFORCE_SAFE_MODE; +#else + unsigned int streams_options = 0; +#endif unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; char * hashed_details = NULL; int hashed_details_len = 0; @@ -121,14 +125,14 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const schem tv.tv_usec = 0; } + DBG_INF_FMT("calling php_stream_xport_create"); net->stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags, hashed_details, (net->options.timeout_connect) ? &tv : NULL, NULL /*ctx*/, errstr, errcode); - if (*errstr || !net->stream) { if (hashed_details) { - efree(hashed_details); + efree(hashed_details); /* allocated by spprintf */ } *errcode = CR_CONNECTION_ERROR; DBG_RETURN(FAIL); @@ -142,8 +146,7 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const schem */ zend_rsrc_list_entry *le; - if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, - (void*) &le) == SUCCESS) { + if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, (void*) &le) == SUCCESS) { /* in_free will let streams code skip destructing - big HACK, but STREAMS suck big time regarding persistent streams. @@ -159,13 +162,21 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const schem #endif efree(hashed_details); } + /* + Streams are not meant for C extensions! Thus we need a hack. Every connected stream will + be registered as resource (in EG(regular_list). So far, so good. However, it won't be + unregistered till the script ends. So, we need to take care of that. + */ + net->stream->in_free = 1; + zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id); + net->stream->in_free = 0; if (!net->options.timeout_read) { /* should always happen because read_timeout cannot be set via API */ net->options.timeout_read = (unsigned int) MYSQLND_G(net_read_timeout); } - if (net->options.timeout_read) - { + if (net->options.timeout_read) { + DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read); tv.tv_sec = net->options.timeout_read; tv.tv_usec = 0; php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); @@ -188,13 +199,18 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const schem /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */ -#define STORE_HEADER_SIZE(safe_storage, buffer) int4store((safe_storage), (*(uint32_t *)(buffer))) +#define COPY_HEADER(T,A) do { \ + *(((char *)(T))) = *(((char *)(A)));\ + *(((char *)(T))+1) = *(((char *)(A))+1);\ + *(((char *)(T))+2) = *(((char *)(A))+2);\ + *(((char *)(T))+3) = *(((char *)(A))+3); } while (0) +#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer)) #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) /* {{{ mysqlnd_net::send */ /* IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!! - This is done for performance reasons in the caller of this function. + This is done for performance reasons in the caller of this function. Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. Neither are quick, thus the clients of this function are obligated to do what they are asked for. @@ -216,14 +232,14 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t size_t to_be_sent; DBG_ENTER("mysqlnd_net::send"); - DBG_INF_FMT("conn=%llu count=%lu compression=%d", conn->thread_id, count, net->compressed); + DBG_INF_FMT("conn=%llu count=%lu compression=%u", conn->thread_id, count, net->compressed); net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE; if (net->compressed == TRUE) { size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE); - DBG_INF_FMT("compress_buf_size=%d", comp_buf_size); - compress_buf = emalloc(comp_buf_size); + DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size); + compress_buf = mnd_emalloc(comp_buf_size); } do { @@ -238,7 +254,9 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t STORE_HEADER_SIZE(safe_storage, uncompressed_payload); int3store(uncompressed_payload, to_be_sent); int1store(uncompressed_payload + 3, net->packet_no); - if (PASS == net->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), tmp_complen, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC)) { + if (PASS == net->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), tmp_complen, + uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC)) + { int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE); payload_size = tmp_complen; } else { @@ -250,14 +268,15 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t int3store(compress_buf, payload_size); int1store(compress_buf + 3, net->packet_no); - DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE); + DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE); ret = conn->net->m.network_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC); net->compressed_envelope_packet_no++; #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY if (res == Z_OK) { size_t decompressed_size = left + MYSQLND_HEADER_SIZE; - zend_uchar * decompressed_data = malloc(decompressed_size); - int error = net->m.decode(decompressed_data, decompressed_size, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size); + zend_uchar * decompressed_data = mnd_malloc(decompressed_size); + int error = net->m.decode(decompressed_data, decompressed_size, + compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size); if (error == Z_OK) { int i; DBG_INF("success decompressing"); @@ -271,7 +290,7 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t } else { DBG_INF("error decompressing"); } - free(decompressed_data); + mnd_free(decompressed_data); } #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */ } else @@ -299,7 +318,7 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t */ } while (ret && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE)); - DBG_INF_FMT("packet_size=%d packet_no=%d", left, net->packet_no); + DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no); /* Even for zero size payload we have to send a packet */ if (!ret) { DBG_ERR_FMT("Can't %u send bytes", count); @@ -314,7 +333,7 @@ MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t net->stream->chunk_size = old_chunk_size; if (compress_buf) { - efree(compress_buf); + mnd_efree(compress_buf); } DBG_RETURN(ret); } @@ -407,7 +426,7 @@ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, /* we need to decompress the data */ if (decompressed_size) { - compressed_data = emalloc(net_payload_size); + compressed_data = mnd_emalloc(net_payload_size); if (FAIL == conn->net->m.network_read(conn, compressed_data, net_payload_size TSRMLS_CC)) { ret = FAIL; goto end; @@ -427,7 +446,7 @@ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, } end: if (compressed_data) { - efree(compressed_data); + mnd_efree(compressed_data); } DBG_RETURN(ret); } @@ -440,16 +459,21 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, size_t uncompressed_data_len, const zend_uchar * const compressed_data, size_t compressed_data_len TSRMLS_DC) { +#ifdef MYSQLND_COMPRESSION_ENABLED int error; uLongf tmp_complen = uncompressed_data_len; DBG_ENTER("mysqlnd_net::decode"); error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len); - DBG_INF_FMT("compressed data: decomp_len=%d compressed_size=%d", tmp_complen, compressed_data_len); + DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len); if (error != Z_OK) { DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); } DBG_RETURN(error == Z_OK? PASS:FAIL); +#else + DBG_ENTER("mysqlnd_net::decode"); + DBG_RETURN(FAIL); +#endif } /* }}} */ @@ -459,6 +483,7 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t compress_buffer_len, const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC) { +#ifdef MYSQLND_COMPRESSION_ENABLED int error; uLongf tmp_complen = compress_buffer_len; DBG_ENTER("mysqlnd_net::encode"); @@ -467,15 +492,19 @@ MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t compres if (error != Z_OK) { DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); } else { - DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen); + DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen); } DBG_RETURN(error == Z_OK? PASS:FAIL); +#else + DBG_ENTER("mysqlnd_net::encode"); + DBG_RETURN(FAIL); +#endif } /* }}} */ /* {{{ mysqlnd_net::receive */ -static size_t +static enum_func_status MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC) { size_t to_read = count; @@ -510,16 +539,16 @@ MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, zend_uchar * buffer, size_t net_payload_size = uint3korr(net_header); packet_no = uint1korr(net_header + 3); if (net->compressed_envelope_packet_no != packet_no) { - DBG_ERR_FMT("Transport level: packets out of order. Expected %d received %d. Packet size=%d", + DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, net->compressed_envelope_packet_no, packet_no, net_payload_size); - php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC, + php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, net->compressed_envelope_packet_no, packet_no, net_payload_size); DBG_RETURN(FAIL); } net->compressed_envelope_packet_no++; #ifdef MYSQLND_DUMP_HEADER_N_BODY - DBG_INF_FMT("HEADER: hwd_packet_no=%d size=%3d", packet_no, net_payload_size); + DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (unsigned long) net_payload_size); #endif /* Now let's read from the wire, decompress it and fill the read buffer */ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC); @@ -545,7 +574,7 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mysqlnd_option option, const char * const value TSRMLS_DC) { DBG_ENTER("mysqlnd_net::set_client_option"); - DBG_INF_FMT("option=%d", option); + DBG_INF_FMT("option=%u", option); switch (option) { case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE"); @@ -569,6 +598,63 @@ MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mys DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT"); net->options.timeout_connect = *(unsigned int*) value; break; + case MYSQLND_OPT_SSL_KEY: + { + zend_bool pers = net->persistent; + if (net->options.ssl_key) { + mnd_pefree(net->options.ssl_key, pers); + } + net->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CERT: + { + zend_bool pers = net->persistent; + if (net->options.ssl_cert) { + mnd_pefree(net->options.ssl_cert, pers); + } + net->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CA: + { + zend_bool pers = net->persistent; + if (net->options.ssl_ca) { + mnd_pefree(net->options.ssl_ca, pers); + } + net->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CAPATH: + { + zend_bool pers = net->persistent; + if (net->options.ssl_capath) { + mnd_pefree(net->options.ssl_capath, pers); + } + net->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CIPHER: + { + zend_bool pers = net->persistent; + if (net->options.ssl_cipher) { + mnd_pefree(net->options.ssl_cipher, pers); + } + net->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_PASSPHRASE: + { + zend_bool pers = net->persistent; + if (net->options.ssl_passphrase) { + mnd_pefree(net->options.ssl_passphrase, pers); + } + net->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE; + break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_READ_TIMEOUT: DBG_INF("MYSQL_OPT_READ_TIMEOUT"); @@ -639,12 +725,113 @@ MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum } /* }}} */ +/* + in libmyusql, if cert and !key then key=cert +*/ +/* {{{ mysqlnd_net::enable_ssl */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) +{ +#ifdef MYSQLND_SSL_SUPPORTED + php_stream_context *context = php_stream_context_alloc(); + DBG_ENTER("mysqlnd_net::enable_ssl"); + if (!context) { + DBG_RETURN(FAIL); + } + + if (net->options.ssl_key) { + zval key_zval; + ZVAL_STRING(&key_zval, net->options.ssl_key, 0); + DBG_INF("key"); + php_stream_context_set_option(context, "ssl", "local_pk", &key_zval); + } + if (net->options.ssl_verify_peer) { + zval verify_peer_zval; + ZVAL_TRUE(&verify_peer_zval); + DBG_INF("verify peer"); + php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval); + } + if (net->options.ssl_cert) { + zval cert_zval; + ZVAL_STRING(&cert_zval, net->options.ssl_cert, 0); + DBG_INF_FMT("local_cert=%s", net->options.ssl_cert); + php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval); + if (!net->options.ssl_key) { + php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval); + } + } + if (net->options.ssl_ca) { + zval cafile_zval; + ZVAL_STRING(&cafile_zval, net->options.ssl_ca, 0); + DBG_INF_FMT("cafile=%s", net->options.ssl_ca); + php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval); + } + if (net->options.ssl_capath) { + zval capath_zval; + ZVAL_STRING(&capath_zval, net->options.ssl_capath, 0); + DBG_INF_FMT("capath=%s", net->options.ssl_capath); + php_stream_context_set_option(context, "ssl", "cafile", &capath_zval); + } + if (net->options.ssl_passphrase) { + zval passphrase_zval; + ZVAL_STRING(&passphrase_zval, net->options.ssl_passphrase, 0); + php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval); + } + if (net->options.ssl_cipher) { + zval cipher_zval; + ZVAL_STRING(&cipher_zval, net->options.ssl_cipher, 0); + DBG_INF_FMT("ciphers=%s", net->options.ssl_cipher); + php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval); + } + php_stream_context_set(net->stream, context); + if (php_stream_xport_crypto_setup(net->stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0 || + php_stream_xport_crypto_enable(net->stream, 1 TSRMLS_CC) < 0) + { + DBG_ERR("Cannot connect to MySQL by using SSL"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot connect to MySQL by using SSL"); + DBG_RETURN(FAIL); + } + /* + get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli, + then the context would not survive cleaning of EG(regular_list), where it is registered, as a + resource. What happens is that after this destruction any use of the network will mean usage + of the context, which means usage of already freed memory, bad. Actually we don't need this + context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it. + */ + php_stream_context_set(net->stream, NULL); + + if (net->options.timeout_read) { + struct timeval tv; + DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read); + tv.tv_sec = net->options.timeout_read; + tv.tv_usec = 0; + php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); + } + + DBG_RETURN(PASS); +#else + DBG_ENTER("mysqlnd_net::enable_ssl"); + DBG_RETURN(PASS); +#endif +} +/* }}} */ + + +/* {{{ mysqlnd_net::disable_ssl */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_net::disable_ssl"); + DBG_RETURN(PASS); +} +/* }}} */ /* {{{ mysqlnd_net::set_client_option */ static void MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC) { + zend_bool pers = net->persistent; DBG_ENTER("mysqlnd_net::free_contents"); #ifdef MYSQLND_COMPRESSION_ENABLED @@ -652,10 +839,47 @@ MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC) net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC); } #endif + if (net->options.ssl_key) { + mnd_pefree(net->options.ssl_key, pers); + net->options.ssl_key = NULL; + } + if (net->options.ssl_cert) { + mnd_pefree(net->options.ssl_cert, pers); + net->options.ssl_cert = NULL; + } + if (net->options.ssl_ca) { + mnd_pefree(net->options.ssl_ca, pers); + net->options.ssl_ca = NULL; + } + if (net->options.ssl_capath) { + mnd_pefree(net->options.ssl_capath, pers); + net->options.ssl_capath = NULL; + } + if (net->options.ssl_cipher) { + mnd_pefree(net->options.ssl_cipher, pers); + net->options.ssl_cipher = NULL; + } + DBG_VOID_RETURN; } /* }}} */ +static +MYSQLND_CLASS_METHODS_START(mysqlnd_net) + MYSQLND_METHOD(mysqlnd_net, connect), + MYSQLND_METHOD(mysqlnd_net, send), + MYSQLND_METHOD(mysqlnd_net, receive), + MYSQLND_METHOD(mysqlnd_net, set_client_option), + MYSQLND_METHOD(mysqlnd_net, network_read), + MYSQLND_METHOD(mysqlnd_net, network_write), + MYSQLND_METHOD(mysqlnd_net, decode), + MYSQLND_METHOD(mysqlnd_net, encode), + MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data), + MYSQLND_METHOD(mysqlnd_net, free_contents), + MYSQLND_METHOD(mysqlnd_net, enable_ssl), + MYSQLND_METHOD(mysqlnd_net, disable_ssl) +MYSQLND_CLASS_METHODS_END; + /* {{{ mysqlnd_net_init */ PHPAPI MYSQLND_NET * @@ -665,23 +889,15 @@ mysqlnd_net_init(zend_bool persistent TSRMLS_DC) MYSQLND_NET * net = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_net_init"); - DBG_INF_FMT("persistent=%d", persistent); - net->persistent = persistent; - - net->m.connect = MYSQLND_METHOD(mysqlnd_net, connect); - net->m.send = MYSQLND_METHOD(mysqlnd_net, send); - net->m.receive = MYSQLND_METHOD(mysqlnd_net, receive); - net->m.set_client_option = MYSQLND_METHOD(mysqlnd_net, set_client_option); - net->m.network_read = MYSQLND_METHOD(mysqlnd_net, network_read); - net->m.network_write = MYSQLND_METHOD(mysqlnd_net, network_write); - net->m.decode = MYSQLND_METHOD(mysqlnd_net, decode); - net->m.encode = MYSQLND_METHOD(mysqlnd_net, encode); - net->m.consume_uneaten_data = MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data); - net->m.free_contents = MYSQLND_METHOD(mysqlnd_net, free_contents); + DBG_INF_FMT("persistent=%u", persistent); + if (net) { + net->persistent = persistent; + net->m = mysqlnd_mysqlnd_net_methods; - { - unsigned int buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/ - net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC); + { + unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ + net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC); + } } DBG_RETURN(net); } @@ -703,13 +919,12 @@ mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC) mnd_pefree(net->cmd_buffer.buffer, pers); net->cmd_buffer.buffer = NULL; } - if (net->stream) { DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract); if (pers) { php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR); } else { - php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE); + php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE); } net->stream = NULL; } @@ -733,6 +948,14 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsi /* }}} */ +/* {{{ mysqlnd_net_get_methods */ +PHPAPI struct st_mysqlnd_net_methods * +mysqlnd_net_get_methods() +{ + return &mysqlnd_mysqlnd_net_methods; +} +/* }}} */ + /* * Local variables: diff --git a/ext/mysqlnd/mysqlnd_net.h b/ext/mysqlnd/mysqlnd_net.h index 8f75a8eb2..d52fca5e7 100644 --- a/ext/mysqlnd/mysqlnd_net.h +++ b/ext/mysqlnd/mysqlnd_net.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -26,6 +26,8 @@ PHPAPI MYSQLND_NET * mysqlnd_net_init(zend_bool persistent TSRMLS_DC); PHPAPI void mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC); +PHPAPI struct st_mysqlnd_net_methods * mysqlnd_net_get_methods(); + #endif /* MYSQLND_NET_H */ /* diff --git a/ext/mysqlnd/mysqlnd_portability.h b/ext/mysqlnd/mysqlnd_portability.h index ed0a5c8b1..532c71959 100644 --- a/ext/mysqlnd/mysqlnd_portability.h +++ b/ext/mysqlnd/mysqlnd_portability.h @@ -38,6 +38,12 @@ This file is public domain and comes with NO WARRANTY of any kind */ # include <ext/mysqlnd/php_mysqlnd_config.h> #endif /* _WIN32... */ +#if __STDC_VERSION__ < 199901L && !defined(atoll) + /* "inline" is a keyword */ + #define atoll atol +#endif + + #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index c9f40cda6..e55522208 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_priv.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_priv.h 298781 2010-04-29 15:49:51Z andrey $ */ #ifndef MYSQLND_PRIV_H #define MYSQLND_PRIV_H @@ -42,8 +42,8 @@ #endif #define MYSQLND_CLASS_METHOD_TABLE_NAME(class) mysqlnd_##class##_methods -#define MYSQLND_CLASS_METHODS_START(class) struct st_##class##_methods MYSQLND_CLASS_METHOD_TABLE_NAME(class) = { -#define MYSQLND_CLASS_METHODS_END } +#define MYSQLND_CLASS_METHODS_START(class) struct st_##class##_methods MYSQLND_CLASS_METHOD_TABLE_NAME(class) = { +#define MYSQLND_CLASS_METHODS_END } #if PHP_MAJOR_VERSION < 6 #define mysqlnd_array_init(arg, field_count) \ @@ -93,10 +93,10 @@ #define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \ {\ if ((buf)) { \ - pefree((buf), (persistent)); \ + mnd_pefree((buf), (persistent)); \ } \ if ((message)) { \ - (buf) = pestrndup((message), (len), (persistent)); \ + (buf) = mnd_pestrndup((message), (len), (persistent)); \ } else { \ buf = NULL; \ } \ @@ -106,7 +106,7 @@ #define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \ {\ if ((buf)) { \ - pefree((buf), (persistent)); \ + mnd_pefree((buf), (persistent)); \ (buf) = NULL; \ } \ (buf_len) = 0; \ @@ -127,6 +127,9 @@ strlcpy(error_info.error, (c), sizeof(error_info.error)); \ } +#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR(error_info, CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory) + + #define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(stmt->error_info, a, b, c) @@ -134,8 +137,8 @@ #define CONN_GET_STATE(c) (c)->m->get_state((c) TSRMLS_CC) #define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s) TSRMLS_CC) #else -#define CONN_GET_STATE(c) (c)->state -#define CONN_SET_STATE(c, s) (c)->state = s +#define CONN_GET_STATE(c) ((c)->state) +#define CONN_SET_STATE(c, s) ((c)->state = s) #endif @@ -157,6 +160,7 @@ extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + PHPAPI extern const char * const mysqlnd_old_passwd; PHPAPI extern const char * const mysqlnd_out_of_sync; PHPAPI extern const char * const mysqlnd_server_gone; +PHPAPI extern const char * const mysqlnd_out_of_memory; enum_func_status mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 06ec510f5..fa8be3ec2 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_ps.c 300735 2010-06-24 19:52:13Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -39,11 +39,7 @@ const char * const mysqlnd_stmt_not_prepared = "Statement not prepared"; static struct st_mysqlnd_stmt_methods *mysqlnd_stmt_methods; /* Exported by mysqlnd_ps_codec.c */ -zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, - zend_bool *free_buffer TSRMLS_DC); - - -MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC); +enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC); enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, @@ -57,20 +53,25 @@ static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_D static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC); -static enum_func_status mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC); /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; - MYSQLND_RES *result; + MYSQLND * conn; + MYSQLND_RES * result; zend_bool to_cache = FALSE; DBG_ENTER("mysqlnd_stmt::store_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { DBG_RETURN(NULL); @@ -78,7 +79,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); + DBG_RETURN(s->m->use_result(s TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -90,7 +91,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) DBG_RETURN(NULL); } - stmt->default_rset_handler = stmt->m->store_result; + stmt->default_rset_handler = s->m->store_result; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -101,7 +102,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered; result->m.fetch_lengths = NULL;/* makes no sense */ - result->result_set_memory_pool = mysqlnd_mempool_create(16000 TSRMLS_CC); + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE, to_cache TSRMLS_CC); @@ -125,14 +126,20 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::get_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC) { - MYSQLND *conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; MYSQLND_RES *result; DBG_ENTER("mysqlnd_stmt::get_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { DBG_RETURN(NULL); @@ -140,7 +147,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); + DBG_RETURN(s->m->use_result(s TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -154,31 +161,47 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) SET_EMPTY_ERROR(stmt->conn->error_info); MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = mysqlnd_result_init(stmt->result->field_count TSRMLS_CC); + do { + result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC); + if (!result) { + SET_OOM_ERROR(stmt->conn->error_info); + break; + } - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + if (!result->meta) { + SET_OOM_ERROR(stmt->conn->error_info); + break; + } - if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { - stmt->upsert_status.affected_rows = result->stored_data->row_count; - stmt->state = MYSQLND_STMT_PREPARED; - result->type = MYSQLND_RES_PS_BUF; - } else { - stmt->error_info = conn->error_info; - stmt->state = MYSQLND_STMT_PREPARED; - } + if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { + stmt->upsert_status.affected_rows = result->stored_data->row_count; + stmt->state = MYSQLND_STMT_PREPARED; + result->type = MYSQLND_RES_PS_BUF; + } else { + stmt->error_info = conn->error_info; + stmt->state = MYSQLND_STMT_PREPARED; + break; + } + DBG_RETURN(result); + } while (0); - DBG_RETURN(result); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + DBG_RETURN(NULL); } /* }}} */ /* {{{ mysqlnd_stmt::more_results */ static zend_bool -MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::more_results"); /* (conn->state == CONN_NEXT_RESULT_PENDING) too */ - DBG_RETURN((stmt->conn && (stmt->conn->upsert_status.server_status & + DBG_RETURN((stmt && stmt->conn && (stmt->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS))? TRUE: FALSE); @@ -188,50 +211,61 @@ MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::next_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC) { - MYSQLND *conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt::next_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); - if (!conn || - CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || - !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) || - !stmt->result) - { + if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS)) { DBG_RETURN(FAIL); } /* Free space for next result */ - mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + mysqlnd_internal_free_stmt_content(s TSRMLS_CC); - DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC)); + DBG_RETURN(s->m->parse_execute_response(s TSRMLS_CC)); } /* }}} */ /* {{{ mysqlnd_stmt_skip_metadata */ static enum_func_status -mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; /* Follows parameter metadata, we have just to skip it, as libmysql does */ unsigned int i = 0; - enum_func_status ret = PASS; + enum_func_status ret = FAIL; MYSQLND_PACKET_RES_FIELD * field_packet; DBG_ENTER("mysqlnd_stmt_skip_metadata"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC); - field_packet->skip_parsing = TRUE; - for (;i < stmt->param_count; i++) { - if (FAIL == PACKET_READ(field_packet, stmt->conn)) { - ret = FAIL; - break; + if (!field_packet) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); + } else { + ret = PASS; + field_packet->skip_parsing = TRUE; + for (;i < stmt->param_count; i++) { + if (FAIL == PACKET_READ(field_packet, stmt->conn)) { + ret = FAIL; + break; + } } + PACKET_FREE(field_packet); } - PACKET_FREE(field_packet); DBG_RETURN(ret); } @@ -240,26 +274,34 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_read_prepare_response */ static enum_func_status -mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp; - enum_func_status ret = PASS; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_stmt_read_prepare_response"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC); + if (!prepare_resp) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); + goto done; + } + if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) { - ret = FAIL; goto done; } if (0xFF == prepare_resp->error_code) { stmt->error_info = stmt->conn->error_info = prepare_resp->error_info; - ret = FAIL; goto done; } - + ret = PASS; stmt->stmt_id = prepare_resp->stmt_id; stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp->warning_count; stmt->field_count = stmt->conn->field_count = prepare_resp->field_count; @@ -274,28 +316,37 @@ done: /* {{{ mysqlnd_stmt_prepare_read_eof */ static enum_func_status -mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_PACKET_EOF * fields_eof; - enum_func_status ret; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_stmt_prepare_read_eof"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC); - if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) { - if (stmt->result) { - stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); - mnd_efree(stmt->result); - memset(stmt, 0, sizeof(MYSQLND_STMT)); - stmt->state = MYSQLND_STMT_INITTED; - } + if (!fields_eof) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); } else { - stmt->upsert_status.server_status = fields_eof->server_status; - stmt->upsert_status.warning_count = fields_eof->warning_count; - stmt->state = MYSQLND_STMT_PREPARED; + if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) { + if (stmt->result) { + stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); + mnd_efree(stmt->result); + memset(stmt, 0, sizeof(MYSQLND_STMT_DATA)); + stmt->state = MYSQLND_STMT_INITTED; + } + } else { + stmt->upsert_status.server_status = fields_eof->server_status; + stmt->upsert_status.warning_count = fields_eof->warning_count; + stmt->state = MYSQLND_STMT_PREPARED; + } + PACKET_FREE(fields_eof); } - PACKET_FREE(fields_eof); DBG_RETURN(ret); } @@ -304,11 +355,16 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::prepare */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC) { - MYSQLND_STMT *stmt_to_prepare = stmt; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND_STMT * s_to_prepare = s; + MYSQLND_STMT_DATA * stmt_to_prepare = stmt; DBG_ENTER("mysqlnd_stmt::prepare"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_ERROR_AFF_ROWS(stmt); @@ -321,29 +377,33 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co /* See if we have to clean the wire */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } /* No 'else' here please :) */ - if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { + if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* Create a new test statement, which we will prepare, but if anything fails, we will scrap it. */ - stmt_to_prepare = mysqlnd_stmt_init(stmt->conn); + s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC); + if (!s_to_prepare) { + goto fail; + } + stmt_to_prepare = s_to_prepare->data; } - if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, - query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) || - FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) { + if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) || + FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC)) + { goto fail; } if (stmt_to_prepare->param_count) { - if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) || - FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) + if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) || + FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC)) { goto fail; } @@ -355,7 +415,11 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co no metadata at prepare. */ if (stmt_to_prepare->field_count) { - MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count TSRMLS_CC); + MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC); + if (!result) { + SET_OOM_ERROR(stmt->conn->error_info); + goto fail; + } /* Allocate the result now as it is needed for the reading of metadata */ stmt_to_prepare->result = result; @@ -364,27 +428,28 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co result->type = MYSQLND_RES_PS_BUF; if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) || - FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) { + FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC)) + { goto fail; } } if (stmt_to_prepare != stmt) { /* Free old buffers, binding and resources on server */ - stmt->m->net_close(stmt, TRUE TSRMLS_CC); + s->m->net_close(s, TRUE TSRMLS_CC); - memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); + memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT_DATA)); /* Now we will have a clean new statement object */ - mnd_efree(stmt_to_prepare); + mnd_pefree(stmt_to_prepare, stmt_to_prepare->persistent); } stmt->state = MYSQLND_STMT_PREPARED; DBG_INF("PASS"); DBG_RETURN(PASS); fail: - if (stmt_to_prepare != stmt) { - stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC); + if (stmt_to_prepare != stmt && s_to_prepare) { + s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC); } stmt->state = MYSQLND_STMT_INITTED; @@ -396,16 +461,20 @@ fail: /* {{{ mysqlnd_stmt_execute_parse_response */ static enum_func_status -mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) +mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt_execute_parse_response"); - + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; CONN_SET_STATE(conn, CONN_QUERY_SENT); - ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); + ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC); if (ret == FAIL) { stmt->error_info = conn->error_info; stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; @@ -443,7 +512,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) use_result() or store_result() and we should be able to scrap the data on the line, if he just decides to close the statement. */ - DBG_INF_FMT("server_status=%d cursor=%d", stmt->upsert_status.server_status, + DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) { @@ -451,7 +520,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->cursor_exists = TRUE; CONN_SET_STATE(conn, CONN_READY); /* Only cursor read */ - stmt->default_rset_handler = stmt->m->use_result; + stmt->default_rset_handler = s->m->use_result; DBG_INF("use_result"); } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) { DBG_INF("asked for cursor but got none"); @@ -466,12 +535,12 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) precached on client and server's resources are freed. */ /* preferred is buffered read */ - stmt->default_rset_handler = stmt->m->store_result; + stmt->default_rset_handler = s->m->store_result; DBG_INF("store_result"); } else { DBG_INF("no cursor"); /* preferred is unbuffered read */ - stmt->default_rset_handler = stmt->m->use_result; + stmt->default_rset_handler = s->m->use_result; DBG_INF("use_result"); } } @@ -485,15 +554,20 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::execute */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; - zend_uchar *request; + MYSQLND * conn; + zend_uchar *request = NULL; size_t request_len; zend_bool free_request; DBG_ENTER("mysqlnd_stmt::execute"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_ERROR_AFF_ROWS(stmt); @@ -538,8 +612,8 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { @@ -583,23 +657,25 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } if (not_bound) { char * msg; - spprintf(&msg, 0, "No data supplied for %d parameter%s in prepared statement", + spprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement", not_bound, not_bound>1 ?"s":""); SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg); if (msg) { - efree(msg); + efree(msg); /* allocated by spprintf */ } DBG_INF("FAIL"); DBG_RETURN(FAIL); } } - request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request TSRMLS_CC); - - /* support for buffer types should be added here ! */ - - ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, - PROT_LAST /* we will handle the response packet*/, - FALSE, FALSE TSRMLS_CC); + ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC); + if (ret == PASS) { + /* support for buffer types should be added here ! */ + ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, + PROT_LAST /* we will handle the response packet*/, + FALSE, FALSE TSRMLS_CC); + } else { + SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM."); + } if (free_request) { mnd_efree(request); @@ -612,7 +688,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } stmt->execute_count++; - ret = mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC); + ret = s->m->parse_execute_response(s TSRMLS_CC); if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status.affected_rows) { MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status.affected_rows); @@ -626,11 +702,13 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES_BUFFERED *set = result->stored_data; unsigned int field_count = result->meta->field_count; DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); + *fetched_anything = FALSE; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); /* If we haven't read everything */ @@ -645,15 +723,18 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + meta->field_count, + meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - meta->field_count, - meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); if (stmt->update_max_length) { for (i = 0; i < result->field_count; i++) { /* @@ -680,7 +761,7 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(current_row[i])); + DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i])); if (Z_TYPE_P(current_row[i]) != IS_NULL) { /* Copy the value. @@ -708,7 +789,6 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f DBG_INF("row fetched"); } else { set->data_cursor = NULL; - *fetched_anything = FALSE; DBG_INF("no more data"); } DBG_INF("PASS"); @@ -722,14 +802,16 @@ static enum_func_status mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND_PACKET_ROW * row_packet; DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); + *fetched_anything = FALSE; + if (result->unbuf->eof_reached) { /* No more rows obviously */ - *fetched_anything = FALSE; DBG_INF("eof reached"); DBG_RETURN(PASS); } @@ -739,6 +821,10 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } + if (!(row_packet = result->row_packet)) { + DBG_RETURN(FAIL); + } + /* Let the row packet fill our buffer and skip additional malloc + memcpy */ row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE; @@ -748,8 +834,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; if (!row_packet->skip_extraction) { result->m.unbuffered_free_last_data(result TSRMLS_CC); @@ -760,14 +844,17 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { @@ -805,11 +892,13 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else if (ret == FAIL) { - *fetched_anything = FALSE; if (row_packet->error_info.error_no) { stmt->conn->error_info = row_packet->error_info; stmt->error_info = row_packet->error_info; @@ -817,7 +906,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { - *fetched_anything = FALSE; DBG_INF("EOF"); /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; @@ -834,7 +922,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int } } - DBG_INF_FMT("ret=%s fetched_anything=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); DBG_RETURN(ret); } /* }}} */ @@ -842,14 +930,20 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int /* {{{ mysqlnd_stmt::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES *result; - MYSQLND *conn = stmt->conn; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt::use_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + if (!stmt->field_count || (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) || (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) || @@ -885,13 +979,14 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */]; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; + MYSQLND_PACKET_ROW * row_packet; DBG_ENTER("mysqlnd_fetch_stmt_row_cursor"); - if (!stmt) { + if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) { DBG_ERR("no statement"); DBG_RETURN(FAIL); } @@ -905,6 +1000,9 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } + if (!(row_packet = result->row_packet)) { + DBG_RETURN(FAIL); + } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -923,10 +1021,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; - DBG_INF_FMT("skip_extraction=%d", row_packet->skip_extraction); + DBG_INF_FMT("skip_extraction=%u", row_packet->skip_extraction); if (!row_packet->skip_extraction) { result->m.unbuffered_free_last_data(result TSRMLS_CC); @@ -936,14 +1032,17 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } /* If no result bind, do nothing. We consumed the data */ for (i = 0; i < field_count; i++) { @@ -956,7 +1055,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_dtor(stmt->result_bind[i].zv); #endif - DBG_INF_FMT("i=%d bound_var=%p type=%d refc=%u", i, stmt->result_bind[i].zv, + DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv, Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv)); if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) { if ((Z_TYPE_P(data) == IS_STRING @@ -982,16 +1081,19 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } /* We asked for one row, the next one should be EOF, eat it */ ret = PACKET_READ(row_packet, result->conn); if (row_packet->row_buffer) { - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR); + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else { *fetched_anything = FALSE; @@ -1012,7 +1114,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla stmt->conn->upsert_status.server_status = row_packet->server_status; - DBG_INF_FMT("ret=%s fetched=%d server_status=%d warnings=%d eof=%d", + DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u", ret == PASS? "PASS":"FAIL", *fetched_anything, row_packet->server_status, row_packet->warning_count, result->unbuf->eof_reached); @@ -1023,10 +1125,14 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla /* {{{ mysqlnd_stmt::fetch */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; DBG_ENTER("mysqlnd_stmt::fetch"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); if (!stmt->result || @@ -1038,14 +1144,14 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Execute only once. We have to free the previous contents of user's bound vars */ - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler(s TSRMLS_CC); } stmt->state = MYSQLND_STMT_USER_FETCHING; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); - DBG_INF_FMT("result_bind=%p separated_once=%d", stmt->result_bind, stmt->result_zvals_separated_once); + DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once); /* The user might have not bound any variables for result. Do the binding once she does it. @@ -1065,7 +1171,7 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const stmt->result_zvals_separated_once = TRUE; } - ret = stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); + ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC); DBG_RETURN(ret); } /* }}} */ @@ -1073,12 +1179,16 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const /* {{{ mysqlnd_stmt::reset */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret = PASS; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; DBG_ENTER("mysqlnd_stmt::reset"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_EMPTY_ERROR(stmt->error_info); @@ -1102,16 +1212,18 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) We have to call the appropriate use_result() or store_result() and clean. */ - if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { - DBG_INF("fetching result set header"); - stmt->default_rset_handler(stmt TSRMLS_CC); - stmt->state = MYSQLND_STMT_USER_FETCHING; - } + do { + if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("fetching result set header"); + stmt->default_rset_handler(s TSRMLS_CC); + stmt->state = MYSQLND_STMT_USER_FETCHING; + } - if (stmt->result) { - DBG_INF("skipping result"); - stmt->result->m.skip_result(stmt->result TSRMLS_CC); - } + if (stmt->result) { + DBG_INF("skipping result"); + stmt->result->m.skip_result(stmt->result TSRMLS_CC); + } + } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS); /* Don't free now, let the result be usable. When the stmt will again be @@ -1138,16 +1250,22 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::send_long_data */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_no, +MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no, const char * const data, unsigned long length TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret = FAIL; - MYSQLND * conn = stmt->conn; + MYSQLND * conn; zend_uchar *cmd_buf; enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; DBG_ENTER("mysqlnd_stmt::send_long_data"); - DBG_INF_FMT("stmt=%lu param_no=%d data_len=%lu", stmt->stmt_id, param_no, length); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length); + + conn = stmt->conn; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -1186,19 +1304,25 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned if (CONN_GET_STATE(conn) == CONN_READY) { size_t packet_len; - stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length); - - int4store(cmd_buf, stmt->stmt_id); - int2store(cmd_buf + STMT_ID_LENGTH, param_no); - memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); - - /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ - ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, - PROT_LAST , FALSE, TRUE TSRMLS_CC); - mnd_efree(cmd_buf); - if (FAIL == ret) { - stmt->error_info = conn->error_info; + if (cmd_buf) { + stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; + + int4store(cmd_buf, stmt->stmt_id); + int2store(cmd_buf + STMT_ID_LENGTH, param_no); + memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); + + /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ + ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, + PROT_LAST , FALSE, TRUE TSRMLS_CC); + mnd_efree(cmd_buf); + if (FAIL == ret) { + stmt->error_info = conn->error_info; + } + } else { + ret = FAIL; + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(conn->error_info); } /* Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not @@ -1240,16 +1364,20 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned /* {{{ mysqlnd_stmt::bind_parameters */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_param"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); DBG_ERR("not prepared"); - if (param_bind && stmt->param_bind_dtor) { - stmt->param_bind_dtor(param_bind TSRMLS_CC); + if (param_bind) { + s->m->free_parameter_bind(s, param_bind TSRMLS_CC); } DBG_RETURN(FAIL); } @@ -1261,8 +1389,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND unsigned int i = 0; if (!param_bind) { - SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - "Re-binding (still) not supported"); + SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported"); DBG_ERR("Re-binding (still) not supported"); DBG_RETURN(FAIL); } else if (stmt->param_bind) { @@ -1281,15 +1408,15 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND zval_ptr_dtor(&stmt->param_bind[i].zv); } } - if (stmt->param_bind != param_bind && stmt->param_bind_dtor) { - stmt->param_bind_dtor(stmt->param_bind TSRMLS_CC); + if (stmt->param_bind != param_bind) { + s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); } } stmt->param_bind = param_bind; for (i = 0; i < stmt->param_count; i++) { /* The client will use stmt_send_long_data */ - DBG_INF_FMT("%d is of type %d", i, stmt->param_bind[i].type); + DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type); /* Prevent from freeing */ /* Don't update is_ref, or we will leak during conversion */ Z_ADDREF_P(stmt->param_bind[i].zv); @@ -1308,11 +1435,15 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND /* {{{ mysqlnd_stmt::bind_one_parameter */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, +MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no, zval * const zv, zend_uchar type TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_one_parameter"); - DBG_INF_FMT("stmt=%lu param_no=%d param_count=%u type=%d", + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1332,6 +1463,9 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsi if (stmt->param_count) { if (!stmt->param_bind) { stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND)); + if (!stmt->param_bind) { + DBG_RETURN(FAIL); + } } /* Prevent from freeing */ @@ -1359,9 +1493,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsi /* {{{ mysqlnd_stmt::refresh_bind_param */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::refresh_bind_param"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1382,32 +1520,22 @@ MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRML /* }}} */ -/* {{{ mysqlnd_stmt::set_bind_param_dtor */ -static void -MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor)(MYSQLND_STMT * const stmt, - void (*param_bind_dtor)(MYSQLND_PARAM_BIND * dtor TSRMLS_DC) TSRMLS_DC) -{ - DBG_ENTER("mysqlnd_stmt::set_bind_param_dtor"); - DBG_INF_FMT("stmt=%p", param_bind_dtor); - stmt->param_bind_dtor = param_bind_dtor; - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); - if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); - if (result_bind && stmt->result_bind_dtor) { - stmt->result_bind_dtor(result_bind TSRMLS_CC); + if (result_bind) { + s->m->free_result_bind(s, result_bind TSRMLS_CC); } DBG_ERR("not prepared"); DBG_RETURN(FAIL); @@ -1424,7 +1552,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, DBG_RETURN(FAIL); } - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); stmt->result_zvals_separated_once = FALSE; stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { @@ -1438,8 +1566,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, */ stmt->result_bind[i].bound = TRUE; } - } else if (result_bind && stmt->result_bind_dtor) { - stmt->result_bind_dtor(result_bind TSRMLS_CC); + } else if (result_bind) { + s->m->free_result_bind(s, result_bind TSRMLS_CC); } DBG_INF("PASS"); DBG_RETURN(PASS); @@ -1449,9 +1577,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1470,19 +1602,22 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigne SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->field_count) { - mysqlnd_stmt_separate_one_result_bind(stmt, param_no TSRMLS_CC); + mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC); /* Guaranteed is that stmt->result_bind is NULL */ if (!stmt->result_bind) { stmt->result_bind = mnd_ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND)); } else { stmt->result_bind = mnd_erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND)); } + if (!stmt->result_bind) { + DBG_RETURN(FAIL); + } ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv); /* Don't update is_ref !!! it's not our job Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt will fail. - */ + */ stmt->result_bind[param_no].bound = TRUE; } DBG_INF("PASS"); @@ -1491,117 +1626,124 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigne /* }}} */ -/* {{{ mysqlnd_stmt::set_bind_result_dtor */ -static void -MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor)(MYSQLND_STMT * const stmt, - void (*result_bind_dtor)(MYSQLND_RESULT_BIND * dtor TSRMLS_DC) TSRMLS_DC) -{ - DBG_ENTER("mysqlnd_stmt::set_bind_param_dtor"); - DBG_INF_FMT("stmt=%p", result_bind_dtor); - stmt->result_bind_dtor = result_bind_dtor; - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_stmt::insert_id */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.last_insert_id; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.last_insert_id : 0; } /* }}} */ /* {{{ mysqlnd_stmt::affected_rows */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.affected_rows; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.affected_rows : 0; } /* }}} */ /* {{{ mysqlnd_stmt::num_rows */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->result? mysqlnd_num_rows(stmt->result):0; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0; } /* }}} */ /* {{{ mysqlnd_stmt::warning_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.warning_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.warning_count : 0; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::server_status */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.server_status : 0; } /* }}} */ /* {{{ mysqlnd_stmt::field_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->field_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->field_count : 0; } /* }}} */ /* {{{ mysqlnd_stmt::param_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->param_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->param_count : 0; } /* }}} */ /* {{{ mysqlnd_stmt::errno */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.error_no; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->error_info.error_no : 0; } /* }}} */ /* {{{ mysqlnd_stmt::error */ static const char * -MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.error; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->error_info.error : 0; } /* }}} */ /* {{{ mysqlnd_stmt::sqlstate */ static const char * -MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; } /* }}} */ /* {{{ mysqlnd_stmt::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, uint64_t row TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC) { - return stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; } /* }}} */ /* {{{ mysqlnd_stmt::param_metadata */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC) { - if (!stmt->param_count) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (!stmt || !stmt->param_count) { return NULL; } - return NULL; } /* }}} */ @@ -1609,11 +1751,15 @@ MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC /* {{{ mysqlnd_stmt::result_metadata */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES *result; DBG_ENTER("mysqlnd_stmt::result_metadata"); + if (!stmt) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count); if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) { @@ -1634,27 +1780,48 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_D In the meantime we don't need a zval cache reference for this fake result set, so we don't get one. */ - result = mysqlnd_result_init(stmt->field_count TSRMLS_CC); - result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); - result->unbuf->eof_reached = TRUE; - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); - - DBG_INF_FMT("result=%p", result); - DBG_RETURN(result); + do { + result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC); + if (!result) { + break; + } + result->type = MYSQLND_RES_NORMAL; + result->m.fetch_row = result->m.fetch_row_normal_unbuffered; + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->unbuf) { + break; + } + result->unbuf->eof_reached = TRUE; + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + if (!result->meta) { + break; + } + + DBG_INF_FMT("result=%p", result); + DBG_RETURN(result); + } while (0); + + SET_OOM_ERROR(stmt->conn->error_info); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + DBG_RETURN(NULL); } /* }}} */ /* {{{ mysqlnd_stmt::attr_set */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; unsigned long val = *(unsigned long *) value; DBG_ENTER("mysqlnd_stmt::attr_set"); + if (!stmt) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu attr_type=%u value=%lu", stmt->stmt_id, attr_type, val); switch (attr_type) { @@ -1697,11 +1864,15 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::attr_get */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::attr_set"); + if (!stmt) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type); switch (attr_type) { @@ -1725,9 +1896,13 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt, /* free_result() doesn't actually free stmt->result but only the buffers */ /* {{{ mysqlnd_stmt::free_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::free_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); if (!stmt->result) { @@ -1742,8 +1917,8 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { @@ -1754,7 +1929,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) Separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); /* Now we can destroy the result set */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); @@ -1774,11 +1949,16 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_separate_result_bind */ -void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) +static void +mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; unsigned int i; DBG_ENTER("mysqlnd_stmt_separate_result_bind"); + if (!stmt) { + DBG_VOID_RETURN; + } DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count); @@ -1794,7 +1974,7 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("%d has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv)); + DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv)); /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free @@ -1817,9 +1997,7 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) } } } - if (stmt->result_bind_dtor) { - stmt->result_bind_dtor(stmt->result_bind TSRMLS_CC); - } + s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC); stmt->result_bind = NULL; DBG_VOID_RETURN; @@ -1828,10 +2006,15 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_separate_one_result_bind */ -void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC) +static void +mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt_separate_one_result_bind"); - DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%d", + if (!stmt) { + DBG_VOID_RETURN; + } + DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no); if (!stmt->result_bind) { @@ -1845,7 +2028,7 @@ void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned i */ /* Let's try with no cache */ if (stmt->result_bind[param_no].bound == TRUE) { - DBG_INF_FMT("%d has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); + DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free @@ -1874,10 +2057,14 @@ void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned i /* {{{ mysqlnd_internal_free_stmt_content */ -static -void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) +static void +mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_internal_free_stmt_content"); + if (!stmt) { + DBG_VOID_RETURN; + } DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count); @@ -1898,9 +2085,7 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) zval_ptr_dtor(&stmt->param_bind[i].zv); } } - if (stmt->param_bind_dtor) { - stmt->param_bind_dtor(stmt->param_bind TSRMLS_CC); - } + s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); stmt->param_bind = NULL; } @@ -1908,7 +2093,7 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) First separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); /* Not every statement has a result set attached */ if (stmt->result) { stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); @@ -1922,15 +2107,21 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::net_close */ static enum_func_status -MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC) { - MYSQLND * conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; enum_mysqlnd_collected_stats stat = STAT_LAST; DBG_ENTER("mysqlnd_stmt::net_close"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -1940,10 +2131,10 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ clean. */ do { - DBG_INF_FMT("stmt->state=%d", stmt->state); + DBG_INF_FMT("stmt->state=%u", stmt->state); if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler(s TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } @@ -1952,7 +2143,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ DBG_INF("skipping result"); stmt->result->m.skip_result(stmt->result TSRMLS_CC); } - } while (mysqlnd_stmt_more_results(stmt) && mysqlnd_stmt_next_result(stmt) == PASS); + } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS); /* After this point we are allowed to free the result set, as we have cleaned the line @@ -1985,11 +2176,11 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ } if (stmt->execute_cmd_buffer.buffer) { - mnd_efree(stmt->execute_cmd_buffer.buffer); + mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent); stmt->execute_cmd_buffer.buffer = NULL; } - mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + mysqlnd_internal_free_stmt_content(s TSRMLS_CC); if (stmt->conn) { stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); @@ -2002,18 +2193,24 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ /* {{{ mysqlnd_stmt::dtor */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; + zend_bool persistent = s->persistent; DBG_ENTER("mysqlnd_stmt::dtor"); - DBG_INF_FMT("stmt=%p", stmt); + if (stmt) { - MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: - STAT_STMT_CLOSE_EXPLICIT); + DBG_INF_FMT("stmt=%p", stmt); - ret = stmt->m->net_close(stmt, implicit TSRMLS_CC); - mnd_efree(stmt); + MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: + STAT_STMT_CLOSE_EXPLICIT); + + ret = s->m->net_close(s, implicit TSRMLS_CC); + mnd_pefree(stmt, persistent); + } + mnd_pefree(s, persistent); DBG_INF(ret == PASS? "PASS":"FAIL"); DBG_RETURN(ret); @@ -2021,6 +2218,59 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit /* }}} */ +/* {{{ mysqlnd_stmt::alloc_param_bind */ +static MYSQLND_PARAM_BIND * +MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + DBG_ENTER("mysqlnd_stmt::alloc_param_bind"); + if (!stmt) { + DBG_RETURN(NULL); + } + DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent)); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::alloc_result_bind */ +static MYSQLND_RESULT_BIND * +MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + DBG_ENTER("mysqlnd_stmt::alloc_result_bind"); + if (!stmt) { + DBG_RETURN(NULL); + } + DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent)); +} +/* }}} */ + + +/* {{{ param_bind::free_parameter_bind */ +PHPAPI void +MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (stmt) { + mnd_pefree(param_bind, stmt->persistent); + } +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::free_result_bind */ +PHPAPI void +MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (stmt) { + mnd_pefree(result_bind, stmt->persistent); + } +} +/* }}} */ + + + MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, prepare), MYSQLND_METHOD(mysqlnd_stmt, execute), @@ -2040,10 +2290,8 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, bind_parameters), MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter), MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param), - MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, bind_result), MYSQLND_METHOD(mysqlnd_stmt, bind_one_result), - MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, send_long_data), MYSQLND_METHOD(mysqlnd_stmt, param_metadata), MYSQLND_METHOD(mysqlnd_stmt, result_metadata), @@ -2061,7 +2309,16 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, sqlstate), MYSQLND_METHOD(mysqlnd_stmt, attr_get), - MYSQLND_METHOD(mysqlnd_stmt, attr_set), + MYSQLND_METHOD(mysqlnd_stmt, attr_set), + + + MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind), + MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind), + MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind), + MYSQLND_METHOD(mysqlnd_stmt, free_result_bind), + MYSQLND_METHOD(mysqlnd_stmt, server_status), + mysqlnd_stmt_execute_generate_request, + mysqlnd_stmt_execute_parse_response MYSQLND_CLASS_METHODS_END; @@ -2069,28 +2326,47 @@ MYSQLND_CLASS_METHODS_END; MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) { size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_STMT *stmt = mnd_ecalloc(1, alloc_size); + MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent); + MYSQLND_STMT_DATA * stmt = NULL; DBG_ENTER("_mysqlnd_stmt_init"); - DBG_INF_FMT("stmt=%p", stmt); - - stmt->m = mysqlnd_stmt_methods; - stmt->state = MYSQLND_STMT_INITTED; - stmt->execute_cmd_buffer.length = 4096; - stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length); + do { + if (!ret) { + break; + } + ret->m = mysqlnd_stmt_methods; + ret->persistent = conn->persistent; - stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; - /* - Mark that we reference the connection, thus it won't be - be destructed till there is open statements. The last statement - or normal query result will close it then. - */ - stmt->conn = conn->m->get_reference(conn TSRMLS_CC); + stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent); + DBG_INF_FMT("stmt=%p", stmt); + if (!stmt) { + break; + } + stmt->persistent = conn->persistent; + stmt->state = MYSQLND_STMT_INITTED; + stmt->execute_cmd_buffer.length = 4096; + stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent); + if (!stmt->execute_cmd_buffer.buffer) { + break; + } - stmt->m->set_param_bind_dtor(stmt, mysqlnd_efree_param_bind_dtor TSRMLS_CC); - stmt->m->set_result_bind_dtor(stmt, mysqlnd_efree_result_bind_dtor TSRMLS_CC); + stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; + /* + Mark that we reference the connection, thus it won't be + be destructed till there is open statements. The last statement + or normal query result will close it then. + */ + stmt->conn = conn->m->get_reference(conn TSRMLS_CC); - DBG_RETURN(stmt); + DBG_RETURN(ret); + } while (0); + + SET_OOM_ERROR(conn->error_info); + if (ret) { + ret->m->dtor(ret, TRUE TSRMLS_CC); + ret = NULL; + } + DBG_RETURN(NULL); } /* }}} */ @@ -2108,23 +2384,6 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, u /* }}} */ -/* {{{ mysqlnd_efree_param_bind_dtor */ -PHPAPI void -mysqlnd_efree_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC) -{ - mnd_efree(param_bind); -} -/* }}} */ - - -/* {{{ mysqlnd_efree_result_bind_dtor */ -PHPAPI void -mysqlnd_efree_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC) -{ - mnd_efree(result_bind); -} -/* }}} */ - /* {{{ _mysqlnd_init_ps_subsystem */ void _mysqlnd_init_ps_subsystem() { @@ -2133,6 +2392,7 @@ void _mysqlnd_init_ps_subsystem() } /* }}} */ + /* {{{ mysqlnd_conn_get_methods */ PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods() { @@ -2140,6 +2400,7 @@ PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods() } /* }}} */ + /* {{{ mysqlnd_conn_set_methods */ PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods) { diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c index 650584c0f..9256763a9 100644 --- a/ext/mysqlnd/mysqlnd_ps_codec.c +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps_codec.c 289030 2009-09-30 23:34:56Z andrey $ */ +/* $Id: mysqlnd_ps_codec.c 300735 2010-06-24 19:52:13Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -61,7 +61,7 @@ void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, size_t tmp_len = 0; zend_bool is_bit = field->type == MYSQL_TYPE_BIT; DBG_ENTER("ps_fetch_from_1_to_8_bytes"); - DBG_INF_FMT("zv=%p byte_count=%d", zv, byte_count); + DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count); if (field->flags & UNSIGNED_FLAG) { uint64_t uval = 0; @@ -106,10 +106,10 @@ void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, } #if SIZEOF_LONG==4 - if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) { + if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) { DBG_INF("stringify"); tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval); - } else + } else #endif /* SIZEOF */ { ZVAL_LONG(zv, lval); @@ -126,7 +126,7 @@ void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field, { DBG_INF("stringify"); ZVAL_STRINGL(zv, tmp, tmp_len, 1); - } + } } (*row)+= byte_count; DBG_VOID_RETURN; @@ -199,7 +199,7 @@ void ps_fetch_float(zval *zv, const MYSQLND_FIELD * const field, DBG_ENTER("ps_fetch_float"); float4get(value, *row); ZVAL_DOUBLE(zv, value); - (*row)+= 4; + (*row)+= 4; DBG_INF_FMT("value=%f", value); DBG_VOID_RETURN; } @@ -256,24 +256,23 @@ void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field, } else { memset(&t, 0, sizeof(t)); t.time_type = MYSQLND_TIMESTAMP_TIME; - } + } /* QQ : How to make this unicode without copying two times the buffer - Unicode equivalent of spprintf? */ - length = spprintf(&to, 0, "%s%02u:%02u:%02u", - (t.neg ? "-" : ""), t.hour, t.minute, t.second); + length = spprintf(&to, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second); DBG_INF_FMT("%s", to); #if PHP_MAJOR_VERSION >= 6 if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - mnd_efree(to); + efree(to); /* allocated by spprintf */ #if PHP_MAJOR_VERSION >= 6 } else { - ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); + ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif DBG_VOID_RETURN; @@ -321,10 +320,10 @@ void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field, if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - mnd_efree(to); + efree(to); /* allocated by spprintf */ #if PHP_MAJOR_VERSION >= 6 } else { - ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); + ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif DBG_VOID_RETURN; @@ -358,7 +357,7 @@ void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field, t.minute = (unsigned int) to[5]; t.second = (unsigned int) to[6]; } else { - t.hour = t.minute = t.second= 0; + t.hour = t.minute = t.second= 0; } t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0; @@ -380,10 +379,10 @@ void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field, if (!as_unicode) { #endif ZVAL_STRINGL(zv, to, length, 1); - mnd_efree(to); + efree(to); /* allocated by spprintf */ #if PHP_MAJOR_VERSION >= 6 } else { - ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); + ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); } #endif DBG_VOID_RETURN; @@ -406,7 +405,7 @@ void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field, DBG_INF_FMT("len = %lu", length); #if PHP_MAJOR_VERSION < 6 DBG_INF("copying from the row buffer"); - ZVAL_STRINGL(zv, (char *)*row, length, 1); + ZVAL_STRINGL(zv, (char *)*row, length, 1); #else if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) { DBG_INF("Binary charset"); @@ -440,92 +439,92 @@ void _mysqlnd_init_ps_fetch_subsystem() { memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions)); mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func = ps_fetch_null; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2; mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2; mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4; mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type = IS_LONG; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4; mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type = IS_DOUBLE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_time; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type = IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_date; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type = IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_date; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type = IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type= IS_STRING; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR; @@ -545,23 +544,23 @@ void _mysqlnd_init_ps_fetch_subsystem() mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING; mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR; mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING; - mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE; + mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE; mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string; mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR; @@ -579,29 +578,38 @@ void _mysqlnd_init_ps_fetch_subsystem() /* {{{ mysqlnd_stmt_copy_it */ -static void +static enum_func_status mysqlnd_stmt_copy_it(zval *** copies, zval *original, unsigned int param_count, unsigned int current TSRMLS_DC) { if (!*copies) { - *copies = mnd_ecalloc(param_count, sizeof(zval *)); + *copies = mnd_ecalloc(param_count, sizeof(zval *)); + } + if (*copies) { + MAKE_STD_ZVAL((*copies)[current]); + *(*copies)[current] = *original; + Z_SET_REFCOUNT_P((*copies)[current], 1); + zval_copy_ctor((*copies)[current]); + return PASS; } - MAKE_STD_ZVAL((*copies)[current]); - *(*copies)[current] = *original; - Z_SET_REFCOUNT_P((*copies)[current], 1); - zval_copy_ctor((*copies)[current]); + return FAIL; } /* }}} */ /* {{{ mysqlnd_stmt_execute_store_params */ -static void -mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p, +static enum_func_status +mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s->data; unsigned int i = 0; + zend_uchar * provided_buffer = *buf; size_t left = (*buf_len - (*p - *buf)); size_t data_size = 0; zval **copies = NULL;/* if there are different types */ + enum_func_status ret = FAIL; + + DBG_ENTER("mysqlnd_stmt_execute_store_params"); /* 1. Store type information */ if (stmt->send_types_to_server) { @@ -612,6 +620,10 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch zend_uchar *tmp_buf; *buf_len = offset + stmt->param_count * 2 + 20; tmp_buf = mnd_emalloc(*buf_len); + if (!tmp_buf) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } memcpy(tmp_buf, *buf, offset); *buf = tmp_buf; @@ -636,14 +648,16 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch unsigned int j; zval *the_var = stmt->param_bind[i].zv; - if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && - Z_TYPE_P(the_var) == IS_NULL)) { + if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) { continue; } for (j = i + 1; j < stmt->param_count; j++) { if (stmt->param_bind[j].zv == the_var) { /* Double binding of the same zval, make a copy */ - mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC); + if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } break; } } @@ -653,7 +667,10 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch data_size += 8; if (Z_TYPE_P(the_var) != IS_DOUBLE) { if (!copies || !copies[i]) { - mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC); + if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } } } break; @@ -668,7 +685,10 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch #endif if (Z_TYPE_P(the_var) != IS_LONG) { if (!copies || !copies[i]) { - mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC); + if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } } } break; @@ -691,7 +711,10 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch #endif { if (!copies || !copies[i]) { - mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC); + if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } } the_var = copies[i]; #if PHP_MAJOR_VERSION >= 6 @@ -714,10 +737,22 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch zend_uchar *tmp_buf; *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */ tmp_buf = mnd_emalloc(*buf_len); + if (!tmp_buf) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } memcpy(tmp_buf, *buf, offset); + /* + When too many columns the buffer provided to the function might not be sufficient. + In this case new buffer has been allocated above. When we allocate a buffer and then + allocate a bigger one here, we should free the first one. + */ + if (*buf != provided_buffer) { + mnd_efree(*buf); + } *buf = tmp_buf; /* Update our pos pointer */ - *p = *buf + offset; + *p = *buf + offset; } /* 2.3 Store the actual data */ @@ -771,27 +806,36 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch } } } + ret = PASS; +end: if (copies) { for (i = 0; i < stmt->param_count; i++) { if (copies[i]) { zval_ptr_dtor(&copies[i]); } } - mnd_efree(copies); + mnd_efree(copies); } + + DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ /* {{{ mysqlnd_stmt_execute_generate_request */ -zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, - zend_bool *free_buffer TSRMLS_DC) +enum_func_status +mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s->data; zend_uchar *p = stmt->execute_cmd_buffer.buffer, *cmd_buffer = stmt->execute_cmd_buffer.buffer; size_t cmd_buffer_length = stmt->execute_cmd_buffer.length; unsigned int null_byte_offset, null_count= (stmt->param_count + 7) / 8; + enum_func_status ret; + + DBG_ENTER("mysqlnd_stmt_execute_generate_request"); int4store(p, stmt->stmt_id); p += 4; @@ -815,11 +859,13 @@ zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *re int1store(p, stmt->send_types_to_server); p++; - mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC); + ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC); *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer); *request_len = (p - cmd_buffer); - return cmd_buffer; + *request = cmd_buffer; + DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL"); + DBG_RETURN(ret); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 2e1d01263..645c81905 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_result.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_result.c 301234 2010-07-13 13:02:00Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -35,31 +35,36 @@ /* {{{ mysqlnd_res::initialize_result_set_rest */ -static void +static enum_func_status MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC) { unsigned int i; - zval **data_cursor = result->stored_data->data; - zval **data_begin = result->stored_data->data; - unsigned int field_count = result->meta->field_count; - unsigned int row_count = result->stored_data->row_count; + zval **data_cursor = result->stored_data? result->stored_data->data:NULL; + zval **data_begin = result->stored_data? result->stored_data->data:NULL; + unsigned int field_count = result->meta? result->meta->field_count : 0; + unsigned int row_count = result->stored_data? result->stored_data->row_count:0; + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_res::initialize_result_set_rest"); if (!data_cursor || row_count == result->stored_data->initialized_rows) { - DBG_VOID_RETURN; + DBG_RETURN(ret); } while ((data_cursor - data_begin) < (row_count * field_count)) { if (NULL == data_cursor[0]) { + enum_func_status rc = result->m.row_decoder( + result->stored_data->row_buffers[(data_cursor - data_begin) / field_count], + data_cursor, + result->meta->field_count, + result->meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + ret = FAIL; + break; + } result->stored_data->initialized_rows++; - result->m.row_decoder( - result->stored_data->row_buffers[(data_cursor - data_begin) / field_count], - data_cursor, - result->meta->field_count, - result->meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -76,7 +81,7 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu } data_cursor += field_count; } - DBG_VOID_RETURN; + DBG_RETURN(ret); } /* }}} */ @@ -87,42 +92,39 @@ void mysqlnd_palloc_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bo { DBG_ENTER("mysqlnd_palloc_zval_ptr_dtor"); *copy_ctor_called = FALSE; - { + + /* + This zval is not from the cache block. + Thus the refcount is -1 than of a zval from the cache, + because the zvals from the cache are owned by it. + */ + if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) { + ; /* do nothing, zval_ptr_dtor will do the job*/ + } else if (Z_REFCOUNT_PP(zv) > 1) { /* - This zval is not from the cache block. - Thus the refcount is -1 than of a zval from the cache, - because the zvals from the cache are owned by it. + Not a prepared statement, then we have to + call copy_ctor and then zval_ptr_dtor() + + In Unicode mode the destruction of the zvals should not call + zval_copy_ctor() because then we will leak. + I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set + to check if we need to call copy_ctor(). + + If the type is IS_UNICODE, which can happen with PHP6, then we don't + need to copy_ctor, as the data doesn't point to our internal buffers. + If it's string (in PHP5 always) and in PHP6 if data is binary, then + it still points to internal buffers and has to be copied. */ - if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) { - ; /* do nothing, zval_ptr_dtor will do the job*/ - } else if (Z_REFCOUNT_PP(zv) > 1) { - /* - Not a prepared statement, then we have to - call copy_ctor and then zval_ptr_dtor() - - In Unicode mode the destruction of the zvals should not call - zval_copy_ctor() because then we will leak. - I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set - to check if we need to call copy_ctor(). - - If the type is IS_UNICODE, which can happen with PHP6, then we don't - need to copy_ctor, as the data doesn't point to our internal buffers. - If it's string (in PHP5 always) and in PHP6 if data is binary, then - it still points to internal buffers and has to be copied. - */ - if (Z_TYPE_PP(zv) == IS_STRING) { - zval_copy_ctor(*zv); - } - *copy_ctor_called = TRUE; - } else { - if (Z_TYPE_PP(zv) == IS_STRING) { - ZVAL_NULL(*zv); - } + if (Z_TYPE_PP(zv) == IS_STRING) { + zval_copy_ctor(*zv); + } + *copy_ctor_called = TRUE; + } else { + if (Z_TYPE_PP(zv) == IS_STRING) { + ZVAL_NULL(*zv); } - zval_ptr_dtor(zv); - DBG_VOID_RETURN; } - + zval_ptr_dtor(zv); DBG_VOID_RETURN; } /* }}} */ @@ -169,7 +171,7 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRM if (unbuf->last_row_buffer) { DBG_INF("Freeing last row buffer"); /* Nothing points to this buffer now, free it */ - unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer, TRUE TSRMLS_CC); + unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer TSRMLS_CC); unbuf->last_row_buffer = NULL; } @@ -180,7 +182,7 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRM /* {{{ mysqlnd_res::free_buffered_data */ static void -MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES *result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC) { MYSQLND_RES_BUFFERED *set = result->stored_data; unsigned int field_count = result->field_count; @@ -189,34 +191,37 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES *result TSRMLS_DC) DBG_ENTER("mysqlnd_res::free_buffered_data"); DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count); - DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); - for (row = set->row_count - 1; row >= 0; row--) { - zval **current_row = set->data + row * field_count; - MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row]; - int col; + DBG_INF("Freeing data & row_buffer"); + if (set->data) { + + DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC)); + for (row = set->row_count - 1; row >= 0; row--) { + zval **current_row = set->data + row * field_count; + MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row]; + int col; - for (col = field_count - 1; col >= 0; --col) { - zend_bool copy_ctor_called; - if (current_row[0] == NULL) { - break;/* row that was never initialized */ - } + for (col = field_count - 1; col >= 0; --col) { + zend_bool copy_ctor_called; + if (current_row == NULL || current_row[0] == NULL) { + break;/* row that was never initialized */ + } mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), result->type, ©_ctor_called TSRMLS_CC); #if MYSQLND_DEBUG_MEMORY - DBG_INF_FMT("Copy_ctor_called=%d", copy_ctor_called); + DBG_INF_FMT("Copy_ctor_called=%u", copy_ctor_called); #endif - MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: + MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED: STAT_COPY_ON_WRITE_SAVED); - } + } #if MYSQLND_DEBUG_MEMORY - DBG_INF("Freeing current_row & current_buffer"); + DBG_INF("Freeing current_row & current_buffer"); #endif - current_buffer->free_chunk(current_buffer, TRUE TSRMLS_CC); - } - DBG_INF("Freeing data & row_buffer"); - if (set->data) { + current_buffer->free_chunk(current_buffer TSRMLS_CC); + } + mnd_pefree(set->data, set->persistent); set->data = NULL; } + if (set->row_buffers) { mnd_pefree(set->row_buffers, set->persistent); set->row_buffers = NULL; @@ -235,7 +240,7 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_res::free_result_buffers */ static void -MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::free_result_buffers"); DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown")); @@ -263,7 +268,7 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) if (result->result_set_memory_pool) { DBG_INF("Freeing memory pool"); mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC); - result->result_set_memory_pool = NULL; + result->result_set_memory_pool = NULL; } DBG_VOID_RETURN; @@ -273,14 +278,14 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_internal_free_result_contents */ static -void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) +void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC) { DBG_ENTER("mysqlnd_internal_free_result_contents"); result->m.free_result_buffers(result TSRMLS_CC); if (result->meta) { - result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); + result->meta->m->free_metadata(result->meta TSRMLS_CC); result->meta = NULL; } @@ -291,7 +296,7 @@ void mysqlnd_internal_free_result_contents(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_internal_free_result */ static -void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) +void mysqlnd_internal_free_result(MYSQLND_RES * result TSRMLS_DC) { DBG_ENTER("mysqlnd_internal_free_result"); result->m.free_result_contents(result TSRMLS_CC); @@ -301,7 +306,7 @@ void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) result->conn = NULL; } - mnd_efree(result); + mnd_pefree(result, result->persistent); DBG_VOID_RETURN; } @@ -310,7 +315,7 @@ void mysqlnd_internal_free_result(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_res::read_result_metadata */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND *conn TSRMLS_DC) { DBG_ENTER("mysqlnd_res::read_result_metadata"); @@ -321,11 +326,15 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND * infrastructure! */ if (result->meta) { - result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC); + result->meta->m->free_metadata(result->meta TSRMLS_CC); result->meta = NULL; } - result->meta = mysqlnd_result_meta_init(result->field_count TSRMLS_CC); + result->meta = result->m.result_meta_init(result->field_count, result->persistent TSRMLS_CC); + if (!result->meta) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } /* 1. Read all fields metadata */ @@ -339,9 +348,9 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND * /* 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata() - should consume. + should consume. 3. If there is a result set, it follows. The last packet will have 'eof' set - If PS, then no result set follows. + If PS, then no result set follows. */ DBG_RETURN(PASS); @@ -351,18 +360,27 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND * /* {{{ mysqlnd_query_read_result_set_header */ enum_func_status -mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s ? s->data:NULL; enum_func_status ret; - MYSQLND_PACKET_RSET_HEADER * rset_header; + MYSQLND_PACKET_RSET_HEADER * rset_header = NULL; + MYSQLND_PACKET_EOF * fields_eof = NULL; DBG_ENTER("mysqlnd_query_read_result_set_header"); - DBG_INF_FMT("stmt=%d", stmt? stmt->stmt_id:0); + DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0); ret = FAIL; - rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC); do { + rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC); + if (!rset_header) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + break; + } + SET_ERROR_AFF_ROWS(conn); + if (FAIL == (ret = PACKET_READ(rset_header, conn))) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header"); break; @@ -385,6 +403,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC */ conn->error_info = rset_header->error_info; ret = FAIL; + DBG_ERR_FMT("error=%s", rset_header->error_info.error); /* Return back from CONN_QUERY_SENT */ CONN_SET_STATE(conn, CONN_READY); break; @@ -422,9 +441,8 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC ret = PASS; MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY); break; - default:{ /* Result set */ - MYSQLND_PACKET_EOF * fields_eof; - MYSQLND_RES *result; + default: do { /* Result set */ + MYSQLND_RES * result; enum_mysqlnd_collected_stats stat = STAT_LAST; DBG_INF("Result set pending"); @@ -440,7 +458,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC /* PS has already allocated it */ conn->field_count = rset_header->field_count; if (!stmt) { - result = conn->current_result = mysqlnd_result_init(rset_header->field_count TSRMLS_CC); + result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent TSRMLS_CC); } else { if (!stmt->result) { DBG_INF("This is 'SHOW'/'EXPLAIN'-like query."); @@ -449,7 +467,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC prepared statements can't send result set metadata for these queries on prepare stage. Read it now. */ - result = stmt->result = mysqlnd_result_init(rset_header->field_count TSRMLS_CC); + result = stmt->result = conn->m->result_init(rset_header->field_count, stmt->persistent TSRMLS_CC); } else { /* Update result set metadata if it for some reason changed between @@ -462,14 +480,19 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC Note, that now (4.1.3) we always send metadata in reply to COM_STMT_EXECUTE (even if it is not necessary), so either this or previous branch always works. - */ + */ } result = stmt->result; } - + if (!result) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + break; + } + if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) { /* For PS, we leave them in Prepared state */ - if (!stmt) { + if (!stmt && conn->current_result) { mnd_efree(conn->current_result); conn->current_result = NULL; } @@ -479,6 +502,11 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC /* Check for SERVER_STATUS_MORE_RESULTS if needed */ fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC); + if (!fields_eof) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + break; + } if (FAIL == (ret = PACKET_READ(fields_eof, conn))) { DBG_ERR("Error ocurred while reading the EOF packet"); result->m.free_result_contents(result TSRMLS_CC); @@ -519,11 +547,9 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC } MYSQLND_INC_CONN_STATISTIC(conn->stats, stat); } - - PACKET_FREE(fields_eof); - - break; - } + } while (0); + PACKET_FREE(fields_eof); + break; /* switch break */ } } while (0); PACKET_FREE(rset_header); @@ -541,8 +567,8 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC of PHP, to be called as separate function. But let's have it for completeness. */ -static -unsigned long * mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC) +static unsigned long * +mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC) { unsigned int i; zval **previous_row; @@ -572,13 +598,14 @@ unsigned long * mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS /* {{{ mysqlnd_fetch_lengths_unbuffered */ -static -unsigned long * mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC) +static unsigned long * +mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC) { return result->lengths; } /* }}} */ + #if !defined(MYSQLND_USE_OPTIMISATIONS) || MYSQLND_USE_OPTIMISATIONS == 0 /* {{{ mysqlnd_res::fetch_lengths */ PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC) @@ -588,16 +615,17 @@ PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_ /* }}} */ #endif + /* {{{ mysqlnd_fetch_row_unbuffered_c */ static MYSQLND_ROW_C -mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) +mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) { - enum_func_status ret; - MYSQLND_ROW_C retrow = NULL; - unsigned int i, - field_count = result->field_count; + enum_func_status ret; + MYSQLND_ROW_C retrow = NULL; + unsigned int i, + field_count = result->field_count; MYSQLND_PACKET_ROW *row_packet = result->row_packet; - unsigned long *lengths = result->lengths; + unsigned long *lengths = result->lengths; DBG_ENTER("mysqlnd_fetch_row_unbuffered_c"); @@ -610,6 +638,10 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); DBG_RETURN(retrow); } + if (!row_packet) { + /* Not fully initialized object that is being cleaned up */ + DBG_RETURN(retrow); + } /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ row_packet->skip_extraction = FALSE; @@ -633,43 +665,49 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) MYSQLND_FIELD *field = result->meta->fields; struct mysqlnd_field_hash_key *zend_hash_key = result->meta->zend_hash_keys; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(retrow); + } retrow = mnd_malloc(result->field_count * sizeof(char *)); + if (retrow) { + for (i = 0; i < field_count; i++, field++, zend_hash_key++) { + zval *data = result->unbuf->last_row_data[i]; + unsigned int len; + + if (Z_TYPE_P(data) != IS_NULL) { + convert_to_string(data); + retrow[i] = Z_STRVAL_P(data); + len = Z_STRLEN_P(data); + } else { + retrow[i] = NULL; + len = 0; + } - for (i = 0; i < field_count; i++, field++, zend_hash_key++) { - zval *data = result->unbuf->last_row_data[i]; - int len; - - if (Z_TYPE_P(data) != IS_NULL) { - convert_to_string(data); - retrow[i] = Z_STRVAL_P(data); - len = Z_STRLEN_P(data); - } else { - retrow[i] = NULL; - len = 0; - } - - if (lengths) { - lengths[i] = len; - } + if (lengths) { + lengths[i] = len; + } - if (field->max_length < len) { - field->max_length = len; + if (field->max_length < len) { + field->max_length = len; + } } + } else { + SET_OOM_ERROR(result->conn->error_info); } } } else if (ret == FAIL) { if (row_packet->error_info.error_no) { result->conn->error_info = row_packet->error_info; - DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error); + DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ @@ -698,23 +736,26 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_fetch_row_unbuffered */ static enum_func_status -mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { - enum_func_status ret; - zval *row = (zval *) param; + enum_func_status ret; + zval *row = (zval *) param; MYSQLND_PACKET_ROW *row_packet = result->row_packet; DBG_ENTER("mysqlnd_fetch_row_unbuffered"); - DBG_INF_FMT("flags=%d", flags); + DBG_INF_FMT("flags=%u", flags); + *fetched_anything = FALSE; if (result->unbuf->eof_reached) { /* No more rows obviously */ - *fetched_anything = FALSE; DBG_RETURN(PASS); } if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) { - SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, - UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_RETURN(FAIL); + } + if (!row_packet) { + /* Not fully initialized object that is being cleaned up */ DBG_RETURN(FAIL); } /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ @@ -725,9 +766,6 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag result->m.unbuffered_free_last_data() before it. The function returns always true. */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { - result->unbuf->row_count++; - *fetched_anything = TRUE; - result->m.unbuffered_free_last_data(result TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; @@ -735,7 +773,6 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag row_packet->fields = NULL; row_packet->row_buffer = NULL; - MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { @@ -745,18 +782,20 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag unsigned int i, field_count = result->field_count; unsigned long *lengths = result->lengths; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); - + enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } for (i = 0; i < field_count; i++, field++, zend_hash_key++) { zval *data = result->unbuf->last_row_data[i]; - int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); if (lengths) { lengths[i] = len; @@ -800,12 +839,13 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag } } } + *fetched_anything = TRUE; + result->unbuf->row_count++; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { result->conn->error_info = row_packet->error_info; - DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error); + DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } - *fetched_anything = FALSE; CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { @@ -824,10 +864,9 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flag CONN_SET_STATE(result->conn, CONN_READY); } result->m.unbuffered_free_last_data(result TSRMLS_CC); - *fetched_anything = FALSE; } - DBG_INF_FMT("ret=%s fetched=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); DBG_RETURN(PASS); } /* }}} */ @@ -838,26 +877,34 @@ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC) { DBG_ENTER("mysqlnd_res::use_result"); - DBG_INF_FMT("ps=%d", ps); + DBG_INF_FMT("ps=%u", ps); SET_EMPTY_ERROR(result->conn->error_info); + if (ps == FALSE) { result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; - result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; + result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); + if (!result->lengths) { + goto oom; + } } else { result->type = MYSQLND_RES_PS_UNBUF; + result->m.fetch_row = NULL; /* result->m.fetch_row() will be set in mysqlnd_ps.c */ result->m.fetch_lengths = NULL; /* makes no sense */ - result->lengths = NULL; result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; + result->lengths = NULL; } - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); - result->result_set_memory_pool = mysqlnd_mempool_create(16000 TSRMLS_CC); + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->result_set_memory_pool || !result->unbuf) { + goto oom; + } /* Will be freed in the mysqlnd_internal_free_result_contents() called @@ -866,6 +913,9 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps */ /* FALSE = non-persistent */ result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC); + if (!result->row_packet) { + goto oom; + } result->row_packet->result_set_memory_pool = result->result_set_memory_pool; result->row_packet->field_count = result->field_count; result->row_packet->binary_protocol = ps; @@ -874,13 +924,16 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; DBG_RETURN(result); +oom: + SET_OOM_ERROR(result->conn->error_info); + DBG_RETURN(NULL); } /* }}} */ /* {{{ mysqlnd_fetch_row_buffered_c */ static MYSQLND_ROW_C -mysqlnd_fetch_row_buffered_c(MYSQLND_RES *result TSRMLS_DC) +mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC) { MYSQLND_ROW_C ret = NULL; MYSQLND_RES_BUFFERED *set = result->stored_data; @@ -898,15 +951,18 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES *result TSRMLS_DC) if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + result->meta->field_count, + result->meta->fields, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(ret); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - result->meta->field_count, - result->meta->fields, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -922,20 +978,23 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES *result TSRMLS_DC) } } - ret = mnd_malloc(result->field_count * sizeof(char *)); + set->data_cursor += result->meta->field_count; + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); - for (i = 0; i < result->field_count; i++, field++, zend_hash_key++) { - zval *data = current_row[i]; + ret = mnd_malloc(result->field_count * sizeof(char *)); + if (ret) { + for (i = 0; i < result->field_count; i++, field++, zend_hash_key++) { + zval *data = current_row[i]; - if (Z_TYPE_P(data) != IS_NULL) { - convert_to_string(data); - ret[i] = Z_STRVAL_P(data); - } else { - ret[i] = NULL; + if (Z_TYPE_P(data) != IS_NULL) { + convert_to_string(data); + ret[i] = Z_STRVAL_P(data); + } else { + ret[i] = NULL; + } } } - set->data_cursor += result->meta->field_count; - MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + /* there is no conn handle in this function thus we can't set OOM in error_info */ } else { set->data_cursor = NULL; DBG_INF("EOF reached"); @@ -947,11 +1006,12 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_fetch_row_buffered */ static enum_func_status -mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { unsigned int i; zval *row = (zval *) param; MYSQLND_RES_BUFFERED *set = result->stored_data; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_fetch_row_buffered"); DBG_INF_FMT("flags=%u row=%p", flags, row); @@ -966,15 +1026,18 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + result->meta->field_count, + result->meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(FAIL); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - result->meta->field_count, - result->meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -1030,13 +1093,15 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, set->data_cursor += result->meta->field_count; *fetched_anything = TRUE; MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + ret = PASS; } else { set->data_cursor = NULL; *fetched_anything = FALSE; + ret = PASS; DBG_INF("EOF reached"); } - DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything); - DBG_RETURN(PASS); + DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); + DBG_RETURN(ret); } /* }}} */ @@ -1045,23 +1110,33 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, /* {{{ mysqlnd_res::store_result_fetch_data */ enum_func_status -MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES *result, +MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool to_cache TSRMLS_DC) { enum_func_status ret; - MYSQLND_PACKET_ROW *row_packet; + MYSQLND_PACKET_ROW *row_packet = NULL; unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1; MYSQLND_RES_BUFFERED *set; DBG_ENTER("mysqlnd_res::store_result_fetch_data"); - DBG_INF_FMT("conn=%llu binary_proto=%d to_cache=%d", + DBG_INF_FMT("conn=%llu binary_proto=%u to_cache=%u", conn->thread_id, binary_protocol, to_cache); result->stored_data = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); + if (!set) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + goto end; + } if (free_rows) { set->row_buffers = mnd_pemalloc(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *), to_cache); + if (!set->row_buffers) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + goto end; + } } set->persistent = to_cache; set->references = 1; @@ -1071,6 +1146,11 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL /* non-persistent */ row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE TSRMLS_CC); + if (!row_packet) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + goto end; + } row_packet->result_set_memory_pool = result->result_set_memory_pool; row_packet->field_count = meta->field_count; row_packet->binary_protocol = binary_protocol; @@ -1083,10 +1163,17 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) { if (!free_rows) { uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */ + MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers; total_allocated_rows += set->row_count; - set->row_buffers = mnd_perealloc(set->row_buffers, - total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *), - set->persistent); + new_row_buffers = mnd_perealloc(set->row_buffers, + total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *), + set->persistent); + if (!new_row_buffers) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + goto end; + } + set->row_buffers = new_row_buffers; } free_rows--; set->row_buffers[set->row_count] = row_packet->row_buffer; @@ -1108,6 +1195,11 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL if (set->row_count) { /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ set->data = mnd_pemalloc(set->row_count * meta->field_count * sizeof(zval *), to_cache); + if (!set->data) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + goto end; + } memset(set->data, 0, set->row_count * meta->field_count * sizeof(zval *)); } @@ -1138,15 +1230,16 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL set->error_info = row_packet->error_info; } else { /* Position at the first row */ - set->data_cursor = set->data; + set->data_cursor = set->data; /* libmysql's documentation says it should be so for SELECT statements */ conn->upsert_status.affected_rows = set->row_count; } - PACKET_FREE(row_packet); - DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); +end: + PACKET_FREE(row_packet); + DBG_RETURN(ret); } /* }}} */ @@ -1162,7 +1255,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, zend_bool to_cache = FALSE; DBG_ENTER("mysqlnd_res::store_result"); - DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol); + DBG_INF_FMT("conn=%u ps_protocol=%u", conn->thread_id, ps_protocol); /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */ result->conn = conn->m->get_reference(conn TSRMLS_CC); @@ -1170,20 +1263,26 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, result->m.fetch_row = result->m.fetch_row_normal_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; - CONN_SET_STATE(conn, CONN_FETCHING_DATA); - - result->result_set_memory_pool = mysqlnd_mempool_create(16000 TSRMLS_CC); + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); + if (!result->result_set_memory_pool || !result->lengths) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(NULL); + } + + CONN_SET_STATE(conn, CONN_FETCHING_DATA); ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol, to_cache TSRMLS_CC); - if (PASS == ret) { - /* libmysql's documentation says it should be so for SELECT statements */ - conn->upsert_status.affected_rows = result->stored_data->row_count; - } else { - conn->error_info = result->stored_data->error_info; - result->m.free_result_internal(result TSRMLS_CC); - result = NULL; + if (FAIL == ret) { + if (result->stored_data) { + conn->error_info = result->stored_data->error_info; + } else { + SET_OOM_ERROR(conn->error_info); + } + DBG_RETURN(NULL); } + /* libmysql's documentation says it should be so for SELECT statements */ + conn->upsert_status.affected_rows = result->stored_data->row_count; DBG_RETURN(result); } @@ -1224,10 +1323,10 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) /* {{{ mysqlnd_res::free_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC) { DBG_ENTER("mysqlnd_res::free_result"); - DBG_INF_FMT("implicit=%d", implicit); + DBG_INF_FMT("implicit=%u", implicit); result->m.skip_result(result TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL, @@ -1242,7 +1341,7 @@ MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES *result, zend_bool implicit /* {{{ mysqlnd_res::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES *result, uint64_t row TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS_DC) { DBG_ENTER("mysqlnd_res::data_seek"); DBG_INF_FMT("row=%lu", row); @@ -1287,24 +1386,28 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_field"); - if (result->meta) { - /* - We optimize the result set, so we don't convert all the data from raw buffer format to - zval arrays during store. In the case someone doesn't read all the lines this will - save time. However, when a metadata call is done, we need to calculate max_length. - We don't have control whether max_length will be used, unfortunately. Otherwise we - could have been able to skip that step. - Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, - then we can have max_length as dynamic property, which will be calculated during runtime and - not during mysqli_fetch_field() time. - */ - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); - /* we have to initialize the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + /* + We optimize the result set, so we don't convert all the data from raw buffer format to + zval arrays during store. In the case someone doesn't read all the lines this will + save time. However, when a metadata call is done, we need to calculate max_length. + We don't have control whether max_length will be used, unfortunately. Otherwise we + could have been able to skip that step. + Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, + then we can have max_length as dynamic property, which will be calculated during runtime and + not during mysqli_fetch_field() time. + */ + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); + /* we have to initialize the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } /* }}} */ @@ -1315,24 +1418,28 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_field_direct"); - if (result->meta) { - /* - We optimize the result set, so we don't convert all the data from raw buffer format to - zval arrays during store. In the case someone doesn't read all the lines this will - save time. However, when a metadata call is done, we need to calculate max_length. - We don't have control whether max_length will be used, unfortunately. Otherwise we - could have been able to skip that step. - Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, - then we can have max_length as dynamic property, which will be calculated during runtime and - not during mysqli_fetch_field_direct() time. - */ - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); - /* we have to initialized the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + /* + We optimize the result set, so we don't convert all the data from raw buffer format to + zval arrays during store. In the case someone doesn't read all the lines this will + save time. However, when a metadata call is done, we need to calculate max_length. + We don't have control whether max_length will be used, unfortunately. Otherwise we + could have been able to skip that step. + Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, + then we can have max_length as dynamic property, which will be calculated during runtime and + not during mysqli_fetch_field_direct() time. + */ + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); + /* we have to initialized the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } @@ -1344,13 +1451,17 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_fields"); - if (result->meta) { - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - /* we have to initialize the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + /* we have to initialize the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } /* }}} */ @@ -1382,14 +1493,14 @@ MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_ /* {{{ mysqlnd_res::fetch_into */ static void -MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, +MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags, zval *return_value, enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC) { zend_bool fetched_anything; DBG_ENTER("mysqlnd_res::fetch_into"); - DBG_INF_FMT("flags=%u mysqlnd_extension=%d", flags, extension); + DBG_INF_FMT("flags=%u mysqlnd_extension=%u", flags, extension); if (!result->m.fetch_row) { RETVAL_NULL(); @@ -1426,7 +1537,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES *result, unsigned int flags, /* {{{ mysqlnd_res::fetch_row_c */ static MYSQLND_ROW_C -MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES *result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC) { MYSQLND_ROW_C ret = NULL; DBG_ENTER("mysqlnd_res::fetch_row_c"); @@ -1447,7 +1558,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES *result TSRMLS_DC) /* {{{ mysqlnd_res::fetch_all */ static void -MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { zval *row; ulong i = 0; @@ -1457,16 +1568,21 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, DBG_INF_FMT("flags=%u", flags); /* mysqlnd_res::fetch_all works with buffered resultsets only */ - if (!set || - !set->row_count || !set->data_cursor || - set->data_cursor >= set->data + set->row_count) - { + if (result->unbuf || (!result->unbuf && !set)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "fetch_all can be used only with buffered sets"); + if (result->conn) { + SET_CLIENT_ERROR(result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets"); + } RETVAL_NULL(); DBG_VOID_RETURN; - } + } mysqlnd_array_init(return_value, (unsigned int) set->row_count); + if (!set->row_count || !set->data_cursor || set->data_cursor >= set->data + set->row_count) { + DBG_VOID_RETURN; + } + while (set->data_cursor && (set->data_cursor - set->data) < (set->row_count * result->meta->field_count)) { @@ -1482,7 +1598,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES *result, unsigned int flags, /* {{{ mysqlnd_res::fetch_field_data */ static void -MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int offset, zval *return_value TSRMLS_DC) { zval row; zval **entry; @@ -1524,48 +1640,60 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int /* }}} */ +static +MYSQLND_CLASS_METHODS_START(mysqlnd_res) + NULL, /* fetch_row */ + mysqlnd_fetch_row_buffered, + mysqlnd_fetch_row_unbuffered, + MYSQLND_METHOD(mysqlnd_res, use_result), + MYSQLND_METHOD(mysqlnd_res, store_result), + MYSQLND_METHOD(mysqlnd_res, fetch_into), + MYSQLND_METHOD(mysqlnd_res, fetch_row_c), + MYSQLND_METHOD(mysqlnd_res, fetch_all), + MYSQLND_METHOD(mysqlnd_res, fetch_field_data), + MYSQLND_METHOD(mysqlnd_res, num_rows), + MYSQLND_METHOD(mysqlnd_res, num_fields), + MYSQLND_METHOD(mysqlnd_res, skip_result), + MYSQLND_METHOD(mysqlnd_res, data_seek), + MYSQLND_METHOD(mysqlnd_res, field_seek), + MYSQLND_METHOD(mysqlnd_res, field_tell), + MYSQLND_METHOD(mysqlnd_res, fetch_field), + MYSQLND_METHOD(mysqlnd_res, fetch_field_direct), + MYSQLND_METHOD(mysqlnd_res, fetch_fields), + MYSQLND_METHOD(mysqlnd_res, read_result_metadata), + NULL, /* fetch_lengths */ + MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data), + MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest), + MYSQLND_METHOD(mysqlnd_res, free_result_buffers), + MYSQLND_METHOD(mysqlnd_res, free_result), + + mysqlnd_internal_free_result, /* free_result_internal */ + mysqlnd_internal_free_result_contents, /* free_result_contents */ + MYSQLND_METHOD(mysqlnd_res, free_buffered_data), + MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data), + + NULL /* row_decoder */, + mysqlnd_result_meta_init +MYSQLND_CLASS_METHODS_END; + + /* {{{ mysqlnd_result_init */ PHPAPI MYSQLND_RES * -mysqlnd_result_init(unsigned int field_count TSRMLS_DC) +mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC) { size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_RES *ret = mnd_ecalloc(1, alloc_size); + MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_result_init"); DBG_INF_FMT("field_count=%u", field_count); + + if (!ret) { + DBG_RETURN(NULL); + } + ret->persistent = persistent; ret->field_count = field_count; - - ret->m.use_result = MYSQLND_METHOD(mysqlnd_res, use_result); - ret->m.store_result = MYSQLND_METHOD(mysqlnd_res, store_result); - ret->m.free_result = MYSQLND_METHOD(mysqlnd_res, free_result); - ret->m.seek_data = MYSQLND_METHOD(mysqlnd_res, data_seek); - ret->m.num_rows = MYSQLND_METHOD(mysqlnd_res, num_rows); - ret->m.num_fields = MYSQLND_METHOD(mysqlnd_res, num_fields); - ret->m.fetch_into = MYSQLND_METHOD(mysqlnd_res, fetch_into); - ret->m.fetch_row_c = MYSQLND_METHOD(mysqlnd_res, fetch_row_c); - ret->m.fetch_all = MYSQLND_METHOD(mysqlnd_res, fetch_all); - ret->m.fetch_field_data = MYSQLND_METHOD(mysqlnd_res, fetch_field_data); - ret->m.seek_field = MYSQLND_METHOD(mysqlnd_res, field_seek); - ret->m.field_tell = MYSQLND_METHOD(mysqlnd_res, field_tell); - ret->m.fetch_field = MYSQLND_METHOD(mysqlnd_res, fetch_field); - ret->m.fetch_field_direct = MYSQLND_METHOD(mysqlnd_res, fetch_field_direct); - ret->m.fetch_fields = MYSQLND_METHOD(mysqlnd_res, fetch_fields); - - ret->m.skip_result = MYSQLND_METHOD(mysqlnd_res, skip_result); - ret->m.free_result_buffers = MYSQLND_METHOD(mysqlnd_res, free_result_buffers); - ret->m.free_result_internal = mysqlnd_internal_free_result; - ret->m.free_result_contents = mysqlnd_internal_free_result_contents; - ret->m.free_buffered_data = MYSQLND_METHOD(mysqlnd_res, free_buffered_data); - ret->m.unbuffered_free_last_data = MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data); - - ret->m.read_result_metadata = MYSQLND_METHOD(mysqlnd_res, read_result_metadata); - ret->m.store_result_fetch_data = MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data); - ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest); - - ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered; - ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered; - ret->m.row_decoder = NULL; + ret->m = mysqlnd_mysqlnd_res_methods; DBG_RETURN(ret); } @@ -1584,6 +1712,15 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result } /* }}} */ + +/* {{{ mysqlnd_result_get_methods */ +PHPAPI struct st_mysqlnd_res_methods * +mysqlnd_result_get_methods() +{ + return &mysqlnd_mysqlnd_res_methods; +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h index 0ed188df9..7c4ab99d3 100644 --- a/ext/mysqlnd/mysqlnd_result.h +++ b/ext/mysqlnd/mysqlnd_result.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,14 +18,15 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_result.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_result.h 299348 2010-05-13 15:29:08Z andrey $ */ #ifndef MYSQLND_RESULT_H #define MYSQLND_RESULT_H -PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count TSRMLS_DC); +PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC); +PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods(); -enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC); +enum_func_status mysqlnd_query_read_result_set_header(MYSQLND * conn, MYSQLND_STMT * stmt TSRMLS_DC); #endif /* MYSQLND_RESULT_H */ diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c index 108b83938..a3897bcbb 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.c +++ b/ext/mysqlnd/mysqlnd_result_meta.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_result_meta.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_result_meta.c 300635 2010-06-21 15:32:26Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" @@ -29,8 +29,8 @@ /* {{{ php_mysqlnd_free_field_metadata */ -static -void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC) +static void +php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC) { if (meta) { if (meta->root) { @@ -51,10 +51,10 @@ void php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent T The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c and modified for the needs of mysqlnd. */ -static -zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx) +static zend_bool +mysqlnd_is_key_numeric(char *key, size_t length, long *idx) { - register char *tmp=key; + register char * tmp = key; if (*tmp=='-') { tmp++; @@ -94,10 +94,10 @@ zend_bool mysqlnd_is_key_numeric(char *key, size_t length, long *idx) #if PHP_MAJOR_VERSION >= 6 /* {{{ mysqlnd_unicode_is_key_numeric */ -static -zend_bool mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx) +static zend_bool +mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx) { - register UChar *tmp=key; + register UChar * tmp=key; if (*tmp==0x2D /*'-'*/) { tmp++; @@ -150,12 +150,17 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met DBG_ENTER("mysqlnd_res_meta::read_metadata"); field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE TSRMLS_CC); + if (!field_packet) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } + field_packet->persistent_alloc = meta->persistent; for (;i < meta->field_count; i++) { long idx; if (meta->fields[i].root) { /* We re-read metadata for PS */ - mnd_efree(meta->fields[i].root); + mnd_pefree(meta->fields[i].root, meta->persistent); meta->fields[i].root = NULL; } @@ -177,10 +182,10 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met } if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) { - DBG_ERR_FMT("Unknown type %d sent by the server. Please send a report to the developers", + DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type); php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Unknown type %d sent by the server. " + "Unknown type %u sent by the server. " "Please send a report to the developers", meta->fields[i].type); PACKET_FREE(field_packet); @@ -260,21 +265,20 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met /* {{{ mysqlnd_res_meta::free */ static void -MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta TSRMLS_DC) { int i; MYSQLND_FIELD *fields; - DBG_ENTER("mysqlnd_res_meta::free"); - DBG_INF_FMT("persistent=%d", persistent); + DBG_INF_FMT("persistent=%u", meta->persistent); if ((fields = meta->fields)) { DBG_INF("Freeing fields metadata"); i = meta->field_count; while (i--) { - php_mysqlnd_free_field_metadata(fields++, persistent TSRMLS_CC); + php_mysqlnd_free_field_metadata(fields++, meta->persistent TSRMLS_CC); } - mnd_pefree(meta->fields, persistent); + mnd_pefree(meta->fields, meta->persistent); meta->fields = NULL; } @@ -284,16 +288,16 @@ MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool per if (UG(unicode)) { for (i = 0; i < meta->field_count; i++) { if (meta->zend_hash_keys[i].ustr.v) { - mnd_pefree(meta->zend_hash_keys[i].ustr.v, persistent); + mnd_pefree(meta->zend_hash_keys[i].ustr.v, meta->persistent); } } } #endif - mnd_pefree(meta->zend_hash_keys, persistent); + mnd_pefree(meta->zend_hash_keys, meta->persistent); meta->zend_hash_keys = NULL; } DBG_INF("Freeing metadata structure"); - mnd_pefree(meta, persistent); + mnd_pefree(meta, meta->persistent); DBG_VOID_RETURN; } @@ -306,17 +310,31 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co { unsigned int i; /* +1 is to have empty marker at the end */ - MYSQLND_RES_METADATA *new_meta = mnd_pemalloc(sizeof(MYSQLND_RES_METADATA), persistent); - MYSQLND_FIELD *new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); - MYSQLND_FIELD *orig_fields = meta->fields; + MYSQLND_RES_METADATA * new_meta = NULL; + MYSQLND_FIELD * new_fields; + MYSQLND_FIELD * orig_fields = meta->fields; size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key); DBG_ENTER("mysqlnd_res_meta::clone_metadata"); - DBG_INF_FMT("persistent=%d", persistent); + DBG_INF_FMT("persistent=%u", persistent); + + new_meta = mnd_pecalloc(1, sizeof(MYSQLND_RES_METADATA), persistent); + if (!new_meta) { + goto oom; + } + new_meta->persistent = persistent; + new_meta->m = meta->m; + + new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent); + if (!new_fields) { + goto oom; + } new_meta->zend_hash_keys = mnd_pemalloc(len, persistent); + if (!new_meta->zend_hash_keys) { + goto oom; + } memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len); - new_meta->m = meta->m; /* This will copy also the strings and the root, which we will have @@ -326,6 +344,9 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co for (i = 0; i < meta->field_count; i++) { /* First copy the root, then field by field adjust the pointers */ new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent); + if (!new_fields[i].root) { + goto oom; + } memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len); if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) { @@ -353,6 +374,9 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co /* def is not on the root, if allocated at all */ if (orig_fields[i].def) { new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent); + if (!new_fields[i].def) { + goto oom; + } /* copy the trailing \0 too */ memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1); } @@ -360,6 +384,9 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co if (new_meta->zend_hash_keys[i].ustr.u) { new_meta->zend_hash_keys[i].ustr.u = eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen); + if (!new_meta->zend_hash_keys[i].ustr.u) { + goto oom; + } } #endif } @@ -369,6 +396,12 @@ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * co new_meta->fields = new_fields; DBG_RETURN(new_meta); +oom: + if (new_meta) { + new_meta->m->free_metadata(new_meta TSRMLS_CC); + new_meta = NULL; + } + DBG_RETURN(NULL); } /* }}} */ @@ -394,7 +427,7 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { DBG_ENTER("mysqlnd_res_meta::fetch_field_direct"); - DBG_INF_FMT("fieldnr=%d", fieldnr); + DBG_INF_FMT("fieldnr=%u", fieldnr); DBG_INF_FMT("name=%s max_length=%u", meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"", meta->fields[meta->current_field].max_length); @@ -422,6 +455,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const /* }}} */ +static MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta) MYSQLND_METHOD(mysqlnd_res_meta, fetch_field), MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct), @@ -435,23 +469,60 @@ MYSQLND_CLASS_METHODS_END; /* {{{ mysqlnd_result_meta_init */ PHPAPI MYSQLND_RES_METADATA * -mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC) +mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC) { - MYSQLND_RES_METADATA *ret; + size_t alloc_size = sizeof(MYSQLND_RES_METADATA) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_METADATA *ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_result_meta_init"); + DBG_INF_FMT("persistent=%u", persistent); + + do { + if (!ret) { + break; + } + ret->m = & mysqlnd_mysqlnd_res_meta_methods; + + ret->persistent = persistent; + ret->field_count = field_count; + /* +1 is to have empty marker at the end */ + ret->fields = mnd_pecalloc(field_count + 1, sizeof(MYSQLND_FIELD), ret->persistent); + ret->zend_hash_keys = mnd_pecalloc(field_count, sizeof(struct mysqlnd_field_hash_key), ret->persistent); + if (!ret->fields || !ret->zend_hash_keys) { + break; + } + DBG_INF_FMT("meta=%p", ret); + DBG_RETURN(ret); + } while (0); + if (ret) { + ret->m->free_metadata(ret TSRMLS_CC); + } + DBG_RETURN(NULL); +} +/* }}} */ - /* +1 is to have empty marker at the end */ - ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA)); - ret->field_count = field_count; - ret->fields = mnd_ecalloc(field_count + 1, sizeof(MYSQLND_FIELD)); - ret->zend_hash_keys = mnd_ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key)); - - ret->m = & mysqlnd_mysqlnd_res_meta_methods; - DBG_INF_FMT("meta=%p", ret); - DBG_RETURN(ret); + +/* {{{ mysqlnd_res_meta_get_methods */ +PHPAPI struct st_mysqlnd_res_meta_methods * +mysqlnd_result_metadata_get_methods() +{ + return &mysqlnd_mysqlnd_res_meta_methods; } +/* }}} */ +/* {{{ _mysqlnd_plugin_get_plugin_result_metadata_data */ +PHPAPI void ** +_mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC) +{ + DBG_ENTER("_mysqlnd_plugin_get_plugin_result_metadata_data"); + DBG_INF_FMT("plugin_id=%u", plugin_id); + if (!meta || plugin_id >= mysqlnd_plugin_count()) { + return NULL; + } + DBG_RETURN((void *)((char *)meta + sizeof(MYSQLND_RES_METADATA) + plugin_id * sizeof(void *))); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h index 6a3d9164f..90554da21 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.h +++ b/ext/mysqlnd/mysqlnd_result_meta.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,15 +18,14 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_result_meta.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_result_meta.h 299389 2010-05-14 16:42:17Z andrey $ */ #ifndef MYSQLND_RESULT_META_H #define MYSQLND_RESULT_META_H - -PHPAPI MYSQLND_RES_METADATA * mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC); - - +PHPAPI MYSQLND_RES_METADATA * mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC); +PHPAPI struct st_mysqlnd_res_meta_methods * mysqlnd_result_metadata_get_methods(); +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC); #endif /* MYSQLND_RESULT_META_H */ diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c index 1fa8d1472..6557e234c 100644 --- a/ext/mysqlnd/mysqlnd_statistics.c +++ b/ext/mysqlnd/mysqlnd_statistics.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_statistics.c 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_statistics.c 298217 2010-04-20 13:50:34Z felipe $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_priv.h" @@ -99,19 +99,25 @@ const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] = { STR_W_LEN("explicit_stmt_close") }, { STR_W_LEN("implicit_stmt_close") }, { STR_W_LEN("mem_emalloc_count") }, - { STR_W_LEN("mem_emalloc_ammount") }, + { STR_W_LEN("mem_emalloc_amount") }, { STR_W_LEN("mem_ecalloc_count") }, - { STR_W_LEN("mem_ecalloc_ammount") }, + { STR_W_LEN("mem_ecalloc_amount") }, { STR_W_LEN("mem_erealloc_count") }, - { STR_W_LEN("mem_erealloc_ammount") }, + { STR_W_LEN("mem_erealloc_amount") }, { STR_W_LEN("mem_efree_count") }, + { STR_W_LEN("mem_efree_amount") }, { STR_W_LEN("mem_malloc_count") }, - { STR_W_LEN("mem_malloc_ammount") }, + { STR_W_LEN("mem_malloc_amount") }, { STR_W_LEN("mem_calloc_count") }, - { STR_W_LEN("mem_calloc_ammount") }, + { STR_W_LEN("mem_calloc_amount") }, { STR_W_LEN("mem_realloc_count") }, - { STR_W_LEN("mem_realloc_ammount") }, + { STR_W_LEN("mem_realloc_amount") }, { STR_W_LEN("mem_free_count") }, + { STR_W_LEN("mem_free_amount") }, + { STR_W_LEN("mem_estrndup_count") }, + { STR_W_LEN("mem_strndup_count") }, + { STR_W_LEN("mem_estndup_count") }, + { STR_W_LEN("mem_strdup_count") }, { STR_W_LEN("proto_text_fetched_null") }, { STR_W_LEN("proto_text_fetched_bit") }, { STR_W_LEN("proto_text_fetched_tinyint") }, @@ -219,7 +225,8 @@ mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING /* {{{ _mysqlnd_get_client_stats */ -PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +PHPAPI void +_mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats; DBG_ENTER("_mysqlnd_get_client_stats"); diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h index a3281b44f..3fffe5fb1 100644 --- a/ext/mysqlnd/mysqlnd_statistics.h +++ b/ext/mysqlnd/mysqlnd_statistics.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_statistics.h 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_statistics.h 298318 2010-04-22 13:03:44Z andrey $ */ #ifndef MYSQLND_STATISTICS_H #define MYSQLND_STATISTICS_H @@ -42,27 +42,34 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; #define MYSQLND_STATS_UNLOCK(stats) #endif +#ifndef MYSQLND_CORE_STATISTICS_TRIGGERS_DISABLED +#define MYSQLND_STAT_CALL_TRIGGER(s_array, statistic, val) \ + if ((s_array)->triggers[(statistic)] && (s_array)->in_trigger == FALSE) { \ + (s_array)->in_trigger = TRUE; \ + MYSQLND_STATS_UNLOCK((s_array)); \ + \ + (s_array)->triggers[(statistic)]((s_array), (statistic), (val) TSRMLS_CC); \ + \ + MYSQLND_STATS_LOCK((s_array)); \ + (s_array)->in_trigger = FALSE; \ + } +#else +#define MYSQLND_STAT_CALL_TRIGGER(s_array, statistic, val) +#endif /* MYSQLND_CORE_STATISTICS_TRIGGERS_DISABLED */ + #define MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(stats, statistic, value) \ { \ - MYSQLND_STATS_LOCK(stats); \ + MYSQLND_STATS_LOCK(stats); \ (stats)->values[(statistic)] += (value); \ - if ((stats)->triggers[(statistic)] && (stats)->in_trigger == FALSE) { \ - (stats)->in_trigger = TRUE; \ - MYSQLND_STATS_UNLOCK(stats); \ - \ - (stats)->triggers[(statistic)]((stats), (statistic), (value) TSRMLS_CC); \ - \ - MYSQLND_STATS_LOCK(stats); \ - (stats)->in_trigger = FALSE; \ - } \ + MYSQLND_STAT_CALL_TRIGGER((stats), (statistic), (value)); \ MYSQLND_STATS_UNLOCK(_p_s); \ - } \ + } #define MYSQLND_DEC_STATISTIC(enabler, stats, statistic) \ { \ enum_mysqlnd_collected_stats _s = (statistic);\ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \ - if ((enabler) && _p_s && _s != _p_s->count) { \ + if ((enabler) && _p_s && _s != _p_s->count) { \ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, -1); \ }\ } @@ -71,7 +78,7 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; { \ enum_mysqlnd_collected_stats _s = (statistic);\ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \ - if ((enabler) && _p_s && _s != _p_s->count) { \ + if ((enabler) && _p_s && _s != _p_s->count) { \ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, 1); \ }\ } @@ -80,7 +87,7 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; { \ enum_mysqlnd_collected_stats _s = (statistic);\ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \ - if ((enabler) && _p_s && _s != _p_s->count) { \ + if ((enabler) && _p_s && _s != _p_s->count) { \ uint64_t v = (uint64_t) (value); \ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, v); \ }\ @@ -89,7 +96,7 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; #define MYSQLND_INC_STATISTIC_W_VALUE2(enabler, stats, statistic1, value1, statistic2, value2) \ { \ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \ - if ((enabler) && _p_s) { \ + if ((enabler) && _p_s) { \ uint64_t v1 = (uint64_t) (value1); \ uint64_t v2 = (uint64_t) (value2); \ enum_mysqlnd_collected_stats _s1 = (statistic1);\ @@ -102,7 +109,7 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; #define MYSQLND_INC_STATISTIC_W_VALUE3(enabler, stats, statistic1, value1, statistic2, value2, statistic3, value3) \ { \ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \ - if ((enabler) && _p_s) { \ + if ((enabler) && _p_s) { \ uint64_t v1 = (uint64_t) (value1); \ uint64_t v2 = (uint64_t) (value2); \ uint64_t v3 = (uint64_t) (value3); \ @@ -116,31 +123,45 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[]; } + +#ifndef MYSQLND_CORE_STATISTICS_DISABLED + #define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \ - MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)) + MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)) #define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \ - MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)) + MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)) #define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \ - MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)) + MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)) #define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \ - MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \ - MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic)); + MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \ + MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic)); #define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \ - MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \ - MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value)); + MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \ + MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value)); #define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \ - MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \ - MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2)); + MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \ + MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2)); #define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \ - MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \ - MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); + MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \ + MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); +#else + +#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) +#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) +#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) +#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) +#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) + +#endif /* MYSQLND_CORE_STATISTICS_DISABLED */ PHPAPI void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); @@ -150,10 +171,8 @@ PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats); PHPAPI mysqlnd_stat_trigger mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats stat, mysqlnd_stat_trigger trigger TSRMLS_DC); PHPAPI mysqlnd_stat_trigger mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats TSRMLS_DC); - #endif /* MYSQLND_STATISTICS_H */ - /* * Local variables: * tab-width: 4 diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 99f40169d..f229d9509 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,11 +18,13 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_structs.h 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_structs.h 300735 2010-06-24 19:52:13Z andrey $ */ #ifndef MYSQLND_STRUCTS_H #define MYSQLND_STRUCTS_H +#define MYSQLND_TYPEDEFED_METHODS + typedef struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL; typedef struct st_mysqlnd_memory_pool_chunk MYSQLND_MEMORY_POOL_CHUNK; typedef struct st_mysqlnd_memory_pool_chunk_llist MYSQLND_MEMORY_POOL_CHUNK_LLIST; @@ -37,20 +39,17 @@ struct st_mysqlnd_memory_pool unsigned int arena_size; unsigned int free_size; - MYSQLND_MEMORY_POOL_CHUNK* free_chunk_list[MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE]; - unsigned int free_chunk_list_elements; - MYSQLND_MEMORY_POOL_CHUNK* (*get_chunk)(MYSQLND_MEMORY_POOL * pool, unsigned int size TSRMLS_DC); }; struct st_mysqlnd_memory_pool_chunk { uint64_t app; - MYSQLND_MEMORY_POOL *pool; + MYSQLND_MEMORY_POOL *pool; zend_uchar *ptr; unsigned int size; - void (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size TSRMLS_DC); - void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, zend_bool cache_it TSRMLS_DC); + enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size TSRMLS_DC); + void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk TSRMLS_DC); zend_bool from_pool; }; @@ -64,12 +63,12 @@ typedef struct st_mysqlnd_cmd_buffer typedef struct st_mysqlnd_field { - char *name; /* Name of column */ - char *org_name; /* Original column name, if an alias */ - char *table; /* Table of column if column was a field */ - char *org_table; /* Org table name, if table was an alias */ - char *db; /* Database for table */ - char *catalog; /* Catalog for table */ + const char *name; /* Name of column */ + const char *org_name; /* Original column name, if an alias */ + const char *table; /* Table of column if column was a field */ + const char *org_table; /* Org table name, if table was an alias */ + const char *db; /* Database for table */ + const char *catalog; /* Catalog for table */ char *def; /* Default value (set by mysql_list_fields) */ unsigned long length; /* Width of column (create length) */ unsigned long max_length; /* Max width for selected set */ @@ -89,7 +88,6 @@ typedef struct st_mysqlnd_field } MYSQLND_FIELD; - typedef struct st_mysqlnd_upsert_result { unsigned int warning_count; @@ -109,24 +107,24 @@ typedef struct st_mysqlnd_error_info typedef struct st_mysqlnd_infile_info { - php_stream *fd; - int error_no; - char error_msg[MYSQLND_ERRMSG_SIZE + 1]; - const char *filename; + php_stream *fd; + int error_no; + char error_msg[MYSQLND_ERRMSG_SIZE + 1]; + const char *filename; } MYSQLND_INFILE_INFO; /* character set information */ typedef struct st_mysqlnd_charset { - unsigned int nr; - const char *name; - const char *collation; - unsigned int char_minlen; - unsigned int char_maxlen; - const char *comment; - unsigned int (*mb_charlen)(unsigned int c); - unsigned int (*mb_valid)(const char *start, const char *end); + unsigned int nr; + const char *name; + const char *collation; + unsigned int char_minlen; + unsigned int char_maxlen; + const char *comment; + unsigned int (*mb_charlen)(unsigned int c); + unsigned int (*mb_valid)(const char *start, const char *end); } MYSQLND_CHARSET; @@ -146,20 +144,24 @@ typedef struct st_mysqlnd_options ulong flags; /* init commands - we need to send them to server directly after connect */ - unsigned int num_commands; - char **init_commands; + unsigned int num_commands; + char **init_commands; /* configuration file information */ char *cfg_file; char *cfg_section; - /* SSL information */ - char *ssl_key; - char *ssl_cert; - char *ssl_ca; - char *ssl_capath; - char *ssl_cipher; - zend_bool use_ssl; + /* + We need to keep these because otherwise st_mysqlnd_conn will be changed. + The ABI will be broken and the methods structure will be somewhere else + in the memory which can crash external code. Feel free to reuse these. + */ + char * unused1; + char * unused2; + char * unused3; + char * unused4; + char * unused5; + zend_bool unused6; char *charset_name; /* maximum allowed packet size for communication */ @@ -179,6 +181,15 @@ typedef struct st_mysqlnd_net_options unsigned int timeout_write; unsigned int net_read_buffer_size; + + /* SSL information */ + char *ssl_key; + char *ssl_cert; + char *ssl_ca; + char *ssl_capath; + char *ssl_cipher; + char *ssl_passphrase; + zend_bool ssl_verify_peer; } MYSQLND_NET_OPTIONS; @@ -187,6 +198,7 @@ typedef struct st_mysqlnd_net MYSQLND_NET; typedef struct st_mysqlnd_protocol MYSQLND_PROTOCOL; typedef struct st_mysqlnd_res MYSQLND_RES; typedef char** MYSQLND_ROW_C; /* return data as array of strings */ +typedef struct st_mysqlnd_stmt_data MYSQLND_STMT_DATA; typedef struct st_mysqlnd_stmt MYSQLND_STMT; typedef unsigned int MYSQLND_FIELD_OFFSET; @@ -215,7 +227,7 @@ typedef void (*mysqlnd_stat_trigger)(MYSQLND_STATS * stats, enum_mysqlnd_collect struct st_mysqlnd_stats { uint64_t *values; - mysqlnd_stat_trigger *triggers; + mysqlnd_stat_trigger *triggers; size_t count; zend_bool in_trigger; #ifdef ZTS @@ -236,18 +248,41 @@ typedef struct st_mysqlnd_read_buffer { } MYSQLND_READ_BUFFER; + +typedef enum_func_status (*func_mysqlnd_net__connect)(MYSQLND_NET * net, const char * const scheme, size_t scheme_len, zend_bool persistent, char **errstr, int * errcode TSRMLS_DC); +typedef size_t (*func_mysqlnd_net__send)(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__receive)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_option option, const char * const value TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__network_read)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC); +typedef size_t (*func_mysqlnd_net__network_write)(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__decode)(zend_uchar * uncompressed_data, size_t uncompressed_data_len, const zend_uchar * const compressed_data, size_t compressed_data_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__encode)(zend_uchar * compress_buffer, size_t compress_buffer_len, const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC); +typedef size_t (*func_mysqlnd_net__consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC); +typedef void (*func_mysqlnd_net__free_contents)(MYSQLND_NET * net TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__enable_ssl)(MYSQLND_NET * const net TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__disable_ssl)(MYSQLND_NET * const net TSRMLS_DC); + + struct st_mysqlnd_net_methods { - enum_func_status (*connect)(MYSQLND_NET * net, const char * const scheme, size_t scheme_len, zend_bool persistent, char **errstr, int * errcode TSRMLS_DC); - size_t (*send)(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC); - size_t (*receive)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC); - enum_func_status (*set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_option option, const char * const value TSRMLS_DC); - enum_func_status (*network_read)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC); - size_t (*network_write)(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC); - enum_func_status (*decode)(zend_uchar * uncompressed_data, size_t uncompressed_data_len, const zend_uchar * const compressed_data, size_t compressed_data_len TSRMLS_DC); - enum_func_status (*encode)(zend_uchar * compress_buffer, size_t compress_buffer_len, const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC); - size_t (*consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC); - void (*free_contents)(MYSQLND_NET * net TSRMLS_DC); + func_mysqlnd_net__connect connect; + func_mysqlnd_net__send send; + func_mysqlnd_net__receive receive; + func_mysqlnd_net__set_client_option set_client_option; + func_mysqlnd_net__network_read network_read; + func_mysqlnd_net__network_write network_write; + func_mysqlnd_net__decode decode; + func_mysqlnd_net__encode encode; + func_mysqlnd_net__consume_uneaten_data consume_uneaten_data; + func_mysqlnd_net__free_contents free_contents; + func_mysqlnd_net__enable_ssl enable_ssl; + func_mysqlnd_net__disable_ssl disable_ssl; + + void * unused1; + void * unused2; + void * unused3; + void * unused4; + void * unused5; }; @@ -264,188 +299,383 @@ struct st_mysqlnd_packet_stats; struct st_mysqlnd_packet_prepare_response; struct st_mysqlnd_packet_chg_user_resp; +typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol__get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol__get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol__get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol__get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol__get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol__get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol__get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol__get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol__get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol__get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); +typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol__get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); + struct st_mysqlnd_protocol_methods { - struct st_mysqlnd_packet_greet * (*get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_auth * (*get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_ok * (*get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_command * (*get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_eof * (*get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_rset_header * (*get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_res_field * (*get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_row * (*get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_stats * (*get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_prepare_response *(*get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); - struct st_mysqlnd_packet_chg_user_resp*(*get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC); + func_mysqlnd_protocol__get_greet_packet get_greet_packet; + func_mysqlnd_protocol__get_auth_packet get_auth_packet; + func_mysqlnd_protocol__get_ok_packet get_ok_packet; + func_mysqlnd_protocol__get_command_packet get_command_packet; + func_mysqlnd_protocol__get_eof_packet get_eof_packet; + func_mysqlnd_protocol__get_rset_header_packet get_rset_header_packet; + func_mysqlnd_protocol__get_result_field_packet get_result_field_packet; + func_mysqlnd_protocol__get_row_packet get_row_packet; + func_mysqlnd_protocol__get_stats_packet get_stats_packet; + func_mysqlnd_protocol__get_prepare_response_packet get_prepare_response_packet; + func_mysqlnd_protocol__get_change_user_response_packet get_change_user_response_packet; + + void * unused1; + void * unused2; + void * unused3; + void * unused4; + void * unused5; }; + +typedef enum_func_status (*func_mysqlnd_conn__init)(MYSQLND * conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__connect)(MYSQLND *conn, const char *host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket, unsigned int mysql_flags TSRMLS_DC); +typedef ulong (*func_mysqlnd_conn__escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__reap_query)(MYSQLND *conn TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__use_result)(MYSQLND * const conn TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__store_result)(MYSQLND * const conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__next_result)(MYSQLND * const conn TSRMLS_DC); +typedef zend_bool (*func_mysqlnd_conn__more_results)(const MYSQLND * const conn TSRMLS_DC); + +typedef MYSQLND_STMT * (*func_mysqlnd_conn__stmt_init)(MYSQLND * const conn TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_conn__shutdown_server)(MYSQLND * const conn, uint8_t level TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__refresh_server)(MYSQLND * const conn, uint8_t options TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_conn__ping)(MYSQLND * const conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__kill_connection)(MYSQLND *conn, unsigned int pid TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db, zend_bool silent TSRMLS_DC); + +typedef unsigned int (*func_mysqlnd_conn__get_error_no)(const MYSQLND * const conn TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__get_error_str)(const MYSQLND * const conn TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__get_sqlstate)(const MYSQLND * const conn TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_conn__get_thread_id)(const MYSQLND * const conn TSRMLS_DC); +typedef void (*func_mysqlnd_conn__get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); + +typedef unsigned long (*func_mysqlnd_conn__get_server_version)(const MYSQLND * const conn TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__get_server_information)(const MYSQLND * const conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__get_host_information)(const MYSQLND * const conn TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_conn__get_protocol_information)(const MYSQLND * const conn TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__get_last_message)(const MYSQLND * const conn TSRMLS_DC); +typedef const char * (*func_mysqlnd_conn__charset_name)(const MYSQLND * const conn TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_conn__list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC); + +typedef uint64_t (*func_mysqlnd_conn__get_last_insert_id)(const MYSQLND * const conn TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_conn__get_affected_rows)(const MYSQLND * const conn TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_conn__get_warning_count)(const MYSQLND * const conn TSRMLS_DC); + +typedef unsigned int (*func_mysqlnd_conn__get_field_count)(const MYSQLND * const conn TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_conn__set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC); +typedef void (*func_mysqlnd_conn__free_contents)(MYSQLND *conn TSRMLS_DC); /* private */ +typedef void (*func_mysqlnd_conn__free_options)(MYSQLND * conn TSRMLS_DC); /* private */ +typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); +typedef void (*func_mysqlnd_conn__dtor)(MYSQLND *conn TSRMLS_DC); /* private */ + +typedef enum_func_status (*func_mysqlnd_conn__query_read_result_set_header)(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC); + +typedef MYSQLND * (*func_mysqlnd_conn__get_reference)(MYSQLND * const conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__free_reference)(MYSQLND * const conn TSRMLS_DC); +typedef enum mysqlnd_connection_state (*func_mysqlnd_conn__get_state)(MYSQLND * const conn TSRMLS_DC); +typedef void (*func_mysqlnd_conn__set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_conn__simple_command)(MYSQLND *conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__simple_command_handle_response)(MYSQLND *conn, enum mysqlnd_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command, zend_bool ignore_upsert_status TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_conn__restart_psession)(MYSQLND *conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__end_psession)(MYSQLND *conn TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn__send_close)(MYSQLND * conn TSRMLS_DC); + +typedef void (*func_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); + +typedef MYSQLND_RES * (*func_mysqlnd_conn__result_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC); + struct st_mysqlnd_conn_methods { - void (*init)(MYSQLND * conn TSRMLS_DC); - enum_func_status (*connect)(MYSQLND *conn, const char *host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket, unsigned int mysql_flags TSRMLS_DC); - ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC); - enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); - enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); - enum_func_status (*send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); - enum_func_status (*reap_query)(MYSQLND *conn TSRMLS_DC); - MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC); - zend_bool (*more_results)(const MYSQLND * const conn TSRMLS_DC); - - MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn TSRMLS_DC); - - enum_func_status (*shutdown_server)(MYSQLND * const conn, uint8_t level TSRMLS_DC); - enum_func_status (*refresh_server)(MYSQLND * const conn, uint8_t options TSRMLS_DC); - - enum_func_status (*ping)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*kill_connection)(MYSQLND *conn, unsigned int pid TSRMLS_DC); - enum_func_status (*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC); - enum_func_status (*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db, zend_bool silent TSRMLS_DC); - - unsigned int (*get_error_no)(const MYSQLND * const conn TSRMLS_DC); - const char * (*get_error_str)(const MYSQLND * const conn TSRMLS_DC); - const char * (*get_sqlstate)(const MYSQLND * const conn TSRMLS_DC); - uint64_t (*get_thread_id)(const MYSQLND * const conn TSRMLS_DC); - void (*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); - - unsigned long (*get_server_version)(const MYSQLND * const conn TSRMLS_DC); - const char * (*get_server_information)(const MYSQLND * const conn TSRMLS_DC); - enum_func_status (*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC); - const char * (*get_host_information)(const MYSQLND * const conn TSRMLS_DC); - unsigned int (*get_protocol_information)(const MYSQLND * const conn TSRMLS_DC); - const char * (*get_last_message)(const MYSQLND * const conn TSRMLS_DC); - const char * (*charset_name)(const MYSQLND * const conn TSRMLS_DC); - MYSQLND_RES * (*list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC); - MYSQLND_RES * (*list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC); - - uint64_t (*get_last_insert_id)(const MYSQLND * const conn TSRMLS_DC); - uint64_t (*get_affected_rows)(const MYSQLND * const conn TSRMLS_DC); - unsigned int (*get_warning_count)(const MYSQLND * const conn TSRMLS_DC); - - unsigned int (*get_field_count)(const MYSQLND * const conn TSRMLS_DC); - - enum_func_status (*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC); - enum_func_status (*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC); - void (*free_contents)(MYSQLND *conn TSRMLS_DC); /* private */ - void (*free_options)(MYSQLND * conn TSRMLS_DC); /* private */ - enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC); - void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */ - - enum_func_status (*query_read_result_set_header)(MYSQLND *conn, MYSQLND_STMT *stmt TSRMLS_DC); - - MYSQLND * (*get_reference)(MYSQLND * const conn TSRMLS_DC); - enum_func_status (*free_reference)(MYSQLND * const conn TSRMLS_DC); - enum mysqlnd_connection_state (*get_state)(MYSQLND * const conn TSRMLS_DC); - void (*set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC); - - enum_func_status (*simple_command)(MYSQLND *conn, enum php_mysqlnd_server_command command, const char * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC); - enum_func_status (*simple_command_handle_response)(MYSQLND *conn, enum mysqlnd_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command, zend_bool ignore_upsert_status TSRMLS_DC); - - enum_func_status (*restart_psession)(MYSQLND *conn TSRMLS_DC); - enum_func_status (*end_psession)(MYSQLND *conn TSRMLS_DC); + func_mysqlnd_conn__init init; + func_mysqlnd_conn__connect connect; + func_mysqlnd_conn__escape_string escape_string; + func_mysqlnd_conn__set_charset set_charset; + func_mysqlnd_conn__query query; + func_mysqlnd_conn__send_query send_query; + func_mysqlnd_conn__reap_query reap_query; + func_mysqlnd_conn__use_result use_result; + func_mysqlnd_conn__store_result store_result; + func_mysqlnd_conn__next_result next_result; + func_mysqlnd_conn__more_results more_results; + + func_mysqlnd_conn__stmt_init stmt_init; + + func_mysqlnd_conn__shutdown_server shutdown_server; + func_mysqlnd_conn__refresh_server refresh_server; + + func_mysqlnd_conn__ping ping; + func_mysqlnd_conn__kill_connection kill_connection; + func_mysqlnd_conn__select_db select_db; + func_mysqlnd_conn__server_dump_debug_information server_dump_debug_information; + func_mysqlnd_conn__change_user change_user; + + func_mysqlnd_conn__get_error_no get_error_no; + func_mysqlnd_conn__get_error_str get_error_str; + func_mysqlnd_conn__get_sqlstate get_sqlstate; + func_mysqlnd_conn__get_thread_id get_thread_id; + func_mysqlnd_conn__get_statistics get_statistics; + + func_mysqlnd_conn__get_server_version get_server_version; + func_mysqlnd_conn__get_server_information get_server_information; + func_mysqlnd_conn__get_server_statistics get_server_statistics; + func_mysqlnd_conn__get_host_information get_host_information; + func_mysqlnd_conn__get_protocol_information get_protocol_information; + func_mysqlnd_conn__get_last_message get_last_message; + func_mysqlnd_conn__charset_name charset_name; + func_mysqlnd_conn__list_fields list_fields; + func_mysqlnd_conn__list_method list_method; + + func_mysqlnd_conn__get_last_insert_id get_last_insert_id; + func_mysqlnd_conn__get_affected_rows get_affected_rows; + func_mysqlnd_conn__get_warning_count get_warning_count; + + func_mysqlnd_conn__get_field_count get_field_count; + + func_mysqlnd_conn__set_server_option set_server_option; + func_mysqlnd_conn__set_client_option set_client_option; + func_mysqlnd_conn__free_contents free_contents; + func_mysqlnd_conn__free_options free_options; + func_mysqlnd_conn__close close; + func_mysqlnd_conn__dtor dtor; + + func_mysqlnd_conn__query_read_result_set_header query_read_result_set_header; + + func_mysqlnd_conn__get_reference get_reference; + func_mysqlnd_conn__free_reference free_reference; + func_mysqlnd_conn__get_state get_state; + func_mysqlnd_conn__set_state set_state; + + func_mysqlnd_conn__simple_command simple_command; + func_mysqlnd_conn__simple_command_handle_response simple_command_handle_response; + + func_mysqlnd_conn__restart_psession restart_psession; + func_mysqlnd_conn__end_psession end_psession; + func_mysqlnd_conn__send_close send_close; + + func_mysqlnd_conn__ssl_set ssl_set; + + func_mysqlnd_conn__result_init result_init; }; + + +typedef mysqlnd_fetch_row_func fetch_row; +typedef mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ +typedef mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ + +typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); +typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); +typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result TSRMLS_DC); +typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); +typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_res__num_fields)(const MYSQLND_RES * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__skip_result)(MYSQLND_RES * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * result, uint64_t row TSRMLS_DC); +typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC); +typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__field_tell)(const MYSQLND_RES * const result TSRMLS_DC); +typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field)(MYSQLND_RES * const result TSRMLS_DC); +typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); +typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result TSRMLS_DC); + +typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES *result, MYSQLND * conn TSRMLS_DC); +typedef unsigned long * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES *result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool to_cache TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC); + +typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ +typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); +typedef void (*func_mysqlnd_res__free_result_internal)(MYSQLND_RES *result TSRMLS_DC); +typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result TSRMLS_DC); +typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result TSRMLS_DC); +typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC); + + /* for decoding - binary or text protocol */ +typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, MYSQLND_FIELD *fields_metadata, + zend_bool persistent, + zend_bool as_unicode, zend_bool as_int_or_float, + MYSQLND_STATS * stats TSRMLS_DC); + +typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC); + struct st_mysqlnd_res_methods { mysqlnd_fetch_row_func fetch_row; mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ - MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC); - void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); - MYSQLND_ROW_C (*fetch_row_c)(MYSQLND_RES *result TSRMLS_DC); - void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); - void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); - uint64_t (*num_rows)(const MYSQLND_RES * const result TSRMLS_DC); - unsigned int (*num_fields)(const MYSQLND_RES * const result TSRMLS_DC); - enum_func_status (*skip_result)(MYSQLND_RES * const result TSRMLS_DC); - enum_func_status (*seek_data)(MYSQLND_RES * result, uint64_t row TSRMLS_DC); - MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC); - MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result TSRMLS_DC); - const MYSQLND_FIELD *(*fetch_field)(MYSQLND_RES * const result TSRMLS_DC); - const MYSQLND_FIELD *(*fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); - const MYSQLND_FIELD *(*fetch_fields)(MYSQLND_RES * const result TSRMLS_DC); - - enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND * conn TSRMLS_DC); - unsigned long * (*fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC); - enum_func_status (*store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES *result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool to_cache TSRMLS_DC); - void (*initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC); - - void (*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ - enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); - void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC); - void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC); - void (*free_buffered_data)(MYSQLND_RES *result TSRMLS_DC); - void (*unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC); + func_mysqlnd_res__use_result use_result; + func_mysqlnd_res__store_result store_result; + func_mysqlnd_res__fetch_into fetch_into; + func_mysqlnd_res__fetch_row_c fetch_row_c; + func_mysqlnd_res__fetch_all fetch_all; + func_mysqlnd_res__fetch_field_data fetch_field_data; + func_mysqlnd_res__num_rows num_rows; + func_mysqlnd_res__num_fields num_fields; + func_mysqlnd_res__skip_result skip_result; + func_mysqlnd_res__seek_data seek_data; + func_mysqlnd_res__seek_field seek_field; + func_mysqlnd_res__field_tell field_tell; + func_mysqlnd_res__fetch_field fetch_field; + func_mysqlnd_res__fetch_field_direct fetch_field_direct; + func_mysqlnd_res__fetch_fields fetch_fields; + func_mysqlnd_res__read_result_metadata read_result_metadata; + func_mysqlnd_res__fetch_lengths fetch_lengths; + func_mysqlnd_res__store_result_fetch_data store_result_fetch_data; + func_mysqlnd_res__initialize_result_set_rest initialize_result_set_rest; + func_mysqlnd_res__free_result_buffers free_result_buffers; + func_mysqlnd_res__free_result free_result; + func_mysqlnd_res__free_result_internal free_result_internal; + func_mysqlnd_res__free_result_contents free_result_contents; + func_mysqlnd_res__free_buffered_data free_buffered_data; + func_mysqlnd_res__unbuffered_free_last_data unbuffered_free_last_data; /* for decoding - binary or text protocol */ - void (*row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, - unsigned int field_count, MYSQLND_FIELD *fields_metadata, - zend_bool persistent, - zend_bool as_unicode, zend_bool as_int_or_float, - MYSQLND_STATS * stats TSRMLS_DC); + func_mysqlnd_res__row_decoder row_decoder; + + func_mysqlnd_res__result_meta_init result_meta_init; + + void * unused1; + void * unused2; + void * unused3; + void * unused4; + void * unused5; }; +typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); +typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); +typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); +typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND * conn TSRMLS_DC); +typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC); +typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta TSRMLS_DC); + struct st_mysqlnd_res_meta_methods { - const MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); - const MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); - const MYSQLND_FIELD * (*fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); - MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC); - enum_func_status (*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC); - MYSQLND_RES_METADATA * (*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC); - void (*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC); + func_mysqlnd_res_meta__fetch_field fetch_field; + func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct; + func_mysqlnd_res_meta__fetch_fields fetch_fields; + func_mysqlnd_res_meta__field_tell field_tell; + func_mysqlnd_res_meta__read_metadata read_metadata; + func_mysqlnd_res_meta__clone_metadata clone_metadata; + func_mysqlnd_res_meta__free_metadata free_metadata; }; +typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__execute)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef zend_bool (*func_mysqlnd_stmt__more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__next_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__seek_data)(const MYSQLND_STMT * const stmt, uint64_t row TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__reset)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ +typedef enum_func_status (*func_mysqlnd_stmt__dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ +typedef enum_func_status (*func_mysqlnd_stmt__fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, zval * const zv, zend_uchar type TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__bind_one_result)(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, const char * const data, unsigned long length TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_parameter_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_stmt__get_last_insert_id)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_stmt__get_affected_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef uint64_t (*func_mysqlnd_stmt__get_num_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_stmt__get_param_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_stmt__get_field_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_stmt__get_warning_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_stmt__get_error_no)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef const char * (*func_mysqlnd_stmt__get_error_str)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef const char * (*func_mysqlnd_stmt__get_sqlstate)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__get_attribute)(const MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC); +typedef MYSQLND_PARAM_BIND *(*func_mysqlnd_stmt__alloc_param_bind)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef MYSQLND_RESULT_BIND*(*func_mysqlnd_stmt__alloc_result_bind)(MYSQLND_STMT * const stmt TSRMLS_DC); +typedef void (*func_mysqlnd_stmt__free_parameter_bind)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * TSRMLS_DC); +typedef void (*func_mysqlnd_stmt__free_result_bind)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_stmt__server_status)(const MYSQLND_STMT * const stmt TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__generate_execute_request)(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_stmt__parse_execute_response)(MYSQLND_STMT * const s TSRMLS_DC); + struct st_mysqlnd_stmt_methods { - enum_func_status (*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC); - enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - zend_bool (*more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*next_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, uint64_t row TSRMLS_DC); - enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC); - enum_func_status (*net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */ - enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */ - - enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC); - - enum_func_status (*bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC); - enum_func_status (*bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, zval * const zv, zend_uchar type TSRMLS_DC); - enum_func_status (*refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC); - void (*set_param_bind_dtor)(MYSQLND_STMT * const stmt, void (*param_bind_dtor)(MYSQLND_PARAM_BIND * TSRMLS_DC) TSRMLS_DC); - enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC); - enum_func_status (*bind_one_result)(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); - void (*set_result_bind_dtor)(MYSQLND_STMT * const stmt, void (*result_bind_dtor)(MYSQLND_RESULT_BIND * TSRMLS_DC) TSRMLS_DC); - enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, const char * const data, unsigned long length TSRMLS_DC); - MYSQLND_RES * (*get_parameter_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); - MYSQLND_RES * (*get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC); - - uint64_t (*get_last_insert_id)(const MYSQLND_STMT * const stmt TSRMLS_DC); - uint64_t (*get_affected_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC); - uint64_t (*get_num_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC); - - unsigned int (*get_param_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); - unsigned int (*get_field_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); - unsigned int (*get_warning_count)(const MYSQLND_STMT * const stmt TSRMLS_DC); - - unsigned int (*get_error_no)(const MYSQLND_STMT * const stmt TSRMLS_DC); - const char * (*get_error_str)(const MYSQLND_STMT * const stmt TSRMLS_DC); - const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt TSRMLS_DC); - - enum_func_status (*get_attribute)(const MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC); - enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC); + func_mysqlnd_stmt__prepare prepare; + func_mysqlnd_stmt__execute execute; + func_mysqlnd_stmt__use_result use_result; + func_mysqlnd_stmt__store_result store_result; + func_mysqlnd_stmt__get_result get_result; + func_mysqlnd_stmt__more_results more_results; + func_mysqlnd_stmt__next_result next_result; + func_mysqlnd_stmt__free_result free_result; + func_mysqlnd_stmt__seek_data seek_data; + func_mysqlnd_stmt__reset reset; + func_mysqlnd_stmt__net_close net_close; + func_mysqlnd_stmt__dtor dtor; + func_mysqlnd_stmt__fetch fetch; + + func_mysqlnd_stmt__bind_parameters bind_parameters; + func_mysqlnd_stmt__bind_one_parameter bind_one_parameter; + func_mysqlnd_stmt__refresh_bind_param refresh_bind_param; + func_mysqlnd_stmt__bind_result bind_result; + func_mysqlnd_stmt__bind_one_result bind_one_result; + func_mysqlnd_stmt__send_long_data send_long_data; + func_mysqlnd_stmt__get_parameter_metadata get_parameter_metadata; + func_mysqlnd_stmt__get_result_metadata get_result_metadata; + + func_mysqlnd_stmt__get_last_insert_id get_last_insert_id; + func_mysqlnd_stmt__get_affected_rows get_affected_rows; + func_mysqlnd_stmt__get_num_rows get_num_rows; + + func_mysqlnd_stmt__get_param_count get_param_count; + func_mysqlnd_stmt__get_field_count get_field_count; + func_mysqlnd_stmt__get_warning_count get_warning_count; + + func_mysqlnd_stmt__get_error_no get_error_no; + func_mysqlnd_stmt__get_error_str get_error_str; + func_mysqlnd_stmt__get_sqlstate get_sqlstate; + + func_mysqlnd_stmt__get_attribute get_attribute; + func_mysqlnd_stmt__set_attribute set_attribute; + + func_mysqlnd_stmt__alloc_param_bind alloc_parameter_bind; + func_mysqlnd_stmt__alloc_result_bind alloc_result_bind; + + func_mysqlnd_stmt__free_parameter_bind free_parameter_bind; + func_mysqlnd_stmt__free_result_bind free_result_bind; + + func_mysqlnd_stmt__server_status get_server_status; + + func_mysqlnd_stmt__generate_execute_request generate_execute_request; + func_mysqlnd_stmt__parse_execute_response parse_execute_response; }; @@ -467,7 +697,7 @@ struct st_mysqlnd_net /* cmd buffer */ MYSQLND_CMD_BUFFER cmd_buffer; - MYSQLND_NET_OPTIONS options; + MYSQLND_NET_OPTIONS options; zend_bool persistent; }; @@ -503,7 +733,7 @@ struct st_mysqlnd_connection unsigned char *scramble; const MYSQLND_CHARSET *charset; const MYSQLND_CHARSET *greet_charset; - char *connect_or_select_db; + char *connect_or_select_db; unsigned int connect_or_select_db_len; MYSQLND_INFILE infile; unsigned int protocol_version; @@ -574,6 +804,7 @@ struct st_mysqlnd_result_metadata /* We need this to make fast allocs in rowp_read */ unsigned int bit_fields_count; size_t bit_fields_total_len; /* trailing \0 not counted */ + zend_bool persistent; struct st_mysqlnd_res_meta_methods *m; }; @@ -628,7 +859,8 @@ struct st_mysqlnd_res struct st_mysqlnd_packet_row * row_packet; - MYSQLND_MEMORY_POOL * result_set_memory_pool; + MYSQLND_MEMORY_POOL * result_set_memory_pool; + zend_bool persistent; }; @@ -646,7 +878,7 @@ struct st_mysqlnd_result_bind }; -struct st_mysqlnd_stmt +struct st_mysqlnd_stmt_data { MYSQLND *conn; unsigned long stmt_id; @@ -660,6 +892,7 @@ struct st_mysqlnd_stmt MYSQLND_PARAM_BIND *param_bind; MYSQLND_RESULT_BIND *result_bind; zend_bool result_zvals_separated_once; + zend_bool persistent; MYSQLND_UPSERT_STATUS upsert_status; @@ -673,11 +906,14 @@ struct st_mysqlnd_stmt MYSQLND_CMD_BUFFER execute_cmd_buffer; unsigned int execute_count;/* count how many times the stmt was executed */ +}; - void (*param_bind_dtor)(MYSQLND_PARAM_BIND * TSRMLS_DC); - void (*result_bind_dtor)(MYSQLND_RESULT_BIND * TSRMLS_DC); +struct st_mysqlnd_stmt +{ + MYSQLND_STMT_DATA * data; struct st_mysqlnd_stmt_methods *m; + zend_bool persistent; }; #endif /* MYSQLND_STRUCTS_H */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 571037788..ba31984d8 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -61,9 +61,16 @@ } +#define BAIL_IF_NO_MORE_DATA \ + if ((size_t)(p - begin) > packet->header.size) { \ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \ + goto premature_end; \ + } \ + + static const char *unknown_sqlstate= "HY000"; -char * const mysqlnd_empty_string = ""; +const char * const mysqlnd_empty_string = ""; /* Used in mysqlnd_debug.c */ const char mysqlnd_read_header_name[] = "mysqlnd_read_header"; @@ -120,7 +127,8 @@ static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_L /* {{{ php_mysqlnd_net_field_length Get next field's length */ -unsigned long php_mysqlnd_net_field_length(zend_uchar **packet) +unsigned long +php_mysqlnd_net_field_length(zend_uchar **packet) { register zend_uchar *p= (zend_uchar *)*packet; @@ -149,7 +157,8 @@ unsigned long php_mysqlnd_net_field_length(zend_uchar **packet) /* {{{ php_mysqlnd_net_field_length_ll Get next field's length */ -uint64_t php_mysqlnd_net_field_length_ll(zend_uchar **packet) +uint64_t +php_mysqlnd_net_field_length_ll(zend_uchar **packet) { register zend_uchar *p= (zend_uchar *)*packet; @@ -177,7 +186,8 @@ uint64_t php_mysqlnd_net_field_length_ll(zend_uchar **packet) /* {{{ php_mysqlnd_net_store_length */ -zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length) +zend_uchar * +php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length) { if (length < (uint64_t) L64(251)) { *packet = (zend_uchar) length; @@ -213,21 +223,31 @@ php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, DBG_ENTER("php_mysqlnd_read_error_from_line"); + *error_no = CR_UNKNOWN_ERROR; + memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH); + if (buf_len > 2) { *error_no = uint2korr(p); p+= 2; - /* sqlstate is following */ + /* + sqlstate is following. No need to check for buf_left_len as we checked > 2 above, + if it was >=2 then we would need a check + */ if (*p == '#') { - memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH); - p+= MYSQLND_SQLSTATE_LENGTH; + ++p; + if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) { + memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH); + p+= MYSQLND_SQLSTATE_LENGTH; + } else { + goto end; + } + } + if ((buf_len - (p - buf)) > 0) { + error_msg_len = MIN((buf_len - (p - buf)), error_buf_len - 1); + memcpy(error, p, error_msg_len); } - error_msg_len = buf_len - (p - buf); - error_msg_len = MIN(error_msg_len, error_buf_len - 1); - memcpy(error, p, error_msg_len); - } else { - *error_no = CR_UNKNOWN_ERROR; - memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH); } +end: sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0'; error[error_msg_len]= '\0'; @@ -244,7 +264,7 @@ mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC) zend_uchar buffer[MYSQLND_HEADER_SIZE]; DBG_ENTER("mysqlnd_read_header_name"); - DBG_INF_FMT("compressed=%d conn_id=%u", net->compressed, conn->thread_id); + DBG_INF_FMT("compressed=%u conn_id=%u", net->compressed, conn->thread_id); if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) { DBG_RETURN(FAIL); } @@ -253,7 +273,7 @@ mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC) header->packet_no = uint1korr(buffer + 3); #ifdef MYSQLND_DUMP_HEADER_N_BODY - DBG_INF_FMT("HEADER: prot_packet_no=%d size=%3d", header->packet_no, header->size); + DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size); #endif MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE, @@ -269,10 +289,10 @@ mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC) DBG_RETURN(PASS); } - DBG_ERR_FMT("Logical link: packets out of order. Expected %d received %d. Packet size=%d", + DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, net->packet_no, header->packet_no, header->size); - php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC, + php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, net->packet_no, header->packet_no, header->size); DBG_RETURN(FAIL); } @@ -291,9 +311,11 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_greet_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET); + BAIL_IF_NO_MORE_DATA; packet->protocol_version = uint1korr(p); p++; + BAIL_IF_NO_MORE_DATA; if (ERROR_MARKER == packet->protocol_version) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, @@ -313,29 +335,37 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->server_version = estrdup((char *)p); p+= strlen(packet->server_version) + 1; /* eat the '\0' */ + BAIL_IF_NO_MORE_DATA; packet->thread_id = uint4korr(p); p+=4; + BAIL_IF_NO_MORE_DATA; memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323); p+= 8; + BAIL_IF_NO_MORE_DATA; /* pad1 */ p++; + BAIL_IF_NO_MORE_DATA; packet->server_capabilities = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; packet->charset_no = uint1korr(p); p++; + BAIL_IF_NO_MORE_DATA; packet->server_status = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; /* pad2 */ p+= 13; + BAIL_IF_NO_MORE_DATA; - if (p - buf < packet->header.size) { + if ((size_t) (p - buf) < packet->header.size) { /* scramble_buf is split into two parts */ memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323); @@ -343,19 +373,18 @@ php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->pre41 = TRUE; } - DBG_INF_FMT("proto=%d server=%s thread_id=%ld", + DBG_INF_FMT("proto=%u server=%s thread_id=%u", packet->protocol_version, packet->server_version, packet->thread_id); - DBG_INF_FMT("server_capabilities=%d charset_no=%d server_status=%d", + DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i", packet->server_capabilities, packet->charset_no, packet->server_status); - if (p - begin > packet->header.size) { - DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } - DBG_RETURN(PASS); +premature_end: + DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ @@ -366,24 +395,19 @@ void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet; if (p->server_version) { - mnd_efree(p->server_version); + efree(p->server_version); p->server_version = NULL; } if (!alloca) { - mnd_efree(p); + mnd_pefree(p, p->header.persistent); } } /* }}} */ -#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \ - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \ - CLIENT_MULTI_RESULTS) - - /* {{{ php_mysqlnd_crypt */ -static -void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len) +static void +php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len) { const zend_uchar *s1_end = s1 + len; while (s1 < s1_end) { @@ -394,8 +418,7 @@ void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_ucha /* {{{ php_mysqlnd_scramble */ -void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, - const zend_uchar * const password) +void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password) { PHP_SHA1_CTX context; zend_uchar sha1[SHA1_MAX_LENGTH]; @@ -424,27 +447,19 @@ void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const sc /* }}} */ +#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SHA1_MAX_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 128) + /* {{{ php_mysqlnd_auth_write */ static -size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) +size_t php_mysqlnd_auth_write(void *_packet, MYSQLND * conn TSRMLS_DC) { - char buffer[1024]; + char buffer[AUTH_WRITE_BUFFER_LEN]; register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */ int len; register MYSQLND_PACKET_AUTH *packet= (MYSQLND_PACKET_AUTH *) _packet; DBG_ENTER("php_mysqlnd_auth_write"); - packet->client_flags |= MYSQLND_CAPABILITIES; - - if (packet->db) { - packet->client_flags |= CLIENT_CONNECT_WITH_DB; - } - - if (PG(open_basedir) && strlen(PG(open_basedir))) { - packet->client_flags ^= CLIENT_LOCAL_FILES; - } - int4store(p, packet->client_flags); p+= 4; @@ -457,31 +472,34 @@ size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) memset(p, 0, 23); /* filler */ p+= 23; - len= strlen(packet->user); - memcpy(p, packet->user, len); - p+= len; - *p++ = '\0'; - - /* copy scrambled pass*/ - if (packet->password && packet->password[0]) { - /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ - int1store(p, 20); - p++; - php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password); - p+= 20; - } else { - /* Zero length */ - int1store(p, 0); - p++; - } + if (!packet->send_half_packet) { + len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN); + memcpy(p, packet->user, len); + p+= len; + *p++ = '\0'; + + /* copy scrambled pass*/ + if (packet->password && packet->password[0]) { + /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ + int1store(p, SHA1_MAX_LENGTH); + p++; + php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password); + p+= SHA1_MAX_LENGTH; + } else { + /* Zero length */ + int1store(p, 0); + p++; + } - if (packet->db) { - memcpy(p, packet->db, packet->db_len); - p+= packet->db_len; - *p++= '\0'; + if (packet->db) { + size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len); + memcpy(p, packet->db, real_db_len); + p+= real_db_len; + *p++= '\0'; + } + /* Handle CLIENT_CONNECT_WITH_DB */ + /* no \0 for no DB */ } - /* Handle CLIENT_CONNECT_WITH_DB */ - /* no \0 for no DB */ DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC)); } @@ -493,7 +511,8 @@ static void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - mnd_pefree((MYSQLND_PACKET_AUTH *) _packet, ((MYSQLND_PACKET_AUTH *)_packet)->header.persistent); + MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet; + mnd_pefree(p, p->header.persistent); } } /* }}} */ @@ -516,10 +535,12 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_ok_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET); + BAIL_IF_NO_MORE_DATA; /* Should be always 0x0 or ERROR_MARKER for error */ packet->field_count = uint1korr(p); p++; + BAIL_IF_NO_MORE_DATA; if (ERROR_MARKER == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, @@ -530,40 +551,47 @@ php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC) } /* Everything was fine! */ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); + BAIL_IF_NO_MORE_DATA; + packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p); + BAIL_IF_NO_MORE_DATA; packet->server_status = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; packet->warning_count = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; /* There is a message */ - if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) { - packet->message = estrndup((char *)p, MIN(i, buf_len - (p - begin))); - packet->message_len = i; + if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) { + packet->message_len = MIN(i, buf_len - (p - begin)); + packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE); } else { packet->message = NULL; + packet->message_len = 0; } - DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d", + DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u", packet->affected_rows, packet->last_insert_id, packet->server_status, packet->warning_count); - if (p - begin > packet->header.size) { - DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } + BAIL_IF_NO_MORE_DATA; DBG_RETURN(PASS); +premature_end: + DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ /* {{{ php_mysqlnd_ok_free_mem */ -static -void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +static void +php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet; if (p->message) { @@ -596,10 +624,12 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_eof_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET); + BAIL_IF_NO_MORE_DATA; /* Should be always EODATA_MARKER */ packet->field_count = uint1korr(p); p++; + BAIL_IF_NO_MORE_DATA; if (ERROR_MARKER == packet->field_count) { php_mysqlnd_read_error_from_line(p, packet->header.size - 1, @@ -617,23 +647,27 @@ php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->header.size > 1) { packet->warning_count = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; + packet->server_status = uint2korr(p); p+= 2; + BAIL_IF_NO_MORE_DATA; } else { packet->warning_count = 0; packet->server_status = 0; } - if (p - begin > packet->header.size) { - DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } + BAIL_IF_NO_MORE_DATA; - DBG_INF_FMT("EOF packet: fields=%d status=%d warnings=%d", + DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u", packet->field_count, packet->server_status, packet->warning_count); DBG_RETURN(PASS); +premature_end: + DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ @@ -656,7 +690,7 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) MYSQLND_PACKET_COMMAND *packet= (MYSQLND_PACKET_COMMAND *) _packet; MYSQLND_NET *net = conn->net; unsigned int error_reporting = EG(error_reporting); - size_t written; + size_t written = 0; DBG_ENTER("php_mysqlnd_cmd_write"); /* @@ -685,6 +719,9 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret; zend_uchar *tmp, *p; tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer; + if (!tmp) { + goto end; + } p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */ int1store(p, packet->command); @@ -699,6 +736,7 @@ size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC) } written = ret; } +end: if (error_reporting) { /* restore error reporting */ EG(error_reporting) = error_reporting; @@ -713,7 +751,8 @@ static void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { - mnd_pefree(_packet, ((MYSQLND_PACKET_COMMAND *)_packet)->header.persistent); + MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet; + mnd_pefree(p, p->header.persistent); } } /* }}} */ @@ -723,6 +762,7 @@ void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) static enum_func_status php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) { + enum_func_status ret = PASS; size_t buf_len = conn->net->cmd_buffer.length; zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer; zend_uchar *p = buf; @@ -733,6 +773,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_rset_header_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET); + BAIL_IF_NO_MORE_DATA; /* Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte @@ -741,6 +782,7 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (ERROR_MARKER == *p) { /* Error */ p++; + BAIL_IF_NO_MORE_DATA; php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate @@ -748,36 +790,58 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_RETURN(PASS); } - packet->field_count= php_mysqlnd_net_field_length(&p); + packet->field_count = php_mysqlnd_net_field_length(&p); + BAIL_IF_NO_MORE_DATA; + switch (packet->field_count) { case MYSQLND_NULL_LENGTH: DBG_INF("LOAD LOCAL"); /* First byte in the packet is the field count. Thus, the name is size - 1. And we add 1 for a trailing \0. + Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed + that packet->header.size is > 0. Which means that len can't underflow, that + would lead to 0 byte allocation but 2^32 or 2^64 bytes copied. */ len = packet->header.size - 1; packet->info_or_local_file = mnd_emalloc(len + 1); - memcpy(packet->info_or_local_file, p, len); - packet->info_or_local_file[len] = '\0'; - packet->info_or_local_file_len = len; + if (packet->info_or_local_file) { + memcpy(packet->info_or_local_file, p, len); + packet->info_or_local_file[len] = '\0'; + packet->info_or_local_file_len = len; + } else { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + } break; case 0x00: DBG_INF("UPSERT"); packet->affected_rows = php_mysqlnd_net_field_length_ll(&p); - packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p); + BAIL_IF_NO_MORE_DATA; + + packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p); + BAIL_IF_NO_MORE_DATA; + packet->server_status = uint2korr(p); p+=2; + BAIL_IF_NO_MORE_DATA; + packet->warning_count = uint2korr(p); p+=2; + BAIL_IF_NO_MORE_DATA; /* Check for additional textual data */ - if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { + if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { packet->info_or_local_file = mnd_emalloc(len + 1); - memcpy(packet->info_or_local_file, p, len); - packet->info_or_local_file[len] = '\0'; - packet->info_or_local_file_len = len; + if (packet->info_or_local_file) { + memcpy(packet->info_or_local_file, p, len); + packet->info_or_local_file[len] = '\0'; + packet->info_or_local_file_len = len; + } else { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + } } - DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%d warning_count=%d", + DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u", packet->affected_rows, packet->last_insert_id, packet->server_status, packet->warning_count); break; @@ -786,13 +850,14 @@ php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC) /* Result set */ break; } - if (p - begin > packet->header.size) { - DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } + BAIL_IF_NO_MORE_DATA; - DBG_RETURN(PASS); + DBG_RETURN(ret); +premature_end: + DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ @@ -853,14 +918,17 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->skip_parsing) { DBG_RETURN(PASS); } + + BAIL_IF_NO_MORE_DATA; if (ERROR_MARKER == *p) { /* Error */ p++; + BAIL_IF_NO_MORE_DATA; php_mysqlnd_read_error_from_line(p, packet->header.size - 1, packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate TSRMLS_CC); - DBG_ERR_FMT("Server error : (%d) %s", packet->error_info.error_no, packet->error_info.error); + DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error); DBG_RETURN(PASS); } else if (EODATA_MARKER == *p && packet->header.size < 8) { /* Premature EOF. That should be COM_FIELD_LIST */ @@ -873,42 +941,51 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) for (i = 0; i < field_count; i += 2) { len = php_mysqlnd_net_field_length(&p); + BAIL_IF_NO_MORE_DATA; switch ((len)) { case 0: - *(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string; + *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0; break; case MYSQLND_NULL_LENGTH: goto faulty_or_fake; default: - *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p; + *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p; *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len; p += len; total_len += len + 1; break; } + BAIL_IF_NO_MORE_DATA; } /* 1 byte filler */ p++; + BAIL_IF_NO_MORE_DATA; meta->charsetnr = uint2korr(p); p += 2; + BAIL_IF_NO_MORE_DATA; meta->length = uint4korr(p); p += 4; + BAIL_IF_NO_MORE_DATA; meta->type = uint1korr(p); p += 1; + BAIL_IF_NO_MORE_DATA; meta->flags = uint2korr(p); p += 2; + BAIL_IF_NO_MORE_DATA; meta->decimals = uint2korr(p); p += 1; + BAIL_IF_NO_MORE_DATA; /* 2 byte filler */ p +=2; + BAIL_IF_NO_MORE_DATA; /* Should we set NUM_FLAG (libmysql does it) ? */ if ( @@ -925,25 +1002,30 @@ php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC) NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL. Otherwise the string is length encoded. */ - if (packet->header.size > (p - buf) && + if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p)) && len != MYSQLND_NULL_LENGTH) { - DBG_INF_FMT("Def found, length %lu", len); - meta->def = mnd_emalloc(len + 1); + BAIL_IF_NO_MORE_DATA; + DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc); + meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc); + if (!meta->def) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); + } memcpy(meta->def, p, len); meta->def[len] = '\0'; meta->def_length = len; p += len; } - if (p - begin > packet->header.size) { - DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes " - "shorter than expected", p - begin - packet->header.size); + DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc); + root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc); + if (!root_ptr) { + SET_OOM_ERROR(conn->error_info); + DBG_RETURN(FAIL); } - root_ptr = meta->root = mnd_emalloc(total_len); meta->root_len = total_len; /* Now do allocs */ if (meta->catalog && meta->catalog != mysqlnd_empty_string) { @@ -998,6 +1080,11 @@ faulty_or_fake: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH." " The server is faulty"); DBG_RETURN(FAIL); +premature_end: + DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes " + "shorter than expected", p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ @@ -1007,7 +1094,6 @@ static void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet; - /* p->metadata was passed to us as temporal buffer */ if (!alloca) { mnd_pefree(p, p->header.persistent); @@ -1018,7 +1104,7 @@ void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) /* {{{ php_mysqlnd_read_row_ex */ static enum_func_status -php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_pool, +php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool, MYSQLND_MEMORY_POOL_CHUNK **buffer, uint64_t *data_size, zend_bool persistent_alloc, unsigned int prealloc_more_bytes TSRMLS_DC) @@ -1054,6 +1140,10 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p to be able to implement read-only variables. Thus, we add + 1. */ *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC); + if (!*buffer) { + ret = FAIL; + break; + } p = (*buffer)->ptr; } else if (!first_iteration) { /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */ @@ -1067,12 +1157,16 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p We need a trailing \0 for the last string, in case of text-mode, to be able to implement read-only variables. */ - (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC); + if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) { + SET_OOM_ERROR(conn->error_info); + ret = FAIL; + break; + } /* The position could have changed, recalculate */ p = (*buffer)->ptr + (*data_size - header.size); } - if ((ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) { + if (PASS != (ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) { DBG_ERR("Empty row packet body"); php_error(E_WARNING, "Empty row packet body"); break; @@ -1083,7 +1177,7 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p } } if (ret == FAIL && *buffer) { - (*buffer)->free_chunk((*buffer), TRUE TSRMLS_CC); + (*buffer)->free_chunk((*buffer) TSRMLS_CC); *buffer = NULL; } *data_size -= prealloc_more_bytes; @@ -1093,13 +1187,14 @@ php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_p /* {{{ php_mysqlnd_rowp_read_binary_protocol */ -void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, - unsigned int field_count, MYSQLND_FIELD *fields_metadata, - zend_bool persistent, - zend_bool as_unicode, zend_bool as_int_or_float, - MYSQLND_STATS * stats TSRMLS_DC) +enum_func_status +php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, MYSQLND_FIELD *fields_metadata, + zend_bool persistent, + zend_bool as_unicode, zend_bool as_int_or_float, + MYSQLND_STATS * stats TSRMLS_DC) { - int i; + unsigned int i; zend_uchar *p = row_buffer->ptr; zend_uchar *null_ptr, bit; zval **current_field, **end_field, **start_field; @@ -1107,19 +1202,24 @@ void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffe DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol"); end_field = (current_field = start_field = fields) + field_count; - + if (!current_field) { + DBG_RETURN(FAIL); + } /* skip the first byte, not EODATA_MARKER -> 0x0, status */ p++; null_ptr= p; - p += (field_count + 9)/8; /* skip null bits */ - bit = 4; /* first 2 bits are reserved */ + p += (field_count + 9)/8; /* skip null bits */ + bit = 4; /* first 2 bits are reserved */ for (i = 0; current_field < end_field; current_field++, i++) { DBG_INF("Directly creating zval"); MAKE_STD_ZVAL(*current_field); + if (!*current_field) { + DBG_RETURN(FAIL); + } - DBG_INF_FMT("Into zval=%p decoding column %d [%s.%s.%s] type=%d field->flags&unsigned=%d flags=%u is_bit=%d as_unicode=%d", + DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u", *current_field, i, fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type, fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode); @@ -1129,8 +1229,7 @@ void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffe MYSQLND_INC_CONN_STATISTIC(stats, STAT_BINARY_TYPE_FETCHED_NULL); } else { enum_mysqlnd_field_types type = fields_metadata[i].type; - mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], - 0, &p, as_unicode TSRMLS_CC); + mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC); if (MYSQLND_G(collect_statistics)) { enum_mysqlnd_collected_stats statistic; @@ -1173,19 +1272,20 @@ void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffe } } - DBG_VOID_RETURN; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_rowp_read_text_protocol */ -void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, - unsigned int field_count, MYSQLND_FIELD *fields_metadata, - zend_bool persistent, - zend_bool as_unicode, zend_bool as_int_or_float, - MYSQLND_STATS * stats TSRMLS_DC) +enum_func_status +php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, MYSQLND_FIELD *fields_metadata, + zend_bool persistent, + zend_bool as_unicode, zend_bool as_int_or_float, + MYSQLND_STATS * stats TSRMLS_DC) { - int i; + unsigned int i; zend_bool last_field_was_string = FALSE; zval **current_field, **end_field, **start_field; zend_uchar *p = row_buffer->ptr; @@ -1195,6 +1295,10 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, DBG_ENTER("php_mysqlnd_rowp_read_text_protocol"); end_field = (current_field = start_field = fields) + field_count; + if (!current_field) { + DBG_RETURN(FAIL); + } + for (i = 0; current_field < end_field; current_field++, i++) { /* Don't reverse the order. It is significant!*/ zend_uchar *this_field_len_pos = p; @@ -1203,6 +1307,9 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, DBG_INF("Directly creating zval"); MAKE_STD_ZVAL(*current_field); + if (!*current_field) { + DBG_RETURN(FAIL); + } if (current_field > start_field && last_field_was_string) { /* @@ -1265,14 +1372,11 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, } #ifdef MYSQLND_STRING_TO_INT_CONVERSION - if (as_int_or_float && perm_bind.php_type == IS_LONG && - perm_bind.pack_len <= SIZEOF_LONG) - { + if (as_int_or_float && perm_bind.php_type == IS_LONG) { zend_uchar save = *(p + len); /* We have to make it ASCIIZ temporarily */ *(p + len) = '\0'; - if (perm_bind.pack_len < SIZEOF_LONG) - { + if (perm_bind.pack_len < SIZEOF_LONG) { /* direct conversion */ int64_t v = #ifndef PHP_WIN32 @@ -1296,6 +1400,8 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, if ((uns == TRUE && v > L64(2147483647)) || (uns == FALSE && (( L64(2147483647) < (int64_t) v) || (L64(-2147483648) > (int64_t) v)))) +#else +#error Need fix for this architecture #endif /* SIZEOF */ { ZVAL_STRINGL(*current_field, (char *)p, len, 0); @@ -1323,8 +1429,7 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, Definitely not nice, _hackish_ :(, but works. */ zend_uchar *start = bit_area; - ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), - 0, &p, as_unicode, len TSRMLS_CC); + ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC); /* We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because later in this function there will be an advancement. @@ -1373,8 +1478,8 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, because then we will leak. XXX: Keep in mind that up there there is an open `else` in - #ifdef MYSQLND_STRING_TO_INT_CONVERSION - which will make with this `if` an `else if`. + #ifdef MYSQLND_STRING_TO_INT_CONVERSION + which will make with this `if` an `else if`. */ if ((perm_bind.is_possibly_blob == TRUE && fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) || @@ -1395,7 +1500,7 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, row_buffer->ptr[data_size] = '\0'; } - DBG_VOID_RETURN; + DBG_RETURN(PASS); } /* }}} */ @@ -1420,8 +1525,7 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (!packet->binary_protocol && packet->bit_fields_count) { /* For every field we need terminating \0 */ - post_alloc_for_bit_fields = - packet->bit_fields_total_len + packet->bit_fields_count; + post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count; } ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size, @@ -1460,8 +1564,7 @@ php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC) p += 2; packet->server_status = uint2korr(p); /* Seems we have 3 bytes reserved for future use */ - DBG_INF_FMT("server_status=%d warning_count=%d", - packet->server_status, packet->warning_count); + DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count); } } else { MYSQLND_INC_CONN_STATISTIC(conn->stats, @@ -1502,24 +1605,24 @@ end: /* {{{ php_mysqlnd_rowp_free_mem */ -static -void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +static void +php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { MYSQLND_PACKET_ROW *p; DBG_ENTER("php_mysqlnd_rowp_free_mem"); p = (MYSQLND_PACKET_ROW *) _packet; if (p->row_buffer) { - p->row_buffer->free_chunk(p->row_buffer, TRUE TSRMLS_CC); + p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC); p->row_buffer = NULL; } - DBG_INF_FMT("alloca=%d persistent=%d", (int)alloca, (int)p->header.persistent); + DBG_INF_FMT("alloca=%u persistent=%u", (int)alloca, (int)p->header.persistent); /* Don't free packet->fields : - normal queries -> store_result() | fetch_row_unbuffered() will transfer - the ownership and NULL it. + the ownership and NULL it. - PS will pass in it the bound variables, we have to use them! and of course - not free the array. As it is passed to us, we should not clean it ourselves. + not free the array. As it is passed to us, we should not clean it ourselves. */ if (!alloca) { mnd_pefree(p, p->header.persistent); @@ -1586,10 +1689,12 @@ php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_prepare_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET); + BAIL_IF_NO_MORE_DATA; data_size = packet->header.size; packet->error_code = uint1korr(p); p++; + BAIL_IF_NO_MORE_DATA; if (ERROR_MARKER == packet->error_code) { php_mysqlnd_read_error_from_line(p, data_size - 1, @@ -1604,45 +1709,50 @@ php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (data_size != PREPARE_RESPONSE_SIZE_41 && data_size != PREPARE_RESPONSE_SIZE_50 && !(data_size > PREPARE_RESPONSE_SIZE_50)) { - DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %d", data_size); - php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size); + DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size); + php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size); DBG_RETURN(FAIL); } packet->stmt_id = uint4korr(p); p += 4; + BAIL_IF_NO_MORE_DATA; /* Number of columns in result set */ packet->field_count = uint2korr(p); p += 2; + BAIL_IF_NO_MORE_DATA; packet->param_count = uint2korr(p); p += 2; + BAIL_IF_NO_MORE_DATA; if (data_size > 9) { /* 0x0 filler sent by the server for 5.0+ clients */ p++; + BAIL_IF_NO_MORE_DATA; packet->warning_count = uint2korr(p); } - DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d", + DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u", packet->stmt_id, packet->field_count, packet->param_count); - if (p - begin > packet->header.size) { - DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } + BAIL_IF_NO_MORE_DATA; DBG_RETURN(PASS); +premature_end: + DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ /* {{{ php_mysqlnd_prepare_free_mem */ -static -void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +static void +php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet; if (!alloca) { @@ -1666,6 +1776,7 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) DBG_ENTER("php_mysqlnd_chg_user_read"); PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET); + BAIL_IF_NO_MORE_DATA; /* Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte @@ -1678,6 +1789,7 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) { /* We don't handle 3.23 authentication */ + packet->server_asked_323_auth = TRUE; DBG_RETURN(FAIL); } @@ -1689,20 +1801,21 @@ php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC) packet->error_info.sqlstate TSRMLS_CC); } - if (p - begin > packet->header.size) { - DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", - p - begin - packet->header.size); - } + BAIL_IF_NO_MORE_DATA; DBG_RETURN(PASS); +premature_end: + DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected", + p - begin - packet->header.size); + DBG_RETURN(FAIL); } /* }}} */ /* {{{ php_mysqlnd_chg_user_free_mem */ -static -void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) +static void +php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) { if (!alloca) { mnd_pefree(_packet, ((MYSQLND_PACKET_CHG_USER_RESPONSE *)_packet)->header.persistent); @@ -1711,8 +1824,7 @@ void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC) /* }}} */ -/* {{{ packet_methods - */ +/* {{{ packet_methods */ static mysqlnd_packet_methods packet_methods[PROT_LAST] = { @@ -1786,15 +1898,16 @@ mysqlnd_packet_methods packet_methods[PROT_LAST] = /* }}} */ - /* {{{ mysqlnd_protocol::get_greet_packet */ static struct st_mysqlnd_packet_greet * MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_greet * packet = pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_greet_packet"); - packet->header.m = &packet_methods[PROT_GREET_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_GREET_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1804,10 +1917,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const prot static struct st_mysqlnd_packet_auth * MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_auth * packet = pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_auth_packet"); - packet->header.m = &packet_methods[PROT_AUTH_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_AUTH_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1817,10 +1932,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const proto static struct st_mysqlnd_packet_ok * MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_ok * packet = pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_ok_packet"); - packet->header.m = &packet_methods[PROT_OK_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_OK_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1830,10 +1947,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protoco static struct st_mysqlnd_packet_eof * MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_eof * packet = pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_eof_packet"); - packet->header.m = &packet_methods[PROT_EOF_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_EOF_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1843,10 +1962,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protoc static struct st_mysqlnd_packet_command * MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_command * packet = pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_command_packet"); - packet->header.m = &packet_methods[PROT_CMD_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_CMD_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1856,10 +1977,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const pr static struct st_mysqlnd_packet_rset_header * MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_rset_header * packet = pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_rset_header_packet"); - packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1869,10 +1992,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * cons static struct st_mysqlnd_packet_res_field * MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_res_field * packet = pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_result_field_packet"); - packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1882,10 +2007,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * con static struct st_mysqlnd_packet_row * MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_row * packet = pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_row_packet"); - packet->header.m = &packet_methods[PROT_ROW_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_ROW_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1895,10 +2022,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protoc static struct st_mysqlnd_packet_stats * MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_stats * packet = pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_stats_packet"); - packet->header.m = &packet_methods[PROT_STATS_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_STATS_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1908,10 +2037,12 @@ MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const prot static struct st_mysqlnd_packet_prepare_response * MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_prepare_response * packet = pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet"); - packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ @@ -1921,15 +2052,33 @@ MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * static struct st_mysqlnd_packet_chg_user_resp* MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC) { - struct st_mysqlnd_packet_chg_user_resp * packet = pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent); + struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent); DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet"); - packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET]; - packet->header.persistent = persistent; + if (packet) { + packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET]; + packet->header.persistent = persistent; + } DBG_RETURN(packet); } /* }}} */ +static +MYSQLND_CLASS_METHODS_START(mysqlnd_protocol) + MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_command_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_row_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet), + MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet) +MYSQLND_CLASS_METHODS_END; + + /* {{{ mysqlnd_protocol_init */ PHPAPI MYSQLND_PROTOCOL * mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC) @@ -1938,20 +2087,11 @@ mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC) MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_protocol_init"); - DBG_INF_FMT("persistent=%d", persistent); - ret->persistent = persistent; - - ret->m.get_greet_packet = MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet); - ret->m.get_auth_packet = MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet); - ret->m.get_ok_packet = MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet); - ret->m.get_command_packet = MYSQLND_METHOD(mysqlnd_protocol, get_command_packet); - ret->m.get_eof_packet = MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet); - ret->m.get_rset_header_packet = MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet); - ret->m.get_result_field_packet = MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet); - ret->m.get_row_packet = MYSQLND_METHOD(mysqlnd_protocol, get_row_packet); - ret->m.get_stats_packet = MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet); - ret->m.get_prepare_response_packet = MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet); - ret->m.get_change_user_response_packet = MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet); + DBG_INF_FMT("persistent=%u", persistent); + if (ret) { + ret->persistent = persistent; + ret->m = mysqlnd_mysqlnd_protocol_methods; + } DBG_RETURN(ret); } @@ -1975,7 +2115,8 @@ mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC) /* {{{ _mysqlnd_plugin_get_plugin_protocol_data */ -PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC) +PHPAPI void ** +_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC) { DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data"); DBG_INF_FMT("plugin_id=%u", plugin_id); @@ -1987,6 +2128,15 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * /* }}} */ +/* {{{ mysqlnd_protocol_get_methods */ +PHPAPI struct st_mysqlnd_protocol_methods * +mysqlnd_protocol_get_methods() +{ + return &mysqlnd_mysqlnd_protocol_methods; +} +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index 29116e050..8d862fc8e 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_wireprotocol.h 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: mysqlnd_wireprotocol.h 299998 2010-05-31 17:57:03Z andrey $ */ #ifndef MYSQLND_WIREPROTOCOL_H #define MYSQLND_WIREPROTOCOL_H @@ -41,7 +41,9 @@ PHPAPI extern const char mysqlnd_read_body_name[]; #define PACKET_FREE(packet) \ do { \ DBG_INF_FMT("PACKET_FREE(%p)", packet); \ - ((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \ + if ((packet)) { \ + ((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \ + } \ } while (0); PHPAPI extern const char * const mysqlnd_command_to_text[COM_END]; @@ -99,6 +101,7 @@ typedef struct st_mysqlnd_packet_auth { /* +1 for \0 because of scramble() */ unsigned char *server_scramble_buf; size_t db_len; + zend_bool send_half_packet; } MYSQLND_PACKET_AUTH; /* OK packet */ @@ -174,6 +177,7 @@ typedef struct st_mysqlnd_packet_res_field { /* For table definitions, empty for result sets */ zend_bool skip_parsing; zend_bool stupid_list_fields_eof; + zend_bool persistent_alloc; MYSQLND_ERROR_INFO error_info; } MYSQLND_PACKET_RES_FIELD; @@ -241,6 +245,7 @@ typedef struct st_mysqlnd_packet_chg_user_resp { uint16_t server_capabilities; /* If error packet, we use these */ MYSQLND_ERROR_INFO error_info; + zend_bool server_asked_323_auth; } MYSQLND_PACKET_CHG_USER_RESPONSE; @@ -249,17 +254,17 @@ PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * c unsigned long php_mysqlnd_net_field_length(zend_uchar **packet); zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length); -PHPAPI extern char * const mysqlnd_empty_string; +PHPAPI const extern char * const mysqlnd_empty_string; -void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, zend_bool as_unicode, zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); -void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, zend_bool as_unicode, zend_bool as_int_or_float, @@ -267,8 +272,8 @@ void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, PHPAPI MYSQLND_PROTOCOL * mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC); -PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * net TSRMLS_DC); - +PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC); +PHPAPI struct st_mysqlnd_protocol_methods * mysqlnd_protocol_get_methods(); #endif /* MYSQLND_WIREPROTOCOL_H */ diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c index 8362a00d3..6bfbe2e4f 100644 --- a/ext/mysqlnd/php_mysqlnd.c +++ b/ext/mysqlnd/php_mysqlnd.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_mysqlnd.c 293779 2010-01-20 17:09:28Z johannes $ */ +/* $Id: php_mysqlnd.c 300352 2010-06-10 12:24:03Z andrey $ */ #include "php.h" #include "php_ini.h" #include "mysqlnd.h" @@ -49,8 +49,8 @@ PHPAPI void mysqlnd_minfo_print_hash(zval *values) zstr string_key; uint string_key_len; ulong num_key; - int s_len; - char *s = NULL; + int s_len; + char *s = NULL; TSRMLS_FETCH(); zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values); @@ -108,6 +108,12 @@ PHP_MINFO_FUNCTION(mysqlnd) #else "not supported"); #endif + php_info_print_table_row(2, "SSL", +#ifdef MYSQLND_SSL_SUPPORTED + "supported"); +#else + "not supported"); +#endif snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_cmd_buffer_size)); php_info_print_table_row(2, "Command buffer size", buf); snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size)); @@ -130,7 +136,7 @@ PHP_MINFO_FUNCTION(mysqlnd) /* }}} */ -ZEND_DECLARE_MODULE_GLOBALS(mysqlnd); +PHPAPI ZEND_DECLARE_MODULE_GLOBALS(mysqlnd); /* {{{ PHP_GINIT_FUNCTION @@ -145,6 +151,13 @@ static PHP_GINIT_FUNCTION(mysqlnd) mysqlnd_globals->net_read_buffer_size = 32768; mysqlnd_globals->net_read_timeout = 31536000; mysqlnd_globals->log_mask = 0; + mysqlnd_globals->mempool_default_size = 16000; + mysqlnd_globals->debug_emalloc_fail_threshold = -1; + mysqlnd_globals->debug_ecalloc_fail_threshold = -1; + mysqlnd_globals->debug_erealloc_fail_threshold = -1; + mysqlnd_globals->debug_malloc_fail_threshold = -1; + mysqlnd_globals->debug_calloc_fail_threshold = -1; + mysqlnd_globals->debug_realloc_fail_threshold = -1; } /* }}} */ @@ -170,6 +183,17 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mysqlnd.net_read_buffer_size", "32768",PHP_INI_ALL, OnUpdateLong, net_read_buffer_size, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.net_read_timeout", "31536000", PHP_INI_SYSTEM, OnUpdateLong, net_read_timeout, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.log_mask", "0", PHP_INI_ALL, OnUpdateLong, log_mask, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.mempool_default_size","16000", PHP_INI_ALL, OnUpdateLong, mempool_default_size, zend_mysqlnd_globals, mysqlnd_globals) + +#ifdef PHP_DEBUG + STD_PHP_INI_ENTRY("mysqlnd.debug_emalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_emalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.debug_ecalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_ecalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.debug_erealloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_erealloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) + + STD_PHP_INI_ENTRY("mysqlnd.debug_malloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_malloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.debug_calloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_calloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_ENTRY("mysqlnd.debug_realloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_realloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) +#endif PHP_INI_END() /* }}} */ diff --git a/ext/mysqlnd/php_mysqlnd.h b/ext/mysqlnd/php_mysqlnd.h index 11232af0b..765f71c9f 100644 --- a/ext/mysqlnd/php_mysqlnd.h +++ b/ext/mysqlnd/php_mysqlnd.h @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2010 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: php_mysqlnd.h 293036 2010-01-03 09:23:27Z sebastian $ + $Id: php_mysqlnd.h 298217 2010-04-20 13:50:34Z felipe $ */ #ifndef PHP_MYSQLND_H @@ -27,3 +27,12 @@ extern zend_module_entry mysqlnd_module_entry; #endif /* PHP_MYSQLND_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ |