mysql2 0.3.1 → 0.5.2

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -151
  3. data/LICENSE +21 -0
  4. data/README.md +634 -0
  5. data/examples/eventmachine.rb +1 -3
  6. data/examples/threaded.rb +5 -9
  7. data/ext/mysql2/client.c +1154 -342
  8. data/ext/mysql2/client.h +20 -33
  9. data/ext/mysql2/extconf.rb +229 -37
  10. data/ext/mysql2/infile.c +122 -0
  11. data/ext/mysql2/infile.h +1 -0
  12. data/ext/mysql2/mysql2_ext.c +3 -1
  13. data/ext/mysql2/mysql2_ext.h +18 -16
  14. data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
  15. data/ext/mysql2/mysql_enc_to_ruby.h +259 -0
  16. data/ext/mysql2/result.c +708 -191
  17. data/ext/mysql2/result.h +15 -6
  18. data/ext/mysql2/statement.c +602 -0
  19. data/ext/mysql2/statement.h +17 -0
  20. data/ext/mysql2/wait_for_single_fd.h +37 -0
  21. data/lib/mysql2.rb +69 -7
  22. data/lib/mysql2/client.rb +126 -211
  23. data/lib/mysql2/console.rb +5 -0
  24. data/lib/mysql2/em.rb +24 -8
  25. data/lib/mysql2/error.rb +93 -8
  26. data/lib/mysql2/field.rb +3 -0
  27. data/lib/mysql2/result.rb +2 -0
  28. data/lib/mysql2/statement.rb +11 -0
  29. data/lib/mysql2/version.rb +2 -2
  30. data/spec/configuration.yml.example +11 -0
  31. data/spec/em/em_spec.rb +101 -15
  32. data/spec/my.cnf.example +9 -0
  33. data/spec/mysql2/client_spec.rb +874 -232
  34. data/spec/mysql2/error_spec.rb +55 -46
  35. data/spec/mysql2/result_spec.rb +306 -154
  36. data/spec/mysql2/statement_spec.rb +712 -0
  37. data/spec/spec_helper.rb +103 -57
  38. data/spec/ssl/ca-cert.pem +17 -0
  39. data/spec/ssl/ca-key.pem +27 -0
  40. data/spec/ssl/ca.cnf +22 -0
  41. data/spec/ssl/cert.cnf +22 -0
  42. data/spec/ssl/client-cert.pem +17 -0
  43. data/spec/ssl/client-key.pem +27 -0
  44. data/spec/ssl/client-req.pem +15 -0
  45. data/spec/ssl/gen_certs.sh +48 -0
  46. data/spec/ssl/pkcs8-client-key.pem +28 -0
  47. data/spec/ssl/pkcs8-server-key.pem +28 -0
  48. data/spec/ssl/server-cert.pem +17 -0
  49. data/spec/ssl/server-key.pem +27 -0
  50. data/spec/ssl/server-req.pem +15 -0
  51. data/spec/test_data +1 -0
  52. data/support/5072E1F5.asc +432 -0
  53. data/support/libmysql.def +219 -0
  54. data/support/mysql_enc_to_ruby.rb +81 -0
  55. data/support/ruby_enc_to_mysql.rb +61 -0
  56. metadata +82 -188
  57. data/.gitignore +0 -12
  58. data/.rspec +0 -2
  59. data/.rvmrc +0 -1
  60. data/Gemfile +0 -3
  61. data/MIT-LICENSE +0 -20
  62. data/README.rdoc +0 -257
  63. data/Rakefile +0 -5
  64. data/benchmark/active_record.rb +0 -51
  65. data/benchmark/active_record_threaded.rb +0 -42
  66. data/benchmark/allocations.rb +0 -33
  67. data/benchmark/escape.rb +0 -36
  68. data/benchmark/query_with_mysql_casting.rb +0 -80
  69. data/benchmark/query_without_mysql_casting.rb +0 -47
  70. data/benchmark/sequel.rb +0 -37
  71. data/benchmark/setup_db.rb +0 -119
  72. data/benchmark/threaded.rb +0 -44
  73. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +0 -64
  74. data/lib/active_record/fiber_patches.rb +0 -104
  75. data/lib/mysql2/em_fiber.rb +0 -31
  76. data/mysql2.gemspec +0 -32
  77. data/spec/em/em_fiber_spec.rb +0 -22
  78. data/tasks/benchmarks.rake +0 -20
  79. data/tasks/compile.rake +0 -71
  80. data/tasks/rspec.rake +0 -16
  81. data/tasks/vendor_mysql.rake +0 -40
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  $LOAD_PATH.unshift 'lib'
4
2
 
5
3
  require 'rubygems'
@@ -18,4 +16,4 @@ EM.run do
18
16
  defer2.callback do |result|
19
17
  puts "Result: #{result.to_a.inspect}"
20
18
  end
21
- end
19
+ end
data/examples/threaded.rb CHANGED
@@ -1,20 +1,16 @@
1
- # encoding: utf-8
2
-
3
1
  $LOAD_PATH.unshift 'lib'
4
2
  require 'mysql2'
5
3
  require 'timeout'
6
4
 
7
- threads = []
8
5
  # Should never exceed worst case 3.5 secs across all 20 threads
9
6
  Timeout.timeout(3.5) do
10
- 20.times do
11
- threads << Thread.new do
7
+ Array.new(20) do
8
+ Thread.new do
12
9
  overhead = rand(3)
13
10
  puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
14
11
  # 3 second overhead per query
15
- Mysql2::Client.new(:host => "localhost", :username => "root").query("SELECT sleep(#{overhead}) as result")
12
+ Mysql2::Client.new(host: "localhost", username: "root").query("SELECT sleep(#{overhead}) as result")
16
13
  puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
17
14
  end
18
- end
19
- threads.each{|t| t.join }
20
- end
15
+ end.each(&:join)
16
+ end
data/ext/mysql2/client.c CHANGED
@@ -1,28 +1,74 @@
1
1
  #include <mysql2_ext.h>
2
- #include <client.h>
2
+
3
+ #include <time.h>
3
4
  #include <errno.h>
5
+ #ifndef _WIN32
6
+ #include <sys/types.h>
7
+ #include <sys/socket.h>
8
+ #endif
9
+ #ifndef _MSC_VER
10
+ #include <unistd.h>
11
+ #endif
12
+ #include <fcntl.h>
13
+ #include "wait_for_single_fd.h"
14
+
15
+ #include "mysql_enc_name_to_ruby.h"
4
16
 
5
17
  VALUE cMysql2Client;
6
- extern VALUE mMysql2, cMysql2Error;
7
- static VALUE intern_encoding_from_charset;
8
- static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
9
- static ID intern_merge, intern_error_number_eql, intern_sql_state_eql;
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
+
23
+ #define REQUIRE_INITIALIZED(wrapper) \
24
+ if (!wrapper->initialized) { \
25
+ rb_raise(cMysql2Error, "MySQL client is not initialized"); \
26
+ }
27
+
28
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
29
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
30
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
31
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
32
+ #endif
10
33
 
11
- #define REQUIRE_OPEN_DB(wrapper) \
12
- if(wrapper->closed) { \
13
- rb_raise(cMysql2Error, "closed MySQL connection"); \
34
+ #define REQUIRE_CONNECTED(wrapper) \
35
+ REQUIRE_INITIALIZED(wrapper) \
36
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
37
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
14
38
  }
15
39
 
16
- #define MARK_CONN_INACTIVE(conn) \
17
- wrapper->active = 0
40
+ #define REQUIRE_NOT_CONNECTED(wrapper) \
41
+ REQUIRE_INITIALIZED(wrapper) \
42
+ if (CONNECTED(wrapper)) { \
43
+ rb_raise(cMysql2Error, "MySQL connection is already open"); \
44
+ }
18
45
 
19
- #define GET_CLIENT(self) \
20
- mysql_client_wrapper *wrapper; \
21
- Data_Get_Struct(self, mysql_client_wrapper, wrapper)
46
+ /*
47
+ * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
49
+ * linking against the server itself
50
+ */
51
+ #if defined(MARIADB_CLIENT_VERSION_STR)
52
+ #define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
53
+ #elif defined(LIBMYSQL_VERSION)
54
+ #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
55
+ #else
56
+ #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
57
+ #endif
58
+
59
+ /*
60
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
61
+ */
62
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
63
+ #define SSL_MODE_DISABLED 1
64
+ #define SSL_MODE_REQUIRED 3
65
+ #define HAVE_CONST_SSL_MODE_DISABLED
66
+ #define HAVE_CONST_SSL_MODE_REQUIRED
67
+ #endif
22
68
 
23
69
  /*
24
70
  * used to pass all arguments to mysql_real_connect while inside
25
- * rb_thread_blocking_region
71
+ * rb_thread_call_without_gvl
26
72
  */
