mysql2 0.3.20 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }