mysql2 0.3.20 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mysql2/client.c CHANGED
@@ -15,51 +15,58 @@
15
15
  #include "mysql_enc_name_to_ruby.h"
16
16
 
17
17
  VALUE cMysql2Client;
18
- extern VALUE mMysql2, cMysql2Error;
18
+ extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
19
19
  static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
20
- static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
21
-
22
- #ifndef HAVE_RB_HASH_DUP
23
- static VALUE rb_hash_dup(VALUE other) {
24
- return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
25
- }
26
- #endif
20
+ static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
21
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
22
+ intern_current_query_options, intern_read_timeout;
27
23
 
28
24
  #define REQUIRE_INITIALIZED(wrapper) \
29
25
  if (!wrapper->initialized) { \
30
26
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
27
  }
32
28
 
29
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
30
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
31
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
32
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
33
+ #endif
34
+
33
35
  #define REQUIRE_CONNECTED(wrapper) \
34
36
  REQUIRE_INITIALIZED(wrapper) \
35
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36
- rb_raise(cMysql2Error, "closed MySQL connection"); \
37
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
38
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
37
39
  }
38
40
 
39
41
  #define REQUIRE_NOT_CONNECTED(wrapper) \
40
42
  REQUIRE_INITIALIZED(wrapper) \
41
- if (wrapper->connected) { \
43
+ if (CONNECTED(wrapper)) { \
42
44
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
43
45
  }
44
46
 
45
- #define MARK_CONN_INACTIVE(conn) \
46
- wrapper->active_thread = Qnil;
47
-
48
- #define GET_CLIENT(self) \
49
- mysql_client_wrapper *wrapper; \
50
- Data_Get_Struct(self, mysql_client_wrapper, wrapper)
51
-
52
47
  /*
53
48
  * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
54
49
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
55
50
  * linking against the server itself
56
51
  */
57
- #ifdef LIBMYSQL_VERSION
52
+ #if defined(MARIADB_CLIENT_VERSION_STR)
53
+ #define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
54
+ #elif defined(LIBMYSQL_VERSION)
58
55
  #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
59
56
  #else
60
57
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
61
58
  #endif
62
59
 
60
+ /*
61
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
62
+ */
63
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
64
+ #define SSL_MODE_DISABLED 1
65
+ #define SSL_MODE_REQUIRED 3
66
+ #define HAVE_CONST_SSL_MODE_DISABLED
67
+ #define HAVE_CONST_SSL_MODE_REQUIRED
68
+ #endif
69
+
63
70
  /*
64
71
  * used to pass all arguments to mysql_real_connect while inside
65
72
  * rb_thread_call_without_gvl
@@ -96,6 +103,46 @@ struct nogvl_select_db_args {
96
103
  char *db;
97
104
  };
98
105
 
106
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
107
+ unsigned long version = mysql_get_client_version();
108
+
109
+ if (version < 50703) {
110
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
111
+ return Qnil;
112
+ }
113
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
114
+ GET_CLIENT(self);
115
+ int val = NUM2INT( setting );
116
+ // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
117
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
118
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
119
+ my_bool b = ( val == SSL_MODE_REQUIRED );
120
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
121
+ return INT2NUM(result);
122
+ } else {
123
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
124
+ return Qnil;
125
+ }
126
+ } else {
127
+ rb_warn( "Your mysql client library does not support ssl_mode as expected." );
128
+ return Qnil;
129
+ }
130
+ #endif
131
+ #ifdef FULL_SSL_MODE_SUPPORT
132
+ GET_CLIENT(self);
133
+ int val = NUM2INT( setting );
134
+
135
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
136
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
137
+ }
138
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
139
+
140
+ return INT2NUM(result);
141
+ #endif
142
+ #ifdef NO_SSL_MODE_SUPPORT
143
+ return Qnil;
144
+ #endif
145
+ }
99
146
  /*
100
147
  * non-blocking mysql_*() functions that we won't be wrapping since
101
148
  * they do not appear to hit the network nor issue any interruptible
@@ -131,21 +178,20 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
131
178
  VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
132
179
  VALUE e;
133
180
 
134
- #ifdef HAVE_RUBY_ENCODING_H
135
181
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
136
182
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137
- #endif
138
183
 
139
- e = rb_funcall(cMysql2Error, rb_intern("new"), 2, rb_error_msg, LONG2FIX(wrapper->server_version));
140
- rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
141
- rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
184
+ e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
185
+ rb_error_msg,
186
+ LONG2FIX(wrapper->server_version),
187
+ UINT2NUM(mysql_errno(wrapper->client)),
188
+ rb_sql_state);
142
189
  rb_exc_raise(e);
143
- return Qnil;
144
190
  }
145
191
 
146
192
  static void *nogvl_init(void *ptr) {
147
193
  MYSQL *client;
148
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
194
+ mysql_client_wrapper *wrapper = ptr;
149
195
 
150
196
  /* may initialize embedded server and read /etc/services off disk */
151
197
  client = mysql_init(wrapper->client);
@@ -221,41 +267,46 @@ static VALUE invalidate_fd(int clientfd)
221
267
  #endif /* _WIN32 */
222
268
 
223
269
  static void *nogvl_close(void *ptr) {
224
- mysql_client_wrapper *wrapper;
225
- wrapper = ptr;
226
- if (wrapper->connected) {
227
- wrapper->active_thread = Qnil;
228
- wrapper->connected = 0;
229
- #ifndef _WIN32
230
- /* Invalidate the socket before calling mysql_close(). This prevents
231
- * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
232
- * the socket. The difference is that invalidate_fd will drop this
233
- * process's reference to the socket only, while a QUIT or shutdown()
234
- * would render the underlying connection unusable, interrupting other
235
- * processes which share this object across a fork().
236
- */
237
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
238
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
239
- close(wrapper->client->net.fd);
240
- return NULL;
241
- }
242
- #endif
270
+ mysql_client_wrapper *wrapper = ptr;
243
271
 
244
- mysql_close(wrapper->client); /* only used to free memory at this point */
272
+ if (!wrapper->closed) {
273
+ mysql_close(wrapper->client);
274
+ wrapper->closed = 1;
275
+ wrapper->reconnect_enabled = 0;
276
+ wrapper->active_thread = Qnil;
245
277
  }
246
278
 
247
279
  return NULL;
248
280
  }
249
281
 
282
+ /* this is called during GC */
250
283
  static void rb_mysql_client_free(void *ptr) {
251
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
284
+ mysql_client_wrapper *wrapper = ptr;
252
285
  decr_mysql2_client(wrapper);
253
286
  }
254
287
 
255
288
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
256
289
  {
257
290
  wrapper->refcount--;
291
+
258
292
  if (wrapper->refcount == 0) {
293
+ #ifndef _WIN32
294
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
295
+ /* The client is being garbage collected while connected. Prevent
296
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
297
+ * the socket by invalidating it. invalidate_fd() will drop this
298
+ * process's reference to the socket only, while a QUIT or shutdown()
299
+ * would render the underlying connection unusable, interrupting other
300
+ * processes which share this object across a fork().
301
+ */
302
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
303
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
304
+ close(wrapper->client->net.fd);
305
+ }
306
+ wrapper->client->net.fd = -1;
307
+ }
308
+ #endif
309
+
259
310
  nogvl_close(wrapper);
260
311
  xfree(wrapper->client);
261
312
  xfree(wrapper);
@@ -268,13 +319,15 @@ static VALUE allocate(VALUE klass) {
268
319
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
269
320
  wrapper->encoding = Qnil;
270
321
  wrapper->active_thread = Qnil;
322
+ wrapper->automatic_close = 1;
271
323
  wrapper->server_version = 0;
272
324
  wrapper->reconnect_enabled = 0;
273
325
  wrapper->connect_timeout = 0;
274
- wrapper->connected = 0; /* means that a database connection is open */
275
326
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
276
327
  wrapper->refcount = 1;
328
+ wrapper->closed = 0;
277
329
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
330
+
278
331
  return obj;
279
332
  }
280
333
 
@@ -302,9 +355,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
302
355
  return str;
303
356
  } else {
304
357
  rb_str = rb_str_new((const char*)newStr, newLen);
305
- #ifdef HAVE_RUBY_ENCODING_H
306
358
  rb_enc_copy(rb_str, str);
307
- #endif
308
359
  xfree(newStr);
309
360
  return rb_str;
310
361
  }
