Class: Mysql2::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/mysql2/client.rb,
ext/mysql2/client.c

Direct Known Subclasses

EM::Client

Constant Summary collapse

@@default_query_options =
{
  :as => :hash,                   # the type of object you want each row back as; also supports :array (an array of values)
  :async => false,                # don't wait for a result after sending the query, you'll have to monitor the socket yourself then eventually call Mysql2::Client#async_result
  :cast_booleans => false,        # cast tinyint(1) fields as true/false in ruby
  :symbolize_keys => false,       # return field names as symbols instead of strings
  :database_timezone => :local,   # timezone Mysql2 will assume datetime objects are stored in
  :application_timezone => nil,   # timezone Mysql2 will convert to before handing the object back to the caller
  :cache_rows => true,            # tells Mysql2 to use it's internal row cache for results
  :connect_flags => REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION,
  :cast => true,
  :default_file => nil,
  :default_group => nil
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Client

Returns a new instance of Client.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 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

Instance Attribute Details

#query_optionsObject (readonly)

Returns the value of attribute query_options.



3
4
5
# File 'lib/mysql2/client.rb', line 3

def query_options
  @query_options
end

#read_timeoutObject (readonly)

Returns the value of attribute read_timeout.



3
4
5
# File 'lib/mysql2/client.rb', line 3

def read_timeout
  @read_timeout
end

Class Method Details

.default_query_optionsObject



73
74
75
# File 'lib/mysql2/client.rb', line 73

def self.default_query_options
  @@default_query_options
end

.Mysql2::Client.escape(string) ⇒ Object

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 Mysql2::Client#escape instead.



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'ext/mysql2/client.c', line 280

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, StringValuePtr(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;
  }
}

Instance Method Details

#abandon_results!Object

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.



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
# File 'ext/mysql2/client.c', line 609

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;
}

#affected_rowsObject

returns the number of rows changed, deleted, or inserted by the last statement if it was an UPDATE, DELETE, or INSERT.



941
942
943
944
945
946
947
948
949
950
951
# File 'ext/mysql2/client.c', line 941

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);
}

#async_resultObject

Returns the result for the last async issued query.



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'ext/mysql2/client.c', line 468

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;
}

#closeObject

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.



389
390
391
392
393
394
395
396
397
# File 'ext/mysql2/client.c', line 389

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;
}

#encodingObject

Returns the encoding set on the client.



1094
1095
1096
1097
# File 'ext/mysql2/client.c', line 1094

static VALUE rb_mysql_client_encoding(VALUE self) {
  GET_CLIENT(self);
  return wrapper->encoding;
}

#escape(string) ⇒ Object

Escape string so that it may be used in a SQL statement.



717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
# File 'ext/mysql2/client.c', line 717

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, StringValuePtr(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_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;
  }
}

#infoObject

Returns a string that represents the client library version.



846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
# File 'ext/mysql2/client.c', line 846

static VALUE rb_mysql_client_info(VALUE self) {
  VALUE version, client_info;
#ifdef HAVE_RUBY_ENCODING_H
  rb_encoding *default_internal_enc;
  rb_encoding *conn_enc;
  GET_CLIENT(self);
#endif
  version = rb_hash_new();

#ifdef HAVE_RUBY_ENCODING_H
  default_internal_enc = rb_default_internal_encoding();
  conn_enc = rb_to_encoding(wrapper->encoding);
#endif

  rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
  client_info = rb_str_new2(mysql_get_client_info());
#ifdef HAVE_RUBY_ENCODING_H
  rb_enc_associate(client_info, conn_enc);
  if (default_internal_enc) {
    client_info = rb_str_export_to_enc(client_info, default_internal_enc);
  }
#endif
  rb_hash_aset(version, sym_version, client_info);
  return version;
}

#last_idObject

Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement.



929
930
931
932
933
# File 'ext/mysql2/client.c', line 929

static VALUE rb_mysql_client_last_id(VALUE self) {
  GET_CLIENT(self);
  REQUIRE_CONNECTED(wrapper);
  return ULL2NUM(mysql_insert_id(wrapper->client));
}

#more_results?Boolean

Returns true or false if there are more results to process.

Returns:

  • (Boolean)


1027
1028
1029
1030
1031
1032
1033
1034
# File 'ext/mysql2/client.c', line 1027

static VALUE rb_mysql_client_more_results(VALUE self)
{
  GET_CLIENT(self);
    if (mysql_more_results(wrapper->client) == 0)
      return Qfalse;
    else
      return Qtrue;
}

#next_resultObject

Fetch the next result set from the server. Returns nothing.



1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
# File 'ext/mysql2/client.c', line 1042

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;
    }
}

#pingObject

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.



1012
1013
1014
1015
1016
1017
1018
1019
1020
# File 'ext/mysql2/client.c', line 1012

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);
  }
}

#query(sql, options = {}) ⇒ Object

Query the database with sql, with optional options. For the possible options, see @@default_query_options on the Mysql2::Client class.



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
# File 'ext/mysql2/client.c', line 637

static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
#ifndef _WIN32
  struct async_query_args async_args;
#endif
  struct nogvl_send_query_args args;
  int async = 0;
  VALUE opts, current;
  VALUE thread_current = rb_thread_current();
#ifdef HAVE_RUBY_ENCODING_H
  rb_encoding *conn_enc;
#endif
  GET_CLIENT(self);

  REQUIRE_CONNECTED(wrapper);
  args.mysql = wrapper->client;

  current = rb_hash_dup(rb_iv_get(self, "@query_options"));
  RB_GC_GUARD(current);
  Check_Type(current, T_HASH);
  rb_iv_set(self, "@current_query_options", current);

  if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
    rb_funcall(current, intern_merge_bang, 1, opts);

    if (rb_hash_aref(current, sym_async) == Qtrue) {
      async = 1;
    }
  }

  Check_Type(args.sql, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H
  conn_enc = rb_to_encoding(wrapper->encoding);
  /* ensure the string is in the encoding the connection is expecting */
  args.sql = rb_str_export_to_enc(args.sql, conn_enc);
#endif
  args.sql_ptr = StringValuePtr(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 (!async) {
    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 {
    return Qnil;
  }
#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
}

#query_infoObject



77
78
79
80
81
82
83
# File 'lib/mysql2/client.rb', line 77

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

#query_info_stringObject



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'ext/mysql2/client.c', line 314

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;
}

#reconnect=(true) ⇒ Object

Enable or disable the automatic reconnect behavior of libmysql. Read dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html for more information.



1107
1108
1109
# File 'ext/mysql2/client.c', line 1107

static VALUE set_reconnect(VALUE self, VALUE value) {
  return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
}

#select_db(name) ⇒ Object

Causes the database specified by name to become the default (current) database on the connection specified by mysql.



982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
# File 'ext/mysql2/client.c', line 982

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 = StringValuePtr(db);

  if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
    rb_raise_mysql2_error(wrapper);

  return db;
}

#server_infoObject

Returns a string that represents the server version number



877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
# File 'ext/mysql2/client.c', line 877

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;
}

#socketObject

Return the file descriptor number for this client.



909
910
911
912
913
914
915
916
917
918
919
920
921
# File 'ext/mysql2/client.c', line 909

static VALUE rb_mysql_client_socket(VALUE self) {
  GET_CLIENT(self);
#ifndef _WIN32
  {
    int fd_set_fd;
    REQUIRE_CONNECTED(wrapper);
    fd_set_fd = wrapper->client->net.fd;
    return INT2NUM(fd_set_fd);
  }
#else
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
#endif
}

#store_resultObject

Return the next result object from a query which yielded multiple result sets.



1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
# File 'ext/mysql2/client.c', line 1063

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;
}

#thread_idObject

Returns the thread ID of the current connection.



958
959
960
961
962
963
964
965
# File 'ext/mysql2/client.c', line 958

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);
}

#warning_countObject



305
306
307
308
309
310
311
312
# File 'ext/mysql2/client.c', line 305

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);
}