27
73
  struct nogvl_connect_args {
28
74
  MYSQL *mysql;
@@ -37,13 +83,61 @@ struct nogvl_connect_args {
37
83
 
38
84
  /*
39
85
  * used to pass all arguments to mysql_send_query while inside
40
- * rb_thread_blocking_region
86
+ * rb_thread_call_without_gvl
41
87
  */
42
88
  struct nogvl_send_query_args {
43
89
  MYSQL *mysql;
44
90
  VALUE sql;
91
+ const char *sql_ptr;
92
+ long sql_len;
93
+ mysql_client_wrapper *wrapper;
45
94
  };
46
95
 
96
+ /*
97
+ * used to pass all arguments to mysql_select_db while inside
98
+ * rb_thread_call_without_gvl
99
+ */
100
+ struct nogvl_select_db_args {
101
+ MYSQL *mysql;
102
+ char *db;
103
+ };
104
+
105
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
106
+ unsigned long version = mysql_get_client_version();
107
+
108
+ if (version < 50703) {
109
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
110
+ return Qnil;
111
+ }
112
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
113
+ GET_CLIENT(self);
114
+ int val = NUM2INT( setting );
115
+ if (version >= 50703 && version < 50711) {
116
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
117
+ my_bool b = ( val == SSL_MODE_REQUIRED );
118
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
119
+ return INT2NUM(result);
120
+ } else {
121
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
122
+ return Qnil;
123
+ }
124
+ }
125
+ #endif
126
+ #ifdef FULL_SSL_MODE_SUPPORT
127
+ GET_CLIENT(self);
128
+ int val = NUM2INT( setting );
129
+
130
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
131
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
132
+ }
133
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
134
+
135
+ return INT2NUM(result);
136
+ #endif
137
+ #ifdef NO_SSL_MODE_SUPPORT
138
+ return Qnil;
139
+ #endif
140
+ }
47
141
  /*
48
142
  * non-blocking mysql_*() functions that we won't be wrapping since
49
143
  * they do not appear to hit the network nor issue any interruptible
@@ -70,94 +164,148 @@ static void rb_mysql_client_mark(void * wrapper) {
70
164
  mysql_client_wrapper * w = wrapper;
71
165
  if (w) {
72
166
  rb_gc_mark(w->encoding);
167
+ rb_gc_mark(w->active_thread);
73
168
  }
74
169
  }
75
170
 
76
171
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
77
172
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
78
173
  VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
79
- #ifdef HAVE_RUBY_ENCODING_H
80
- rb_encoding *default_internal_enc = rb_default_internal_encoding();
81
- rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
174
+ VALUE e;
82
175
 
83
- rb_enc_associate(rb_error_msg, conn_enc);
84
- rb_enc_associate(rb_sql_state, conn_enc);
85
- if (default_internal_enc) {
86
- rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
87
- rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
88
- }
89
- #endif
176
+ rb_enc_associate(rb_error_msg, rb_utf8_encoding());
177
+ rb_enc_associate(rb_sql_state, rb_usascii_encoding());
90
178
 
91
- VALUE e = rb_exc_new3(cMysql2Error, rb_error_msg);
92
- rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
93
- rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
179
+ e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
180
+ rb_error_msg,
181
+ LONG2FIX(wrapper->server_version),
182
+ UINT2NUM(mysql_errno(wrapper->client)),
183
+ rb_sql_state);
94
184
  rb_exc_raise(e);
95
- return Qnil;
96
185
  }
97
186
 
98
- static VALUE nogvl_init(void *ptr) {
187
+ static void *nogvl_init(void *ptr) {
99
188
  MYSQL *client;
189
+ mysql_client_wrapper *wrapper = ptr;
100
190
 
101
191
  /* may initialize embedded server and read /etc/services off disk */
102
- client = mysql_init((MYSQL *)ptr);
103
- return client ? Qtrue : Qfalse;
192
+ client = mysql_init(wrapper->client);
193
+
194
+ if (client) mysql2_set_local_infile(client, wrapper);
195
+
196
+ return (void*)(client ? Qtrue : Qfalse);
104
197
  }
105
198
 
106
- static VALUE nogvl_connect(void *ptr) {
199
+ static void *nogvl_connect(void *ptr) {
107
200
  struct nogvl_connect_args *args = ptr;
108
201
  MYSQL *client;
109
202
 
110
- do {
111
- client = mysql_real_connect(args->mysql, args->host,
112
- args->user, args->passwd,
113
- args->db, args->port, args->unix_socket,
114
- args->client_flag);
115
- } while (! client && errno == EINTR && (errno = 0) == 0);
203
+ client = mysql_real_connect(args->mysql, args->host,
204
+ args->user, args->passwd,
205
+ args->db, args->port, args->unix_socket,
206
+ args->client_flag);
116
207
 
117
- return client ? Qtrue : Qfalse;
208
+ return (void *)(client ? Qtrue : Qfalse);
118
209
  }
119
210
 
120
- static VALUE nogvl_close(void *ptr) {
121
- mysql_client_wrapper *wrapper;
122
211
  #ifndef _WIN32
123
- int flags;
212
+ /*
213
+ * Redirect clientfd to /dev/null for mysql_close and SSL_close to write,
214
+ * shutdown, and close. The hack is needed to prevent shutdown() from breaking
215
+ * a socket that may be in use by the parent or other processes after fork.
216
+ *
217
+ * /dev/null is used to absorb writes; previously a dummy socket was used, but
218
+ * it could not abosrb writes and caused openssl to go into an infinite loop.
219
+ *
220
+ * Returns Qtrue or Qfalse (success or failure)
221
+ *
222
+ * Note: if this function is needed on Windows, use "nul" instead of "/dev/null"
223
+ */
224
+ static VALUE invalidate_fd(int clientfd)
225
+ {
226
+ #ifdef O_CLOEXEC
227
+ /* Atomically set CLOEXEC on the new FD in case another thread forks */
228
+ int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
229
+ #else
230
+ /* Well we don't have O_CLOEXEC, trigger the fallback code below */
231
+ int sockfd = -1;
124
232
  #endif
125
- wrapper = ptr;
126
- if (!wrapper->closed) {
127
- wrapper->closed = 1;
128
233
 
129
- /*
130
- * we'll send a QUIT message to the server, but that message is more of a
131
- * formality than a hard requirement since the socket is getting shutdown
132
- * anyways, so ensure the socket write does not block our interpreter
133
- *
134
- *
135
- * if the socket is dead we have no chance of blocking,
136
- * so ignore any potential fcntl errors since they don't matter
234
+ if (sockfd < 0) {
235
+ /* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
236
+ * compile time, but isn't available at run-time. So we'll just be quick
237
+ * about setting FD_CLOEXEC now.
137
238
  */
138
- #ifndef _WIN32
139
- flags = fcntl(wrapper->client->net.fd, F_GETFL);
140
- if (flags > 0 && !(flags & O_NONBLOCK))
141
- fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
142
- #else
143
- u_long iMode;
144
- iMode = 1;
145
- ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode);
146
- #endif
239
+ int flags;
240
+ sockfd = open("/dev/null", O_RDWR);
241
+ flags = fcntl(sockfd, F_GETFD);
242
+ /* Do the flags dance in case there are more defined flags in the future */
243
+ if (flags != -1) {
244
+ flags |= FD_CLOEXEC;
245
+ fcntl(sockfd, F_SETFD, flags);
246
+ }
247
+ }
248
+
249
+ if (sockfd < 0) {
250
+ /* Cannot raise here, because one or both of the following may be true:
251
+ * a) we have no GVL (in C Ruby)
252
+ * b) are running as a GC finalizer
253
+ */
254
+ return Qfalse;
255
+ }
256
+
257
+ dup2(sockfd, clientfd);
258
+ close(sockfd);
147
259
 
260
+ return Qtrue;
261
+ }
262
+ #endif /* _WIN32 */
263
+
264
+ static void *nogvl_close(void *ptr) {
265
+ mysql_client_wrapper *wrapper = ptr;
266
+
267
+ if (!wrapper->closed) {
148
268
  mysql_close(wrapper->client);
149
- xfree(wrapper->client);
269
+ wrapper->closed = 1;
270
+ wrapper->reconnect_enabled = 0;
271
+ wrapper->active_thread = Qnil;
150
272
  }
151
273
 
152
- return Qnil;
274
+ return NULL;
153
275
  }
154
276
 
155
- static void rb_mysql_client_free(void * ptr) {
156
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
277
+ /* this is called during GC */
278
+ static void rb_mysql_client_free(void *ptr) {
279
+ mysql_client_wrapper *wrapper = ptr;
280
+ decr_mysql2_client(wrapper);
281
+ }
282
+
283
+ void decr_mysql2_client(mysql_client_wrapper *wrapper)
284
+ {
285
+ wrapper->refcount--;
157
286
 
158
- nogvl_close(wrapper);
287
+ if (wrapper->refcount == 0) {
288
+ #ifndef _WIN32
289
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
290
+ /* The client is being garbage collected while connected. Prevent
291
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
292
+ * the socket by invalidating it. invalidate_fd() will drop this
293
+ * process's reference to the socket only, while a QUIT or shutdown()
294
+ * would render the underlying connection unusable, interrupting other
295
+ * processes which share this object across a fork().
296
+ */
297
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
298
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
299
+ close(wrapper->client->net.fd);
300
+ }
301
+ wrapper->client->net.fd = -1;
302
+ }
303
+ #endif
159
304
 
160
- xfree(ptr);
305
+ nogvl_close(wrapper);
306
+ xfree(wrapper->client);
307
+ xfree(wrapper);
308
+ }
161
309
  }
162
310
 
