Class: Mysql2::Client

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

Direct Known Subclasses

EM::Client, Fibered::Client

Constant Summary collapse

CHARSET_MAP =
{
  "armscii8" => nil,
  "ascii"    => Encoding::US_ASCII,
  "big5"     => Encoding::Big5,
  "binary"   => Encoding::ASCII_8BIT,
  "cp1250"   => Encoding::Windows_1250,
  "cp1251"   => Encoding::Windows_1251,
  "cp1256"   => Encoding::Windows_1256,
  "cp1257"   => Encoding::Windows_1257,
  "cp850"    => Encoding::CP850,
  "cp852"    => Encoding::CP852,
  "cp866"    => Encoding::IBM866,
  "cp932"    => Encoding::Windows_31J,
  "dec8"     => nil,
  "eucjpms"  => Encoding::EucJP_ms,
  "euckr"    => Encoding::EUC_KR,
  "gb2312"   => Encoding::EUC_CN,
  "gbk"      => Encoding::GBK,
  "geostd8"  => nil,
  "greek"    => Encoding::ISO_8859_7,
  "hebrew"   => Encoding::ISO_8859_8,
  "hp8"      => nil,
  "keybcs2"  => nil,
  "koi8r"    => Encoding::KOI8_R,
  "koi8u"    => Encoding::KOI8_U,
  "latin1"   => Encoding::ISO_8859_1,
  "latin2"   => Encoding::ISO_8859_2,
  "latin5"   => Encoding::ISO_8859_9,
  "latin7"   => Encoding::ISO_8859_13,
  "macce"    => Encoding::MacCentEuro,
  "macroman" => Encoding::MacRoman,
  "sjis"     => Encoding::SHIFT_JIS,
  "swe7"     => nil,
  "tis620"   => Encoding::TIS_620,
  "ucs2"     => Encoding::UTF_16BE,
  "ujis"     => Encoding::EucJP_ms,
  "utf8"     => Encoding::UTF_8,
}
MYSQL_CHARSET_MAP =
{
  1 => {:name => "big5",      :collation => "big5_chinese_ci"},
  2 => {:name => "latin2",    :collation => "latin2_czech_cs"},
  3 => {:name => "dec8",      :collation => "dec8_swedish_ci"},
  4 => {:name => "cp850",     :collation => "cp850_general_ci"},
  5 => {:name => "latin1",    :collation => "latin1_german1_ci"},
  6 => {:name => "hp8",       :collation => "hp8_english_ci"},
  7 => {:name => "koi8r",     :collation => "koi8r_general_ci"},
  8 => {:name => "latin1",    :collation => "latin1_swedish_ci"},
  9 => {:name => "latin2",    :collation => "latin2_general_ci"},
  10 => {:name => "swe7",     :collation => "swe7_swedish_ci"},
  11 => {:name => "ascii",    :collation => "ascii_general_ci"},
  12 => {:name => "ujis",     :collation => "ujis_japanese_ci"},
  13 => {:name => "sjis",     :collation => "sjis_japanese_ci"},
  14 => {:name => "cp1251",   :collation => "cp1251_bulgarian_ci"},
  15 => {:name => "latin1",   :collation => "latin1_danish_ci"},
  16 => {:name => "hebrew",   :collation => "hebrew_general_ci"},
  17 => {:name => "filename", :collation => "filename"},
  18 => {:name => "tis620",   :collation => "tis620_thai_ci"},
  19 => {:name => "euckr",    :collation => "euckr_korean_ci"},
  20 => {:name => "latin7",   :collation => "latin7_estonian_cs"},
  21 => {:name => "latin2",   :collation => "latin2_hungarian_ci"},
  22 => {:name => "koi8u",    :collation => "koi8u_general_ci"},
  23 => {:name => "cp1251",   :collation => "cp1251_ukrainian_ci"},
  24 => {:name => "gb2312",   :collation => "gb2312_chinese_ci"},
  25 => {:name => "greek",    :collation => "greek_general_ci"},
  26 => {:name => "cp1250",   :collation => "cp1250_general_ci"},
  27 => {:name => "latin2",   :collation => "latin2_croatian_ci"},
  28 => {:name => "gbk",      :collation => "gbk_chinese_ci"},
  29 => {:name => "cp1257",   :collation => "cp1257_lithuanian_ci"},
  30 => {:name => "latin5",   :collation => "latin5_turkish_ci"},
  31 => {:name => "latin1",   :collation => "latin1_german2_ci"},
  32 => {:name => "armscii8", :collation => "armscii8_general_ci"},
  33 => {:name => "utf8",     :collation => "utf8_general_ci"},
  34 => {:name => "cp1250",   :collation => "cp1250_czech_cs"},
  35 => {:name => "ucs2",     :collation => "ucs2_general_ci"},
  36 => {:name => "cp866",    :collation => "cp866_general_ci"},
  37 => {:name => "keybcs2",  :collation => "keybcs2_general_ci"},
  38 => {:name => "macce",    :collation => "macce_general_ci"},
  39 => {:name => "macroman", :collation => "macroman_general_ci"},
  40 => {:name => "cp852",    :collation => "cp852_general_ci"},
  41 => {:name => "latin7",   :collation => "latin7_general_ci"},
  42 => {:name => "latin7",   :collation => "latin7_general_cs"},
  43 => {:name => "macce",    :collation => "macce_bin"},
  44 => {:name => "cp1250",   :collation => "cp1250_croatian_ci"},
  47 => {:name => "latin1",   :collation => "latin1_bin"},
  48 => {:name => "latin1",   :collation => "latin1_general_ci"},
  49 => {:name => "latin1",   :collation => "latin1_general_cs"},
  50 => {:name => "cp1251",   :collation => "cp1251_bin"},
  51 => {:name => "cp1251",   :collation => "cp1251_general_ci"},
  52 => {:name => "cp1251",   :collation => "cp1251_general_cs"},
  53 => {:name => "macroman", :collation => "macroman_bin"},
  57 => {:name => "cp1256",   :collation => "cp1256_general_ci"},
  58 => {:name => "cp1257",   :collation => "cp1257_bin"},
  59 => {:name => "cp1257",   :collation => "cp1257_general_ci"},
  63 => {:name => "binary",   :collation => "binary"},
  64 => {:name => "armscii8", :collation => "armscii8_bin"},
  65 => {:name => "ascii",    :collation => "ascii_bin"},
  66 => {:name => "cp1250",   :collation => "cp1250_bin"},
  67 => {:name => "cp1256",   :collation => "cp1256_bin"},
  68 => {:name => "cp866",    :collation => "cp866_bin"},
  69 => {:name => "dec8",     :collation => "dec8_bin"},
  70 => {:name => "greek",    :collation => "greek_bin"},
  71 => {:name => "hebrew",   :collation => "hebrew_bin"},
  72 => {:name => "hp8",      :collation => "hp8_bin"},
  73 => {:name => "keybcs2",  :collation => "keybcs2_bin"},
  74 => {:name => "koi8r",    :collation => "koi8r_bin"},
  75 => {:name => "koi8u",    :collation => "koi8u_bin"},
  77 => {:name => "latin2",   :collation => "latin2_bin"},
  78 => {:name => "latin5",   :collation => "latin5_bin"},
  79 => {:name => "latin7",   :collation => "latin7_bin"},
  80 => {:name => "cp850",    :collation => "cp850_bin"},
  81 => {:name => "cp852",    :collation => "cp852_bin"},
  82 => {:name => "swe7",     :collation => "swe7_bin"},
  83 => {:name => "utf8",     :collation => "utf8_bin"},
  84 => {:name => "big5",     :collation => "big5_bin"},
  85 => {:name => "euckr",    :collation => "euckr_bin"},
  86 => {:name => "gb2312",   :collation => "gb2312_bin"},
  87 => {:name => "gbk",      :collation => "gbk_bin"},
  88 => {:name => "sjis",     :collation => "sjis_bin"},
  89 => {:name => "tis620",   :collation => "tis620_bin"},
  90 => {:name => "ucs2",     :collation => "ucs2_bin"},
  91 => {:name => "ujis",     :collation => "ujis_bin"},
  92 => {:name => "geostd8",  :collation => "geostd8_general_ci"},
  93 => {:name => "geostd8",  :collation => "geostd8_bin"},
  94 => {:name => "latin1",   :collation => "latin1_spanish_ci"},
  95 => {:name => "cp932",    :collation => "cp932_japanese_ci"},
  96 => {:name => "cp932",    :collation => "cp932_bin"},
  97 => {:name => "eucjpms",  :collation => "eucjpms_japanese_ci"},
  98 => {:name => "eucjpms",  :collation => "eucjpms_bin"},
  99 => {:name => "cp1250",   :collation => "cp1250_polish_ci"},
  128 => {:name => "ucs2",    :collation => "ucs2_unicode_ci"},
  129 => {:name => "ucs2",    :collation => "ucs2_icelandic_ci"},
  130 => {:name => "ucs2",    :collation => "ucs2_latvian_ci"},
  131 => {:name => "ucs2",    :collation => "ucs2_romanian_ci"},
  132 => {:name => "ucs2",    :collation => "ucs2_slovenian_ci"},
  133 => {:name => "ucs2",    :collation => "ucs2_polish_ci"},
  134 => {:name => "ucs2",    :collation => "ucs2_estonian_ci"},
  135 => {:name => "ucs2",    :collation => "ucs2_spanish_ci"},
  136 => {:name => "ucs2",    :collation => "ucs2_swedish_ci"},
  137 => {:name => "ucs2",    :collation => "ucs2_turkish_ci"},
  138 => {:name => "ucs2",    :collation => "ucs2_czech_ci"},
  139 => {:name => "ucs2",    :collation => "ucs2_danish_ci"},
  140 => {:name => "ucs2",    :collation => "ucs2_lithuanian_ci"},
  141 => {:name => "ucs2",    :collation => "ucs2_slovak_ci"},
  142 => {:name => "ucs2",    :collation => "ucs2_spanish2_ci"},
  143 => {:name => "ucs2",    :collation => "ucs2_roman_ci"},
  144 => {:name => "ucs2",    :collation => "ucs2_persian_ci"},
  145 => {:name => "ucs2",    :collation => "ucs2_esperanto_ci"},
  146 => {:name => "ucs2",    :collation => "ucs2_hungarian_ci"},
  192 => {:name => "utf8",    :collation => "utf8_unicode_ci"},
  193 => {:name => "utf8",    :collation => "utf8_icelandic_ci"},
  194 => {:name => "utf8",    :collation => "utf8_latvian_ci"},
  195 => {:name => "utf8",    :collation => "utf8_romanian_ci"},
  196 => {:name => "utf8",    :collation => "utf8_slovenian_ci"},
  197 => {:name => "utf8",    :collation => "utf8_polish_ci"},
  198 => {:name => "utf8",    :collation => "utf8_estonian_ci"},
  199 => {:name => "utf8",    :collation => "utf8_spanish_ci"},
  200 => {:name => "utf8",    :collation => "utf8_swedish_ci"},
  201 => {:name => "utf8",    :collation => "utf8_turkish_ci"},
  202 => {:name => "utf8",    :collation => "utf8_czech_ci"},
  203 => {:name => "utf8",    :collation => "utf8_danish_ci"},
  204 => {:name => "utf8",    :collation => "utf8_lithuanian_ci"},
  205 => {:name => "utf8",    :collation => "utf8_slovak_ci"},
  206 => {:name => "utf8",    :collation => "utf8_spanish2_ci"},
  207 => {:name => "utf8",    :collation => "utf8_roman_ci"},
  208 => {:name => "utf8",    :collation => "utf8_persian_ci"},
  209 => {:name => "utf8",    :collation => "utf8_esperanto_ci"},
  210 => {:name => "utf8",    :collation => "utf8_hungarian_ci"},
  254 => {:name => "utf8",    :collation => "utf8_general_cs"}
}
@@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
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Client

