mysql2 0.2.24 → 0.5.4

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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +237 -85
  5. data/ext/mysql2/client.c +582 -249
  6. data/ext/mysql2/client.h +10 -38
  7. data/ext/mysql2/extconf.rb +217 -66
  8. data/ext/mysql2/infile.c +2 -2
  9. data/ext/mysql2/mysql2_ext.c +9 -2
  10. data/ext/mysql2/mysql2_ext.h +13 -14
  11. data/ext/mysql2/mysql_enc_name_to_ruby.h +62 -58
  12. data/ext/mysql2/mysql_enc_to_ruby.h +82 -18
  13. data/ext/mysql2/result.c +736 -200
  14. data/ext/mysql2/result.h +13 -6
  15. data/ext/mysql2/statement.c +612 -0
  16. data/ext/mysql2/statement.h +17 -0
  17. data/ext/mysql2/wait_for_single_fd.h +2 -1
  18. data/lib/mysql2/client.rb +110 -28
  19. data/lib/mysql2/console.rb +1 -1
  20. data/lib/mysql2/em.rb +15 -9
  21. data/lib/mysql2/error.rb +57 -36
  22. data/lib/mysql2/field.rb +3 -0
  23. data/lib/mysql2/result.rb +2 -0
  24. data/lib/mysql2/statement.rb +9 -0
  25. data/lib/mysql2/version.rb +1 -1
  26. data/lib/mysql2.rb +66 -15
  27. data/support/3A79BD29.asc +49 -0
  28. data/support/5072E1F5.asc +432 -0
  29. data/support/libmysql.def +219 -0
  30. data/support/mysql_enc_to_ruby.rb +15 -11
  31. data/support/ruby_enc_to_mysql.rb +8 -6
  32. metadata +30 -94
  33. data/MIT-LICENSE +0 -20
  34. data/examples/eventmachine.rb +0 -21
  35. data/examples/threaded.rb +0 -20
  36. data/lib/active_record/connection_adapters/mysql2_adapter.rb +0 -635
  37. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +0 -11
  38. data/spec/configuration.yml.example +0 -17
  39. data/spec/em/em_spec.rb +0 -114
  40. data/spec/my.cnf.example +0 -9
  41. data/spec/mysql2/client_spec.rb +0 -897
  42. data/spec/mysql2/error_spec.rb +0 -83
  43. data/spec/mysql2/result_spec.rb +0 -505
  44. data/spec/rcov.opts +0 -3
  45. data/spec/spec_helper.rb +0 -87
  46. data/spec/test_data +0 -1
data/ext/mysql2/client.c CHANGED
@@ -15,51 +15,73 @@
15
15
  #include "mysql_enc_name_to_ruby.h"
16
16
 
17
17
  VALUE cMysql2Client;
18
- extern VALUE mMysql2, cMysql2Error;
19
- static VALUE sym_id, sym_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
18
+ extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
19
+ static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
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
- * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * compatibility 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
+ * mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
62
+ * while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
63
+ * This is a hack to take care of both clients.
64
+ */
65
+ #if defined(CLIENT_SESSION_TRACK)
66
+ #elif defined(CLIENT_SESSION_TRACKING)
67
+ #define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
68
+ #define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
69
+ #endif
70
+
71
+ /*
72
+ * compatibility with mysql-connector-c 6.1.x, MySQL 5.7.3 - 5.7.10 & with MariaDB 10.x and later.
73
+ */
74
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
75
+ #define SSL_MODE_VERIFY_IDENTITY 5
76
+ #define HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
77
+ #endif
78
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
79
+ #define SSL_MODE_DISABLED 1
80
+ #define SSL_MODE_REQUIRED 3
81
+ #define HAVE_CONST_SSL_MODE_DISABLED
82
+ #define HAVE_CONST_SSL_MODE_REQUIRED
83
+ #endif
84
+
63
85
  /*
64
86
  * used to pass all arguments to mysql_real_connect while inside
65
87
  * rb_thread_call_without_gvl
@@ -96,6 +118,55 @@ struct nogvl_select_db_args {
96
118
  char *db;
97
119
  };
98
120
 
121
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
122
+ unsigned long version = mysql_get_client_version();
123
+
124
+ if (version < 50703) {
125
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
126
+ return Qnil;
127
+ }
128
+ #if defined(HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT) || defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE)
129
+ GET_CLIENT(self);
130
+ int val = NUM2INT( setting );
131
+ // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x, or MariaDB 10.x and later
132
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200) || version >= 100000) {
133
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
134
+ if (val == SSL_MODE_VERIFY_IDENTITY) {
135
+ my_bool b = 1;
136
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &b );
137
+ return INT2NUM(result);
138
+ }
139
+ #endif
140
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
141
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
142
+ my_bool b = ( val == SSL_MODE_REQUIRED );
143
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
144
+ return INT2NUM(result);
145
+ }
146
+ #endif
147
+ rb_warn( "Your mysql client library does not support ssl_mode %d.", val );
148
+ return Qnil;
149
+ } else {
150
+ rb_warn( "Your mysql client library does not support ssl_mode as expected." );
151
+ return Qnil;
152
+ }
153
+ #endif
154
+ #ifdef FULL_SSL_MODE_SUPPORT
155
+ GET_CLIENT(self);
156
+ int val = NUM2INT( setting );
157
+
158
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
159
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
160
+ }
161
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
162
+
163
+ return INT2NUM(result);
164
+ #endif
165
+ #ifdef NO_SSL_MODE_SUPPORT
166
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
167
+ return Qnil;
168
+ #endif
169
+ }
99
170
  /*
100
171
  * non-blocking mysql_*() functions that we won't be wrapping since
101
172
  * they do not appear to hit the network nor issue any interruptible
@@ -128,24 +199,23 @@ static void rb_mysql_client_mark(void * wrapper) {
128
199
 
129
200
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
130
201
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
131
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
202
+ VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
132
203
  VALUE e;
133
204
 
134
- #ifdef HAVE_RUBY_ENCODING_H
135
205
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
136
206
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137
- #endif
138
207
 
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);
208
+ e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
209
+ rb_error_msg,
210
+ LONG2FIX(wrapper->server_version),
211
+ UINT2NUM(mysql_errno(wrapper->client)),
212
+ rb_sql_state);
142
213
  rb_exc_raise(e);
143
- return Qnil;
144
214
  }
145
215
 
146
216
  static void *nogvl_init(void *ptr) {
147
217
  MYSQL *client;
148
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
218
+ mysql_client_wrapper *wrapper = ptr;
149
219
 
150
220
  /* may initialize embedded server and read /etc/services off disk */