163
311
  static VALUE allocate(VALUE klass) {
@@ -165,13 +313,27 @@ static VALUE allocate(VALUE klass) {
165
313
  mysql_client_wrapper * wrapper;
166
314
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
167
315
  wrapper->encoding = Qnil;
168
- wrapper->active = 0;
169
- wrapper->closed = 1;
316
+ wrapper->active_thread = Qnil;
317
+ wrapper->automatic_close = 1;
318
+ wrapper->server_version = 0;
319
+ wrapper->reconnect_enabled = 0;
320
+ wrapper->connect_timeout = 0;
321
+ wrapper->initialized = 0; /* means that that the wrapper is initialized */
322
+ wrapper->refcount = 1;
323
+ wrapper->closed = 0;
170
324
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
325
+
171
326
  return obj;
172
327
  }
173
328
 
174
- static VALUE rb_mysql_client_escape(VALUE klass, VALUE str) {
329
+ /* call-seq:
330
+ * Mysql2::Client.escape(string)
331
+ *
332
+ * Escape +string+ so that it may be used in a SQL statement.
333
+ * Note that this escape method is not connection encoding aware.
334
+ * If you need encoding support use Mysql2::Client#escape instead.
335
+ */
336
+ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
175
337
  unsigned char *newStr;
176
338
  VALUE rb_str;
177
339
  unsigned long newLen, oldLen;
@@ -181,72 +343,180 @@ static VALUE rb_mysql_client_escape(VALUE klass, VALUE str) {
181
343
  oldLen = RSTRING_LEN(str);
182
344
  newStr = xmalloc(oldLen*2+1);
183
345
 
184
- newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
346
+ newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
185
347
  if (newLen == oldLen) {
186
- // no need to return a new ruby string if nothing changed
348
+ /* no need to return a new ruby string if nothing changed */
187
349
  xfree(newStr);
188
350
  return str;
189
351
  } else {
190
352
  rb_str = rb_str_new((const char*)newStr, newLen);
191
- #ifdef HAVE_RUBY_ENCODING_H
192
353
  rb_enc_copy(rb_str, str);
193
- #endif
194
354
  xfree(newStr);
195
355
  return rb_str;
196
356
  }
197
357
  }
198
358
 
199
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
359
+ static VALUE rb_mysql_client_warning_count(VALUE self) {
360
+ unsigned int warning_count;
361
+ GET_CLIENT(self);
362
+
363
+ warning_count = mysql_warning_count(wrapper->client);
364
+
365
+ return UINT2NUM(warning_count);
366
+ }
367
+
368
+ static VALUE rb_mysql_info(VALUE self) {
369
+ const char *info;
370
+ VALUE rb_str;
371
+ GET_CLIENT(self);
372
+
373
+ info = mysql_info(wrapper->client);
374
+
375
+ if (info == NULL) {
376
+ return Qnil;
377
+ }
378
+
379
+ rb_str = rb_str_new2(info);
380
+ rb_enc_associate(rb_str, rb_utf8_encoding());
381
+
382
+ return rb_str;
383
+ }
384
+
385
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
386
+ {
387
+ const char *cipher;
388
+ VALUE rb_str;
389
+ GET_CLIENT(self);
390
+
391
+ cipher = mysql_get_ssl_cipher(wrapper->client);
392
+
393
+ if (cipher == NULL) {
394
+ return Qnil;
395
+ }
396
+
397
+ rb_str = rb_str_new2(cipher);
398
+ rb_enc_associate(rb_str, rb_utf8_encoding());
399
+
400
+ return rb_str;
401
+ }
402
+
403
+ #ifdef CLIENT_CONNECT_ATTRS
404
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
405
+ {
406
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
407
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
408
+ key = rb_str_export_to_enc(key, enc);
409
+ value = rb_str_export_to_enc(value, enc);
410
+
411
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
412
+ return ST_CONTINUE;
413
+ }
414
+ #endif
415
+
416
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
200
417
  struct nogvl_connect_args args;
418
+ time_t start_time, end_time, elapsed_time, connect_timeout;
419
+ VALUE rv;
201
420
  GET_CLIENT(self);
202
421
 
203
- args.host = NIL_P(host) ? "localhost" : StringValuePtr(host);
204
- args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
205
- args.port = NIL_P(port) ? 3306 : NUM2INT(port);
206
- args.user = NIL_P(user) ? NULL : StringValuePtr(user);
207
- args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
208
- args.db = NIL_P(database) ? NULL : StringValuePtr(database);
209
- args.mysql = wrapper->client;
422
+ args.host = NIL_P(host) ? NULL : StringValueCStr(host);
423
+ args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
424
+ args.port = NIL_P(port) ? 0 : NUM2INT(port);
425
+ args.user = NIL_P(user) ? NULL : StringValueCStr(user);
426
+ args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
427
+ args.db = NIL_P(database) ? NULL : StringValueCStr(database);
428
+ args.mysql = wrapper->client;
210
429
  args.client_flag = NUM2ULONG(flags);
211
430
 
212
- if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
213
- // unable to connect
214
- return rb_raise_mysql2_error(wrapper);
431
+ #ifdef CLIENT_CONNECT_ATTRS
432
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
433
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
434
+ #endif
435
+
436
+ if (wrapper->connect_timeout)
437
+ time(&start_time);
438
+ rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
439
+ if (rv == Qfalse) {
440
+ while (rv == Qfalse && errno == EINTR) {
441
+ if (wrapper->connect_timeout) {
442
+ time(&end_time);
443
+ /* avoid long connect timeout from system time changes */
444
+ if (end_time < start_time)
445
+ start_time = end_time;
446
+ elapsed_time = end_time - start_time;
447
+ /* avoid an early timeout due to time truncating milliseconds off the start time */
448
+ if (elapsed_time > 0)
449
+ elapsed_time--;
450
+ if (elapsed_time >= (time_t)wrapper->connect_timeout)
451
+ break;
452
+ connect_timeout = wrapper->connect_timeout - elapsed_time;
453
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
454
+ }
455
+ errno = 0;
456
+ rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
457
+ }
458
+ /* restore the connect timeout for reconnecting */
459
+ if (wrapper->connect_timeout)
460
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
461
+ if (rv == Qfalse)
462
+ rb_raise_mysql2_error(wrapper);
215
463
  }
216
464
 
465
+ wrapper->server_version = mysql_get_server_version(wrapper->client);
217
466
  return self;
218
467
  }
219
468
 
220
469
  /*
221
- * Immediately disconnect from the server, normally the garbage collector
470
+ * Immediately disconnect from the server; normally the garbage collector
222
471
  * will disconnect automatically when a connection is no longer needed.
223
472
  * Explicitly closing this will free up server resources sooner than waiting
224
473
  * for the garbage collector.
474
+ *
475
+ * @return [nil]
225
476
  */
226
477
  static VALUE rb_mysql_client_close(VALUE self) {
227
478
  GET_CLIENT(self);
228
479
 
229
- if (!wrapper->closed) {
230
- rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
480
+ if (wrapper->client) {
481
+ rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
231
482
  }
232
483
 
233
484
  return Qnil;
234
485
  }
235
486
 
487
+ /* call-seq:
488
+ * client.closed?
489
+ *
490
+ * @return [Boolean]
491
+ */
492
+ static VALUE rb_mysql_client_closed(VALUE self) {
493
+ GET_CLIENT(self);
494
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
495
+ }
496
+
236
497
  /*
237
498
  * mysql_send_query is unlikely to block since most queries are small
238
499
  * enough to fit in a socket buffer, but sometimes large UPDATE and
239
500
  * INSERTs will cause the process to block
240
501
  */
241
- static VALUE nogvl_send_query(void *ptr) {
502
+ static void *nogvl_send_query(void *ptr) {
242
503
  struct nogvl_send_query_args *args = ptr;
243
504
  int rv;
244
- const char *sql = StringValuePtr(args->sql);
245
- long sql_len = RSTRING_LEN(args->sql);
246
505
 
247
- rv = mysql_send_query(args->mysql, sql, sql_len);
506
+ rv = mysql_send_query(args->mysql, args->sql_ptr, args->sql_len);
248
507
 
249
- return rv == 0 ? Qtrue : Qfalse;
508
+ return (void*)(rv == 0 ? Qtrue : Qfalse);
509
+ }
510
+
511
+ static VALUE do_send_query(void *args) {
512
+ struct nogvl_send_query_args *query_args = args;
513
+ mysql_client_wrapper *wrapper = query_args->wrapper;
514
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
515
+ /* an error occurred, we're not active anymore */
516
+ wrapper->active_thread = Qnil;
517
+ rb_raise_mysql2_error(wrapper);
518
+ }
519
+ return Qnil;
250
520
  }
251
521
 
252
522
  /*
@@ -254,117 +524,130 @@ static VALUE nogvl_send_query(void *ptr) {
254
524
  * response can overflow the socket buffers and cause us to eventually
255
525
  * block while calling mysql_read_query_result
256
526
  */
257
- static VALUE nogvl_read_query_result(void *ptr) {
527
+ static void *nogvl_read_query_result(void *ptr) {
258
528
  MYSQL * client = ptr;
259
529
  my_bool res = mysql_read_query_result(client);
260
530
 
261
- return res == 0 ? Qtrue : Qfalse;
531
+ return (void *)(res == 0 ? Qtrue : Qfalse);
532
+ }
533
+
534
+ static void *nogvl_do_result(void *ptr, char use_result) {
535
+ mysql_client_wrapper *wrapper = ptr;
536
+ MYSQL_RES *result;
537
+
538
+ if (use_result) {
539
+ result = mysql_use_result(wrapper->client);
540
+ } else {
541
+ result = mysql_store_result(wrapper->client);
542
+ }
543
+
544
+ /* once our result is stored off, this connection is
545
+ ready for another command to be issued */
546
+ wrapper->active_thread = Qnil;
547
+
548
+ return result;
262
549
  }
263
550
 
264
551
  /* mysql_store_result may (unlikely) read rows off the socket */
265
- static VALUE nogvl_store_result(void *ptr) {
266
- MYSQL * client = ptr;
267
- return (VALUE)mysql_store_result(client);
552
+ static void *nogvl_store_result(void *ptr) {
553
+ return nogvl_do_result(ptr, 0);
268
554
  }
269
555
 
556
+ static void *nogvl_use_result(void *ptr) {
557
+ return nogvl_do_result(ptr, 1);
558
+ }
559
+
560
+ /* call-seq:
561
+ * client.async_result
562
+ *
563
+ * Returns the result for the last async issued query.
564
+ */
270
565
  static VALUE rb_mysql_client_async_result(VALUE self) {
271
566
  MYSQL_RES * result;
272
567
  VALUE resultObj;
273
- #ifdef HAVE_RUBY_ENCODING_H
274
- mysql2_result_wrapper * result_wrapper;
275
- #endif
568
+ VALUE current, is_streaming;
276
569
  GET_CLIENT(self);
277
570
 
278
- REQUIRE_OPEN_DB(wrapper);
279
- if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
280
- // an error occurred, mark this connection inactive
281
- MARK_CONN_INACTIVE(self);
282
- return rb_raise_mysql2_error(wrapper);
283
- }
571
+ /* if we're not waiting on a result, do nothing */
572
+ if (NIL_P(wrapper->active_thread))
573
+ return Qnil;
284
574
 
285
- result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0);
575
+ REQUIRE_CONNECTED(wrapper);
576
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
577
+ /* an error occurred, mark this connection inactive */
578
+ wrapper->active_thread = Qnil;
579
+ rb_raise_mysql2_error(wrapper);
580
+ }
286
581
 
287
- // we have our result, mark this connection inactive
288
- MARK_CONN_INACTIVE(self);
582
+ is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
583
+ if (is_streaming == Qtrue) {
584
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
585
+ } else {
586
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
587
+ }
289
588
 
290
589
  if (result == NULL) {
291
- if (mysql_field_count(wrapper->client) != 0) {
590
+ if (mysql_errno(wrapper->client) != 0) {
591
+ wrapper->active_thread = Qnil;
292
592
  rb_raise_mysql2_error(wrapper);
293
593
  }
594
+ /* no data and no error, so query was not a SELECT */
294
595
  return Qnil;
295
596
  }
296
597
 
297
- resultObj = rb_mysql_result_to_obj(result);
298
- // pass-through query options for result construction later
299
- rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0));
598
+ // Duplicate the options hash and put the copy in the Result object
599
+ current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
600
+ (void)RB_GC_GUARD(current);
601
+ Check_Type(current, T_HASH);
602
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
603
+
604
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
300
605
 
301
- #ifdef HAVE_RUBY_ENCODING_H
302
- GetMysql2Result(resultObj, result_wrapper);
303
- result_wrapper->encoding = wrapper->encoding;
304
- #endif
305
606
  return resultObj;
306
607
  }
