#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
#include "ext/standard/php_smart_str.h"
extern MYSQLND_CHARSET *mysqlnd_charsets;
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 void
MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
zend_bool pers = conn->persistent;
if (conn->options->charset_name) {
mnd_pefree(conn->options->charset_name, pers);
conn->options->charset_name = NULL;
}
if (conn->options->auth_protocol) {
mnd_pefree(conn->options->auth_protocol, pers);
conn->options->auth_protocol = NULL;
}
if (conn->options->num_commands) {
unsigned int i;
for (i = 0; i < conn->options->num_commands; i++) {
mnd_pefree(conn->options->init_commands[i], pers);
}
mnd_pefree(conn->options->init_commands, pers);
conn->options->init_commands = NULL;
}
if (conn->options->cfg_file) {
mnd_pefree(conn->options->cfg_file, pers);
conn->options->cfg_file = NULL;
}
if (conn->options->cfg_section) {
mnd_pefree(conn->options->cfg_section, pers);
conn->options->cfg_section = NULL;
}
if (conn->options->connect_attr) {
zend_hash_destroy(conn->options->connect_attr);
mnd_pefree(conn->options->connect_attr, pers);
conn->options->connect_attr = NULL;
}
}
static void
MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
zend_bool pers = conn->persistent;
DBG_ENTER("mysqlnd_conn_data::free_contents");
mysqlnd_local_infile_default(conn);
if (conn->current_result) {
conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
conn->current_result = NULL;
}
if (conn->net) {
conn->net->data->m.free_contents(conn->net TSRMLS_CC);
}
DBG_INF("Freeing memory of members");
if (conn->host) {
mnd_pefree(conn->host, pers);
conn->host = NULL;
}
if (conn->user) {
mnd_pefree(conn->user, pers);
conn->user = NULL;
}
if (conn->passwd) {
mnd_pefree(conn->passwd, pers);
conn->passwd = NULL;
}
if (conn->connect_or_select_db) {
mnd_pefree(conn->connect_or_select_db, pers);
conn->connect_or_select_db = NULL;
}
if (conn->unix_socket) {
mnd_pefree(conn->unix_socket, pers);
conn->unix_socket = NULL;
}
DBG_INF_FMT("scheme=%s", conn->scheme);
if (conn->scheme) {
mnd_pefree(conn->scheme, pers);
conn->scheme = NULL;
}
if (conn->server_version) {
mnd_pefree(conn->server_version, pers);
conn->server_version = NULL;
}
if (conn->host_info) {
mnd_pefree(conn->host_info, pers);
conn->host_info = NULL;
}
if (conn->auth_plugin_data) {
mnd_pefree(conn->auth_plugin_data, pers);
conn->auth_plugin_data = NULL;
}
if (conn->last_message) {
mnd_pefree(conn->last_message, pers);
conn->last_message = NULL;
}
if (conn->error_info->error_list) {
zend_llist_clean(conn->error_info->error_list);
mnd_pefree(conn->error_info->error_list, pers);
conn->error_info->error_list = NULL;
}
conn->charset = NULL;
conn->greet_charset = NULL;
DBG_VOID_RETURN;
}
static void
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::dtor");
DBG_INF_FMT("conn=%llu", conn->thread_id);
conn->m->free_contents(conn TSRMLS_CC);
conn->m->free_options(conn TSRMLS_CC);
if (conn->net) {
mysqlnd_net_free(conn->net, conn->stats, conn->error_info TSRMLS_CC);
conn->net = NULL;
}
if (conn->protocol) {
mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
conn->protocol = NULL;
}
if (conn->stats) {
mysqlnd_stats_end(conn->stats);
}
mnd_pefree(conn, conn->persistent);
DBG_VOID_RETURN;
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * 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 = FAIL;
DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
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=%u",
mysqlnd_command_to_text[command], getpid());
}
} else {
DBG_INF_FMT("OK from server");
if (0xFF == ok_response->field_count) {
SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
ret = FAIL;
conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
SET_ERROR_AFF_ROWS(conn);
} else {
SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
ok_response->message, ok_response->message_len,
conn->persistent);
if (!ignore_upsert_status) {
memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
conn->upsert_status->warning_count = ok_response->warning_count;
conn->upsert_status->server_status = ok_response->server_status;
conn->upsert_status->affected_rows = ok_response->affected_rows;
conn->upsert_status->last_insert_id = ok_response->last_insert_id;
}
}
}
PACKET_FREE(ok_response);
break;
}
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());
}
} else if (0xFF == ok_response->field_count) {
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");
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",
ok_response->field_count);
}
} else {
DBG_INF_FMT("OK from server");
}
PACKET_FREE(ok_response);
break;
}
default:
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");
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC)
{
enum_func_status ret = PASS;
MYSQLND_PACKET_COMMAND * cmd_packet;
DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_INF_FMT("sending %u bytes", arg_len + 1);
switch (CONN_GET_STATE(conn)) {
case CONN_READY:
break;
case CONN_QUIT_SENT:
SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
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_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
DBG_RETURN(FAIL);
}
SET_ERROR_AFF_ROWS(conn);
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;
cmd_packet->arg_len = arg_len;
}
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 );
if (! PACKET_WRITE(cmd_packet, conn)) {
if (!silent) {
DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
}
CONN_SET_STATE(conn, CONN_QUIT_SENT);
conn->m->send_close(conn TSRMLS_CC);
DBG_ERR("Server is gone");
ret = FAIL;
}
PACKET_FREE(cmd_packet);
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
const zend_uchar * 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 ret;
DBG_ENTER("mysqlnd_conn_data::simple_command");
ret = conn->m->simple_command_send_request(conn, command, arg, arg_len, silent, ignore_upsert_status TSRMLS_CC);
if (PASS == ret && ok_packet != PROT_LAST) {
ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
}
DBG_INF(ret == PASS ? "PASS":"FAIL");
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
zend_uchar buffer[2];
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_server_option");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
int2store(buffer, (unsigned int) option);
ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::restart_psession");
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
if (conn->last_message) {
mnd_pefree(conn->last_message, conn->persistent);
conn->last_message = NULL;
}
DBG_RETURN(PASS);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::end_psession");
DBG_RETURN(PASS);
}
static enum_func_status
mysqlnd_switch_to_ssl_if_needed(
MYSQLND_CONN_DATA * conn,
const MYSQLND_PACKET_GREET * const greet_packet,
const MYSQLND_OPTIONS * const options,
unsigned long mysql_flags
TSRMLS_DC
)
{
enum_func_status ret = FAIL;
const MYSQLND_CHARSET * charset;
MYSQLND_PACKET_AUTH * auth_packet;
DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
DBG_INF_FMT("CLIENT_LONG_FLAG= %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
DBG_INF_FMT("CLIENT_COMPRESS= %d", mysql_flags & CLIENT_COMPRESS? 1:0);
DBG_INF_FMT("CLIENT_ODBC= %d", mysql_flags & CLIENT_ODBC? 1:0);
DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
DBG_INF_FMT("CLIENT_INTERACTIVE= %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
DBG_INF_FMT("CLIENT_SSL= %d", mysql_flags & CLIENT_SSL? 1:0);
DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
DBG_INF_FMT("CLIENT_RESERVED= %d", mysql_flags & CLIENT_RESERVED? 1:0);
DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
DBG_INF_FMT("CLIENT_SSL_DONT_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? 1:0);
DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
if (!auth_packet) {
SET_OOM_ERROR(*conn->error_info);
goto end;
}
auth_packet->client_flags = mysql_flags;
auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
auth_packet->charset_no = charset->nr;
} else {
auth_packet->charset_no = greet_packet->charset_no;
}
#ifdef MYSQLND_SSL_SUPPORTED
if (mysql_flags & CLIENT_SSL) {
zend_bool server_has_ssl = (greet_packet->server_capabilities & CLIENT_SSL)? TRUE:FALSE;
if (server_has_ssl == FALSE) {
goto close_conn;
} else {
enum mysqlnd_ssl_peer verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT?
MYSQLND_SSL_PEER_VERIFY:
(mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
MYSQLND_SSL_PEER_DONT_VERIFY:
MYSQLND_SSL_PEER_DEFAULT);
DBG_INF("Switching to SSL");
if (!PACKET_WRITE(auth_packet, conn)) {
goto close_conn;
}
conn->net->data->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
if (FAIL == conn->net->data->m.enable_ssl(conn->net TSRMLS_CC)) {
goto end;
}
}
}
#else
auth_packet->client_flags &= ~CLIENT_SSL;
if (!PACKET_WRITE(auth_packet, conn)) {
goto close_conn;
}
#endif
ret = PASS;
end:
PACKET_FREE(auth_packet);
DBG_RETURN(ret);
close_conn:
CONN_SET_STATE(conn, CONN_QUIT_SENT);
conn->m->send_close(conn TSRMLS_CC);
SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
PACKET_FREE(auth_packet);
DBG_RETURN(ret);
}
static struct st_mysqlnd_authentication_plugin *
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC)
{
struct st_mysqlnd_authentication_plugin * auth_plugin;
char * plugin_name = NULL;
DBG_ENTER("mysqlnd_conn_data::fetch_auth_plugin_by_name");
mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
DBG_INF_FMT("looking for %s auth plugin", plugin_name);
auth_plugin = mysqlnd_plugin_find(plugin_name);
mnd_sprintf_free(plugin_name);
DBG_RETURN(auth_plugin);
}
static enum_func_status
mysqlnd_run_authentication(
MYSQLND_CONN_DATA * conn,
const char * const user,
const char * const passwd,
const size_t passwd_len,
const char * const db,
const size_t db_len,
const zend_uchar * const auth_plugin_data,
const size_t auth_plugin_data_len,
const char * const auth_protocol,
unsigned int charset_no,
const MYSQLND_OPTIONS * const options,
unsigned long mysql_flags,
zend_bool silent,
zend_bool is_change_user
TSRMLS_DC)
{
enum_func_status ret = FAIL;
zend_bool first_call = TRUE;
char * switch_to_auth_protocol = NULL;
size_t switch_to_auth_protocol_len = 0;
char * requested_protocol = NULL;
zend_uchar * plugin_data;
size_t plugin_data_len;
DBG_ENTER("mysqlnd_run_authentication");
plugin_data_len = auth_plugin_data_len;
plugin_data = mnd_emalloc(plugin_data_len + 1);
if (!plugin_data) {
goto end;
}
memcpy(plugin_data, auth_plugin_data, plugin_data_len);
plugin_data[plugin_data_len] = '\0';
requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
if (!requested_protocol) {
goto end;
}
do {
struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol TSRMLS_CC);
if (!auth_plugin) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
goto end;
}
DBG_INF("plugin found");
{
zend_uchar * switch_to_auth_protocol_data = NULL;
size_t switch_to_auth_protocol_data_len = 0;
zend_uchar * scrambled_data = NULL;
size_t scrambled_data_len = 0;
switch_to_auth_protocol = NULL;
switch_to_auth_protocol_len = 0;
if (conn->auth_plugin_data) {
mnd_pefree(conn->auth_plugin_data, conn->persistent);
conn->auth_plugin_data = NULL;
}
conn->auth_plugin_data_len = plugin_data_len;
conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
if (!conn->auth_plugin_data) {
SET_OOM_ERROR(*conn->error_info);
goto end;
}
memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
scrambled_data =
auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
plugin_data, plugin_data_len, options, &conn->net->data->options, mysql_flags TSRMLS_CC);
if (conn->error_info->error_no) {
goto end;
}
if (FALSE == is_change_user) {
ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
charset_no,
first_call,
requested_protocol,
scrambled_data, scrambled_data_len,
&switch_to_auth_protocol, &switch_to_auth_protocol_len,
&switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
TSRMLS_CC);
} else {
ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
first_call,
requested_protocol,
scrambled_data, scrambled_data_len,
&switch_to_auth_protocol, &switch_to_auth_protocol_len,
&switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
TSRMLS_CC);
}
first_call = FALSE;
free(scrambled_data);
DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
if (requested_protocol && switch_to_auth_protocol) {
mnd_efree(requested_protocol);
requested_protocol = switch_to_auth_protocol;
}
if (plugin_data) {
mnd_efree(plugin_data);
}
plugin_data_len = switch_to_auth_protocol_data_len;
plugin_data = switch_to_auth_protocol_data;
}
DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
} while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
if (ret == PASS) {
DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
}
end:
if (plugin_data) {
mnd_efree(plugin_data);
}
if (requested_protocol) {
mnd_efree(requested_protocol);
}
DBG_RETURN(ret);
}
static enum_func_status
mysqlnd_connect_run_authentication(
MYSQLND_CONN_DATA * conn,
const char * const user,
const char * const passwd,
const char * const db,
size_t db_len,
size_t passwd_len,
const MYSQLND_PACKET_GREET * const greet_packet,
const MYSQLND_OPTIONS * const options,
unsigned long mysql_flags
TSRMLS_DC)
{
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_connect_run_authentication");
ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
if (PASS == ret) {
ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
greet_packet->auth_plugin_data, greet_packet->auth_plugin_data_len, greet_packet->auth_protocol,
greet_packet->charset_no, options, mysql_flags, FALSE , FALSE TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::execute_init_commands");
if (conn->options->init_commands) {
unsigned int current_command = 0;
for (; current_command < conn->options->num_commands; ++current_command) {
const char * const command = conn->options->init_commands[current_command];
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);
ret = FAIL;
break;
}
if (conn->last_query_type == QUERY_SELECT) {
MYSQLND_RES * result = conn->m->use_result(conn, 0 TSRMLS_CC);
if (result) {
result->m.free_result(result, TRUE TSRMLS_CC);
}
}
}
}
}
DBG_RETURN(ret);
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags TSRMLS_DC)
{
MYSQLND_NET * net = conn->net;
DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
mysql_flags |= MYSQLND_CAPABILITIES;
mysql_flags |= conn->options->flags;
#ifndef MYSQLND_COMPRESSION_ENABLED
if (mysql_flags & CLIENT_COMPRESS) {
mysql_flags &= ~CLIENT_COMPRESS;
}
#else
if (net && net->data->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
mysql_flags |= CLIENT_COMPRESS;
}
#endif
#ifndef MYSQLND_SSL_SUPPORTED
if (mysql_flags & CLIENT_SSL) {
mysql_flags &= ~CLIENT_SSL;
}
#else
if (net && (net->data->options.ssl_key || net->data->options.ssl_cert ||
net->data->options.ssl_ca || net->data->options.ssl_capath || net->data->options.ssl_cipher))
{
mysql_flags |= CLIENT_SSL;
}
#endif
DBG_RETURN(mysql_flags);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
const char * const host, const char * const user,
const char * const passwd, const unsigned int passwd_len,
const char * const db, const unsigned int db_len,
const unsigned int mysql_flags TSRMLS_DC)
{
MYSQLND_PACKET_GREET * greet_packet;
MYSQLND_NET * net = conn->net;
DBG_ENTER("mysqlnd_conn_data::connect_handshake");
greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
if (!greet_packet) {
SET_OOM_ERROR(*conn->error_info);
DBG_RETURN(FAIL);
}
if (FAIL == net->data->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
conn->stats, conn->error_info TSRMLS_CC))
{
goto err;
}
DBG_INF_FMT("stream=%p", net->data->m.get_stream(net TSRMLS_CC));
if (FAIL == PACKET_READ(greet_packet, conn)) {
DBG_ERR("Error while reading greeting packet");
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=%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) {
DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
" is not supported. Server is %-.32s", greet_packet->server_version);
SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
"Connecting to 3.22, 3.23 & 4.0 servers is not supported");
goto err;
}
conn->thread_id = greet_packet->thread_id;
conn->protocol_version = greet_packet->protocol_version;
conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
if (!conn->greet_charset) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
"Server sent charset unknown to the client. Please, report to the developers");
goto err;
}
conn->client_flag = mysql_flags;
conn->server_capabilities = greet_packet->server_capabilities;
if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
greet_packet, conn->options, mysql_flags TSRMLS_CC))
{
goto err;
}
memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
conn->upsert_status->warning_count = 0;
conn->upsert_status->server_status = greet_packet->server_status;
conn->upsert_status->affected_rows = 0;
PACKET_FREE(greet_packet);
DBG_RETURN(PASS);
err:
conn->client_flag = 0;
conn->server_capabilities = 0;
PACKET_FREE(greet_packet);
DBG_RETURN(FAIL);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * 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_or_pipe,
unsigned int mysql_flags
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
size_t host_len;
zend_bool unix_socket = FALSE;
zend_bool named_pipe = FALSE;
zend_bool reconnect = FALSE;
zend_bool saved_compression = FALSE;
zend_bool local_tx_started = FALSE;
MYSQLND_NET * net = conn->net;
DBG_ENTER("mysqlnd_conn_data::connect");
DBG_INF_FMT("conn=%p", conn);
if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
goto err;
}
local_tx_started = TRUE;
SET_EMPTY_ERROR(*conn->error_info);
SET_ERROR_AFF_ROWS(conn);
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_GET_STATE(conn) > CONN_ALLOCED && 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;
conn->m->send_close(conn TSRMLS_CC);
}
conn->m->free_contents(conn TSRMLS_CC);
MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
if (conn->persistent) {
MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
}
if (net->data->compressed) {
saved_compression = TRUE;
net->data->compressed = FALSE;
}
if (net->data->ssl) {
net->data->ssl = FALSE;
}
} else {
unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size TSRMLS_CC);
}
if (!host || !host[0]) {
host = "localhost";
}
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;
} else {
mysql_flags |= CLIENT_CONNECT_WITH_DB;
}
host_len = strlen(host);
{
char * transport = NULL;
int transport_len;
#ifndef PHP_WIN32
if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
if (!socket_or_pipe) {
socket_or_pipe = "/tmp/mysql.sock";
}
transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
unix_socket = TRUE;
#else
if (host_len == sizeof(".") - 1 && host[0] == '.') {
if (!socket_or_pipe) {
socket_or_pipe = "\\\\.\\pipe\\MySQL";
}
transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
named_pipe = TRUE;
#endif
} else {
if (!port) {
port = 3306;
}
transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
}
if (!transport) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
conn->scheme_len = transport_len;
mnd_sprintf_free(transport);
transport = NULL;
if (!conn->scheme) {
goto err;
}
}
mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags TSRMLS_CC);
if (FAIL == conn->m->connect_handshake(conn, host, user, passwd, passwd_len, db, db_len, mysql_flags TSRMLS_CC)) {
goto err;
}
{
CONN_SET_STATE(conn, CONN_READY);
if (saved_compression) {
net->data->compressed = TRUE;
}
net->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
conn->user = mnd_pestrdup(user, conn->persistent);
conn->user_len = strlen(conn->user);
conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
conn->passwd_len = passwd_len;
conn->port = port;
conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
conn->connect_or_select_db_len = db_len;
if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
if (!unix_socket && !named_pipe) {
conn->host = mnd_pestrdup(host, conn->persistent);
if (!conn->host) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
conn->host_len = strlen(conn->host);
{
char *p;
mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
if (!p) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
}
} else {
conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
if (unix_socket) {
conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
} else if (named_pipe) {
char *p;
mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
if (!p) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
}
if (!conn->unix_socket || !conn->host_info) {
SET_OOM_ERROR(*conn->error_info);
goto err;
}
conn->unix_socket_len = strlen(conn->unix_socket);
}
conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
SET_EMPTY_ERROR(*conn->error_info);
mysqlnd_local_infile_default(conn);
if (FAIL == conn->m->execute_init_commands(conn TSRMLS_CC)) {
goto err;
}
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);
conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
DBG_RETURN(PASS);
}
err:
DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
if (!conn->error_info->error_no) {
SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)",
conn->error_info->error_no, conn->error_info->error, conn->scheme);
}
conn->m->free_contents(conn TSRMLS_CC);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
if (TRUE == local_tx_started) {
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
}
DBG_RETURN(FAIL);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
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_or_pipe,
unsigned int mysql_flags
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
enum_func_status ret = FAIL;
MYSQLND_CONN_DATA * conn = conn_handle->data;
DBG_ENTER("mysqlnd_conn::connect");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
}
DBG_RETURN(ret);
}
PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
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_or_pipe,
unsigned int mysql_flags,
unsigned int client_api_flags
TSRMLS_DC)
{
enum_func_status ret = FAIL;
zend_bool self_alloced = FALSE;
DBG_ENTER("mysqlnd_connect");
DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
if (!conn_handle) {
self_alloced = TRUE;
if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) {
DBG_RETURN(NULL);
}
}
ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
if (ret == FAIL) {
if (self_alloced) {
conn_handle->m->dtor(conn_handle TSRMLS_CC);
}
DBG_RETURN(NULL);
}
DBG_RETURN(conn_handle);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::query");
DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
PASS == conn->m->reap_query(conn TSRMLS_CC))
{
ret = PASS;
if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
}
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::send_query");
DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
PROT_LAST ,
FALSE, FALSE TSRMLS_CC);
if (PASS == ret) {
CONN_SET_STATE(conn, CONN_QUERY_SENT);
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::reap_query");
DBG_INF_FMT("conn=%llu", conn->thread_id);
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
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=%u", state);
DBG_RETURN(ret);
}
ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(ret);
}
#include "php_network.h"
MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
{
int cnt = 0;
MYSQLND **p = conn_array, **p_p;
MYSQLND **ret = NULL;
while (*p) {
if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
cnt++;
}
p++;
}
if (cnt) {
MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
p_p = p = conn_array;
while (*p) {
if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
*ret_p = *p;
*p = NULL;
ret_p++;
} else {
*p_p = *p;
p_p++;
}
p++;
}
*ret_p = NULL;
}
return ret;
}
static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd TSRMLS_DC)
{
php_socket_t this_fd;
php_stream *stream = NULL;
unsigned int cnt = 0;
MYSQLND **p = conn_array;
DBG_ENTER("mysqlnd_stream_array_to_fd_set");
while (*p) {
stream = (*p)->data->net->data->m.get_stream((*p)->data->net TSRMLS_CC);
DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
(void*)&this_fd, 1) && this_fd >= 0) {
PHP_SAFE_FD_SET(this_fd, fds);
if (this_fd > *max_fd) {
*max_fd = this_fd;
}
cnt++;
}
p++;
}
DBG_RETURN(cnt ? 1 : 0);
}
static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds TSRMLS_DC)
{
php_socket_t this_fd;
php_stream *stream = NULL;
int ret = 0;
zend_bool disproportion = FALSE;
MYSQLND **fwd = conn_array, **bckwd = conn_array;
DBG_ENTER("mysqlnd_stream_array_from_fd_set");
while (*fwd) {
stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net TSRMLS_CC);
DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
(void*)&this_fd, 1) && this_fd >= 0) {
if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
if (disproportion) {
*bckwd = *fwd;
}
bckwd++;
fwd++;
ret++;
continue;
}
}
disproportion = TRUE;
fwd++;
}
*bckwd = NULL;
DBG_RETURN(ret);
}
#ifndef PHP_WIN32
#define php_select(m, r, w, e, t) select(m, r, w, e, t)
#else
#include "win32/select.h"
#endif
PHPAPI enum_func_status
_mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num TSRMLS_DC)
{
struct timeval tv;
struct timeval *tv_p = NULL;
fd_set rfds, wfds, efds;
php_socket_t max_fd = 0;
int retval, sets = 0;
int set_count, max_set_count = 0;
DBG_ENTER("_mysqlnd_poll");
if (sec < 0 || usec < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
DBG_RETURN(FAIL);
}
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
if (r_array != NULL) {
*dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
if (set_count > max_set_count) {
max_set_count = set_count;
}
sets += set_count;
}
if (e_array != NULL) {
set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
if (set_count > max_set_count) {
max_set_count = set_count;
}
sets += set_count;
}
if (!sets) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
DBG_RETURN(FAIL);
}
PHP_SAFE_MAX_FD(max_fd, max_set_count);
if (usec > 999999) {
tv.tv_sec = sec + (usec / 1000000);
tv.tv_usec = usec % 1000000;
} else {
tv.tv_sec = sec;
tv.tv_usec = usec;
}
tv_p = &tv;
retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
if (retval == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
errno, strerror(errno), max_fd);
DBG_RETURN(FAIL);
}
if (r_array != NULL) {
mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
}
if (e_array != NULL) {
mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
}
*desc_num = retval;
DBG_RETURN(PASS);
}
MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
size_t table_len, wild_len;
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::list_fields");
DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
p = buff;
if (table && (table_len = strlen(table))) {
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))) {
size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
memcpy(p, achtung_wild, to_copy);
p += to_copy;
*p++ = '\0';
}
if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
PROT_LAST ,
FALSE, TRUE TSRMLS_CC)) {
conn->m->local_tx_end(conn, 0, FAIL TSRMLS_CC);
break;
}
result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
if (!result) {
break;
}
if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
DBG_ERR("Error occurred while reading metadata");
result->m.free_result(result, TRUE TSRMLS_CC);
result = NULL;
break;
}
result->type = MYSQLND_RES_NORMAL;
result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent TSRMLS_CC);
if (!result->unbuf) {
SET_OOM_ERROR(*conn->error_info);
result->m.free_result(result, TRUE TSRMLS_CC);
result = NULL;
break;
}
result->unbuf->eof_reached = TRUE;
} while (0);
conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
}
DBG_RETURN(result);
}
MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
char * show_query = NULL;
size_t show_query_len;
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::list_method");
DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
if (par1) {
if (achtung_wild) {
show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
} else {
show_query_len = mnd_sprintf(&show_query, 0, query, par1);
}
} else {
if (achtung_wild) {
show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
} else {
show_query_len = strlen(show_query = (char *)query);
}
}
if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY TSRMLS_CC);
}
if (show_query != query) {
mnd_sprintf_free(show_query);
}
conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
}
DBG_RETURN(result);
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->error_info->error_no;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->error_info->error;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
}
PHPAPI ulong
mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
{
DBG_ENTER("mysqlnd_old_escape_string");
DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
enum_func_status ret = FAIL;
MYSQLND_NET * net = conn->net;
DBG_ENTER("mysqlnd_conn_data::ssl_set");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static ulong
MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
ulong ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::escape_string");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
} else {
ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
}
conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::select_db");
DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
SET_ERROR_AFF_ROWS(conn);
if (ret == PASS) {
if (conn->connect_or_select_db) {
mnd_pefree(conn->connect_or_select_db, 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) {
SET_OOM_ERROR(*conn->error_info);
ret = FAIL;
}
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::ping");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
SET_ERROR_AFF_ROWS(conn);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_INF_FMT("ret=%u", ret);
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, char **message, unsigned int * message_len TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
enum_func_status ret = FAIL;
MYSQLND_PACKET_STATS * stats_header;
DBG_ENTER("mysqlnd_conn_data::statistic");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
if (FAIL == ret) {
break;
}
stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
if (!stats_header) {
SET_OOM_ERROR(*conn->error_info);
break;
}
if (PASS == (ret = PACKET_READ(stats_header, conn))) {
*message = estrndup(stats_header->message, stats_header->message_len);
*message_len = stats_header->message_len;
DBG_INF(*message);
}
PACKET_FREE(stats_header);
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
enum_func_status ret = FAIL;
zend_uchar buff[4];
DBG_ENTER("mysqlnd_conn_data::kill");
DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
int4store(buff, pid);
if (pid != conn->thread_id) {
ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
SET_ERROR_AFF_ROWS(conn);
} else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
CONN_SET_STATE(conn, CONN_QUIT_SENT);
conn->m->send_close(conn TSRMLS_CC);
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
enum_func_status ret = FAIL;
const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
DBG_ENTER("mysqlnd_conn_data::set_charset");
DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
if (!charset) {
SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
"Invalid characterset or character set not supported");
DBG_RETURN(ret);
}
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
char * query;
size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
if (FAIL == (ret = conn->m->query(conn, query, query_len TSRMLS_CC))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
} else if (conn->error_info->error_no) {
ret = FAIL;
} else {
conn->charset = charset;
}
mnd_sprintf_free(query);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_INF(ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
enum_func_status ret = FAIL;
zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::refresh");
DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
int1store(bits, options);
ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
enum_func_status ret = FAIL;
zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::shutdown");
DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
int1store(bits, level);
ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
enum_func_status ret = PASS;
MYSQLND_NET * net = conn->net;
php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
enum mysqlnd_connection_state state;
DBG_ENTER("mysqlnd_send_close");
DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
if (CONN_GET_STATE(conn) >= CONN_READY) {
MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
if (conn->persistent) {
MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
}
}
state = CONN_GET_STATE(conn);
DBG_INF_FMT("state=%u", state);
switch (state) {
case CONN_READY:
DBG_INF("Connection clean, sending COM_QUIT");
if (net_stream) {
ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
}
CONN_SET_STATE(conn, CONN_QUIT_SENT);
break;
case CONN_SENDING_LOAD_DATA:
case CONN_NEXT_RESULT_PENDING:
case CONN_QUERY_SENT:
case CONN_FETCHING_DATA:
MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
case CONN_ALLOCED:
CONN_SET_STATE(conn, CONN_QUIT_SENT);
case CONN_QUIT_SENT:
net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
break;
}
DBG_RETURN(ret);
}
static MYSQLND_CONN_DATA *
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_reference");
++conn->refcount;
DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
DBG_RETURN(conn);
}
static enum_func_status
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::free_reference");
DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
if (!(--conn->refcount)) {
ret = conn->m->send_close(conn TSRMLS_CC);
conn->m->dtor(conn TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum mysqlnd_connection_state
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_state");
DBG_RETURN(conn->state);
}
static void
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::set_state");
DBG_INF_FMT("New state=%u", new_state);
conn->state = new_state;
DBG_VOID_RETURN;
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->field_count;
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->upsert_status->server_status;
}
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->upsert_status->last_insert_id;
}
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->upsert_status->affected_rows;
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->upsert_status->warning_count;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->last_message;
}
PHPAPI const char * mysqlnd_get_client_info()
{
return MYSQLND_VERSION;
}
PHPAPI unsigned int mysqlnd_get_client_version()
{
return MYSQLND_VERSION_ID;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->server_version;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->host_info;
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->protocol_version;
}
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->charset->name;
}
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
return conn->thread_id;
}
static unsigned long
MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
long major, minor, patch;
char *p;
if (!(p = conn->server_version)) {
return 0;
}
major = strtol(p, &p, 10);
p += 1;
minor = strtol(p, &p, 10);
p += 1;
patch = strtol(p, &p, 10);
return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
}
static zend_bool
MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::more_results");
DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::next_result");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
break;
}
SET_EMPTY_ERROR(*conn->error_info);
SET_ERROR_AFF_ROWS(conn);
if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
if (!conn->error_info->error_no) {
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);
conn->m->send_close(conn TSRMLS_CC);
} else {
DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
}
break;
}
if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
}
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
{
switch(field_type) {
case FIELD_TYPE_JSON:
return "json";
case FIELD_TYPE_STRING:
case FIELD_TYPE_VAR_STRING:
return "string";
case FIELD_TYPE_TINY:
case FIELD_TYPE_SHORT:
case FIELD_TYPE_LONG:
case FIELD_TYPE_LONGLONG:
case FIELD_TYPE_INT24:
return "int";
case FIELD_TYPE_FLOAT:
case FIELD_TYPE_DOUBLE:
case FIELD_TYPE_DECIMAL:
case FIELD_TYPE_NEWDECIMAL:
return "real";
case FIELD_TYPE_TIMESTAMP:
return "timestamp";
case FIELD_TYPE_YEAR:
return "year";
case FIELD_TYPE_DATE:
case FIELD_TYPE_NEWDATE:
return "date";
case FIELD_TYPE_TIME:
return "time";
case FIELD_TYPE_SET:
return "set";
case FIELD_TYPE_ENUM:
return "enum";
case FIELD_TYPE_GEOMETRY:
return "geometry";
case FIELD_TYPE_DATETIME:
return "datetime";
case FIELD_TYPE_TINY_BLOB:
case FIELD_TYPE_MEDIUM_BLOB:
case FIELD_TYPE_LONG_BLOB:
case FIELD_TYPE_BLOB:
return "blob";
case FIELD_TYPE_NULL:
return "null";
case FIELD_TYPE_BIT:
return "bit";
default:
return "unknown";
}
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
const char * user,
const char * passwd,
const char * db,
zend_bool silent,
size_t passwd_len
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::change_user");
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 );
if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
goto end;
}
SET_EMPTY_ERROR(*conn->error_info);
SET_ERROR_AFF_ROWS(conn);
if (!user) {
user = "";
}
if (!passwd) {
passwd = "";
}
if (!db) {
db = "";
}
ret = mysqlnd_run_authentication(conn, user, passwd, strlen(passwd), db, strlen(db),
conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
0 , conn->options, conn->server_capabilities, silent, TRUE TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
end:
DBG_INF(ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
enum mysqlnd_option option,
const char * const value
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
goto end;
}
switch (option) {
case MYSQL_OPT_COMPRESS:
#ifdef WHEN_SUPPORTED_BY_MYSQLI
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:
case MYSQL_SERVER_PUBLIC_KEY:
ret = conn->net->data->m.set_client_option(conn->net, option, value TSRMLS_CC);
break;
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
conn->options->int_and_float_native = *(unsigned int*) value;
break;
#endif
case MYSQL_OPT_LOCAL_INFILE:
if (value && (*(unsigned int*) value) ? 1 : 0) {
conn->options->flags |= CLIENT_LOCAL_FILES;
} else {
conn->options->flags &= ~CLIENT_LOCAL_FILES;
}
break;
case MYSQL_INIT_COMMAND:
{
char ** new_init_commands;
char * new_command;
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:
#endif
break;
case MYSQL_SET_CHARSET_NAME:
{
char * new_charset_name;
if (!mysqlnd_find_charset_name(value)) {
SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
ret = FAIL;
break;
}
new_charset_name = mnd_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;
}
case MYSQL_OPT_NAMED_PIPE:
conn->options->protocol = MYSQL_PROTOCOL_PIPE;
break;
case MYSQL_OPT_PROTOCOL:
if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
conn->options->protocol = *(unsigned int*) value;
}
break;
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_SET_CHARSET_DIR:
case MYSQL_OPT_RECONNECT:
case MYSQL_OPT_USE_REMOTE_CONNECTION:
case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
case MYSQL_OPT_GUESS_CONNECTION:
break;
#endif
case MYSQLND_OPT_MAX_ALLOWED_PACKET:
if (*(unsigned int*) value > (1<<16)) {
conn->options->max_allowed_packet = *(unsigned int*) value;
}
break;
case MYSQLND_OPT_AUTH_PROTOCOL:
{
char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
if (value && !new_auth_protocol) {
goto oom;
}
if (conn->options->auth_protocol) {
mnd_pefree(conn->options->auth_protocol, conn->persistent);
}
conn->options->auth_protocol = new_auth_protocol;
DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
break;
}
case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
if (value && (*(unsigned int*) value) ? 1 : 0) {
conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
} else {
conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
}
break;
case MYSQL_OPT_CONNECT_ATTR_RESET:
if (conn->options->connect_attr) {
DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
zend_hash_clean(conn->options->connect_attr);
}
break;
case MYSQL_OPT_CONNECT_ATTR_DELETE:
if (conn->options->connect_attr && value) {
DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
zend_hash_del(conn->options->connect_attr, value, strlen(value));
DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
}
break;
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_SHARED_MEMORY_BASE_NAME:
case MYSQL_OPT_USE_RESULT:
case MYSQL_SECURE_AUTH:
#endif
default:
ret = FAIL;
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
DBG_RETURN(ret);
oom:
SET_OOM_ERROR(*conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
end:
DBG_RETURN(FAIL);
}
static void
connect_attr_item_edtor(void * pDest)
{
#ifdef ZTS
TSRMLS_FETCH();
#endif
DBG_ENTER("connect_attr_item_edtor");
mnd_efree(*(char **) pDest);
DBG_VOID_RETURN;
}
static void
connect_attr_item_pdtor(void * pDest)
{
#ifdef ZTS
TSRMLS_FETCH();
#endif
DBG_ENTER("connect_attr_item_pdtor");
mnd_pefree(*(char **) pDest, 1);
DBG_VOID_RETURN;
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
enum mysqlnd_option option,
const char * const key,
const char * const value
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
goto end;
}
switch (option) {
case MYSQL_OPT_CONNECT_ATTR_ADD:
if (!conn->options->connect_attr) {
DBG_INF("Initializing connect_attr hash");
conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
if (!conn->options->connect_attr) {
goto oom;
}
zend_hash_init(conn->options->connect_attr, 0, NULL, conn->persistent? connect_attr_item_pdtor:connect_attr_item_edtor, conn->persistent);
}
DBG_INF_FMT("Adding [%s][%s]", key, value);
{
const char * copyv = mnd_pestrdup(value, conn->persistent);
if (!copyv) {
goto oom;
}
zend_hash_update(conn->options->connect_attr, key, strlen(key), ©v, sizeof(char *), NULL);
}
break;
default:
ret = FAIL;
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
DBG_RETURN(ret);
oom:
SET_OOM_ERROR(*conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
end:
DBG_RETURN(FAIL);
}
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::use_result");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
if (!conn->current_result) {
break;
}
if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
if (!result) {
conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
}
conn->current_result = NULL;
} while (0);
conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
}
DBG_RETURN(result);
}
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::store_result");
DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
unsigned int f = flags;
if (!conn->current_result) {
break;
}
if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
if ((conn->m->get_client_api_capabilities(conn TSRMLS_CC) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
if (MYSQLND_G(fetch_data_copy)) {
f &= ~MYSQLND_STORE_NO_COPY;
f |= MYSQLND_STORE_COPY;
}
} else {
if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
f |= MYSQLND_STORE_COPY;
}
}
if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
DBG_ERR("Unknown fetch mode");
break;
}
result = conn->current_result->m.store_result(conn->current_result, conn, f TSRMLS_CC);
if (!result) {
conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
}
conn->current_result = NULL;
} while (0);
conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
}
DBG_RETURN(result);
}
static void
MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
zval * return_value TSRMLS_DC ZEND_FILE_LINE_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
DBG_VOID_RETURN;
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_autocommit");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
}
static void
MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode TSRMLS_DC)
{
if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
if (str->len) {
smart_str_appendl(str, " ", sizeof(" ") - 1);
}
smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
} else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
if (str->len) {
smart_str_appendl(str, " ", sizeof(" ") - 1);
}
smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
}
if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
if (str->len) {
smart_str_appendl(str, " ", sizeof(" ") - 1);
}
smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
} else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
if (str->len) {
smart_str_appendl(str, " ", sizeof(" ") - 1);
}
smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
}
smart_str_0(str);
}
static char *
mysqlnd_escape_string_for_tx_name_in_comment(const char * const name TSRMLS_DC)
{
char * ret = NULL;
DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
if (name) {
zend_bool warned = FALSE;
const char * p_orig = name;
char * p_copy;
p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1);
*p_copy++ = ' ';
*p_copy++ = '/';
*p_copy++ = '*';
while (1) {
register char v = *p_orig;
if (v == 0) {
break;
}
if ((v >= '0' && v <= '9') ||
(v >= 'a' && v <= 'z') ||
(v >= 'A' && v <= 'Z') ||
v == '-' ||
v == '_' ||
v == ' ' ||
v == '=')
{
*p_copy++ = v;
} else if (warned == FALSE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
warned = TRUE;
}
++p_orig;
}
*p_copy++ = '*';
*p_copy++ = '/';
*p_copy++ = 0;
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
smart_str tmp_str = {0, 0, 0};
conn->m->tx_cor_options_to_string(conn, &tmp_str, flags TSRMLS_CC);
smart_str_0(&tmp_str);
{
char * query;
size_t query_len;
char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
smart_str_free(&tmp_str);
if (name_esc) {
mnd_efree(name_esc);
name_esc = NULL;
}
if (!query) {
SET_OOM_ERROR(*conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len TSRMLS_CC);
mnd_sprintf_free(query);
}
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_begin");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
smart_str tmp_str = {0, 0, 0};
if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
if (tmp_str.len) {
smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
}
smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
}
if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) {
unsigned long server_version = conn->m->get_server_version(conn TSRMLS_CC);
if (server_version < 50605L) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
smart_str_free(&tmp_str);
break;
} else if (mode & TRANS_START_READ_WRITE) {
if (tmp_str.len) {
smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
}
smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
} else if (mode & TRANS_START_READ_ONLY) {
if (tmp_str.len) {
smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
}
smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
}
}
smart_str_0(&tmp_str);
{
char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
char * query;
unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
smart_str_free(&tmp_str);
if (name_esc) {
mnd_efree(name_esc);
name_esc = NULL;
}
if (!query) {
SET_OOM_ERROR(*conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len TSRMLS_CC);
mnd_sprintf_free(query);
}
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
char * query;
unsigned int query_len;
if (!name) {
SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
if (!query) {
SET_OOM_ERROR(*conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len TSRMLS_CC);
mnd_sprintf_free(query);
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
char * query;
unsigned int query_len;
if (!name) {
SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
if (!query) {
SET_OOM_ERROR(*conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len TSRMLS_CC);
mnd_sprintf_free(query);
} while (0);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
}
DBG_RETURN(ret);
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
{
unsigned int ret = 0;
DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
if (conn) {
ret = conn->client_api_capabilities;
conn->client_api_capabilities = flags;
}
DBG_RETURN(ret);
}
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
DBG_RETURN(conn? conn->client_api_capabilities : 0);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC)
{
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::local_tx_start");
DBG_RETURN(ret);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::local_tx_end");
DBG_RETURN(status);
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn_data::init");
mysqlnd_stats_init(&conn->stats, STAT_LAST);
SET_ERROR_AFF_ROWS(conn);
conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info TSRMLS_CC);
conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
}
MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, init),
MYSQLND_METHOD(mysqlnd_conn_data, connect),
MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
MYSQLND_METHOD(mysqlnd_conn_data, query),
MYSQLND_METHOD(mysqlnd_conn_data, send_query),
MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
MYSQLND_METHOD(mysqlnd_conn_data, use_result),
MYSQLND_METHOD(mysqlnd_conn_data, store_result),
MYSQLND_METHOD(mysqlnd_conn_data, next_result),
MYSQLND_METHOD(mysqlnd_conn_data, more_results),
_mysqlnd_stmt_init,
MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
MYSQLND_METHOD(mysqlnd_conn_data, refresh),
MYSQLND_METHOD(mysqlnd_conn_data, ping),
MYSQLND_METHOD(mysqlnd_conn_data, kill),
MYSQLND_METHOD(mysqlnd_conn_data, select_db),
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
MYSQLND_METHOD(mysqlnd_conn_data, change_user),
MYSQLND_METHOD(mysqlnd_conn_data, errno),
MYSQLND_METHOD(mysqlnd_conn_data, error),
MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
MYSQLND_METHOD(mysqlnd_conn_data, statistic),
MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
MYSQLND_METHOD(mysqlnd_conn_data, info),
MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
MYSQLND_METHOD(mysqlnd_conn_data, list_method),
MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
MYSQLND_METHOD(mysqlnd_conn_data, field_count),
MYSQLND_METHOD(mysqlnd_conn_data, server_status),
MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
MYSQLND_METHOD(mysqlnd_conn_data, free_options),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
mysqlnd_query_read_result_set_header,
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
MYSQLND_METHOD(mysqlnd_conn_data, send_close),
MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
mysqlnd_result_init,
MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
MYSQLND_CLASS_METHODS_END;
static MYSQLND *
MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn TSRMLS_DC)
{
MYSQLND * ret;
DBG_ENTER("mysqlnd_conn::get_reference");
ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn TSRMLS_CC);
DBG_RETURN(ret);
}
static void
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
{
DBG_ENTER("mysqlnd_conn::dtor");
DBG_INF_FMT("conn=%llu", conn->data->thread_id);
conn->data->m->free_reference(conn->data TSRMLS_CC);
mnd_pefree(conn, conn->persistent);
DBG_VOID_RETURN;
}
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
MYSQLND_CONN_DATA * conn = conn_handle->data;
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn::close");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
if (CONN_GET_STATE(conn) >= CONN_READY) {
static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
STAT_CLOSE_EXPLICIT,
STAT_CLOSE_IMPLICIT,
STAT_CLOSE_DISCONNECT
};
MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
}
ret = conn->m->send_close(conn TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
conn_handle->m->dtor(conn_handle TSRMLS_CC);
}
DBG_RETURN(ret);
}
MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
MYSQLND_METHOD(mysqlnd_conn, connect),
MYSQLND_METHOD(mysqlnd_conn, clone_object),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
MYSQLND_METHOD(mysqlnd_conn, close)
MYSQLND_CLASS_METHODS_END;
PHPAPI MYSQLND *
_mysqlnd_init(unsigned int flags, zend_bool persistent TSRMLS_DC)
{
MYSQLND * ret;
DBG_ENTER("mysqlnd_init");
ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC);
if (ret && ret->data) {
ret->data->m->negotiate_client_api_capabilities(ret->data, flags TSRMLS_CC);
}
DBG_RETURN(ret);
}