151
221
  client = mysql_init(wrapper->client);
@@ -182,23 +252,31 @@ static void *nogvl_connect(void *ptr) {
182
252
  */
183
253
  static VALUE invalidate_fd(int clientfd)
184
254
  {
185
- #ifdef SOCK_CLOEXEC
255
+ #ifdef O_CLOEXEC
186
256
  /* Atomically set CLOEXEC on the new FD in case another thread forks */
187
257
  int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
188
- if (sockfd < 0) {
189
- /* Maybe SOCK_CLOEXEC is defined but not available on this kernel */
190
- int sockfd = open("/dev/null", O_RDWR);
191
- fcntl(sockfd, F_SETFD, FD_CLOEXEC);
192
- }
193
258
  #else
194
- /* Well we don't have SOCK_CLOEXEC, so just set FD_CLOEXEC quickly */
195
- int sockfd = open("/dev/null", O_RDWR);
196
- fcntl(sockfd, F_SETFD, FD_CLOEXEC);
259
+ /* Well we don't have O_CLOEXEC, trigger the fallback code below */
260
+ int sockfd = -1;
197
261
  #endif
198
262
 
199
263
  if (sockfd < 0) {
200
- /*
201
- * Cannot raise here, because one or both of the following may be true:
264
+ /* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
265
+ * compile time, but isn't available at run-time. So we'll just be quick
266
+ * about setting FD_CLOEXEC now.
267
+ */
268
+ int flags;
269
+ sockfd = open("/dev/null", O_RDWR);
270
+ flags = fcntl(sockfd, F_GETFD);
271
+ /* Do the flags dance in case there are more defined flags in the future */
272
+ if (flags != -1) {
273
+ flags |= FD_CLOEXEC;
274
+ fcntl(sockfd, F_SETFD, flags);
275
+ }
276
+ }
277
+
278
+ if (sockfd < 0) {
279
+ /* Cannot raise here, because one or both of the following may be true:
202
280
  * a) we have no GVL (in C Ruby)
203
281
  * b) are running as a GC finalizer
204
282
  */
@@ -213,41 +291,46 @@ static VALUE invalidate_fd(int clientfd)
213
291
  #endif /* _WIN32 */
214
292
 
215
293
  static void *nogvl_close(void *ptr) {
216
- mysql_client_wrapper *wrapper;
217
- wrapper = ptr;
218
- if (wrapper->connected) {
219
- wrapper->active_thread = Qnil;
220
- wrapper->connected = 0;
221
- #ifndef _WIN32
222
- /* Invalidate the socket before calling mysql_close(). This prevents
223
- * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
224
- * the socket. The difference is that invalidate_fd will drop this
225
- * process's reference to the socket only, while a QUIT or shutdown()
226
- * would render the underlying connection unusable, interrupting other
227
- * processes which share this object across a fork().
228
- */
229
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
230
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
231
- close(wrapper->client->net.fd);
232
- return NULL;
233
- }
234
- #endif
294
+ mysql_client_wrapper *wrapper = ptr;
235
295
 
236
- mysql_close(wrapper->client); /* only used to free memory at this point */
296
+ if (wrapper->initialized && !wrapper->closed) {
297
+ mysql_close(wrapper->client);
298
+ wrapper->closed = 1;
299
+ wrapper->reconnect_enabled = 0;
300
+ wrapper->active_thread = Qnil;
237
301
  }
238
302
 
239
303
  return NULL;
240
304
  }
241
305
 
306
+ /* this is called during GC */
242
307
  static void rb_mysql_client_free(void *ptr) {
243
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
308
+ mysql_client_wrapper *wrapper = ptr;
244
309
  decr_mysql2_client(wrapper);
245
310
  }
246
311
 
247
312
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
248
313
  {
249
314
  wrapper->refcount--;
315
+
250
316
  if (wrapper->refcount == 0) {
317
+ #ifndef _WIN32
318
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
319
+ /* The client is being garbage collected while connected. Prevent
320
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
321
+ * the socket by invalidating it. invalidate_fd() will drop this
322
+ * process's reference to the socket only, while a QUIT or shutdown()
323
+ * would render the underlying connection unusable, interrupting other
324
+ * processes which share this object across a fork().
325
+ */
326
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
327
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
328
+ close(wrapper->client->net.fd);
329
+ }
330
+ wrapper->client->net.fd = -1;
331
+ }
332
+ #endif
333
+
251
334
  nogvl_close(wrapper);
252
335
  xfree(wrapper->client);
253
336
  xfree(wrapper);
@@ -260,13 +343,15 @@ static VALUE allocate(VALUE klass) {
260
343
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
344
  wrapper->encoding = Qnil;
262
345
  wrapper->active_thread = Qnil;
346
+ wrapper->automatic_close = 1;
263
347
  wrapper->server_version = 0;
264
348
  wrapper->reconnect_enabled = 0;
265
349
  wrapper->connect_timeout = 0;
266
- wrapper->connected = 0; /* means that a database connection is open */
267
- wrapper->initialized = 0; /* means that that the wrapper is initialized */
350
+ wrapper->initialized = 0; /* will be set true after calling mysql_init */
351
+ wrapper->closed = 1; /* will be set false after calling mysql_real_connect */
268
352
  wrapper->refcount = 1;
269
353
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
354
+
270
355
  return obj;
271
356
  }
272
357
 
@@ -287,16 +372,14 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
287
372
  oldLen = RSTRING_LEN(str);
288
373
  newStr = xmalloc(oldLen*2+1);
289
374
 
290
- newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
375
+ newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
291
376
  if (newLen == oldLen) {
292
377
  /* no need to return a new ruby string if nothing changed */
293
378
  xfree(newStr);
294
379
  return str;
295
380
  } else {
296
381
  rb_str = rb_str_new((const char*)newStr, newLen);
297
- #ifdef HAVE_RUBY_ENCODING_H
298
382
  rb_enc_copy(rb_str, str);
299
- #endif
300
383
  xfree(newStr);
301
384
  return rb_str;
302
385
  }
@@ -323,29 +406,62 @@ static VALUE rb_mysql_info(VALUE self) {
323
406
  }
324
407
 
325
408
  rb_str = rb_str_new2(info);
326
- #ifdef HAVE_RUBY_ENCODING_H
327
409
  rb_enc_associate(rb_str, rb_utf8_encoding());
328
- #endif
329
410
 
330
411
  return rb_str;
331
412
  }
332
413
 
333
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
414
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
415
+ {
416
+ const char *cipher;
417
+ VALUE rb_str;
418
+ GET_CLIENT(self);
419
+
420
+ cipher = mysql_get_ssl_cipher(wrapper->client);
421
+
422
+ if (cipher == NULL) {
423
+ return Qnil;
424
+ }
425
+
426
+ rb_str = rb_str_new2(cipher);
427
+ rb_enc_associate(rb_str, rb_utf8_encoding());
428
+
429
+ return rb_str;
430
+ }
431
+
432
+ #ifdef CLIENT_CONNECT_ATTRS
433
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
434
+ {
435
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
436
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
437
+ key = rb_str_export_to_enc(key, enc);
438
+ value = rb_str_export_to_enc(value, enc);
439
+
440
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
441
+ return ST_CONTINUE;
442
+ }
443
+ #endif
444
+
445
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
334
446
  struct nogvl_connect_args args;
335
- time_t start_time, end_time;
336
- unsigned int elapsed_time, connect_timeout;
447
+ time_t start_time, end_time, elapsed_time, connect_timeout;
337
448
  VALUE rv;
338
449
  GET_CLIENT(self);
339
450
 
340
- args.host = NIL_P(host) ? NULL : StringValuePtr(host);
341
- args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
342
- args.port = NIL_P(port) ? 0 : NUM2INT(port);
343
- args.user = NIL_P(user) ? NULL : StringValuePtr(user);
344
- args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
345
- args.db = NIL_P(database) ? NULL : StringValuePtr(database);
346
- args.mysql = wrapper->client;
451
+ args.host = NIL_P(host) ? NULL : StringValueCStr(host);
452
+ args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
453
+ args.port = NIL_P(port) ? 0 : NUM2INT(port);
454
+ args.user = NIL_P(user) ? NULL : StringValueCStr(user);
455
+ args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
456
+ args.db = NIL_P(database) ? NULL : StringValueCStr(database);
457
+ args.mysql = wrapper->client;
347
458
  args.client_flag = NUM2ULONG(flags);
348
459
 
460
+ #ifdef CLIENT_CONNECT_ATTRS
461
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
462
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
463
+ #endif
464
+
349
465
  if (wrapper->connect_timeout)
350
466
  time(&start_time);
351
467
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -360,7 +476,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
360
476
  /* avoid an early timeout due to time truncating milliseconds off the start time */
361
477
  if (elapsed_time > 0)
362
478
  elapsed_time--;
363
- if (elapsed_time >= wrapper->connect_timeout)
479
+ if (elapsed_time >= (time_t)wrapper->connect_timeout)
364
480
  break;
365
481
  connect_timeout = wrapper->connect_timeout - elapsed_time;
366
482
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
@@ -372,30 +488,42 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
488
  if (wrapper->connect_timeout)
373
489
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
490
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
491
+ rb_raise_mysql2_error(wrapper);
376
492
  }
377
493
 
494
+ wrapper->closed = 0;
378
495
  wrapper->server_version = mysql_get_server_version(wrapper->client);
379
- wrapper->connected = 1;
380
496
  return self;
381
497
  }
382
498
 
383
499
  /*
384
- * Immediately disconnect from the server, normally the garbage collector
500
+ * Immediately disconnect from the server; normally the garbage collector
385
501
  * will disconnect automatically when a connection is no longer needed.
386
502
  * Explicitly closing this will free up server resources sooner than waiting
387
503
  * for the garbage collector.
504
+ *
505
+ * @return [nil]
388
506
  */
389
507
  static VALUE rb_mysql_client_close(VALUE self) {
390
508
  GET_CLIENT(self);
391
509
 
392
- if (wrapper->connected) {
510
+ if (wrapper->client) {
393
511
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
394
512
  }
395
513
 
396
514
  return Qnil;
397
515
  }
398
516
 
517
+ /* call-seq:
518
+ * client.closed?
519
+ *
520
+ * @return [Boolean]
521
+ */
522
+ static VALUE rb_mysql_client_closed(VALUE self) {
523
+ GET_CLIENT(self);
524
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
525
+ }
526
+
399
527
  /*
400
528
  * mysql_send_query is unlikely to block since most queries are small
401
529
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -410,13 +538,13 @@ static void *nogvl_send_query(void *ptr) {
410
538
  return (void*)(rv == 0 ? Qtrue : Qfalse);
411
539
  }
412
540
 
413
- static VALUE do_send_query(void *args) {
414
- struct nogvl_send_query_args *query_args = args;
541
+ static VALUE do_send_query(VALUE args) {
542
+ struct nogvl_send_query_args *query_args = (void *)args;
415
543
  mysql_client_wrapper *wrapper = query_args->wrapper;
416
- if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
544
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
417
545
  /* an error occurred, we're not active anymore */
418
- MARK_CONN_INACTIVE(self);
419
- return rb_raise_mysql2_error(wrapper);
546
+ wrapper->active_thread = Qnil;
547
+ rb_raise_mysql2_error(wrapper);
420
548
  }
421
549
  return Qnil;
422
550
  }
@@ -434,11 +562,10 @@ static void *nogvl_read_query_result(void *ptr) {
434
562
  }
435
563
 
436
564
  static void *nogvl_do_result(void *ptr, char use_result) {
437
- mysql_client_wrapper *wrapper;
565
+ mysql_client_wrapper *wrapper = ptr;
438
566
  MYSQL_RES *result;
439
567
 
440
- wrapper = (mysql_client_wrapper *)ptr;
441
- if(use_result) {
568
+ if (use_result) {
442
569
  result = mysql_use_result(wrapper->client);
443
570
  } else {
444
571
  result = mysql_store_result(wrapper->client);
@@ -478,12 +605,12 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
478
605
  REQUIRE_CONNECTED(wrapper);
479
606
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
480
607
  /* an error occurred, mark this connection inactive */
481
- MARK_CONN_INACTIVE(self);
482
- return rb_raise_mysql2_error(wrapper);
608
+ wrapper->active_thread = Qnil;
609
+ rb_raise_mysql2_error(wrapper);
483
610
  }
484
611
 
485
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
486
- if(is_streaming == Qtrue) {
612
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
613
+ if (is_streaming == Qtrue) {
487
614
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
488
615
  } else {
489
616
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
@@ -491,17 +618,20 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
491
618
 
492
619
  if (result == NULL) {
493
620
  if (mysql_errno(wrapper->client) != 0) {
494
- MARK_CONN_INACTIVE(self);
621
+ wrapper->active_thread = Qnil;
495
622
  rb_raise_mysql2_error(wrapper);
496
623
  }
497
624
  /* no data and no error, so query was not a SELECT */
498
625
  return Qnil;
499
626
  }
500
627
 
501
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
502
- RB_GC_GUARD(current);
628
+ // Duplicate the options hash and put the copy in the Result object
629
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
630
+ (void)RB_GC_GUARD(current);
503
631
  Check_Type(current, T_HASH);
504
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
632
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
633
+
634
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
505
635
 
506
636
  return resultObj;
507
637
  }
@@ -516,31 +646,30 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
516
646
  GET_CLIENT(self);
517
647
 
518
648
  wrapper->active_thread = Qnil;
519
- wrapper->connected = 0;
520
649
 
521
650
  /* Invalidate the MySQL socket to prevent further communication.
522
651
  * The GC will come along later and call mysql_close to free it.
523
652
  */
524
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
525
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
526
- close(wrapper->client->net.fd);
653
+ if (CONNECTED(wrapper)) {
654
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
655
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
656
+ close(wrapper->client->net.fd);
657
+ }
658
+ wrapper->client->net.fd = -1;
527
659
  }
528
660
 
529
661
  rb_exc_raise(error);
530
-
531
- return Qnil;
532
662
  }
533
663
 
534
- static VALUE do_query(void *args) {
535
- struct async_query_args *async_args;
664
+ static VALUE do_query(VALUE args) {
665
+ struct async_query_args *async_args = (void *)args;
536
666
  struct timeval tv;
537
- struct timeval* tvp;
667
+ struct timeval *tvp;
538
668
  long int sec;
539
669
  int retval;
540
670
  VALUE read_timeout;
541
671
 
542
- async_args = (struct async_query_args *)args;
543
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
672
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
544
673
 
545
674
  tvp = NULL;
546
675
  if (!NIL_P(read_timeout)) {
@@ -561,7 +690,7 @@ static VALUE do_query(void *args) {
561
690
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
562
691
 
563
692
  if (retval == 0) {
564
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
693
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
565
694
  }
566
695
 
567
696
  if (retval < 0) {
@@ -575,28 +704,50 @@ static VALUE do_query(void *args) {
575
704
 
576
705
  return Qnil;
577
706
  }
578
- #else
579
- static VALUE finish_and_mark_inactive(void *args) {
580
- VALUE self;
581
- MYSQL_RES *result;
582
-
583
- self = (VALUE)args;
707
+ #endif
584
708
 
709
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
585
710
  GET_CLIENT(self);
586
711
 
712
+ /* Check if execution terminated while result was still being read. */
587
713
  if (!NIL_P(wrapper->active_thread)) {
588
- /* if we got here, the result hasn't been read off the wire yet
589
- so lets do that and then throw it away because we have no way
590
- of getting it back up to the caller from here */
591
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
592
- mysql_free_result(result);
593
-
714
+ if (CONNECTED(wrapper)) {
715
+ /* Invalidate the MySQL socket to prevent further communication. */
716
+ #ifndef _WIN32
717
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
718
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
719
+ close(wrapper->client->net.fd);
720
+ }
721
+ #else
722
+ close(wrapper->client->net.fd);
723
+ #endif
724
+ wrapper->client->net.fd = -1;
725
+ }
726
+ /* Skip mysql client check performed before command execution. */
727
+ wrapper->client->status = MYSQL_STATUS_READY;
594
728
  wrapper->active_thread = Qnil;
595
729
  }
596
730
 
597
731
  return Qnil;
598
732
  }
599
- #endif
733
+
734
+ void rb_mysql_client_set_active_thread(VALUE self) {
735
+ VALUE thread_current = rb_thread_current();
736
+ GET_CLIENT(self);
737
+
738
+ // see if this connection is still waiting on a result from a previous query
739
+ if (NIL_P(wrapper->active_thread)) {
740
+ // mark this connection active
741
+ wrapper->active_thread = thread_current;
742
+ } else if (wrapper->active_thread == thread_current) {
743
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
744
+ } else {
745
+ VALUE inspect = rb_inspect(wrapper->active_thread);
746
+ const char *thr = StringValueCStr(inspect);
747
+
748
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
749
+ }
750
+ }
600
751
 
601
752
  /* call-seq:
602
753
  * client.abandon_results!
@@ -632,80 +783,51 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
632
783
  * client.query(sql, options = {})
633
784
  *
634
785
  * Query the database with +sql+, with optional +options+. For the possible
635
- * options, see @@default_query_options on the Mysql2::Client class.
786
+ * options, see default_query_options on the Mysql2::Client class.
636
787
  */
637
- static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
788
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
638
789
  #ifndef _WIN32
639
790
  struct async_query_args async_args;
640
791
  #endif
641
792
  struct nogvl_send_query_args args;
642
- int async = 0;
643
- VALUE opts, current;
644
- VALUE thread_current = rb_thread_current();
645
- #ifdef HAVE_RUBY_ENCODING_H
646
- rb_encoding *conn_enc;
647
- #endif
648
793
  GET_CLIENT(self);
649
794
 
650
795
  REQUIRE_CONNECTED(wrapper);
651
796
  args.mysql = wrapper->client;
652
797
 
653
- current = rb_hash_dup(rb_iv_get(self, "@query_options"));
654
- RB_GC_GUARD(current);
798
+ (void)RB_GC_GUARD(current);
655
799
  Check_Type(current, T_HASH);
656
- rb_iv_set(self, "@current_query_options", current);
657
-
658
- if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
659
- rb_funcall(current, intern_merge_bang, 1, opts);
660
-
661
- if (rb_hash_aref(current, sym_async) == Qtrue) {
662
- async = 1;
663
- }
664
- }
800
+ rb_ivar_set(self, intern_current_query_options, current);
665
801
 
666
- Check_Type(args.sql, T_STRING);
667
- #ifdef HAVE_RUBY_ENCODING_H
668
- conn_enc = rb_to_encoding(wrapper->encoding);
802
+ Check_Type(sql, T_STRING);
669
803
  /* ensure the string is in the encoding the connection is expecting */
670
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
671
- #endif
672
- args.sql_ptr = StringValuePtr(args.sql);
804
+ args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
805
+ args.sql_ptr = RSTRING_PTR(args.sql);
673
806
  args.sql_len = RSTRING_LEN(args.sql);
674
-
675
- /* see if this connection is still waiting on a result from a previous query */
676
- if (NIL_P(wrapper->active_thread)) {
677
- /* mark this connection active */
678
- wrapper->active_thread = thread_current;
679
- } else if (wrapper->active_thread == thread_current) {
680
- rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
681
- } else {
682
- VALUE inspect = rb_inspect(wrapper->active_thread);
683
- const char *thr = StringValueCStr(inspect);
684
-
685
- rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
686
- RB_GC_GUARD(inspect);
687
- }
688
-
689
807
  args.wrapper = wrapper;
690
808
 
809
+ rb_mysql_client_set_active_thread(self);
810
+
691
811
  #ifndef _WIN32
692
812
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
813
+ (void)RB_GC_GUARD(sql);
693
814
 
694
- if (!async) {
815
+ if (rb_hash_aref(current, sym_async) == Qtrue) {
816
+ return Qnil;
817
+ } else {
695
818
  async_args.fd = wrapper->client->net.fd;
696
819
  async_args.self = self;
697
820
 
698
821
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
699
822
 
700
- return rb_mysql_client_async_result(self);
701
- } else {
702
- return Qnil;
823
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
703
824
  }
704
825
  #else
705
- do_send_query(&args);
826
+ do_send_query((VALUE)&args);
827
+ (void)RB_GC_GUARD(sql);
706
828
 
707
829
  /* this will just block until the result is ready */
708
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
830
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
709
831
  #endif
710
832
  }
711
833
 
@@ -718,37 +840,34 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
718
840
  unsigned char *newStr;
719
841
  VALUE rb_str;
720
842
  unsigned long newLen, oldLen;
721
- #ifdef HAVE_RUBY_ENCODING_H
722
843
  rb_encoding *default_internal_enc;
723
844
  rb_encoding *conn_enc;
724
- #endif
725
845
  GET_CLIENT(self);
726
846
 
727
847
  REQUIRE_CONNECTED(wrapper);
728
848
  Check_Type(str, T_STRING);
729
- #ifdef HAVE_RUBY_ENCODING_H
730
849
  default_internal_enc = rb_default_internal_encoding();
731
850
  conn_enc = rb_to_encoding(wrapper->encoding);
732
851
  /* ensure the string is in the encoding the connection is expecting */
733
852
  str = rb_str_export_to_enc(str, conn_enc);
734
- #endif
735
853
 
736
854
  oldLen = RSTRING_LEN(str);
737
855
  newStr = xmalloc(oldLen*2+1);
738
856
 
739
- newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
857
+ newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
740
858
  if (newLen == oldLen) {
741
859
  /* no need to return a new ruby string if nothing changed */
860
+ if (default_internal_enc) {
861
+ str = rb_str_export_to_enc(str, default_internal_enc);
862
+ }
742
863
  xfree(newStr);
743
864
  return str;
744
865
  } else {
745
866
  rb_str = rb_str_new((const char*)newStr, newLen);
746
- #ifdef HAVE_RUBY_ENCODING_H
747
867
  rb_enc_associate(rb_str, conn_enc);
748
868
  if (default_internal_enc) {
749
869
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
750
870
  }
751
- #endif
752
871
  xfree(newStr);
753
872
  return rb_str;
754
873
  }
@@ -794,25 +913,41 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
794
913
  retval = &boolval;
795
914
  break;
796
915
 
916
+ #ifdef MYSQL_SECURE_AUTH
797
917
  case MYSQL_SECURE_AUTH:
798
918
  boolval = (value == Qfalse ? 0 : 1);
799
919
  retval = &boolval;
800
920
  break;
921
+ #endif
801
922
 
802
923
  case MYSQL_READ_DEFAULT_FILE:
803
- charval = (const char *)StringValuePtr(value);
924
+ charval = (const char *)StringValueCStr(value);
804
925
  retval = charval;
805
926
  break;
806
927
 
807
928
  case MYSQL_READ_DEFAULT_GROUP:
808
- charval = (const char *)StringValuePtr(value);
929
+ charval = (const char *)StringValueCStr(value);
809
930
  retval = charval;
810
931
  break;
811
932
 
812
933
  case MYSQL_INIT_COMMAND:
813
- charval = (const char *)StringValuePtr(value);
934
+ charval = (const char *)StringValueCStr(value);
935
+ retval = charval;
936
+ break;
937
+
938
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
939
+ case MYSQL_DEFAULT_AUTH:
940
+ charval = (const char *)StringValueCStr(value);
814
941
  retval = charval;
815
942
  break;
943
+ #endif
944
+
945
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
946
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
947
+ boolval = (value == Qfalse ? 0 : 1);
948
+ retval = &boolval;
949
+ break;
950
+ #endif
816
951
 
817
952
  default:
818
953
  return Qfalse;
@@ -843,30 +978,21 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
843
978
  *
844
979
  * Returns a string that represents the client library version.
845
980
  */
846
- static VALUE rb_mysql_client_info(VALUE self) {
847
- VALUE version, client_info;
848
- #ifdef HAVE_RUBY_ENCODING_H
849
- rb_encoding *default_internal_enc;
850
- rb_encoding *conn_enc;
851
- GET_CLIENT(self);
852
- #endif
853
- version = rb_hash_new();
981
+ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
982
+ VALUE version_info, version, header_version;
983
+ version_info = rb_hash_new();
854
984
 
855
- #ifdef HAVE_RUBY_ENCODING_H
856
- default_internal_enc = rb_default_internal_encoding();
857
- conn_enc = rb_to_encoding(wrapper->encoding);
858
- #endif
985
+ version = rb_str_new2(mysql_get_client_info());
986
+ header_version = rb_str_new2(MYSQL_LINK_VERSION);
859
987
 
860
- rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
861
- client_info = rb_str_new2(mysql_get_client_info());
862
- #ifdef HAVE_RUBY_ENCODING_H
863
- rb_enc_associate(client_info, conn_enc);
864
- if (default_internal_enc) {
865
- client_info = rb_str_export_to_enc(client_info, default_internal_enc);
866
- }
867
- #endif
868
- rb_hash_aset(version, sym_version, client_info);
869
- return version;
988
+ rb_enc_associate(version, rb_usascii_encoding());
989
+ rb_enc_associate(header_version, rb_usascii_encoding());
990
+
991
+ rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
992
+ rb_hash_aset(version_info, sym_version, version);
993
+ rb_hash_aset(version_info, sym_header_version, header_version);
994
+
995
+ return version_info;
870
996
  }
871
997
 
872
998
  /* call-seq:
@@ -876,27 +1002,21 @@ static VALUE rb_mysql_client_info(VALUE self) {
876
1002
  */
877
1003
  static VALUE rb_mysql_client_server_info(VALUE self) {
878
1004
  VALUE version, server_info;
879
- #ifdef HAVE_RUBY_ENCODING_H
880
1005
  rb_encoding *default_internal_enc;
881
1006
  rb_encoding *conn_enc;
882
- #endif
883
1007
  GET_CLIENT(self);
884
1008
 
885
1009
  REQUIRE_CONNECTED(wrapper);
886
- #ifdef HAVE_RUBY_ENCODING_H
887
1010
  default_internal_enc = rb_default_internal_encoding();
888
1011
  conn_enc = rb_to_encoding(wrapper->encoding);
889
- #endif
890
1012
 
891
1013
  version = rb_hash_new();
892
1014
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
893
1015
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
894
- #ifdef HAVE_RUBY_ENCODING_H
895
1016
  rb_enc_associate(server_info, conn_enc);
896
1017
  if (default_internal_enc) {
897
1018
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
898
1019
  }
899
- #endif
900
1020
  rb_hash_aset(version, sym_version, server_info);
901
1021
  return version;
902
1022
  }
@@ -906,19 +1026,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
906
1026
  *
907
1027
  * Return the file descriptor number for this client.
908
1028
  */
1029
+ #ifndef _WIN32
909
1030
  static VALUE rb_mysql_client_socket(VALUE self) {
910
1031
  GET_CLIENT(self);
911
- #ifndef _WIN32
912
- {
913
- int fd_set_fd;
914
- REQUIRE_CONNECTED(wrapper);
915
- fd_set_fd = wrapper->client->net.fd;
916
- return INT2NUM(fd_set_fd);
917
- }
1032
+ REQUIRE_CONNECTED(wrapper);
1033
+ return INT2NUM(wrapper->client->net.fd);
1034
+ }
918
1035
  #else
1036
+ static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
919
1037
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
920
- #endif
921
1038
  }
1039
+ #endif
922
1040
 
923
1041
  /* call-seq:
924
1042
  * client.last_id
@@ -932,6 +1050,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
932
1050
  return ULL2NUM(mysql_insert_id(wrapper->client));
933
1051
  }
934
1052
 
1053
+ /* call-seq:
1054
+ * client.session_track
1055
+ *
1056
+ * Returns information about changes to the session state on the server.
1057
+ */
1058
+ static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
1059
+ #ifdef CLIENT_SESSION_TRACK
1060
+ const char *data;
1061
+ size_t length;
1062
+ my_ulonglong retVal;
1063
+ GET_CLIENT(self);
1064
+
1065
+ REQUIRE_CONNECTED(wrapper);
1066
+ retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
1067
+ if (retVal != 0) {
1068
+ return Qnil;
1069
+ }
1070
+ VALUE rbAry = rb_ary_new();
1071
+ VALUE rbFirst = rb_str_new(data, length);
1072
+ rb_ary_push(rbAry, rbFirst);
1073
+ while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
1074
+ VALUE rbNext = rb_str_new(data, length);
1075
+ rb_ary_push(rbAry, rbNext);
1076
+ }
1077
+ return rbAry;
1078
+ #else
1079
+ return Qnil;
1080
+ #endif
1081
+ }
1082
+
935
1083
  /* call-seq:
936
1084
  * client.affected_rows
937
1085
  *
@@ -987,7 +1135,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
987
1135
  REQUIRE_CONNECTED(wrapper);
988
1136
 
989
1137
  args.mysql = wrapper->client;
990
- args.db = StringValuePtr(db);
1138
+ args.db = StringValueCStr(db);
991
1139
 
992
1140
  if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
993
1141
  rb_raise_mysql2_error(wrapper);
@@ -1012,13 +1160,30 @@ static void *nogvl_ping(void *ptr) {
1012
1160
  static VALUE rb_mysql_client_ping(VALUE self) {
1013
1161
  GET_CLIENT(self);
1014
1162
 
1015
- if (!wrapper->connected) {
1163
+ if (!CONNECTED(wrapper)) {
1016
1164
  return Qfalse;
1017
1165
  } else {
1018
1166
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1019
1167
  }
1020
1168
  }
1021
1169
 
1170
+ /* call-seq:
1171
+ * client.set_server_option(value)
1172
+ *
1173
+ * Enables or disables an option for the connection.
1174
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1175
+ * for more information.
1176
+ */
1177
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1178
+ GET_CLIENT(self);
1179
+
1180
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1181
+ return Qtrue;
1182
+ } else {
1183
+ return Qfalse;
1184
+ }
1185
+ }
1186
+
1022
1187
  /* call-seq:
1023
1188
  * client.more_results?
1024
1189
  *
@@ -1027,10 +1192,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1027
1192
  static VALUE rb_mysql_client_more_results(VALUE self)
1028
1193
  {
1029
1194
  GET_CLIENT(self);
1030
- if (mysql_more_results(wrapper->client) == 0)
1031
- return Qfalse;
1032
- else
1033
- return Qtrue;
1195
+ if (mysql_more_results(wrapper->client) == 0)
1196
+ return Qfalse;
1197
+ else
1198
+ return Qtrue;
1034
1199
  }
1035
1200
 
1036
1201
  /* call-seq:
@@ -1077,15 +1242,15 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1077
1242
  return Qnil;
1078
1243
  }
1079
1244
 
1080
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1081
- RB_GC_GUARD(current);
1245
+ // Duplicate the options hash and put the copy in the Result object
1246
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1247
+ (void)RB_GC_GUARD(current);
1082
1248
  Check_Type(current, T_HASH);
1083
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1249
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
1084
1250
 
1085
1251
  return resultObj;
1086
1252
  }
1087
1253
 
1088
- #ifdef HAVE_RUBY_ENCODING_H
1089
1254
  /* call-seq:
1090
1255
  * client.encoding
1091
1256
  *
@@ -1095,7 +1260,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1095
1260
  GET_CLIENT(self);
1096
1261
  return wrapper->encoding;
1097
1262
  }
1263
+
1264
+ /* call-seq:
1265
+ * client.automatic_close?
1266
+ *
1267
+ * @return [Boolean]
1268
+ */
1269
+ static VALUE get_automatic_close(VALUE self) {
1270
+ GET_CLIENT(self);
1271
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1272
+ }
1273
+
1274
+ /* call-seq:
1275
+ * client.automatic_close = false
1276
+ *
1277
+ * Set this to +false+ to leave the connection open after it is garbage
1278
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1279
+ * call +close+ when the connection is no longer needed.
1280
+ *
1281
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1282
+ */
1283
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1284
+ GET_CLIENT(self);
1285
+ if (RTEST(value)) {
1286
+ wrapper->automatic_close = 1;
1287
+ } else {
1288
+ #ifndef _WIN32
1289
+ wrapper->automatic_close = 0;
1290
+ #else
1291
+ rb_warn("Connections are always closed by garbage collector on Windows");
1098
1292
  #endif
1293
+ }
1294
+ return value;
1295
+ }
1099
1296
 