307
608
 
308
- static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
309
- struct nogvl_send_query_args args;
310
- fd_set fdset;
311
- int fd, retval;
312
- int async = 0;
313
- VALUE opts, defaults, read_timeout;
314
- #ifdef HAVE_RUBY_ENCODING_H
315
- rb_encoding *conn_enc;
316
- #endif
317
- struct timeval tv;
318
- struct timeval* tvp;
319
- long int sec;
320
- VALUE result;
321
- GET_CLIENT(self);
322
-
323
- REQUIRE_OPEN_DB(wrapper);
324
- args.mysql = wrapper->client;
609
+ #ifndef _WIN32
610
+ struct async_query_args {
611
+ int fd;
612
+ VALUE self;
613
+ };
325
614
 
326
- // see if this connection is still waiting on a result from a previous query
327
- if (wrapper->active == 0) {
328
- // mark this connection active
329
- wrapper->active = 1;
330
- } else {
331
- rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
332
- }
615
+ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
616
+ GET_CLIENT(self);
333
617
 
334
- defaults = rb_iv_get(self, "@query_options");
335
- if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
336
- opts = rb_funcall(defaults, intern_merge, 1, opts);
337
- rb_iv_set(self, "@query_options", opts);
618
+ wrapper->active_thread = Qnil;
338
619
 
339
- if (rb_hash_aref(opts, sym_async) == Qtrue) {
340
- async = 1;
620
+ /* Invalidate the MySQL socket to prevent further communication.
621
+ * The GC will come along later and call mysql_close to free it.
622
+ */
623
+ if (CONNECTED(wrapper)) {
624
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
625
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
626
+ close(wrapper->client->net.fd);
341
627
  }
342
- } else {
343
- opts = defaults;
628
+ wrapper->client->net.fd = -1;
344
629
  }
345
630
 
346
- Check_Type(args.sql, T_STRING);
347
- #ifdef HAVE_RUBY_ENCODING_H
348
- conn_enc = rb_to_encoding(wrapper->encoding);
349
- // ensure the string is in the encoding the connection is expecting
350
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
351
- #endif
631
+ rb_exc_raise(error);
632
+ }
352
633
 
353
- if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
354
- // an error occurred, we're not active anymore
355
- MARK_CONN_INACTIVE(self);
356
- return rb_raise_mysql2_error(wrapper);
357
- }
634
+ static VALUE do_query(void *args) {
635
+ struct async_query_args *async_args = args;
636
+ struct timeval tv;
637
+ struct timeval *tvp;
638
+ long int sec;
639
+ int retval;
640
+ VALUE read_timeout;
358
641
 
359
- read_timeout = rb_iv_get(self, "@read_timeout");
642
+ read_timeout = rb_iv_get(async_args->self, "@read_timeout");
360
643
 
361
644
  tvp = NULL;
362
645
  if (!NIL_P(read_timeout)) {
363
646
  Check_Type(read_timeout, T_FIXNUM);
364
647
  tvp = &tv;
365
648
  sec = FIX2INT(read_timeout);
366
- // TODO: support partial seconds?
367
- // also, this check is here for sanity, we also check up in Ruby
649
+ /* TODO: support partial seconds?
650
+ also, this check is here for sanity, we also check up in Ruby */
368
651
  if (sec >= 0) {
369
652
  tvp->tv_sec = sec;
370
653
  } else {
@@ -373,176 +656,372 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
373
656
  tvp->tv_usec = 0;
374
657
  }
375
658
 
376
- if (!async) {
377
- // the below code is largely from do_mysql
378
- // http://github.com/datamapper/do
379
- fd = wrapper->client->net.fd;
380
- for(;;) {
381
- int fd_set_fd = fd;
659
+ for(;;) {
660
+ retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
382
661
 
383
- #ifdef _WIN32
384
- WSAPROTOCOL_INFO wsa_pi;
385
- // dupicate the SOCKET from libmysql
386
- int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi);
387
- SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
388
- // create the CRT fd so ruby can get back to the SOCKET
389
- fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
390
- #endif
662
+ if (retval == 0) {
663
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
664
+ }
391
665
 
392
- FD_ZERO(&fdset);
393
- FD_SET(fd_set_fd, &fdset);
666
+ if (retval < 0) {
667
+ rb_sys_fail(0);
668
+ }
394
669
 
395
- retval = rb_thread_select(fd_set_fd + 1, &fdset, NULL, NULL, tvp);
670
+ if (retval > 0) {
671
+ break;
672
+ }
673
+ }
396
674
 
397
- #ifdef _WIN32
398
- // cleanup the CRT fd
399
- _close(fd_set_fd);
400
- // cleanup the duplicated SOCKET
401
- closesocket(s);
675
+ return Qnil;
676
+ }
402
677
  #endif
403
678
 
404
- if (retval == 0) {
405
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
406
- }
407
-
408
- if (retval < 0) {
409
- rb_sys_fail(0);
410
- }
679
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
680
+ GET_CLIENT(self);
411
681
 
412
- if (retval > 0) {
413
- break;
682
+ /* Check if execution terminated while result was still being read. */
683
+ if (!NIL_P(wrapper->active_thread)) {
684
+ if (CONNECTED(wrapper)) {
685
+ /* Invalidate the MySQL socket to prevent further communication. */
686
+ #ifndef _WIN32
687
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
688
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
689
+ close(wrapper->client->net.fd);
414
690
  }
691
+ #else
692
+ close(wrapper->client->net.fd);
693
+ #endif
694
+ wrapper->client->net.fd = -1;
415
695
  }
696
+ /* Skip mysql client check performed before command execution. */
697
+ wrapper->client->status = MYSQL_STATUS_READY;
698
+ wrapper->active_thread = Qnil;
699
+ }
416
700
 
417
- result = rb_mysql_client_async_result(self);
701
+ return Qnil;
702
+ }
418
703
 
419
- return result;
704
+ void rb_mysql_client_set_active_thread(VALUE self) {
705
+ VALUE thread_current = rb_thread_current();
706
+ GET_CLIENT(self);
707
+
708
+ // see if this connection is still waiting on a result from a previous query
709
+ if (NIL_P(wrapper->active_thread)) {
710
+ // mark this connection active
711
+ wrapper->active_thread = thread_current;
712
+ } else if (wrapper->active_thread == thread_current) {
713
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
420
714
  } else {
715
+ VALUE inspect = rb_inspect(wrapper->active_thread);
716
+ const char *thr = StringValueCStr(inspect);
717
+
718
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
719
+ }
720
+ }
721
+
722
+ /* call-seq:
723
+ * client.abandon_results!
724
+ *
725
+ * When using MULTI_STATEMENTS support, calling this will throw
726
+ * away any unprocessed results as fast as it can in order to
727
+ * put the connection back into a state where queries can be issued
728
+ * again.
729
+ */
730
+ static VALUE rb_mysql_client_abandon_results(VALUE self) {
731
+ MYSQL_RES *result;
732
+ int ret;
733
+
734
+ GET_CLIENT(self);
735
+
736
+ while (mysql_more_results(wrapper->client) == 1) {
737
+ ret = mysql_next_result(wrapper->client);
738
+ if (ret > 0) {
739
+ rb_raise_mysql2_error(wrapper);
740
+ }
741
+
742
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
743
+
744
+ if (result != NULL) {
745
+ mysql_free_result(result);
746
+ }
747
+ }
748
+
749
+ return Qnil;
750
+ }
751
+
752
+ /* call-seq:
753
+ * client.query(sql, options = {})
754
+ *
755
+ * Query the database with +sql+, with optional +options+. For the possible
756
+ * options, see default_query_options on the Mysql2::Client class.
757
+ */
758
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
759
+ #ifndef _WIN32
760
+ struct async_query_args async_args;
761
+ #endif
762
+ struct nogvl_send_query_args args;
763
+ GET_CLIENT(self);
764
+
765
+ REQUIRE_CONNECTED(wrapper);
766
+ args.mysql = wrapper->client;
767
+
768
+ (void)RB_GC_GUARD(current);
769
+ Check_Type(current, T_HASH);
770
+ rb_iv_set(self, "@current_query_options", current);
771
+
772
+ Check_Type(sql, T_STRING);
773
+ /* ensure the string is in the encoding the connection is expecting */
774
+ args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
775
+ args.sql_ptr = RSTRING_PTR(args.sql);
776
+ args.sql_len = RSTRING_LEN(args.sql);
777
+ args.wrapper = wrapper;
778
+
779
+ rb_mysql_client_set_active_thread(self);
780
+
781
+ #ifndef _WIN32
782
+ rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
783
+
784
+ if (rb_hash_aref(current, sym_async) == Qtrue) {
421
785
  return Qnil;
786
+ } else {
787
+ async_args.fd = wrapper->client->net.fd;
788
+ async_args.self = self;
789
+
790
+ rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
791
+
792
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
422
793
  }
794
+ #else
795
+ do_send_query(&args);
796
+
797
+ /* this will just block until the result is ready */
798
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
799
+ #endif
423
800
  }