@@ -331,17 +382,45 @@ static VALUE rb_mysql_info(VALUE self) {
331
382
  }
332
383
 
333
384
  rb_str = rb_str_new2(info);
334
- #ifdef HAVE_RUBY_ENCODING_H
335
385
  rb_enc_associate(rb_str, rb_utf8_encoding());
336
- #endif
337
386
 
338
387
  return rb_str;
339
388
  }
340
389
 
341
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
390
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
391
+ {
392
+ const char *cipher;
393
+ VALUE rb_str;
394
+ GET_CLIENT(self);
395
+
396
+ cipher = mysql_get_ssl_cipher(wrapper->client);
397
+
398
+ if (cipher == NULL) {
399
+ return Qnil;
400
+ }
401
+
402
+ rb_str = rb_str_new2(cipher);
403
+ rb_enc_associate(rb_str, rb_utf8_encoding());
404
+
405
+ return rb_str;
406
+ }
407
+
408
+ #ifdef CLIENT_CONNECT_ATTRS
409
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
410
+ {
411
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
412
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
413
+ key = rb_str_export_to_enc(key, enc);
414
+ value = rb_str_export_to_enc(value, enc);
415
+
416
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
417
+ return ST_CONTINUE;
418
+ }
419
+ #endif
420
+
421
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
342
422
  struct nogvl_connect_args args;