1100
1297
  /* call-seq:
1101
1298
  * client.reconnect = true
@@ -1132,7 +1329,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1132
1329
  /* Set the instance variable here even though _mysql_client_options
1133
1330
  might not succeed, because the timeout is used in other ways
1134
1331
  elsewhere */
1135
- rb_iv_set(self, "@read_timeout", value);
1332
+ rb_ivar_set(self, intern_read_timeout, value);
1136
1333
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1137
1334
  }
1138
1335
 
@@ -1148,19 +1345,15 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1148
1345
 
1149
1346
  static VALUE set_charset_name(VALUE self, VALUE value) {
1150
1347
  char *charset_name;
1151
- #ifdef HAVE_RUBY_ENCODING_H
1152
- size_t charset_name_len;
1153
1348
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1154
1349
  rb_encoding *enc;
1155
1350
  VALUE rb_enc;
1156
- #endif
1157
1351
  GET_CLIENT(self);
1158
1352
 
1353
+ Check_Type(value, T_STRING);
1159
1354
  charset_name = RSTRING_PTR(value);
1160
1355
 
1161
- #ifdef HAVE_RUBY_ENCODING_H
1162
- charset_name_len = RSTRING_LEN(value);
1163
- mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
1356
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1164
1357
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1165
1358
  VALUE inspect = rb_inspect(value);
1166
1359
  rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
@@ -1169,7 +1362,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1169
1362
  rb_enc = rb_enc_from_encoding(enc);
1170
1363
  wrapper->encoding = rb_enc;
1171
1364
  }