424
801
 
802
+ /* call-seq:
803
+ * client.escape(string)
804
+ *
805
+ * Escape +string+ so that it may be used in a SQL statement.
806
+ */
425
807
  static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
426
808
  unsigned char *newStr;
427
809
  VALUE rb_str;
428
810
  unsigned long newLen, oldLen;
429
- #ifdef HAVE_RUBY_ENCODING_H
430
811
  rb_encoding *default_internal_enc;
431
812
  rb_encoding *conn_enc;
432
- #endif
433
813
  GET_CLIENT(self);
434
814
 
435
- REQUIRE_OPEN_DB(wrapper);
815
+ REQUIRE_CONNECTED(wrapper);
436
816
  Check_Type(str, T_STRING);
437
- #ifdef HAVE_RUBY_ENCODING_H
438
817
  default_internal_enc = rb_default_internal_encoding();
439
818
  conn_enc = rb_to_encoding(wrapper->encoding);
440
- // ensure the string is in the encoding the connection is expecting
819
+ /* ensure the string is in the encoding the connection is expecting */
441
820
  str = rb_str_export_to_enc(str, conn_enc);
442
- #endif
443
821
 
444
822
  oldLen = RSTRING_LEN(str);
445
823
  newStr = xmalloc(oldLen*2+1);
446
824
 
447
- newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
825
+ newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
448
826
  if (newLen == oldLen) {
449
- // no need to return a new ruby string if nothing changed
827
+ /* no need to return a new ruby string if nothing changed */
828
+ if (default_internal_enc) {
829
+ str = rb_str_export_to_enc(str, default_internal_enc);
830
+ }
450
831
  xfree(newStr);
451
832
  return str;
452
833
  } else {
453
834
  rb_str = rb_str_new((const char*)newStr, newLen);
454
- #ifdef HAVE_RUBY_ENCODING_H
455
835
  rb_enc_associate(rb_str, conn_enc);
456
836
  if (default_internal_enc) {
457
837
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
458
838
  }
459
- #endif
460
839
  xfree(newStr);
461
840
  return rb_str;
462
841
  }
463
842
  }
464
843
 
465
- static VALUE rb_mysql_client_info(VALUE self) {
466
- VALUE version, client_info;
467
- #ifdef HAVE_RUBY_ENCODING_H
468
- rb_encoding *default_internal_enc;
469
- rb_encoding *conn_enc;
470
- #endif
844
+ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
845
+ int result;
846
+ const void *retval = NULL;
847
+ unsigned int intval = 0;
848
+ const char * charval = NULL;
849
+ my_bool boolval;
850
+
471
851
  GET_CLIENT(self);
472
- version = rb_hash_new();
473
852
 
474
- #ifdef HAVE_RUBY_ENCODING_H
475
- default_internal_enc = rb_default_internal_encoding();
476
- conn_enc = rb_to_encoding(wrapper->encoding);
853
+ REQUIRE_NOT_CONNECTED(wrapper);
854
+
855
+ if (NIL_P(value))
856
+ return Qfalse;
857
+
858
+ switch(opt) {
859
+ case MYSQL_OPT_CONNECT_TIMEOUT:
860
+ intval = NUM2UINT(value);
861
+ retval = &intval;
862
+ break;
863
+
864
+ case MYSQL_OPT_READ_TIMEOUT:
865
+ intval = NUM2UINT(value);
866
+ retval = &intval;
867
+ break;
868
+
869
+ case MYSQL_OPT_WRITE_TIMEOUT:
870
+ intval = NUM2UINT(value);
871
+ retval = &intval;
872
+ break;
873
+
874
+ case MYSQL_OPT_LOCAL_INFILE:
875
+ intval = (value == Qfalse ? 0 : 1);
876
+ retval = &intval;
877
+ break;
878
+
879
+ case MYSQL_OPT_RECONNECT:
880
+ boolval = (value == Qfalse ? 0 : 1);
881
+ retval = &boolval;
882
+ break;
883
+
884
+ #ifdef MYSQL_SECURE_AUTH
885
+ case MYSQL_SECURE_AUTH:
886
+ boolval = (value == Qfalse ? 0 : 1);
887
+ retval = &boolval;
888
+ break;
477
889
  #endif
478
890
 
479
- rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
480
- client_info = rb_str_new2(mysql_get_client_info());
481
- #ifdef HAVE_RUBY_ENCODING_H
482
- rb_enc_associate(client_info, conn_enc);
483
- if (default_internal_enc) {
484
- client_info = rb_str_export_to_enc(client_info, default_internal_enc);
485
- }
891
+ case MYSQL_READ_DEFAULT_FILE:
892
+ charval = (const char *)StringValueCStr(value);
893
+ retval = charval;
894
+ break;
895
+
896
+ case MYSQL_READ_DEFAULT_GROUP:
897
+ charval = (const char *)StringValueCStr(value);
898
+ retval = charval;
899
+ break;
900
+
901
+ case MYSQL_INIT_COMMAND:
902
+ charval = (const char *)StringValueCStr(value);
903
+ retval = charval;
904
+ break;
905
+
906
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
907
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
908
+ boolval = (value == Qfalse ? 0 : 1);
909
+ retval = &boolval;
910
+ break;
486
911
  #endif
487
- rb_hash_aset(version, sym_version, client_info);
488
- return version;
912
+
913
+ default:
914
+ return Qfalse;
915
+ }
916
+
917
+ result = mysql_options(wrapper->client, opt, retval);
918
+
919
+ /* Zero means success */
920
+ if (result != 0) {
921
+ rb_warn("%s\n", mysql_error(wrapper->client));
922
+ } else {
923
+ /* Special case for options that are stored in the wrapper struct */
924
+ switch (opt) {
925
+ case MYSQL_OPT_RECONNECT:
926
+ wrapper->reconnect_enabled = boolval;
927
+ break;
928
+ case MYSQL_OPT_CONNECT_TIMEOUT:
929
+ wrapper->connect_timeout = intval;
930
+ break;
931
+ }
932
+ }
933
+
934
+ return (result == 0) ? Qtrue : Qfalse;
489
935
  }
490
936
 
937
+ /* call-seq:
938
+ * client.info
939
+ *
940
+ * Returns a string that represents the client library version.
941
+ */
942
+ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
943
+ VALUE version_info, version, header_version;
944
+ version_info = rb_hash_new();
945
+
946
+ version = rb_str_new2(mysql_get_client_info());
947
+ header_version = rb_str_new2(MYSQL_LINK_VERSION);
948
+
949
+ rb_enc_associate(version, rb_usascii_encoding());
950
+ rb_enc_associate(header_version, rb_usascii_encoding());
951
+
952
+ rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
953
+ rb_hash_aset(version_info, sym_version, version);
954
+ rb_hash_aset(version_info, sym_header_version, header_version);
955
+
956
+ return version_info;
957
+ }
958
+
959
+ /* call-seq:
960
+ * client.server_info
961
+ *
962
+ * Returns a string that represents the server version number
963
+ */
491
964
  static VALUE rb_mysql_client_server_info(VALUE self) {
492
965
  VALUE version, server_info;
493
- #ifdef HAVE_RUBY_ENCODING_H
494
966
  rb_encoding *default_internal_enc;
495
967
  rb_encoding *conn_enc;
496
- #endif
497
968
  GET_CLIENT(self);
498
969
 
499
- REQUIRE_OPEN_DB(wrapper);
500
- #ifdef HAVE_RUBY_ENCODING_H
970
+ REQUIRE_CONNECTED(wrapper);
501
971
  default_internal_enc = rb_default_internal_encoding();
502
972
  conn_enc = rb_to_encoding(wrapper->encoding);
503
- #endif
504
973
 
505
974
  version = rb_hash_new();
506
975
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
507
976
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
508
- #ifdef HAVE_RUBY_ENCODING_H
509
977
  rb_enc_associate(server_info, conn_enc);
510
978
  if (default_internal_enc) {
511
979
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
512
980
  }
513
- #endif
514
981
  rb_hash_aset(version, sym_version, server_info);
515
982
  return version;
516
983
  }
517
984
 
985
+ /* call-seq:
986
+ * client.socket
987
+ *
988
+ * Return the file descriptor number for this client.
989
+ */
990
+ #ifndef _WIN32
518
991
  static VALUE rb_mysql_client_socket(VALUE self) {
519
992
  GET_CLIENT(self);
520
- REQUIRE_OPEN_DB(wrapper);
521
- int fd_set_fd = wrapper->client->net.fd;
522
- #ifdef _WIN32
523
- WSAPROTOCOL_INFO wsa_pi;
524
- // dupicate the SOCKET from libmysql
525
- int r = WSADuplicateSocket(wrapper->client->net.fd, GetCurrentProcessId(), &wsa_pi);
526
- SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
527
- // create the CRT fd so ruby can get back to the SOCKET
528
- fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
529
- return INT2NUM(fd_set_fd);
993
+ REQUIRE_CONNECTED(wrapper);
994
+ return INT2NUM(wrapper->client->net.fd);
995
+ }
530
996
  #else
531
- return INT2NUM(fd_set_fd);
532
- #endif
997
+ static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
998
+ rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
533
999
  }
1000
+ #endif
534
1001
 
1002
+ /* call-seq:
1003
+ * client.last_id
1004
+ *
1005
+ * Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
1006
+ * statement.
1007
+ */
535
1008
  static VALUE rb_mysql_client_last_id(VALUE self) {
536
1009
  GET_CLIENT(self);
537
- REQUIRE_OPEN_DB(wrapper);
1010
+ REQUIRE_CONNECTED(wrapper);
538
1011
  return ULL2NUM(mysql_insert_id(wrapper->client));
539
1012
  }
540
1013
 
1014
+ /* call-seq:
1015
+ * client.affected_rows
1016
+ *
1017
+ * returns the number of rows changed, deleted, or inserted by the last statement
1018
+ * if it was an UPDATE, DELETE, or INSERT.
1019
+ */
541
1020
  static VALUE rb_mysql_client_affected_rows(VALUE self) {
542
1021
  my_ulonglong retVal;
543
1022
  GET_CLIENT(self);
544
1023
 
545
- REQUIRE_OPEN_DB(wrapper);
1024
+ REQUIRE_CONNECTED(wrapper);
546
1025
  retVal = mysql_affected_rows(wrapper->client);
547
1026
  if (retVal == (my_ulonglong)-1) {
548
1027
  rb_raise_mysql2_error(wrapper);
@@ -550,92 +1029,270 @@ static VALUE rb_mysql_client_affected_rows(VALUE self) {
550
1029
  return ULL2NUM(retVal);
551
1030
  }
552
1031
 
1032
+ /* call-seq:
1033
+ * client.thread_id
1034
+ *
1035
+ * Returns the thread ID of the current connection.
1036
+ */
553
1037
  static VALUE rb_mysql_client_thread_id(VALUE self) {
554
1038
  unsigned long retVal;
555
1039
  GET_CLIENT(self);
556
1040
 
557
- REQUIRE_OPEN_DB(wrapper);
1041
+ REQUIRE_CONNECTED(wrapper);
558
1042
  retVal = mysql_thread_id(wrapper->client);
559
1043
  return ULL2NUM(retVal);
560
1044
  }
561
1045
 
562
- static VALUE nogvl_ping(void *ptr)
1046
+ static void *nogvl_select_db(void *ptr) {
1047
+ struct nogvl_select_db_args *args = ptr;
1048
+
1049
+ if (mysql_select_db(args->mysql, args->db) == 0)
1050
+ return (void *)Qtrue;
1051
+ else
1052
+ return (void *)Qfalse;
1053
+ }
1054
+
1055
+ /* call-seq:
1056
+ * client.select_db(name)
1057
+ *
1058
+ * Causes the database specified by +name+ to become the default (current)
1059
+ * database on the connection specified by mysql.
1060
+ */
1061
+ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
563
1062
  {
1063
+ struct nogvl_select_db_args args;
1064
+
1065
+ GET_CLIENT(self);
1066
+ REQUIRE_CONNECTED(wrapper);
1067
+
1068
+ args.mysql = wrapper->client;
1069
+ args.db = StringValueCStr(db);
1070
+
1071
+ if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
1072
+ rb_raise_mysql2_error(wrapper);
1073
+
1074
+ return db;
1075
+ }
1076
+
1077
+ static void *nogvl_ping(void *ptr) {
564
1078
  MYSQL *client = ptr;
565
1079
 
566
- return mysql_ping(client) == 0 ? Qtrue : Qfalse;
1080
+ return (void *)(mysql_ping(client) == 0 ? Qtrue : Qfalse);
567
1081
  }
568
1082
 
1083
+ /* call-seq:
1084
+ * client.ping
1085
+ *
1086
+ * Checks whether the connection to the server is working. If the connection
1087
+ * has gone down and auto-reconnect is enabled an attempt to reconnect is made.
1088
+ * If the connection is down and auto-reconnect is disabled, ping returns an
1089
+ * error.
1090
+ */
569
1091
  static VALUE rb_mysql_client_ping(VALUE self) {
570
1092
  GET_CLIENT(self);
571
1093
 
572
- if (wrapper->closed) {
1094
+ if (!CONNECTED(wrapper)) {
573
1095
  return Qfalse;
574
1096
  } else {
575
- return rb_thread_blocking_region(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1097
+ return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
576
1098
  }
577
1099
  }
578
1100
 
579
- #ifdef HAVE_RUBY_ENCODING_H
580
- static VALUE rb_mysql_client_encoding(VALUE self) {
1101
+ /* call-seq:
1102
+ * client.set_server_option(value)
1103
+ *
1104
+ * Enables or disables an option for the connection.
1105
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1106
+ * for more information.
1107
+ */
1108
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
581
1109
  GET_CLIENT(self);
582
- return wrapper->encoding;
1110
+
1111
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1112
+ return Qtrue;
1113
+ } else {
1114
+ return Qfalse;
1115
+ }
583
1116
  }
584
- #endif
585
1117
 
586
- static VALUE set_reconnect(VALUE self, VALUE value) {
587
- my_bool reconnect;
1118
+ /* call-seq:
1119
+ * client.more_results?
1120
+ *
1121
+ * Returns true or false if there are more results to process.
1122
+ */
1123
+ static VALUE rb_mysql_client_more_results(VALUE self)
1124
+ {
1125
+ GET_CLIENT(self);
1126
+ if (mysql_more_results(wrapper->client) == 0)
1127
+ return Qfalse;
1128
+ else
1129
+ return Qtrue;
1130
+ }
1131
+
1132
+ /* call-seq:
1133
+ * client.next_result
1134
+ *
1135
+ * Fetch the next result set from the server.
1136
+ * Returns nothing.
1137
+ */
1138
+ static VALUE rb_mysql_client_next_result(VALUE self)
1139
+ {
1140
+ int ret;
1141
+ GET_CLIENT(self);
1142
+ ret = mysql_next_result(wrapper->client);
1143
+ if (ret > 0) {
1144
+ rb_raise_mysql2_error(wrapper);
1145
+ return Qfalse;
1146
+ } else if (ret == 0) {
1147
+ return Qtrue;
1148
+ } else {
1149
+ return Qfalse;
1150
+ }
1151
+ }
1152
+
1153
+ /* call-seq:
1154
+ * client.store_result
1155
+ *
1156
+ * Return the next result object from a query which
1157
+ * yielded multiple result sets.
1158
+ */
1159
+ static VALUE rb_mysql_client_store_result(VALUE self)
1160
+ {
1161
+ MYSQL_RES * result;
1162
+ VALUE resultObj;
1163
+ VALUE current;
588
1164
  GET_CLIENT(self);
589
1165
 
590
- if(!NIL_P(value)) {
591
- reconnect = value == Qfalse ? 0 : 1;
1166
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
592
1167
 
593
- /* set default reconnect behavior */
594
- if (mysql_options(wrapper->client, MYSQL_OPT_RECONNECT, &reconnect)) {
595
- /* TODO: warning - unable to set reconnect behavior */
596
- rb_warn("%s\n", mysql_error(wrapper->client));
1168
+ if (result == NULL) {
1169
+ if (mysql_errno(wrapper->client) != 0) {
1170
+ rb_raise_mysql2_error(wrapper);
597
1171
  }
1172
+ /* no data and no error, so query was not a SELECT */
1173
+ return Qnil;
598
1174
  }
599
- return value;
1175
+
1176
+ // Duplicate the options hash and put the copy in the Result object
1177
+ current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1178
+ (void)RB_GC_GUARD(current);
1179
+ Check_Type(current, T_HASH);
1180
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
1181
+
1182
+ return resultObj;
600
1183
  }
601
1184
 
602
- static VALUE set_connect_timeout(VALUE self, VALUE value) {
603
- unsigned int connect_timeout = 0;
1185
+ /* call-seq:
1186
+ * client.encoding
1187
+ *
1188
+ * Returns the encoding set on the client.
1189
+ */
1190
+ static VALUE rb_mysql_client_encoding(VALUE self) {
604
1191
  GET_CLIENT(self);
1192
+ return wrapper->encoding;
1193
+ }
605
1194
 
606
- if(!NIL_P(value)) {
607
- connect_timeout = NUM2INT(value);
608
- if(0 == connect_timeout) return value;
1195
+ /* call-seq:
1196
+ * client.automatic_close?
1197
+ *
1198
+ * @return [Boolean]
1199
+ */
1200
+ static VALUE get_automatic_close(VALUE self) {
1201
+ GET_CLIENT(self);
1202
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1203
+ }
609
1204
 
610
- /* set default connection timeout behavior */
611
- if (mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
612
- /* TODO: warning - unable to set connection timeout */
613
- rb_warn("%s\n", mysql_error(wrapper->client));
614
- }
1205
+ /* call-seq:
1206
+ * client.automatic_close = false
1207
+ *
1208
+ * Set this to +false+ to leave the connection open after it is garbage
1209
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1210
+ * call +close+ when the connection is no longer needed.
1211
+ *
1212
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1213
+ */
1214
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1215
+ GET_CLIENT(self);
1216
+ if (RTEST(value)) {
1217
+ wrapper->automatic_close = 1;
1218
+ } else {
1219
+ #ifndef _WIN32
1220
+ wrapper->automatic_close = 0;
1221
+ #else
1222
+ rb_warn("Connections are always closed by garbage collector on Windows");
1223
+ #endif
615
1224
  }
616
1225
  return value;
617
1226
  }
618
1227
 
1228
+ /* call-seq:
1229
+ * client.reconnect = true
1230
+ *
1231
+ * Enable or disable the automatic reconnect behavior of libmysql.
1232
+ * Read http://dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html
1233
+ * for more information.
1234
+ */
1235
+ static VALUE set_reconnect(VALUE self, VALUE value) {
1236
+ return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
1237
+ }
1238
+
1239
+ static VALUE set_local_infile(VALUE self, VALUE value) {
1240
+ return _mysql_client_options(self, MYSQL_OPT_LOCAL_INFILE, value);
1241
+ }
1242
+
1243
+ static VALUE set_connect_timeout(VALUE self, VALUE value) {
1244
+ long int sec;
1245
+ Check_Type(value, T_FIXNUM);
1246
+ sec = FIX2INT(value);
1247
+ if (sec < 0) {
1248
+ rb_raise(cMysql2Error, "connect_timeout must be a positive integer, you passed %ld", sec);
1249
+ }
1250
+ return _mysql_client_options(self, MYSQL_OPT_CONNECT_TIMEOUT, value);
1251
+ }
1252
+
1253
+ static VALUE set_read_timeout(VALUE self, VALUE value) {
1254
+ long int sec;
1255
+ Check_Type(value, T_FIXNUM);
1256
+ sec = FIX2INT(value);
1257
+ if (sec < 0) {
1258
+ rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
1259
+ }
1260
+ /* Set the instance variable here even though _mysql_client_options
1261
+ might not succeed, because the timeout is used in other ways
1262
+ elsewhere */
1263
+ rb_iv_set(self, "@read_timeout", value);
1264
+ return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1265
+ }
1266
+
1267
+ static VALUE set_write_timeout(VALUE self, VALUE value) {
1268
+ long int sec;
1269
+ Check_Type(value, T_FIXNUM);
1270
+ sec = FIX2INT(value);
1271
+ if (sec < 0) {
1272
+ rb_raise(cMysql2Error, "write_timeout must be a positive integer, you passed %ld", sec);
1273
+ }
1274
+ return _mysql_client_options(self, MYSQL_OPT_WRITE_TIMEOUT, value);
1275
+ }
1276
+
619
1277
  static VALUE set_charset_name(VALUE self, VALUE value) {
620
- char * charset_name;
621
- #ifdef HAVE_RUBY_ENCODING_H
622
- VALUE new_encoding;
623
- #endif
1278
+ char *charset_name;
1279
+ const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1280
+ rb_encoding *enc;
1281
+ VALUE rb_enc;
624
1282
  GET_CLIENT(self);
625
1283
 
626
- #ifdef HAVE_RUBY_ENCODING_H
627
- new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset, 1, value);
628
- if (new_encoding == Qnil) {
1284
+ Check_Type(value, T_STRING);
1285
+ charset_name = RSTRING_PTR(value);
1286
+
1287
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1288
+ if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
629
1289
  VALUE inspect = rb_inspect(value);
630
1290
  rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
631
1291
  } else {
632
- if (wrapper->encoding == Qnil) {
633
- wrapper->encoding = new_encoding;
634
- }
1292
+ enc = rb_enc_find(mysql2rb->rb_name);
1293
+ rb_enc = rb_enc_from_encoding(enc);
1294
+ wrapper->encoding = rb_enc;
635
1295
  }
636
- #endif
637
-
638
- charset_name = StringValuePtr(value);
639
1296
 
640
1297
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
641
1298
  /* TODO: warning - unable to set charset */
@@ -648,192 +1305,347 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
648
1305
  static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
649
1306
  GET_CLIENT(self);
650
1307
 
651
- if(!NIL_P(ca) || !NIL_P(key)) {
652
- mysql_ssl_set(wrapper->client,
653
- NIL_P(key) ? NULL : StringValuePtr(key),
654
- NIL_P(cert) ? NULL : StringValuePtr(cert),
655
- NIL_P(ca) ? NULL : StringValuePtr(ca),
656
- NIL_P(capath) ? NULL : StringValuePtr(capath),
657
- NIL_P(cipher) ? NULL : StringValuePtr(cipher));
658
- }
1308
+ mysql_ssl_set(wrapper->client,
1309
+ NIL_P(key) ? NULL : StringValueCStr(key),
1310
+ NIL_P(cert) ? NULL : StringValueCStr(cert),
1311
+ NIL_P(ca) ? NULL : StringValueCStr(ca),
1312
+ NIL_P(capath) ? NULL : StringValueCStr(capath),
1313
+ NIL_P(cipher) ? NULL : StringValueCStr(cipher));
659
1314
 
660
1315
  return self;
661
1316
  }
662
1317
 
663
- static VALUE init_connection(VALUE self) {
1318
+ static VALUE set_secure_auth(VALUE self, VALUE value) {
1319
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1320
+ #ifdef MYSQL_SECURE_AUTH
1321
+ return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1322
+ #else
1323
+ return Qfalse;
1324
+ #endif
1325
+ }
1326
+
1327
+ static VALUE set_read_default_file(VALUE self, VALUE value) {
1328
+ return _mysql_client_options(self, MYSQL_READ_DEFAULT_FILE, value);
1329
+ }
1330
+
1331
+ static VALUE set_read_default_group(VALUE self, VALUE value) {
1332
+ return _mysql_client_options(self, MYSQL_READ_DEFAULT_GROUP, value);
1333
+ }
1334
+
1335
+ static VALUE set_init_command(VALUE self, VALUE value) {
1336
+ return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1337
+ }
1338
+
1339
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1342
+ #else
1343
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1344
+ #endif
1345
+ }
1346
+
1347
+ static VALUE initialize_ext(VALUE self) {
664
1348
  GET_CLIENT(self);
665
1349
 
666
- if (rb_thread_blocking_region(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
1350
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
667
1351
  /* TODO: warning - not enough memory? */
668
- return rb_raise_mysql2_error(wrapper);
1352
+ rb_raise_mysql2_error(wrapper);
669
1353
  }
670
1354
 
671
- wrapper->closed = 0;
1355
+ wrapper->initialized = 1;
672
1356
  return self;
673
1357
  }
674
1358
 
1359
+ /* call-seq: client.prepare # => Mysql2::Statement
1360
+ *
1361
+ * Create a new prepared statement.
1362
+ */
1363
+ static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1364
+ GET_CLIENT(self);
1365
+ REQUIRE_CONNECTED(wrapper);
1366
+
1367
+ return rb_mysql_stmt_new(self, sql);
1368
+ }
1369
+
675
1370
  void init_mysql2_client() {
676
- // verify the libmysql we're about to use was the version we were built against
677
- // https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99
1371
+ #ifdef _WIN32
1372
+ /* verify the libmysql we're about to use was the version we were built against
1373
+ https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
678
1374
  int i;
679
1375
  int dots = 0;
680
1376
  const char *lib = mysql_get_client_info();
681
- for (i = 0; lib[i] != 0 && MYSQL_SERVER_VERSION[i] != 0; i++) {
1377
+
1378
+ for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
682
1379
  if (lib[i] == '.') {
683
1380
  dots++;
684
- // we only compare MAJOR and MINOR
1381
+ /* we only compare MAJOR and MINOR */
685
1382
  if (dots == 2) break;
686
1383
  }
687
- if (lib[i] != MYSQL_SERVER_VERSION[i]) {
688
- rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_SERVER_VERSION, lib);
689
- return;
1384
+ if (lib[i] != MYSQL_LINK_VERSION[i]) {
1385
+ 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);
690
1386
  }
691
1387
  }
1388
+ #endif
1389
+
1390
+ /* Initializing mysql library, so different threads could call Client.new */
1391
+ /* without race condition in the library */
1392
+ if (mysql_library_init(0, NULL, NULL) != 0) {
1393
+ rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1394
+ }
692
1395
 
1396
+ #if 0
1397
+ mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1398
+ #endif
693
1399
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
694
1400
 
695
1401
  rb_define_alloc_func(cMysql2Client, allocate);
696
1402
 
697
1403
  rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
1404
+ rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
698
1405
 
699
1406
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
700
- rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1407
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1408
+ rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
701
1409
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
702
- rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
703
1410
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
704
1411
  rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
705
1412
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
706
1413
  rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
707
1414
  rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1415
+ rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
708
1416
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
709
1417
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
710
- #ifdef HAVE_RUBY_ENCODING_H
1418
+ rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1419
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1420
+ rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1421
+ rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1422
+ rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1423
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1424
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1425
+ rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1426
+ rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1427
+ rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1428
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
711
1429
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
712
- #endif
713
1430
 
714
- rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
715
1431
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1432
+ rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
1433
+ rb_define_private_method(cMysql2Client, "write_timeout=", set_write_timeout, 1);
1434
+ rb_define_private_method(cMysql2Client, "local_infile=", set_local_infile, 1);
716
1435
  rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
1436
+ rb_define_private_method(cMysql2Client, "secure_auth=", set_secure_auth, 1);
1437
+ rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1438
+ rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1439
+ rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
717
1440
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
718
- rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
719
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
720
-
721
- intern_encoding_from_charset = rb_intern("encoding_from_charset");
1441
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1442
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1443
+ rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1444
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1445
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
722
1446
 
723
1447
  sym_id = ID2SYM(rb_intern("id"));
724
1448
  sym_version = ID2SYM(rb_intern("version"));
1449
+ sym_header_version = ID2SYM(rb_intern("header_version"));
725
1450
  sym_async = ID2SYM(rb_intern("async"));
726
1451
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
727
1452
  sym_as = ID2SYM(rb_intern("as"));
728
1453
  sym_array = ID2SYM(rb_intern("array"));
1454
+ sym_stream = ID2SYM(rb_intern("stream"));
729
1455
 
1456
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1457
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1458
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1459
+
1460
+ intern_brackets = rb_intern("[]");
730
1461
  intern_merge = rb_intern("merge");
731
- intern_error_number_eql = rb_intern("error_number=");
732
- intern_sql_state_eql = rb_intern("sql_state=");
1462
+ intern_merge_bang = rb_intern("merge!");
1463
+ intern_new_with_args = rb_intern("new_with_args");
733
1464
 
734
1465
  #ifdef CLIENT_LONG_PASSWORD
735
1466
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
736
- INT2NUM(CLIENT_LONG_PASSWORD));
1467
+ LONG2NUM(CLIENT_LONG_PASSWORD));
1468
+ #else
1469
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1470
+ * but we're using it in our default connection flags. */
1471
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
737
1472
  #endif
738
1473
 
739
1474
  #ifdef CLIENT_FOUND_ROWS
740
1475
  rb_const_set(cMysql2Client, rb_intern("FOUND_ROWS"),
741
- INT2NUM(CLIENT_FOUND_ROWS));
1476
+ LONG2NUM(CLIENT_FOUND_ROWS));
742
1477
  #endif
743
1478
 
744
1479
  #ifdef CLIENT_LONG_FLAG
745
1480
  rb_const_set(cMysql2Client, rb_intern("LONG_FLAG"),
746
- INT2NUM(CLIENT_LONG_FLAG));
1481
+ LONG2NUM(CLIENT_LONG_FLAG));
747
1482
  #endif
