mysql2 0.2.24 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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
  }