343
- time_t start_time, end_time;
344
- unsigned int elapsed_time, connect_timeout;
423
+ time_t start_time, end_time, elapsed_time, connect_timeout;
345
424
  VALUE rv;
346
425
  GET_CLIENT(self);
347
426
 
@@ -354,6 +433,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
354
433
  args.mysql = wrapper->client;
355
434
  args.client_flag = NUM2ULONG(flags);
356
435
 
436
+ #ifdef CLIENT_CONNECT_ATTRS
437
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
438
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
439
+ #endif
440
+
357
441
  if (wrapper->connect_timeout)
358
442
  time(&start_time);
359
443
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -368,7 +452,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
368
452
  /* avoid an early timeout due to time truncating milliseconds off the start time */
369
453
  if (elapsed_time > 0)
370
454
  elapsed_time--;
371
- if (elapsed_time >= wrapper->connect_timeout)
455
+ if (elapsed_time >= (time_t)wrapper->connect_timeout)
372
456
  break;
373
457
  connect_timeout = wrapper->connect_timeout - elapsed_time;
374
458
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
@@ -380,30 +464,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
380
464
  if (wrapper->connect_timeout)
381
465
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
382
466
  if (rv == Qfalse)
383
- return rb_raise_mysql2_error(wrapper);
467
+ rb_raise_mysql2_error(wrapper);
384
468
  }
385
469
 
386
470
  wrapper->server_version = mysql_get_server_version(wrapper->client);
387
- wrapper->connected = 1;
388
471
  return self;
389
472
  }
390
473
 
391
474
  /*
392
- * Immediately disconnect from the server, normally the garbage collector
475
+ * Immediately disconnect from the server; normally the garbage collector
393
476
  * will disconnect automatically when a connection is no longer needed.
394
477
  * Explicitly closing this will free up server resources sooner than waiting
395
478
  * for the garbage collector.
479
+ *
480
+ * @return [nil]
396
481
  */
397
482
  static VALUE rb_mysql_client_close(VALUE self) {
398
483
  GET_CLIENT(self);
399
484
 
400
- if (wrapper->connected) {
485
+ if (wrapper->client) {
401
486
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
402
487
  }
403
488
 
404
489
  return Qnil;
405
490
  }
406
491
 
492
+ /* call-seq:
493
+ * client.closed?
494
+ *
495
+ * @return [Boolean]
496
+ */
497
+ static VALUE rb_mysql_client_closed(VALUE self) {
498
+ GET_CLIENT(self);
499
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
500
+ }
501
+
407
502
  /*
408
503
  * mysql_send_query is unlikely to block since most queries are small
409
504
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -423,8 +518,8 @@ static VALUE do_send_query(void *args) {
423
518
  mysql_client_wrapper *wrapper = query_args->wrapper;
424
519
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
425
520
  /* an error occurred, we're not active anymore */
426
- MARK_CONN_INACTIVE(self);
427
- return rb_raise_mysql2_error(wrapper);
521
+ wrapper->active_thread = Qnil;
522
+ rb_raise_mysql2_error(wrapper);
428
523
  }
429
524
  return Qnil;
430
525
  }
@@ -442,10 +537,9 @@ static void *nogvl_read_query_result(void *ptr) {
442
537
  }
443
538
 
444
539
  static void *nogvl_do_result(void *ptr, char use_result) {
445
- mysql_client_wrapper *wrapper;
540
+ mysql_client_wrapper *wrapper = ptr;
446
541
  MYSQL_RES *result;
447
542
 
448
- wrapper = (mysql_client_wrapper *)ptr;
449
543
  if (use_result) {
450
544
  result = mysql_use_result(wrapper->client);
451
545
  } else {
@@ -486,11 +580,11 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
486
580
  REQUIRE_CONNECTED(wrapper);
487
581
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
488
582
  /* an error occurred, mark this connection inactive */
489
- MARK_CONN_INACTIVE(self);
490
- return rb_raise_mysql2_error(wrapper);
583
+ wrapper->active_thread = Qnil;
584
+ rb_raise_mysql2_error(wrapper);
491
585
  }
492
586
 
493
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
587
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
494
588
  if (is_streaming == Qtrue) {
495
589
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
496
590
  } else {
@@ -499,17 +593,20 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
499
593
 
500
594
  if (result == NULL) {
501
595
  if (mysql_errno(wrapper->client) != 0) {
502
- MARK_CONN_INACTIVE(self);
596
+ wrapper->active_thread = Qnil;
503
597
  rb_raise_mysql2_error(wrapper);
504
598
  }
505
599
  /* no data and no error, so query was not a SELECT */
506
600
  return Qnil;
507
601
  }
508
602
 
509
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
510
- RB_GC_GUARD(current);
603
+ // Duplicate the options hash and put the copy in the Result object
604
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
605
+ (void)RB_GC_GUARD(current);
511
606
  Check_Type(current, T_HASH);
512
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
607
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
608
+
609
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
513
610
 
514
611
  return resultObj;
515
612
  }
@@ -524,31 +621,30 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
524
621
  GET_CLIENT(self);
525
622
 
526
623
  wrapper->active_thread = Qnil;
527
- wrapper->connected = 0;
528
624
 
529
625
  /* Invalidate the MySQL socket to prevent further communication.
530
626
  * The GC will come along later and call mysql_close to free it.
531
627
  */
532
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
533
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
534
- close(wrapper->client->net.fd);
628
+ if (CONNECTED(wrapper)) {
629
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
630
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
631
+ close(wrapper->client->net.fd);
632
+ }
633
+ wrapper->client->net.fd = -1;
535
634
  }