1172
- #endif
1173
1365
 
1174
1366
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1175
1367
  /* TODO: warning - unable to set charset */
@@ -1183,17 +1375,22 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1183
1375
  GET_CLIENT(self);
1184
1376
 
1185
1377
  mysql_ssl_set(wrapper->client,
1186
- NIL_P(key) ? NULL : StringValuePtr(key),
1187
- NIL_P(cert) ? NULL : StringValuePtr(cert),
1188
- NIL_P(ca) ? NULL : StringValuePtr(ca),
1189
- NIL_P(capath) ? NULL : StringValuePtr(capath),
1190
- NIL_P(cipher) ? NULL : StringValuePtr(cipher));
1378
+ NIL_P(key) ? NULL : StringValueCStr(key),
1379
+ NIL_P(cert) ? NULL : StringValueCStr(cert),
1380
+ NIL_P(ca) ? NULL : StringValueCStr(ca),
1381
+ NIL_P(capath) ? NULL : StringValueCStr(capath),
1382
+ NIL_P(cipher) ? NULL : StringValueCStr(cipher));
1191
1383
 
1192
1384
  return self;
1193
1385
  }
1194
1386
 
1195
1387
  static VALUE set_secure_auth(VALUE self, VALUE value) {
1388
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1389
+ #ifdef MYSQL_SECURE_AUTH
1196
1390
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1391
+ #else
1392
+ return Qfalse;
1393
+ #endif
1197
1394
  }