748
1483
 
749
1484
  #ifdef CLIENT_CONNECT_WITH_DB
750
1485
  rb_const_set(cMysql2Client, rb_intern("CONNECT_WITH_DB"),
751
- INT2NUM(CLIENT_CONNECT_WITH_DB));
1486
+ LONG2NUM(CLIENT_CONNECT_WITH_DB));
752
1487
  #endif
753
1488
 
754
1489
  #ifdef CLIENT_NO_SCHEMA
755
1490
  rb_const_set(cMysql2Client, rb_intern("NO_SCHEMA"),
756
- INT2NUM(CLIENT_NO_SCHEMA));
1491
+ LONG2NUM(CLIENT_NO_SCHEMA));
757
1492
  #endif
758
1493
 
759
1494
  #ifdef CLIENT_COMPRESS
760
- rb_const_set(cMysql2Client, rb_intern("COMPRESS"), INT2NUM(CLIENT_COMPRESS));
1495
+ rb_const_set(cMysql2Client, rb_intern("COMPRESS"), LONG2NUM(CLIENT_COMPRESS));
761
1496
  #endif
762
1497
 
763
1498
  #ifdef CLIENT_ODBC
764
- rb_const_set(cMysql2Client, rb_intern("ODBC"), INT2NUM(CLIENT_ODBC));
1499
+ rb_const_set(cMysql2Client, rb_intern("ODBC"), LONG2NUM(CLIENT_ODBC));
765
1500
  #endif
766
1501
 
767
1502
  #ifdef CLIENT_LOCAL_FILES
768
1503
  rb_const_set(cMysql2Client, rb_intern("LOCAL_FILES"),
769
- INT2NUM(CLIENT_LOCAL_FILES));
1504
+ LONG2NUM(CLIENT_LOCAL_FILES));
770
1505
  #endif
771
1506
 
772
1507
  #ifdef CLIENT_IGNORE_SPACE
773
1508
  rb_const_set(cMysql2Client, rb_intern("IGNORE_SPACE"),
774
- INT2NUM(CLIENT_IGNORE_SPACE));
1509
+ LONG2NUM(CLIENT_IGNORE_SPACE));
775
1510
  #endif
776
1511
 
777
1512
  #ifdef CLIENT_PROTOCOL_41
778
1513
  rb_const_set(cMysql2Client, rb_intern("PROTOCOL_41"),
779
- INT2NUM(CLIENT_PROTOCOL_41));
1514
+ LONG2NUM(CLIENT_PROTOCOL_41));
780
1515
  #endif
781
1516
 
782
1517
  #ifdef CLIENT_INTERACTIVE
783
1518
  rb_const_set(cMysql2Client, rb_intern("INTERACTIVE"),
784
- INT2NUM(CLIENT_INTERACTIVE));
1519
+ LONG2NUM(CLIENT_INTERACTIVE));
785
1520
  #endif
786
1521
 
787
1522
  #ifdef CLIENT_SSL
788
- rb_const_set(cMysql2Client, rb_intern("SSL"), INT2NUM(CLIENT_SSL));
1523
+ rb_const_set(cMysql2Client, rb_intern("SSL"), LONG2NUM(CLIENT_SSL));
789
1524
  #endif
790
1525
 
791
1526
  #ifdef CLIENT_IGNORE_SIGPIPE
792
1527
  rb_const_set(cMysql2Client, rb_intern("IGNORE_SIGPIPE"),
793
- INT2NUM(CLIENT_IGNORE_SIGPIPE));
1528
+ LONG2NUM(CLIENT_IGNORE_SIGPIPE));
794
1529
  #endif
795
1530
 
796
1531
  #ifdef CLIENT_TRANSACTIONS
797
1532
  rb_const_set(cMysql2Client, rb_intern("TRANSACTIONS"),
798
- INT2NUM(CLIENT_TRANSACTIONS));
1533
+ LONG2NUM(CLIENT_TRANSACTIONS));
799
1534
  #endif
800
1535
 
801
1536
  #ifdef CLIENT_RESERVED
802
- rb_const_set(cMysql2Client, rb_intern("RESERVED"), INT2NUM(CLIENT_RESERVED));
1537
+ rb_const_set(cMysql2Client, rb_intern("RESERVED"), LONG2NUM(CLIENT_RESERVED));
803
1538
  #endif
804
1539
 
805
1540
  #ifdef CLIENT_SECURE_CONNECTION
806
1541
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"),
807
- INT2NUM(CLIENT_SECURE_CONNECTION));
1542
+ LONG2NUM(CLIENT_SECURE_CONNECTION));
1543
+ #else
1544
+ /* HACK because MySQL5.7 no longer defines this constant,
1545
+ * but we're using it in our default connection flags. */
1546
+ rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1547
+ #endif
1548
+
1549
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1550
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1551
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1552
+ #endif
1553
+
1554
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1555
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1556
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
808
1557
  #endif
809
1558
 
810
1559
  #ifdef CLIENT_MULTI_STATEMENTS
811
1560
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
812
- INT2NUM(CLIENT_MULTI_STATEMENTS));
1561
+ LONG2NUM(CLIENT_MULTI_STATEMENTS));
813
1562
  #endif
814
1563
 
815
1564
  #ifdef CLIENT_PS_MULTI_RESULTS
816
1565
  rb_const_set(cMysql2Client, rb_intern("PS_MULTI_RESULTS"),
817
- INT2NUM(CLIENT_PS_MULTI_RESULTS));
1566
+ LONG2NUM(CLIENT_PS_MULTI_RESULTS));
818
1567
  #endif
819
1568
 
820
1569
  #ifdef CLIENT_SSL_VERIFY_SERVER_CERT
821
1570
  rb_const_set(cMysql2Client, rb_intern("SSL_VERIFY_SERVER_CERT"),
822
- INT2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
1571
+ LONG2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
823
1572
  #endif
824
1573
 
825
1574
  #ifdef CLIENT_REMEMBER_OPTIONS
826
1575
  rb_const_set(cMysql2Client, rb_intern("REMEMBER_OPTIONS"),
827
- INT2NUM(CLIENT_REMEMBER_OPTIONS));
1576
+ LONG2NUM(CLIENT_REMEMBER_OPTIONS));
828
1577
  #endif
829
1578
 
830
1579
  #ifdef CLIENT_ALL_FLAGS
831
1580
  rb_const_set(cMysql2Client, rb_intern("ALL_FLAGS"),
832
- INT2NUM(CLIENT_ALL_FLAGS));
1581
+ LONG2NUM(CLIENT_ALL_FLAGS));
833
1582
  #endif
834
1583
 
835
1584
  #ifdef CLIENT_BASIC_FLAGS
836
1585
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
837
- INT2NUM(CLIENT_BASIC_FLAGS));
1586
+ LONG2NUM(CLIENT_BASIC_FLAGS));
1587
+ #endif
1588
+
1589
+ #ifdef CLIENT_CONNECT_ATTRS
1590
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1591
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1592
+ #else
1593
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1594
+ * but we're using it in our default connection flags. */
1595
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1596
+ INT2NUM(0));
1597
+ #endif
1598
+
1599
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1600
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1601
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1602
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1603
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1604
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1605
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1606
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1607
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1608
+ #endif
1609
+
1610
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1611
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1612
+ #endif
1613
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1614
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1615
+ #endif
1616
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1617
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1618
+ #endif
1619
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1620
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
838
1621
  #endif
1622
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1623
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1624
+ #endif
1625
+ }
1626
+
1627
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1628
+
1629
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1630
+ VALUE server_flags = rb_hash_new();
1631
+
1632
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1633
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1634
+ #else
1635
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1636
+ #endif
1637
+
1638
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1639
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1640
+ #else
1641
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1642
+ #endif
1643
+
1644
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1645
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1646
+ #else
1647
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1648
+ #endif
1649
+
1650
+ rb_iv_set(result, "@server_flags", server_flags);
839
1651
  }