536
635
 
537
636
  rb_exc_raise(error);
538
-
539
- return Qnil;
540
637
  }
541
638
 
542
639
  static VALUE do_query(void *args) {
543
- struct async_query_args *async_args;
640
+ struct async_query_args *async_args = args;
544
641
  struct timeval tv;
545
- struct timeval* tvp;
642
+ struct timeval *tvp;
546
643
  long int sec;
547
644
  int retval;
548
645
  VALUE read_timeout;
549
646
 
550
- async_args = (struct async_query_args *)args;
551
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
647
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
552
648
 
553
649
  tvp = NULL;
554
650
  if (!NIL_P(read_timeout)) {
@@ -569,7 +665,7 @@ static VALUE do_query(void *args) {
569
665
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
570
666
 
571
667
  if (retval == 0) {
572
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
668
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
573
669
  }
574
670
 
575
671
  if (retval < 0) {
@@ -583,28 +679,50 @@ static VALUE do_query(void *args) {
583
679
 
584
680
  return Qnil;
585
681
  }
586
- #else
587
- static VALUE finish_and_mark_inactive(void *args) {
588
- VALUE self;
589
- MYSQL_RES *result;
590
-
591
- self = (VALUE)args;
682
+ #endif
592
683
 
684
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
593
685
  GET_CLIENT(self);
594
686
 
687
+ /* Check if execution terminated while result was still being read. */
595
688
  if (!NIL_P(wrapper->active_thread)) {
596
- /* if we got here, the result hasn't been read off the wire yet
597
- so lets do that and then throw it away because we have no way
598
- of getting it back up to the caller from here */
599
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
600
- mysql_free_result(result);
601
-
689
+ if (CONNECTED(wrapper)) {
690
+ /* Invalidate the MySQL socket to prevent further communication. */
691
+ #ifndef _WIN32
692
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
693
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
694
+ close(wrapper->client->net.fd);
695
+ }
696
+ #else
697
+ close(wrapper->client->net.fd);
698
+ #endif
699
+ wrapper->client->net.fd = -1;
700
+ }
701
+ /* Skip mysql client check performed before command execution. */
702
+ wrapper->client->status = MYSQL_STATUS_READY;
602
703
  wrapper->active_thread = Qnil;
603
704
  }
604
705
 
605
706
  return Qnil;
606
707
  }
607
- #endif
708
+
709
+ void rb_mysql_client_set_active_thread(VALUE self) {
710
+ VALUE thread_current = rb_thread_current();
711
+ GET_CLIENT(self);
712
+
713
+ // see if this connection is still waiting on a result from a previous query
714
+ if (NIL_P(wrapper->active_thread)) {
715
+ // mark this connection active
716
+ wrapper->active_thread = thread_current;
717
+ } else if (wrapper->active_thread == thread_current) {
718
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
719
+ } else {
720
+ VALUE inspect = rb_inspect(wrapper->active_thread);
721
+ const char *thr = StringValueCStr(inspect);
722
+
723
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
724
+ }
725
+ }
608
726
 
609
727
  /* call-seq:
610
728
  * client.abandon_results!
@@ -640,80 +758,49 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
640
758
  * client.query(sql, options = {})
641
759
  *
642
760
  * Query the database with +sql+, with optional +options+. For the possible
643
- * options, see @@default_query_options on the Mysql2::Client class.
761
+ * options, see default_query_options on the Mysql2::Client class.
644
762
  */
645
- static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
763
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
646
764
  #ifndef _WIN32
647
765
  struct async_query_args async_args;
648
766
  #endif
649
767
  struct nogvl_send_query_args args;
650
- int async = 0;
651
- VALUE opts, current;
652
- VALUE thread_current = rb_thread_current();
653
- #ifdef HAVE_RUBY_ENCODING_H
654
- rb_encoding *conn_enc;
655
- #endif
656
768
  GET_CLIENT(self);
657
769
 
658
770
  REQUIRE_CONNECTED(wrapper);
659
771
  args.mysql = wrapper->client;
660
772
 
661
- current = rb_hash_dup(rb_iv_get(self, "@query_options"));
662
- RB_GC_GUARD(current);
773
+ (void)RB_GC_GUARD(current);
663
774
  Check_Type(current, T_HASH);
664
- rb_iv_set(self, "@current_query_options", current);
775
+ rb_ivar_set(self, intern_current_query_options, current);
665
776
 
666
- if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
667
- rb_funcall(current, intern_merge_bang, 1, opts);
668
-
669
- if (rb_hash_aref(current, sym_async) == Qtrue) {
670
- async = 1;
671
- }
672
- }
673
-
674
- Check_Type(args.sql, T_STRING);
675
- #ifdef HAVE_RUBY_ENCODING_H
676
- conn_enc = rb_to_encoding(wrapper->encoding);
777
+ Check_Type(sql, T_STRING);
677
778
  /* ensure the string is in the encoding the connection is expecting */
678
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
679
- #endif
779
+ args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
680
780
  args.sql_ptr = RSTRING_PTR(args.sql);
681
781
  args.sql_len = RSTRING_LEN(args.sql);
682
-
683
- /* see if this connection is still waiting on a result from a previous query */
684
- if (NIL_P(wrapper->active_thread)) {
685
- /* mark this connection active */
686
- wrapper->active_thread = thread_current;
687
- } else if (wrapper->active_thread == thread_current) {
688
- rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
689
- } else {
690
- VALUE inspect = rb_inspect(wrapper->active_thread);
691
- const char *thr = StringValueCStr(inspect);
692
-
693
- rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
694
- RB_GC_GUARD(inspect);
695
- }
696
-
697
782
  args.wrapper = wrapper;
698
783
 
784
+ rb_mysql_client_set_active_thread(self);
785
+
699
786
  #ifndef _WIN32
700
787
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
701
788
 
702
- if (!async) {
789
+ if (rb_hash_aref(current, sym_async) == Qtrue) {
790
+ return Qnil;
791
+ } else {
703
792
  async_args.fd = wrapper->client->net.fd;
704
793
  async_args.self = self;
705
794
 
706
795
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
707
796
 
708
- return rb_mysql_client_async_result(self);
709
- } else {
710
- return Qnil;
797
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
711
798
  }
712
799
  #else
713
800
  do_send_query(&args);
714
801
 
715
802
  /* this will just block until the result is ready */
716
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
803
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
717
804
  #endif
718
805
  }
719
806
 
@@ -726,20 +813,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
726
813
  unsigned char *newStr;
727
814
  VALUE rb_str;
728
815
  unsigned long newLen, oldLen;
729
- #ifdef HAVE_RUBY_ENCODING_H
730
816
  rb_encoding *default_internal_enc;
731
817
  rb_encoding *conn_enc;
732
- #endif
733
818
  GET_CLIENT(self);
734
819
 
735
820
  REQUIRE_CONNECTED(wrapper);
736
821
  Check_Type(str, T_STRING);
737
- #ifdef HAVE_RUBY_ENCODING_H
738
822
  default_internal_enc = rb_default_internal_encoding();
739
823
  conn_enc = rb_to_encoding(wrapper->encoding);
740
824
  /* ensure the string is in the encoding the connection is expecting */
741
825
  str = rb_str_export_to_enc(str, conn_enc);
742
- #endif
743
826
 
744
827
  oldLen = RSTRING_LEN(str);
745
828
  newStr = xmalloc(oldLen*2+1);
@@ -747,21 +830,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
747
830
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
748
831
  if (newLen == oldLen) {
749
832
  /* no need to return a new ruby string if nothing changed */
750
- #ifdef HAVE_RUBY_ENCODING_H
751
833
  if (default_internal_enc) {
752
834
  str = rb_str_export_to_enc(str, default_internal_enc);
753
835
  }
754
- #endif
755
836
  xfree(newStr);
756
837
  return str;
757
838
  } else {
758
839
  rb_str = rb_str_new((const char*)newStr, newLen);
759
- #ifdef HAVE_RUBY_ENCODING_H
760
840
  rb_enc_associate(rb_str, conn_enc);
761
841
  if (default_internal_enc) {
762
842
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
763
843
  }
764
- #endif
765
844
  xfree(newStr);
766
845
  return rb_str;
767
846
  }
@@ -807,10 +886,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
807
886
  retval = &boolval;
808
887
  break;
809
888
 
889
+ #ifdef MYSQL_SECURE_AUTH
810
890
  case MYSQL_SECURE_AUTH:
811
891
  boolval = (value == Qfalse ? 0 : 1);
812
892
  retval = &boolval;
813
893
  break;
894
+ #endif
814
895
 
815
896
  case MYSQL_READ_DEFAULT_FILE:
816
897
  charval = (const char *)StringValueCStr(value);
@@ -827,6 +908,18 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
827
908
  retval = charval;
828
909
  break;
829
910
 
911
+ case MYSQL_DEFAULT_AUTH:
912
+ charval = (const char *)StringValueCStr(value);
913
+ retval = charval;
914
+ break;
915
+
916
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
917
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
918
+ boolval = (value == Qfalse ? 0 : 1);
919
+ retval = &boolval;
920
+ break;
921
+ #endif
922
+
830
923
  default:
831
924
  return Qfalse;
832
925
  }
@@ -863,10 +956,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
863
956
  version = rb_str_new2(mysql_get_client_info());
864
957
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
865
958
 
866
- #ifdef HAVE_RUBY_ENCODING_H
867
959
  rb_enc_associate(version, rb_usascii_encoding());
868
960
  rb_enc_associate(header_version, rb_usascii_encoding());
869
- #endif
870
961
 
871
962
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
872
963
  rb_hash_aset(version_info, sym_version, version);
@@ -882,27 +973,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
882
973
  */
883
974
  static VALUE rb_mysql_client_server_info(VALUE self) {
884
975
  VALUE version, server_info;
885
- #ifdef HAVE_RUBY_ENCODING_H
886
976
  rb_encoding *default_internal_enc;
887
977
  rb_encoding *conn_enc;
888
- #endif
889
978
  GET_CLIENT(self);
890
979
 
891
980
  REQUIRE_CONNECTED(wrapper);
892
- #ifdef HAVE_RUBY_ENCODING_H
893
981
  default_internal_enc = rb_default_internal_encoding();
894
982
  conn_enc = rb_to_encoding(wrapper->encoding);
895
- #endif
896
983
 
897
984
  version = rb_hash_new();
898
985
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
899
986
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
900
- #ifdef HAVE_RUBY_ENCODING_H
901
987
  rb_enc_associate(server_info, conn_enc);
902
988
  if (default_internal_enc) {
903
989
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
904
990
  }
905
- #endif
906
991
  rb_hash_aset(version, sym_version, server_info);
907
992
  return version;
908
993
  }
@@ -912,15 +997,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
912
997
  *
913
998
  * Return the file descriptor number for this client.
914
999
  */
915
- static VALUE rb_mysql_client_socket(VALUE self) {
916
1000
  #ifndef _WIN32
1001
+ static VALUE rb_mysql_client_socket(VALUE self) {
917
1002
  GET_CLIENT(self);
918
1003
  REQUIRE_CONNECTED(wrapper);
919
1004
  return INT2NUM(wrapper->client->net.fd);
1005
+ }
920
1006
  #else
1007
+ static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
921
1008
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
922
- #endif
923
1009
  }
1010
+ #endif
924
1011
 
925
1012
  /* call-seq:
926
1013
  * client.last_id
@@ -1014,13 +1101,30 @@ static void *nogvl_ping(void *ptr) {
1014
1101
  static VALUE rb_mysql_client_ping(VALUE self) {
1015
1102
  GET_CLIENT(self);
1016
1103
 
1017
- if (!wrapper->connected) {
1104
+ if (!CONNECTED(wrapper)) {
1018
1105
  return Qfalse;
1019
1106
  } else {
1020
1107
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1021
1108
  }
1022
1109
  }
1023
1110
 
1111
+ /* call-seq:
1112
+ * client.set_server_option(value)
1113
+ *
1114
+ * Enables or disables an option for the connection.
1115
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1116
+ * for more information.
1117
+ */
1118
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1119
+ GET_CLIENT(self);
1120
+
1121
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1122
+ return Qtrue;
1123
+ } else {
1124
+ return Qfalse;
1125
+ }
1126
+ }
1127
+
1024
1128
  /* call-seq:
1025
1129
  * client.more_results?
1026
1130
  *
@@ -1029,10 +1133,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1029
1133
  static VALUE rb_mysql_client_more_results(VALUE self)
1030
1134
  {
1031
1135
  GET_CLIENT(self);
1032
- if (mysql_more_results(wrapper->client) == 0)
1033
- return Qfalse;
1034
- else
1035
- return Qtrue;
1136
+ if (mysql_more_results(wrapper->client) == 0)
1137
+ return Qfalse;
1138
+ else
1139
+ return Qtrue;
1036
1140
  }
1037
1141
 
1038
1142
  /* call-seq:
@@ -1079,15 +1183,15 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1079
1183
  return Qnil;
1080
1184
  }
1081
1185
 
1082
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1083
- RB_GC_GUARD(current);
1186
+ // Duplicate the options hash and put the copy in the Result object
1187
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1188
+ (void)RB_GC_GUARD(current);
1084
1189
  Check_Type(current, T_HASH);
1085
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1190
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
1086
1191
 
1087
1192
  return resultObj;
1088
1193
  }
1089
1194
 
1090
- #ifdef HAVE_RUBY_ENCODING_H
1091
1195
  /* call-seq:
1092
1196
  * client.encoding
1093
1197
  *
@@ -1097,7 +1201,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1097
1201
  GET_CLIENT(self);
1098
1202
  return wrapper->encoding;
1099
1203
  }
1204
+
1205
+ /* call-seq:
1206
+ * client.automatic_close?
1207
+ *
1208
+ * @return [Boolean]
1209
+ */
1210
+ static VALUE get_automatic_close(VALUE self) {
1211
+ GET_CLIENT(self);
1212
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1213
+ }
1214
+
1215
+ /* call-seq:
1216
+ * client.automatic_close = false
1217
+ *
1218
+ * Set this to +false+ to leave the connection open after it is garbage
1219
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1220
+ * call +close+ when the connection is no longer needed.
1221
+ *
1222
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1223
+ */
1224
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1225
+ GET_CLIENT(self);
1226
+ if (RTEST(value)) {
1227
+ wrapper->automatic_close = 1;
1228
+ } else {
1229
+ #ifndef _WIN32
1230
+ wrapper->automatic_close = 0;
1231
+ #else
1232
+ rb_warn("Connections are always closed by garbage collector on Windows");
1100
1233
  #endif
1234
+ }
1235
+ return value;
1236
+ }
1101
1237
 
1102
1238
  /* call-seq:
1103
1239
  * client.reconnect = true
@@ -1134,7 +1270,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1134
1270
  /* Set the instance variable here even though _mysql_client_options
1135
1271
  might not succeed, because the timeout is used in other ways
1136
1272
  elsewhere */
1137
- rb_iv_set(self, "@read_timeout", value);
1273
+ rb_ivar_set(self, intern_read_timeout, value);
1138
1274
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1139
1275
  }
1140
1276
 
@@ -1150,19 +1286,15 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1150
1286
 
1151
1287
  static VALUE set_charset_name(VALUE self, VALUE value) {
1152
1288
  char *charset_name;
1153
- #ifdef HAVE_RUBY_ENCODING_H
1154
- size_t charset_name_len;
1155
1289
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1156
1290
  rb_encoding *enc;
1157
1291
  VALUE rb_enc;
1158
- #endif
1159
1292
  GET_CLIENT(self);
1160
1293
 
1294
+ Check_Type(value, T_STRING);
1161
1295
  charset_name = RSTRING_PTR(value);
1162
1296
 
1163
- #ifdef HAVE_RUBY_ENCODING_H
1164
- charset_name_len = RSTRING_LEN(value);
1165
- mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
1297
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1166
1298
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1167
1299
  VALUE inspect = rb_inspect(value);
1168
1300
  rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
@@ -1171,7 +1303,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1171
1303
  rb_enc = rb_enc_from_encoding(enc);
1172
1304
  wrapper->encoding = rb_enc;
1173
1305
  }