Returns a new instance of Client.



15
16
17
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
# File 'lib/mysql2/client.rb', line 15

def initialize(opts = {})
  @query_options = @@default_query_options.dup

  init_connection

  [:reconnect, :connect_timeout].each do |key|
    next unless opts.key?(key)
    send(:"#{key}=", opts[key])
  end
  # force the encoding to utf8
  self.charset_name = opts[:encoding] || 'utf8'

  @read_timeout = opts[:read_timeout]
  if @read_timeout and @read_timeout < 0
    raise Mysql2::Error, "read_timeout must be a positive integer, you passed #{@read_timeout}"
  end

  ssl_set(*opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslciper))

  user     = opts[:username]
  pass     = opts[:password]
  host     = opts[:host] || 'localhost'
  port     = opts[:port] || 3306
  database = opts[:database]
  socket   = opts[:socket]
  flags    = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags]

  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

Class Method Details

.default_query_optionsObject



45
46
47
# File 'lib/mysql2/client.rb', line 45

def self.default_query_options
  @@default_query_options
end

.encoding_from_charset(charset) ⇒ Object



222
223
224
# File 'lib/mysql2/client.rb', line 222

def self.encoding_from_charset(charset)
  CHARSET_MAP[charset.to_s.downcase]
end

.encoding_from_charset_code(code) ⇒ Object



