class Mysql2::Client
Attributes
Public Class Methods
# File lib/mysql2/client.rb, line 73 def self.default_query_options @@default_query_options end
Escape string so that it may be used in a SQL statement. Note
that this escape method is not connection encoding aware. If you need
encoding support use #escape
instead.
static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
unsigned char *newStr;
VALUE rb_str;
unsigned long newLen, oldLen;
Check_Type(str, T_STRING);
oldLen = RSTRING_LEN(str);
newStr = xmalloc(oldLen*2+1);
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
if (newLen == oldLen) {
/* no need to return a new ruby string if nothing changed */
xfree(newStr);
return str;
} else {
rb_str = rb_str_new((const char*)newStr, newLen);
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_copy(rb_str, str);
#endif
xfree(newStr);
return rb_str;
}
}
Returns a string that represents the client library version.
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
VALUE version_info, version, header_version;
version_info = rb_hash_new();
version = rb_str_new2(mysql_get_client_info());
header_version = rb_str_new2(MYSQL_LINK_VERSION);
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_associate(version, rb_usascii_encoding());
rb_enc_associate(header_version, rb_usascii_encoding());
#endif
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
rb_hash_aset(version_info, sym_version, version);
rb_hash_aset(version_info, sym_header_version, header_version);
return version_info;
}
# File lib/mysql2/client.rb, line 18 def initialize(opts = {}) opts = Mysql2::Util.key_hash_as_symbols( opts ) @read_timeout = nil @query_options = @@default_query_options.dup @query_options.merge! opts initialize_ext # Set default connect_timeout to avoid unlimited retries from signal interruption opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout) [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key| next unless opts.key?(key) case key when :reconnect, :local_infile, :secure_auth send(:"#{key}=", !!opts[key]) when :connect_timeout, :read_timeout, :write_timeout send(:"#{key}=", opts[key].to_i) else send(:"#{key}=", opts[key]) end end # force the encoding to utf8 self.charset_name = opts[:encoding] || 'utf8' ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher) ssl_set(*ssl_options) if ssl_options.any? if [:user,:pass,:hostname,:dbname,:db,:sock].any?{|k| @query_options.has_key?(k) } warn "============= WARNING FROM mysql2 =============" warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be deprecated at some point in the future." warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options." warn "============= END WARNING FROM mysql2 =========" end user = opts[:username] || opts[:user] pass = opts[:password] || opts[:pass] host = opts[:host] || opts[:hostname] port = opts[:port] database = opts[:database] || opts[:dbname] || opts[:db] socket = opts[:socket] || opts[:sock] flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags] # Correct the data types before passing these values down to the C level user = user.to_s unless user.nil? pass = pass.to_s unless pass.nil? host = host.to_s unless host.nil? port = port.to_i unless port.nil? database = database.to_s unless database.nil? socket = socket.to_s unless socket.nil? connect user, pass, host, port, database, socket, flags end
Private Class Methods
# File lib/mysql2/client.rb, line 102 def self.local_offset ::Time.local(2010).utc_offset.to_r / 86400 end
Public Instance Methods
When using MULTI_STATEMENTS support, calling this will throw away any unprocessed results as fast as it can in order to put the connection back into a state where queries can be issued again.
static VALUE rb_mysql_client_abandon_results(VALUE self) {
MYSQL_RES *result;
int ret;
GET_CLIENT(self);
while (mysql_more_results(wrapper->client) == 1) {
ret = mysql_next_result(wrapper->client);
if (ret > 0) {
rb_raise_mysql2_error(wrapper);
}
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
if (result != NULL) {
mysql_free_result(result);
}
}
return Qnil;
}
returns the number of rows changed, deleted, or inserted by the last statement if it was an UPDATE, DELETE, or INSERT.
static VALUE rb_mysql_client_affected_rows(VALUE self) {
my_ulonglong retVal;
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
retVal = mysql_affected_rows(wrapper->client);
if (retVal == (my_ulonglong)-1) {
rb_raise_mysql2_error(wrapper);
}
return ULL2NUM(retVal);
}
Returns the result for the last async issued query.
static VALUE rb_mysql_client_async_result(VALUE self) {
MYSQL_RES * result;
VALUE resultObj;
VALUE current, is_streaming;
GET_CLIENT(self);
/* if we're not waiting on a result, do nothing */
if (NIL_P(wrapper->active_thread))
return Qnil;
REQUIRE_CONNECTED(wrapper);
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
/* an error occurred, mark this connection inactive */
MARK_CONN_INACTIVE(self);
return rb_raise_mysql2_error(wrapper);
}
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
if (is_streaming == Qtrue) {
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
} else {
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
}
if (result == NULL) {
if (mysql_errno(wrapper->client) != 0) {
MARK_CONN_INACTIVE(self);
rb_raise_mysql2_error(wrapper);
}
/* no data and no error, so query was not a SELECT */
return Qnil;
}
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
RB_GC_GUARD(current);
Check_Type(current, T_HASH);
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
return resultObj;
}
Immediately disconnect from the server, normally the garbage collector will disconnect automatically when a connection is no longer needed. Explicitly closing this will free up server resources sooner than waiting for the garbage collector.
static VALUE rb_mysql_client_close(VALUE self) {
GET_CLIENT(self);
if (wrapper->connected) {
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
}
return Qnil;
}
Returns the encoding set on the client.
static VALUE rb_mysql_client_encoding(VALUE self) {
GET_CLIENT(self);
return wrapper->encoding;
}
Escape string so that it may be used in a SQL statement.
static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
unsigned char *newStr;
VALUE rb_str;
unsigned long newLen, oldLen;
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc;
rb_encoding *conn_enc;
#endif
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
Check_Type(str, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H
default_internal_enc = rb_default_internal_encoding();
conn_enc = rb_to_encoding(wrapper->encoding);
/* ensure the string is in the encoding the connection is expecting */
str = rb_str_export_to_enc(str, conn_enc);
#endif
oldLen = RSTRING_LEN(str);
newStr = xmalloc(oldLen*2+1);
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
if (newLen == oldLen) {
/* no need to return a new ruby string if nothing changed */
#ifdef HAVE_RUBY_ENCODING_H
if (default_internal_enc) {
str = rb_str_export_to_enc(str, default_internal_enc);
}
#endif
xfree(newStr);
return str;
} else {
rb_str = rb_str_new((const char*)newStr, newLen);
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_associate(rb_str, conn_enc);
if (default_internal_enc) {
rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
}
#endif
xfree(newStr);
return rb_str;
}
}
# File lib/mysql2/client.rb, line 97 def info self.class.info end
Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement.
static VALUE rb_mysql_client_last_id(VALUE self) {
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
return ULL2NUM(mysql_insert_id(wrapper->client));
}
Returns true or false if there are more results to process.
static VALUE rb_mysql_client_more_results(VALUE self)
{
GET_CLIENT(self);
if (mysql_more_results(wrapper->client) == 0)
return Qfalse;
else
return Qtrue;
}
Fetch the next result set from the server. Returns nothing.
static VALUE rb_mysql_client_next_result(VALUE self)
{
int ret;
GET_CLIENT(self);
ret = mysql_next_result(wrapper->client);
if (ret > 0) {
rb_raise_mysql2_error(wrapper);
return Qfalse;
} else if (ret == 0) {
return Qtrue;
} else {
return Qfalse;
}
}
Checks whether the connection to the server is working. If the connection has gone down and auto-reconnect is enabled an attempt to reconnect is made. If the connection is down and auto-reconnect is disabled, ping returns an error.
static VALUE rb_mysql_client_ping(VALUE self) {
GET_CLIENT(self);
if (!wrapper->connected) {
return Qfalse;
} else {
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
}
}
# File lib/mysql2/client.rb, line 78 def query(sql, options = {}) Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do _query(sql, @query_options.merge(options)) end end
# File lib/mysql2/client.rb, line 89 def query_info info = query_info_string return {} unless info info_hash = {} info.split.each_slice(2) { |s| info_hash[s[0].downcase.delete(':').to_sym] = s[1].to_i } info_hash end
static VALUE rb_mysql_info(VALUE self) {
const char *info;
VALUE rb_str;
GET_CLIENT(self);
info = mysql_info(wrapper->client);
if (info == NULL) {
return Qnil;
}
rb_str = rb_str_new2(info);
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_associate(rb_str, rb_utf8_encoding());
#endif
return rb_str;
}
Enable or disable the automatic reconnect behavior of libmysql. Read dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html for more information.
static VALUE set_reconnect(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
}
Causes the database specified by name to become the default
(current) database on the connection specified by mysql.
static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
{
struct nogvl_select_db_args args;
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
args.mysql = wrapper->client;
args.db = StringValueCStr(db);
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
rb_raise_mysql2_error(wrapper);
return db;
}
Returns a string that represents the server version number
static VALUE rb_mysql_client_server_info(VALUE self) {
VALUE version, server_info;
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc;
rb_encoding *conn_enc;
#endif
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
#ifdef HAVE_RUBY_ENCODING_H
default_internal_enc = rb_default_internal_encoding();
conn_enc = rb_to_encoding(wrapper->encoding);
#endif
version = rb_hash_new();
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_associate(server_info, conn_enc);
if (default_internal_enc) {
server_info = rb_str_export_to_enc(server_info, default_internal_enc);
}
#endif
rb_hash_aset(version, sym_version, server_info);
return version;
}
Return the file descriptor number for this client.
static VALUE rb_mysql_client_socket(VALUE self) {
#ifndef _WIN32
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
return INT2NUM(wrapper->client->net.fd);
#else
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
#endif
}
Return the next result object from a query which yielded multiple result sets.
static VALUE rb_mysql_client_store_result(VALUE self)
{
MYSQL_RES * result;
VALUE resultObj;
VALUE current;
GET_CLIENT(self);
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
if (result == NULL) {
if (mysql_errno(wrapper->client) != 0) {
rb_raise_mysql2_error(wrapper);
}
/* no data and no error, so query was not a SELECT */
return Qnil;
}
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
RB_GC_GUARD(current);
Check_Type(current, T_HASH);
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
return resultObj;
}
Returns the thread ID of the current connection.
static VALUE rb_mysql_client_thread_id(VALUE self) {
unsigned long retVal;
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
retVal = mysql_thread_id(wrapper->client);
return ULL2NUM(retVal);
}
static VALUE rb_mysql_client_warning_count(VALUE self) {
unsigned int warning_count;
GET_CLIENT(self);
warning_count = mysql_warning_count(wrapper->client);
return UINT2NUM(warning_count);
}
Private Instance Methods
Query the database with sql, with optional
options. For the possible options, see
@@default_query_options on the Mysql2::Client
class.
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
#ifndef _WIN32
struct async_query_args async_args;
#endif
struct nogvl_send_query_args args;
VALUE thread_current = rb_thread_current();
GET_CLIENT(self);
REQUIRE_CONNECTED(wrapper);
args.mysql = wrapper->client;
RB_GC_GUARD(current);
Check_Type(current, T_HASH);
rb_iv_set(self, "@current_query_options", current);
Check_Type(sql, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H
/* ensure the string is in the encoding the connection is expecting */
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
#else
args.sql = sql;
#endif
args.sql_ptr = RSTRING_PTR(args.sql);
args.sql_len = RSTRING_LEN(args.sql);
/* see if this connection is still waiting on a result from a previous query */
if (NIL_P(wrapper->active_thread)) {
/* mark this connection active */
wrapper->active_thread = thread_current;
} else if (wrapper->active_thread == thread_current) {
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
} else {
VALUE inspect = rb_inspect(wrapper->active_thread);
const char *thr = StringValueCStr(inspect);
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
RB_GC_GUARD(inspect);
}
args.wrapper = wrapper;
#ifndef _WIN32
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
if (rb_hash_aref(current, sym_async) == Qtrue) {
return Qnil;
} else {
async_args.fd = wrapper->client->net.fd;
async_args.self = self;
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
return rb_mysql_client_async_result(self);
}
#else
do_send_query(&args);
/* this will just block until the result is ready */
return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
#endif
}
static VALUE set_charset_name(VALUE self, VALUE value) {
char *charset_name;
#ifdef HAVE_RUBY_ENCODING_H
size_t charset_name_len;
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
rb_encoding *enc;
VALUE rb_enc;
#endif
GET_CLIENT(self);
charset_name = RSTRING_PTR(value);
#ifdef HAVE_RUBY_ENCODING_H
charset_name_len = RSTRING_LEN(value);
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
VALUE inspect = rb_inspect(value);
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
} else {
enc = rb_enc_find(mysql2rb->rb_name);
rb_enc = rb_enc_from_encoding(enc);
wrapper->encoding = rb_enc;
}
#endif
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
/* TODO: warning - unable to set charset */
rb_warn("%s\n", mysql_error(wrapper->client));
}
return value;
}
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
struct nogvl_connect_args args;
time_t start_time, end_time;
unsigned int elapsed_time, connect_timeout;
VALUE rv;
GET_CLIENT(self);
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
args.port = NIL_P(port) ? 0 : NUM2INT(port);
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
args.mysql = wrapper->client;
args.client_flag = NUM2ULONG(flags);
if (wrapper->connect_timeout)
time(&start_time);
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
if (rv == Qfalse) {
while (rv == Qfalse && errno == EINTR) {
if (wrapper->connect_timeout) {
time(&end_time);
/* avoid long connect timeout from system time changes */
if (end_time < start_time)
start_time = end_time;
elapsed_time = end_time - start_time;
/* avoid an early timeout due to time truncating milliseconds off the start time */
if (elapsed_time > 0)
elapsed_time--;
if (elapsed_time >= wrapper->connect_timeout)
break;
connect_timeout = wrapper->connect_timeout - elapsed_time;
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
}
errno = 0;
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
}
/* restore the connect timeout for reconnecting */
if (wrapper->connect_timeout)
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
if (rv == Qfalse)
return rb_raise_mysql2_error(wrapper);
}
wrapper->server_version = mysql_get_server_version(wrapper->client);
wrapper->connected = 1;
return self;
}
static VALUE set_connect_timeout(VALUE self, VALUE value) {
long int sec;
Check_Type(value, T_FIXNUM);
sec = FIX2INT(value);
if (sec < 0) {
rb_raise(cMysql2Error, "connect_timeout must be a positive integer, you passed %ld", sec);
}
return _mysql_client_options(self, MYSQL_OPT_CONNECT_TIMEOUT, value);
}
static VALUE set_read_default_file(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_READ_DEFAULT_FILE, value);
}
static VALUE set_read_default_group(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_READ_DEFAULT_GROUP, value);
}
static VALUE set_init_command(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
}
static VALUE initialize_ext(VALUE self) {
GET_CLIENT(self);
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
/* TODO: warning - not enough memory? */
return rb_raise_mysql2_error(wrapper);
}
wrapper->initialized = 1;
return self;
}
static VALUE set_local_infile(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_OPT_LOCAL_INFILE, value);
}
static VALUE set_read_timeout(VALUE self, VALUE value) {
long int sec;
Check_Type(value, T_FIXNUM);
sec = FIX2INT(value);
if (sec < 0) {
rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
}
/* Set the instance variable here even though _mysql_client_options
might not succeed, because the timeout is used in other ways
elsewhere */
rb_iv_set(self, "@read_timeout", value);
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
}
static VALUE set_secure_auth(VALUE self, VALUE value) {
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
}
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
GET_CLIENT(self);
mysql_ssl_set(wrapper->client,
NIL_P(key) ? NULL : StringValueCStr(key),
NIL_P(cert) ? NULL : StringValueCStr(cert),
NIL_P(ca) ? NULL : StringValueCStr(ca),
NIL_P(capath) ? NULL : StringValueCStr(capath),
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
return self;
}
static VALUE set_write_timeout(VALUE self, VALUE value) {
long int sec;
Check_Type(value, T_FIXNUM);
sec = FIX2INT(value);
if (sec < 0) {
rb_raise(cMysql2Error, "write_timeout must be a positive integer, you passed %ld", sec);
}
return _mysql_client_options(self, MYSQL_OPT_WRITE_TIMEOUT, value);
}