1198
1395
 
1199
1396
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1208,19 +1405,47 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1208
1405
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1209
1406
  }
1210
1407
 
1408
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1409
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1410
+ return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1411
+ #else
1412
+ rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
1413
+ #endif
1414
+ }
1415
+
1416
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1417
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1418
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1419
+ #else
1420
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1421
+ #endif
1422
+ }
1423
+
1211
1424
  static VALUE initialize_ext(VALUE self) {
1212
1425
  GET_CLIENT(self);
1213
1426
 
1214
1427
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1215
1428
  /* TODO: warning - not enough memory? */
1216
- return rb_raise_mysql2_error(wrapper);
1429
+ rb_raise_mysql2_error(wrapper);
1217
1430
  }
1218
1431
 
1219
1432
  wrapper->initialized = 1;
1220
1433
  return self;
1221
1434
  }
1222
1435
 
1436
+ /* call-seq: client.prepare # => Mysql2::Statement
1437
+ *
1438
+ * Create a new prepared statement.
1439
+ */
1440
+ static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1441
+ GET_CLIENT(self);
1442
+ REQUIRE_CONNECTED(wrapper);
1443
+
1444
+ return rb_mysql_stmt_new(self, sql);
1445
+ }
1446
+
1223
1447
  void init_mysql2_client() {
1448
+ #ifdef _WIN32
1224
1449
  /* verify the libmysql we're about to use was the version we were built against
1225
1450
  https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
1226
1451
  int i;
@@ -1235,48 +1460,52 @@ void init_mysql2_client() {
1235
1460
  }
1236
1461
  if (lib[i] != MYSQL_LINK_VERSION[i]) {
1237
1462
  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);
1238
- return;
1239
1463
  }
1240
1464
  }
1465
+ #endif
1241
1466
 
1242
1467
  /* Initializing mysql library, so different threads could call Client.new */
1243
1468
  /* without race condition in the library */
1244
1469
  if (mysql_library_init(0, NULL, NULL) != 0) {
1245
1470
  rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1246
- return;
1247
1471
  }
1248
1472
 
1249
1473
  #if 0
1250
1474
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1251
1475
  #endif
1252
1476
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1477
+ rb_global_variable(&cMysql2Client);
1253
1478
 
1254
1479
  rb_define_alloc_func(cMysql2Client, allocate);
1255
1480
 
1256
1481
  rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
1482
+ rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1257
1483
 
1258
1484
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1259
- rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1485
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1260
1486
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1261
1487
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1262
- rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1263
1488
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
1264
1489
  rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
1265
1490
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
1266
1491
  rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
1267
1492
  rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1493
+ rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
1268
1494
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1269
1495
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1270
1496
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1497
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1271
1498
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1272
1499
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1273
1500
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1501
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1502
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1274
1503
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1275
1504
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1276
1505
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1277
- #ifdef HAVE_RUBY_ENCODING_H
1506
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1278
1507
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1279
- #endif
1508
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1280
1509
 
1281
1510
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1282
1511
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1287,26 +1516,41 @@ void init_mysql2_client() {
1287
1516
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1288
1517
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1289
1518
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1519
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1290
1520
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1521
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1522
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1291
1523
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1292
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1524
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1525
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1293
1526
 
1294
1527
  sym_id = ID2SYM(rb_intern("id"));
1295
1528
  sym_version = ID2SYM(rb_intern("version"));
1529
+ sym_header_version = ID2SYM(rb_intern("header_version"));
1296
1530
  sym_async = ID2SYM(rb_intern("async"));
1297
1531
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1298
1532
  sym_as = ID2SYM(rb_intern("as"));
1299
1533
  sym_array = ID2SYM(rb_intern("array"));
1300
1534
  sym_stream = ID2SYM(rb_intern("stream"));
1301
1535
 
1536
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1537
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1538
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1539
+
1540
+ intern_brackets = rb_intern("[]");
1302
1541
  intern_merge = rb_intern("merge");
1303
1542
  intern_merge_bang = rb_intern("merge!");
1304
- intern_error_number_eql = rb_intern("error_number=");
1305
- intern_sql_state_eql = rb_intern("sql_state=");
1543
+ intern_new_with_args = rb_intern("new_with_args");
1544
+ intern_current_query_options = rb_intern("@current_query_options");
1545
+ intern_read_timeout = rb_intern("@read_timeout");
1306
1546
 
1307
1547
  #ifdef CLIENT_LONG_PASSWORD
1308
1548
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1309
1549
  LONG2NUM(CLIENT_LONG_PASSWORD));
1550
+ #else
1551
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1552
+ * but we're using it in our default connection flags. */
1553
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1310
1554
  #endif
1311
1555
 
1312
1556
  #ifdef CLIENT_FOUND_ROWS
@@ -1384,6 +1628,16 @@ void init_mysql2_client() {
1384
1628
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1385
1629
  #endif
1386
1630
 
1631
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1632
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1633
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1634
+ #endif
1635
+
1636
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1637
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1638
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1639
+ #endif
1640
+
1387
1641
  #ifdef CLIENT_MULTI_STATEMENTS
1388
1642
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1389
1643
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1413,4 +1667,83 @@ void init_mysql2_client() {
1413
1667
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1414
1668
  LONG2NUM(CLIENT_BASIC_FLAGS));
1415
1669
  #endif
1670
+
1671
+ #ifdef CLIENT_CONNECT_ATTRS
1672
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1673
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1674
+ #else
1675
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1676
+ * but we're using it in our default connection flags. */
1677
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1678
+ INT2NUM(0));
1679
+ #endif
1680
+
1681
+ #ifdef CLIENT_SESSION_TRACK
1682
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1683
+ /* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1684
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1685
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1686
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1687
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1688
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1689
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1690
+ #endif
1691
+
1692
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1693
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1694
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1695
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1696
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1697
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1698
+ #else
1699
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1700
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1701
+ #endif
1702
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1703
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1704
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1705
+ #endif
1706
+ #endif
1707
+
1708
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1709
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1710
+ #endif
1711
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1712
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1713
+ #endif
1714
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1715
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1716
+ #endif
1717
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1718
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1719
+ #endif
1720
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1721
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1722
+ #endif
1723
+ }
1724
+
1725
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1726
+
1727
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1728
+ VALUE server_flags = rb_hash_new();
1729
+
1730
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1731
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1732
+ #else
1733
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1734
+ #endif
1735
+
1736
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1737
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1738
+ #else
1739
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1740
+ #endif
1741
+
1742
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1743
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1744
+ #else
1745
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1746
+ #endif
1747
+
1748
+ rb_iv_set(result, "@server_flags", server_flags);
1416
1749
  }