226
227
228
229
230
231
232
# File 'lib/mysql2/client.rb', line 226

def self.encoding_from_charset_code(code)
  if mapping = MYSQL_CHARSET_MAP[code]
    encoding_from_charset(mapping[:name])
  else
    nil
  end
end

.escape(str) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'ext/mysql2/client.c', line 174

static VALUE rb_mysql_client_escape(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

#affected_rowsObject



541
542
543
544
545
546
547
548
549
550
551
# File 'ext/mysql2/client.c', line 541

static VALUE rb_mysql_client_affected_rows(VALUE self) {
  my_ulonglong retVal;
  GET_CLIENT(self);

  REQUIRE_OPEN_DB(wrapper);
  retVal = mysql_affected_rows(wrapper->client);
  if (retVal == (my_ulonglong)-1) {
    rb_raise_mysql2_error(wrapper);
  }
  return ULL2NUM(retVal);
}

#async_resultObject



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'ext/mysql2/client.c', line 270

static VALUE rb_mysql_client_async_result(VALUE self) {
  MYSQL_RES * result;
  VALUE resultObj;
#ifdef HAVE_RUBY_ENCODING_H
  mysql2_result_wrapper * result_wrapper;
#endif
  GET_CLIENT(self);

  REQUIRE_OPEN_DB(wrapper);
  if (rb_thread_blocking_region(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);
  }

  result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0);

  // we have our result, mark this connection inactive
  MARK_CONN_INACTIVE(self);

  if (result == NULL) {
    if (mysql_field_count(wrapper->client) != 0) {
      rb_raise_mysql2_error(wrapper);
    }
    return Qnil;
  }

  resultObj = rb_mysql_result_to_obj(result);
  // pass-through query options for result construction later
  rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0));

#ifdef HAVE_RUBY_ENCODING_H
  GetMysql2Result(resultObj, result_wrapper);
  result_wrapper->encoding = wrapper->encoding;
#endif
  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.



226
227
228
229
230
231
232
233
234
# File 'ext/mysql2/client.c', line 226

static VALUE rb_mysql_client_close(VALUE self) {
  GET_CLIENT(self);

  if (!wrapper->closed) {
    rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
  }

  return Qnil;
}

#encodingObject



580
581
582
583
# File 'ext/mysql2/client.c', line 580

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

#escape(str) ⇒ Object



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'ext/mysql2/client.c', line 425

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_OPEN_DB(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



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'ext/mysql2/client.c', line 465

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;
#endif
  GET_CLIENT(self);
  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



535
536
537
538
539
# File 'ext/mysql2/client.c', line 535

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

#pingObject



569
570
571
572
573
574
575
576
577
# File 'ext/mysql2/client.c', line 569

static VALUE rb_mysql_client_ping(VALUE self) {
  GET_CLIENT(self);

  if (wrapper->closed) {
    return Qfalse;
  } else {
    return rb_thread_blocking_region(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
  }
}

#query(*args) ⇒ Object



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'ext/mysql2/client.c', line 308

static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
  struct nogvl_send_query_args args;
  fd_set fdset;
  int fd, retval;
  int async = 0;
  VALUE opts, defaults, read_timeout;
#ifdef HAVE_RUBY_ENCODING_H
  rb_encoding *conn_enc;
#endif
  struct timeval tv;
  struct timeval* tvp;
  long int sec;
  VALUE result;
  GET_CLIENT(self);

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

  // see if this connection is still waiting on a result from a previous query
  if (wrapper->active == 0) {
    // mark this connection active
    wrapper->active = 1;
  } else {
    rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
  }

  defaults = rb_iv_get(self, "@query_options");
  if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
    opts = rb_funcall(defaults, intern_merge, 1, opts);
    rb_iv_set(self, "@query_options", opts);

    if (rb_hash_aref(opts, sym_async) == Qtrue) {
      async = 1;
    }
  } else {
    opts = defaults;
  }

  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

  if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
    // an error occurred, we're not active anymore
    MARK_CONN_INACTIVE(self);
    return rb_raise_mysql2_error(wrapper);
  }

  read_timeout = rb_iv_get(self, "@read_timeout");

  tvp = NULL;
  if (!NIL_P(read_timeout)) {
    Check_Type(read_timeout, T_FIXNUM);
    tvp = &tv;
    sec = FIX2INT(read_timeout);
    // TODO: support partial seconds?
    // also, this check is here for sanity, we also check up in Ruby
    if (sec >= 0) {
      tvp->tv_sec = sec;
    } else {
      rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
    }
    tvp->tv_usec = 0;
  }

  if (!async) {
    // the below code is largely from do_mysql
    // http://github.com/datamapper/do
    fd = wrapper->client->net.fd;
    for(;;) {
      int fd_set_fd = fd;

#if defined(_WIN32) && !defined(HAVE_RB_THREAD_BLOCKING_REGION) // don't need this in 1.9
      WSAPROTOCOL_INFO wsa_pi;
      // dupicate the SOCKET from libmysql
      int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi);
      SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
      // create the CRT fd so ruby can get back to the SOCKET
      fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
#endif

      FD_ZERO(&fdset);
      FD_SET(fd_set_fd, &fdset);

      retval = rb_thread_select(fd_set_fd + 1, &fdset, NULL, NULL, tvp);

#if defined(_WIN32) && !defined(HAVE_RB_THREAD_BLOCKING_REGION)
      // cleanup the CRT fd
      _close(fd_set_fd);
      // cleanup the duplicated SOCKET
      closesocket(s);
#endif

      if (retval == 0) {
        rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
      }

      if (retval < 0) {
        rb_sys_fail(0);
      }

      if (retval > 0) {
        break;
      }
    }

    result = rb_mysql_client_async_result(self);

    return result;
  } else {
    return Qnil;
  }
}

#server_infoObject



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
# File 'ext/mysql2/client.c', line 491

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_OPEN_DB(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



518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'ext/mysql2/client.c', line 518

static VALUE rb_mysql_client_socket(VALUE self) {
  GET_CLIENT(self);
  REQUIRE_OPEN_DB(wrapper);
  int fd_set_fd = wrapper->client->net.fd;
#ifdef _WIN32
  WSAPROTOCOL_INFO wsa_pi;
  // dupicate the SOCKET from libmysql
  int r = WSADuplicateSocket(wrapper->client->net.fd, GetCurrentProcessId(), &wsa_pi);
  SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
  // create the CRT fd so ruby can get back to the SOCKET
  fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
  return INT2NUM(fd_set_fd);
#else
  return INT2NUM(fd_set_fd);
#endif
}

#thread_idObject



553
554
555
556
557
558
559
560
# File 'ext/mysql2/client.c', line 553

static VALUE rb_mysql_client_thread_id(VALUE self) {
  unsigned long retVal;
  GET_CLIENT(self);

  REQUIRE_OPEN_DB(wrapper);
  retVal = mysql_thread_id(wrapper->client);
  return ULL2NUM(retVal);
}