1174
- #endif
1175
1306
 
1176
1307
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1177
1308
  /* TODO: warning - unable to set charset */
@@ -1195,7 +1326,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1195
1326
  }
1196
1327
 
1197
1328
  static VALUE set_secure_auth(VALUE self, VALUE value) {
1329
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1330
+ #ifdef MYSQL_SECURE_AUTH
1198
1331
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1332
+ #else
1333
+ return Qfalse;
1334
+ #endif
1199
1335
  }
1200
1336
 
1201
1337
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1210,19 +1346,43 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1210
1346
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1211
1347
  }
1212
1348
 
1349
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1350
+ return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1351
+ }
1352
+
1353
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1354
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1355
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1356
+ #else
1357
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1358
+ #endif
1359
+ }
1360
+
1213
1361
  static VALUE initialize_ext(VALUE self) {
1214
1362
  GET_CLIENT(self);
1215
1363
 
1216
1364
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1217
1365
  /* TODO: warning - not enough memory? */
1218
- return rb_raise_mysql2_error(wrapper);
1366
+ rb_raise_mysql2_error(wrapper);
1219
1367
  }
1220
1368
 
1221
1369
  wrapper->initialized = 1;
1222
1370
  return self;
1223
1371
  }
1224
1372
 
1373
+ /* call-seq: client.prepare # => Mysql2::Statement
1374
+ *
1375
+ * Create a new prepared statement.
1376
+ */
1377
+ static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1378
+ GET_CLIENT(self);
1379
+ REQUIRE_CONNECTED(wrapper);
1380
+
1381
+ return rb_mysql_stmt_new(self, sql);
1382
+ }
1383
+
1225
1384
  void init_mysql2_client() {
1385
+ #ifdef _WIN32
1226
1386
  /* verify the libmysql we're about to use was the version we were built against
1227
1387
  https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
1228
1388
  int i;
@@ -1237,15 +1397,14 @@ void init_mysql2_client() {
1237
1397
  }
1238
1398
  if (lib[i] != MYSQL_LINK_VERSION[i]) {
1239
1399
  rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
1240
- return;
1241
1400
  }
1242
1401
  }
1402
+ #endif
1243
1403
 
1244
1404
  /* Initializing mysql library, so different threads could call Client.new */
1245
1405
  /* without race condition in the library */
1246
1406
  if (mysql_library_init(0, NULL, NULL) != 0) {
1247
1407
  rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1248
- return;
1249
1408
  }
1250
1409
 
1251
1410
  #if 0
@@ -1259,7 +1418,7 @@ void init_mysql2_client() {
1259
1418
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1260
1419
 
1261
1420
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1262
- rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1421
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1263
1422
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1264
1423
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1265
1424
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1267,18 +1426,21 @@ void init_mysql2_client() {
1267
1426
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
1268
1427
  rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
1269
1428
  rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1429
+ rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
1270
1430
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1271
1431
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1272
1432
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1433
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1273
1434
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1274
1435
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1275
1436
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1437
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1438
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1276
1439
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1277
1440
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1278
1441
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1279
- #ifdef HAVE_RUBY_ENCODING_H
1442
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1280
1443
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1281
- #endif
1282
1444
 
1283
1445
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1284
1446
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1289,9 +1451,13 @@ void init_mysql2_client() {
1289
1451
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1290
1452
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1291
1453
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1454
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1292
1455
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1456
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1457
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1293
1458
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1294
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1459
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1460
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1295
1461
 
1296
1462
  sym_id = ID2SYM(rb_intern("id"));
1297
1463
  sym_version = ID2SYM(rb_intern("version"));
@@ -1302,14 +1468,24 @@ void init_mysql2_client() {
1302
1468
  sym_array = ID2SYM(rb_intern("array"));
1303
1469
  sym_stream = ID2SYM(rb_intern("stream"));
1304
1470
 
1471
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1472
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1473
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1474
+
1475
+ intern_brackets = rb_intern("[]");
1305
1476
  intern_merge = rb_intern("merge");
1306
1477
  intern_merge_bang = rb_intern("merge!");
1307
- intern_error_number_eql = rb_intern("error_number=");
1308
- intern_sql_state_eql = rb_intern("sql_state=");
1478
+ intern_new_with_args = rb_intern("new_with_args");
1479
+ intern_current_query_options = rb_intern("@current_query_options");
1480
+ intern_read_timeout = rb_intern("@read_timeout");
1309
1481
 
1310
1482
  #ifdef CLIENT_LONG_PASSWORD
1311
1483
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1312
1484
  LONG2NUM(CLIENT_LONG_PASSWORD));
1485
+ #else
1486
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1487
+ * but we're using it in our default connection flags. */
1488
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1313
1489
  #endif
1314
1490
 
1315
1491
  #ifdef CLIENT_FOUND_ROWS
@@ -1387,6 +1563,16 @@ void init_mysql2_client() {
1387
1563
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1388
1564
  #endif
1389
1565
 
1566
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1567
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1568
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1569
+ #endif
1570
+
1571
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1572
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1573
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1574
+ #endif
1575
+
1390
1576
  #ifdef CLIENT_MULTI_STATEMENTS
1391
1577
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1392
1578
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1416,4 +1602,67 @@ void init_mysql2_client() {
1416
1602
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1417
1603
  LONG2NUM(CLIENT_BASIC_FLAGS));
1418
1604
  #endif
1605
+
1606
+ #ifdef CLIENT_CONNECT_ATTRS
1607
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1608
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1609
+ #else
1610
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1611
+ * but we're using it in our default connection flags. */
1612
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1613
+ INT2NUM(0));
1614
+ #endif
1615
+
1616
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1617
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1618
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1619
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1620
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1621
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1622
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1623
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1624
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1625
+ #endif
1626
+
1627
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1628
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1629
+ #endif
1630
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1631
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1632
+ #endif
1633
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1634
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1635
+ #endif
1636
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1637
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1638
+ #endif
1639
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1640
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1641
+ #endif
1642
+ }
1643
+
1644
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1645
+
1646
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1647
+ VALUE server_flags = rb_hash_new();
1648
+
1649
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1650
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1651
+ #else
1652
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1653
+ #endif
1654
+
1655
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1656
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1657
+ #else
1658
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1659
+ #endif
1660
+
1661
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1662
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1663
+ #else
1664
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1665
+ #endif
1666
+
1667
+ rb_iv_set(result, "@server_flags", server_flags);
1419
1668
  }