mysql2 0.3.18 → 0.4.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/LICENSE +21 -0
- data/README.md +135 -55
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +368 -197
- data/ext/mysql2/client.h +13 -3
- data/ext/mysql2/extconf.rb +118 -35
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +7 -6
- data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
- data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
- data/ext/mysql2/result.c +512 -138
- data/ext/mysql2/result.h +13 -6
- data/ext/mysql2/statement.c +585 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +85 -26
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +5 -6
- data/lib/mysql2/error.rb +18 -27
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/statement.rb +17 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +38 -18
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +22 -21
- data/spec/mysql2/client_spec.rb +525 -388
- data/spec/mysql2/error_spec.rb +38 -39
- data/spec/mysql2/result_spec.rb +223 -214
- data/spec/mysql2/statement_spec.rb +757 -0
- data/spec/spec_helper.rb +80 -59
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/support/mysql_enc_to_ruby.rb +7 -8
- data/support/ruby_enc_to_mysql.rb +1 -1
- metadata +42 -47
data/ext/mysql2/client.c
CHANGED
@@ -16,12 +16,12 @@
|
|
16
16
|
|
17
17
|
VALUE cMysql2Client;
|
18
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,
|
19
|
+
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
|
21
21
|
|
22
22
|
#ifndef HAVE_RB_HASH_DUP
|
23
|
-
|
24
|
-
return rb_funcall(rb_cHash,
|
23
|
+
VALUE rb_hash_dup(VALUE other) {
|
24
|
+
return rb_funcall(rb_cHash, intern_brackets, 1, other);
|
25
25
|
}
|
26
26
|
#endif
|
27
27
|
|
@@ -30,25 +30,24 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
30
30
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
31
|
}
|
32
32
|
|
33
|
+
#if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
|
34
|
+
#define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
|
35
|
+
#elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
|
36
|
+
#define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
|
37
|
+
#endif
|
38
|
+
|
33
39
|
#define REQUIRE_CONNECTED(wrapper) \
|
34
40
|
REQUIRE_INITIALIZED(wrapper) \
|
35
|
-
if (!wrapper
|
36
|
-
rb_raise(cMysql2Error, "
|
41
|
+
if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
|
42
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
37
43
|
}
|
38
44
|
|
39
45
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
40
46
|
REQUIRE_INITIALIZED(wrapper) \
|
41
|
-
if (wrapper
|
47
|
+
if (CONNECTED(wrapper)) { \
|
42
48
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
43
49
|
}
|
44
50
|
|
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
51
|
/*
|
53
52
|
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
54
53
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
@@ -60,6 +59,16 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
60
59
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
61
60
|
#endif
|
62
61
|
|
62
|
+
/*
|
63
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
64
|
+
*/
|
65
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
66
|
+
#define SSL_MODE_DISABLED 1
|
67
|
+
#define SSL_MODE_REQUIRED 3
|
68
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
69
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
70
|
+
#endif
|
71
|
+
|
63
72
|
/*
|
64
73
|
* used to pass all arguments to mysql_real_connect while inside
|
65
74
|
* rb_thread_call_without_gvl
|
@@ -96,6 +105,42 @@ struct nogvl_select_db_args {
|
|
96
105
|
char *db;
|
97
106
|
};
|
98
107
|
|
108
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
109
|
+
unsigned long version = mysql_get_client_version();
|
110
|
+
|
111
|
+
if (version < 50703) {
|
112
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
116
|
+
GET_CLIENT(self);
|
117
|
+
int val = NUM2INT( setting );
|
118
|
+
if (version >= 50703 && version < 50711) {
|
119
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
120
|
+
bool b = ( val == SSL_MODE_REQUIRED );
|
121
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
122
|
+
return INT2NUM(result);
|
123
|
+
} else {
|
124
|
+
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
125
|
+
return Qnil;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
#endif
|
129
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
130
|
+
GET_CLIENT(self);
|
131
|
+
int val = NUM2INT( setting );
|
132
|
+
|
133
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
134
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
135
|
+
}
|
136
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
137
|
+
|
138
|
+
return INT2NUM(result);
|
139
|
+
#endif
|
140
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
141
|
+
return Qnil;
|
142
|
+
#endif
|
143
|
+
}
|
99
144
|
/*
|
100
145
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
101
146
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -136,16 +181,17 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
136
181
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
137
182
|
#endif
|
138
183
|
|
139
|
-
e = rb_funcall(cMysql2Error,
|
140
|
-
|
141
|
-
|
184
|
+
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
185
|
+
rb_error_msg,
|
186
|
+
LONG2FIX(wrapper->server_version),
|
187
|
+
UINT2NUM(mysql_errno(wrapper->client)),
|
188
|
+
rb_sql_state);
|
142
189
|
rb_exc_raise(e);
|
143
|
-
return Qnil;
|
144
190
|
}
|
145
191
|
|
146
192
|
static void *nogvl_init(void *ptr) {
|
147
193
|
MYSQL *client;
|
148
|
-
mysql_client_wrapper *wrapper =
|
194
|
+
mysql_client_wrapper *wrapper = ptr;
|
149
195
|
|
150
196
|
/* may initialize embedded server and read /etc/services off disk */
|
151
197
|
client = mysql_init(wrapper->client);
|
@@ -182,23 +228,31 @@ static void *nogvl_connect(void *ptr) {
|
|
182
228
|
*/
|
183
229
|
static VALUE invalidate_fd(int clientfd)
|
184
230
|
{
|
185
|
-
#ifdef
|
231
|
+
#ifdef O_CLOEXEC
|
186
232
|
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
187
233
|
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
234
|
#else
|
194
|
-
/* Well we don't have
|
195
|
-
int sockfd =
|
196
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
235
|
+
/* Well we don't have O_CLOEXEC, trigger the fallback code below */
|
236
|
+
int sockfd = -1;
|
197
237
|
#endif
|
198
238
|
|
199
239
|
if (sockfd < 0) {
|
200
|
-
/*
|
201
|
-
*
|
240
|
+
/* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
|
241
|
+
* compile time, but isn't available at run-time. So we'll just be quick
|
242
|
+
* about setting FD_CLOEXEC now.
|
243
|
+
*/
|
244
|
+
int flags;
|
245
|
+
sockfd = open("/dev/null", O_RDWR);
|
246
|
+
flags = fcntl(sockfd, F_GETFD);
|
247
|
+
/* Do the flags dance in case there are more defined flags in the future */
|
248
|
+
if (flags != -1) {
|
249
|
+
flags |= FD_CLOEXEC;
|
250
|
+
fcntl(sockfd, F_SETFD, flags);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
if (sockfd < 0) {
|
255
|
+
/* Cannot raise here, because one or both of the following may be true:
|
202
256
|
* a) we have no GVL (in C Ruby)
|
203
257
|
* b) are running as a GC finalizer
|
204
258
|
*/
|
@@ -213,41 +267,46 @@ static VALUE invalidate_fd(int clientfd)
|
|
213
267
|
#endif /* _WIN32 */
|
214
268
|
|
215
269
|
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
|
270
|
+
mysql_client_wrapper *wrapper = ptr;
|
235
271
|
|
236
|
-
|
272
|
+
if (!wrapper->closed) {
|
273
|
+
mysql_close(wrapper->client);
|
274
|
+
wrapper->closed = 1;
|
275
|
+
wrapper->reconnect_enabled = 0;
|
276
|
+
wrapper->active_thread = Qnil;
|
237
277
|
}
|
238
278
|
|
239
279
|
return NULL;
|
240
280
|
}
|
241
281
|
|
282
|
+
/* this is called during GC */
|
242
283
|
static void rb_mysql_client_free(void *ptr) {
|
243
|
-
mysql_client_wrapper *wrapper =
|
284
|
+
mysql_client_wrapper *wrapper = ptr;
|
244
285
|
decr_mysql2_client(wrapper);
|
245
286
|
}
|
246
287
|
|
247
288
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
248
289
|
{
|
249
290
|
wrapper->refcount--;
|
291
|
+
|
250
292
|
if (wrapper->refcount == 0) {
|
293
|
+
#ifndef _WIN32
|
294
|
+
if (CONNECTED(wrapper) && !wrapper->automatic_close) {
|
295
|
+
/* The client is being garbage collected while connected. Prevent
|
296
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
297
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
298
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
299
|
+
* would render the underlying connection unusable, interrupting other
|
300
|
+
* processes which share this object across a fork().
|
301
|
+
*/
|
302
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
303
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
304
|
+
close(wrapper->client->net.fd);
|
305
|
+
}
|
306
|
+
wrapper->client->net.fd = -1;
|
307
|
+
}
|
308
|
+
#endif
|
309
|
+
|
251
310
|
nogvl_close(wrapper);
|
252
311
|
xfree(wrapper->client);
|
253
312
|
xfree(wrapper);
|
@@ -260,13 +319,15 @@ static VALUE allocate(VALUE klass) {
|
|
260
319
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
261
320
|
wrapper->encoding = Qnil;
|
262
321
|
wrapper->active_thread = Qnil;
|
322
|
+
wrapper->automatic_close = 1;
|
263
323
|
wrapper->server_version = 0;
|
264
324
|
wrapper->reconnect_enabled = 0;
|
265
325
|
wrapper->connect_timeout = 0;
|
266
|
-
wrapper->connected = 0; /* means that a database connection is open */
|
267
326
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
268
327
|
wrapper->refcount = 1;
|
328
|
+
wrapper->closed = 0;
|
269
329
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
330
|
+
|
270
331
|
return obj;
|
271
332
|
}
|
272
333
|
|
@@ -287,7 +348,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
287
348
|
oldLen = RSTRING_LEN(str);
|
288
349
|
newStr = xmalloc(oldLen*2+1);
|
289
350
|
|
290
|
-
newLen = mysql_escape_string((char *)newStr,
|
351
|
+
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
|
291
352
|
if (newLen == oldLen) {
|
292
353
|
/* no need to return a new ruby string if nothing changed */
|
293
354
|
xfree(newStr);
|
@@ -330,20 +391,39 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
330
391
|
return rb_str;
|
331
392
|
}
|
332
393
|
|
394
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
395
|
+
{
|
396
|
+
const char *cipher;
|
397
|
+
VALUE rb_str;
|
398
|
+
GET_CLIENT(self);
|
399
|
+
|
400
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
401
|
+
|
402
|
+
if (cipher == NULL) {
|
403
|
+
return Qnil;
|
404
|
+
}
|
405
|
+
|
406
|
+
rb_str = rb_str_new2(cipher);
|
407
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
408
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
409
|
+
#endif
|
410
|
+
|
411
|
+
return rb_str;
|
412
|
+
}
|
413
|
+
|
333
414
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
334
415
|
struct nogvl_connect_args args;
|
335
|
-
time_t start_time, end_time;
|
336
|
-
unsigned int elapsed_time, connect_timeout;
|
416
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
337
417
|
VALUE rv;
|
338
418
|
GET_CLIENT(self);
|
339
419
|
|
340
|
-
args.host
|
341
|
-
args.unix_socket = NIL_P(socket)
|
342
|
-
args.port
|
343
|
-
args.user
|
344
|
-
args.passwd
|
345
|
-
args.db
|
346
|
-
args.mysql
|
420
|
+
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
|
421
|
+
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
|
422
|
+
args.port = NIL_P(port) ? 0 : NUM2INT(port);
|
423
|
+
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
|
424
|
+
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
|
425
|
+
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
|
426
|
+
args.mysql = wrapper->client;
|
347
427
|
args.client_flag = NUM2ULONG(flags);
|
348
428
|
|
349
429
|
if (wrapper->connect_timeout)
|
@@ -360,7 +440,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
360
440
|
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
361
441
|
if (elapsed_time > 0)
|
362
442
|
elapsed_time--;
|
363
|
-
if (elapsed_time >= wrapper->connect_timeout)
|
443
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
364
444
|
break;
|
365
445
|
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
366
446
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
@@ -372,30 +452,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
372
452
|
if (wrapper->connect_timeout)
|
373
453
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
374
454
|
if (rv == Qfalse)
|
375
|
-
|
455
|
+
rb_raise_mysql2_error(wrapper);
|
376
456
|
}
|
377
457
|
|
378
458
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
379
|
-
wrapper->connected = 1;
|
380
459
|
return self;
|
381
460
|
}
|
382
461
|
|
383
462
|
/*
|
384
|
-
* Immediately disconnect from the server
|
463
|
+
* Immediately disconnect from the server; normally the garbage collector
|
385
464
|
* will disconnect automatically when a connection is no longer needed.
|
386
465
|
* Explicitly closing this will free up server resources sooner than waiting
|
387
466
|
* for the garbage collector.
|
467
|
+
*
|
468
|
+
* @return [nil]
|
388
469
|
*/
|
389
470
|
static VALUE rb_mysql_client_close(VALUE self) {
|
390
471
|
GET_CLIENT(self);
|
391
472
|
|
392
|
-
if (wrapper->
|
473
|
+
if (wrapper->client) {
|
393
474
|
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
394
475
|
}
|
395
476
|
|
396
477
|
return Qnil;
|
397
478
|
}
|
398
479
|
|
480
|
+
/* call-seq:
|
481
|
+
* client.closed?
|
482
|
+
*
|
483
|
+
* @return [Boolean]
|
484
|
+
*/
|
485
|
+
static VALUE rb_mysql_client_closed(VALUE self) {
|
486
|
+
GET_CLIENT(self);
|
487
|
+
return CONNECTED(wrapper) ? Qfalse : Qtrue;
|
488
|
+
}
|
489
|
+
|
399
490
|
/*
|
400
491
|
* mysql_send_query is unlikely to block since most queries are small
|
401
492
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
@@ -415,8 +506,8 @@ static VALUE do_send_query(void *args) {
|
|
415
506
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
416
507
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
417
508
|
/* an error occurred, we're not active anymore */
|
418
|
-
|
419
|
-
|
509
|
+
wrapper->active_thread = Qnil;
|
510
|
+
rb_raise_mysql2_error(wrapper);
|
420
511
|
}
|
421
512
|
return Qnil;
|
422
513
|
}
|
@@ -428,16 +519,15 @@ static VALUE do_send_query(void *args) {
|
|
428
519
|
*/
|
429
520
|
static void *nogvl_read_query_result(void *ptr) {
|
430
521
|
MYSQL * client = ptr;
|
431
|
-
|
522
|
+
bool res = mysql_read_query_result(client);
|
432
523
|
|
433
524
|
return (void *)(res == 0 ? Qtrue : Qfalse);
|
434
525
|
}
|
435
526
|
|
436
527
|
static void *nogvl_do_result(void *ptr, char use_result) {
|
437
|
-
mysql_client_wrapper *wrapper;
|
528
|
+
mysql_client_wrapper *wrapper = ptr;
|
438
529
|
MYSQL_RES *result;
|
439
530
|
|
440
|
-
wrapper = (mysql_client_wrapper *)ptr;
|
441
531
|
if (use_result) {
|
442
532
|
result = mysql_use_result(wrapper->client);
|
443
533
|
} else {
|
@@ -478,8 +568,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
478
568
|
REQUIRE_CONNECTED(wrapper);
|
479
569
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
480
570
|
/* an error occurred, mark this connection inactive */
|
481
|
-
|
482
|
-
|
571
|
+
wrapper->active_thread = Qnil;
|
572
|
+
rb_raise_mysql2_error(wrapper);
|
483
573
|
}
|
484
574
|
|
485
575
|
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
@@ -491,7 +581,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
491
581
|
|
492
582
|
if (result == NULL) {
|
493
583
|
if (mysql_errno(wrapper->client) != 0) {
|
494
|
-
|
584
|
+
wrapper->active_thread = Qnil;
|
495
585
|
rb_raise_mysql2_error(wrapper);
|
496
586
|
}
|
497
587
|
/* no data and no error, so query was not a SELECT */
|
@@ -499,9 +589,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
499
589
|
}
|
500
590
|
|
501
591
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
502
|
-
RB_GC_GUARD(current);
|
592
|
+
(void)RB_GC_GUARD(current);
|
503
593
|
Check_Type(current, T_HASH);
|
504
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
594
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
505
595
|
|
506
596
|
return resultObj;
|
507
597
|
}
|
@@ -516,30 +606,29 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
516
606
|
GET_CLIENT(self);
|
517
607
|
|
518
608
|
wrapper->active_thread = Qnil;
|
519
|
-
wrapper->connected = 0;
|
520
609
|
|
521
610
|
/* Invalidate the MySQL socket to prevent further communication.
|
522
611
|
* The GC will come along later and call mysql_close to free it.
|
523
612
|
*/
|
524
|
-
if (
|
525
|
-
|
526
|
-
|
613
|
+
if (CONNECTED(wrapper)) {
|
614
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
615
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
616
|
+
close(wrapper->client->net.fd);
|
617
|
+
}
|
618
|
+
wrapper->client->net.fd = -1;
|
527
619
|
}
|
528
620
|
|
529
621
|
rb_exc_raise(error);
|
530
|
-
|
531
|
-
return Qnil;
|
532
622
|
}
|
533
623
|
|
534
624
|
static VALUE do_query(void *args) {
|
535
|
-
struct async_query_args *async_args;
|
625
|
+
struct async_query_args *async_args = args;
|
536
626
|
struct timeval tv;
|
537
|
-
struct timeval*
|
627
|
+
struct timeval *tvp;
|
538
628
|
long int sec;
|
539
629
|
int retval;
|
540
630
|
VALUE read_timeout;
|
541
631
|
|
542
|
-
async_args = (struct async_query_args *)args;
|
543
632
|
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
544
633
|
|
545
634
|
tvp = NULL;
|
@@ -575,28 +664,50 @@ static VALUE do_query(void *args) {
|
|
575
664
|
|
576
665
|
return Qnil;
|
577
666
|
}
|
578
|
-
#
|
579
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
580
|
-
VALUE self;
|
581
|
-
MYSQL_RES *result;
|
582
|
-
|
583
|
-
self = (VALUE)args;
|
667
|
+
#endif
|
584
668
|
|
669
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
585
670
|
GET_CLIENT(self);
|
586
671
|
|
672
|
+
/* Check if execution terminated while result was still being read. */
|
587
673
|
if (!NIL_P(wrapper->active_thread)) {
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
674
|
+
if (CONNECTED(wrapper)) {
|
675
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
676
|
+
#ifndef _WIN32
|
677
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
678
|
+
rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
|
679
|
+
close(wrapper->client->net.fd);
|
680
|
+
}
|
681
|
+
#else
|
682
|
+
close(wrapper->client->net.fd);
|
683
|
+
#endif
|
684
|
+
wrapper->client->net.fd = -1;
|
685
|
+
}
|
686
|
+
/* Skip mysql client check performed before command execution. */
|
687
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
594
688
|
wrapper->active_thread = Qnil;
|
595
689
|
}
|
596
690
|
|
597
691
|
return Qnil;
|
598
692
|
}
|
599
|
-
|
693
|
+
|
694
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
695
|
+
VALUE thread_current = rb_thread_current();
|
696
|
+
GET_CLIENT(self);
|
697
|
+
|
698
|
+
// see if this connection is still waiting on a result from a previous query
|
699
|
+
if (NIL_P(wrapper->active_thread)) {
|
700
|
+
// mark this connection active
|
701
|
+
wrapper->active_thread = thread_current;
|
702
|
+
} else if (wrapper->active_thread == thread_current) {
|
703
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
704
|
+
} else {
|
705
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
706
|
+
const char *thr = StringValueCStr(inspect);
|
707
|
+
|
708
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
709
|
+
}
|
710
|
+
}
|
600
711
|
|
601
712
|
/* call-seq:
|
602
713
|
* client.abandon_results!
|
@@ -632,80 +743,53 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
632
743
|
* client.query(sql, options = {})
|
633
744
|
*
|
634
745
|
* Query the database with +sql+, with optional +options+. For the possible
|
635
|
-
* options, see
|
746
|
+
* options, see default_query_options on the Mysql2::Client class.
|
636
747
|
*/
|
637
|
-
static VALUE
|
748
|
+
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
638
749
|
#ifndef _WIN32
|
639
750
|
struct async_query_args async_args;
|
640
751
|
#endif
|
641
752
|
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
753
|
GET_CLIENT(self);
|
649
754
|
|
650
755
|
REQUIRE_CONNECTED(wrapper);
|
651
756
|
args.mysql = wrapper->client;
|
652
757
|
|
653
|
-
|
654
|
-
RB_GC_GUARD(current);
|
758
|
+
(void)RB_GC_GUARD(current);
|
655
759
|
Check_Type(current, T_HASH);
|
656
760
|
rb_iv_set(self, "@current_query_options", current);
|
657
761
|
|
658
|
-
|
659
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
660
|
-
|
661
|
-
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
662
|
-
async = 1;
|
663
|
-
}
|
664
|
-
}
|
665
|
-
|
666
|
-
Check_Type(args.sql, T_STRING);
|
762
|
+
Check_Type(sql, T_STRING);
|
667
763
|
#ifdef HAVE_RUBY_ENCODING_H
|
668
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
669
764
|
/* ensure the string is in the encoding the connection is expecting */
|
670
|
-
args.sql = rb_str_export_to_enc(
|
765
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
766
|
+
#else
|
767
|
+
args.sql = sql;
|
671
768
|
#endif
|
672
|
-
args.sql_ptr =
|
769
|
+
args.sql_ptr = RSTRING_PTR(args.sql);
|
673
770
|
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
771
|
args.wrapper = wrapper;
|
690
772
|
|
773
|
+
rb_mysql_client_set_active_thread(self);
|
774
|
+
|
691
775
|
#ifndef _WIN32
|
692
776
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
693
777
|
|
694
|
-
if (
|
778
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
779
|
+
return Qnil;
|
780
|
+
} else {
|
695
781
|
async_args.fd = wrapper->client->net.fd;
|
696
782
|
async_args.self = self;
|
697
783
|
|
698
784
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
699
785
|
|
700
|
-
return rb_mysql_client_async_result
|
701
|
-
} else {
|
702
|
-
return Qnil;
|
786
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
703
787
|
}
|
704
788
|
#else
|
705
789
|
do_send_query(&args);
|
706
790
|
|
707
791
|
/* this will just block until the result is ready */
|
708
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
792
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
709
793
|
#endif
|
710
794
|
}
|
711
795
|
|
@@ -736,9 +820,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
736
820
|
oldLen = RSTRING_LEN(str);
|
737
821
|
newStr = xmalloc(oldLen*2+1);
|
738
822
|
|
739
|
-
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr,
|
823
|
+
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
740
824
|
if (newLen == oldLen) {
|
741
825
|
/* no need to return a new ruby string if nothing changed */
|
826
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
827
|
+
if (default_internal_enc) {
|
828
|
+
str = rb_str_export_to_enc(str, default_internal_enc);
|
829
|
+
}
|
830
|
+
#endif
|
742
831
|
xfree(newStr);
|
743
832
|
return str;
|
744
833
|
} else {
|
@@ -759,7 +848,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
759
848
|
const void *retval = NULL;
|
760
849
|
unsigned int intval = 0;
|
761
850
|
const char * charval = NULL;
|
762
|
-
|
851
|
+
bool boolval;
|
763
852
|
|
764
853
|
GET_CLIENT(self);
|
765
854
|
|
@@ -800,20 +889,25 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
800
889
|
break;
|
801
890
|
|
802
891
|
case MYSQL_READ_DEFAULT_FILE:
|
803
|
-
charval = (const char *)
|
892
|
+
charval = (const char *)StringValueCStr(value);
|
804
893
|
retval = charval;
|
805
894
|
break;
|
806
895
|
|
807
896
|
case MYSQL_READ_DEFAULT_GROUP:
|
808
|
-
charval = (const char *)
|
897
|
+
charval = (const char *)StringValueCStr(value);
|
809
898
|
retval = charval;
|
810
899
|
break;
|
811
900
|
|
812
901
|
case MYSQL_INIT_COMMAND:
|
813
|
-
charval = (const char *)
|
902
|
+
charval = (const char *)StringValueCStr(value);
|
814
903
|
retval = charval;
|
815
904
|
break;
|
816
905
|
|
906
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
907
|
+
boolval = (value == Qfalse ? 0 : 1);
|
908
|
+
retval = &boolval;
|
909
|
+
break;
|
910
|
+
|
817
911
|
default:
|
818
912
|
return Qfalse;
|
819
913
|
}
|
@@ -843,30 +937,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
843
937
|
*
|
844
938
|
* Returns a string that represents the client library version.
|
845
939
|
*/
|
846
|
-
static VALUE rb_mysql_client_info(VALUE
|
847
|
-
VALUE version,
|
848
|
-
|
849
|
-
rb_encoding *default_internal_enc;
|
850
|
-
rb_encoding *conn_enc;
|
851
|
-
GET_CLIENT(self);
|
852
|
-
#endif
|
853
|
-
version = rb_hash_new();
|
940
|
+
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
941
|
+
VALUE version_info, version, header_version;
|
942
|
+
version_info = rb_hash_new();
|
854
943
|
|
855
|
-
|
856
|
-
|
857
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
858
|
-
#endif
|
944
|
+
version = rb_str_new2(mysql_get_client_info());
|
945
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
859
946
|
|
860
|
-
rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
|
861
|
-
client_info = rb_str_new2(mysql_get_client_info());
|
862
947
|
#ifdef HAVE_RUBY_ENCODING_H
|
863
|
-
rb_enc_associate(
|
864
|
-
|
865
|
-
client_info = rb_str_export_to_enc(client_info, default_internal_enc);
|
866
|
-
}
|
948
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
949
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
867
950
|
#endif
|
868
|
-
|
869
|
-
|
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;
|
870
957
|
}
|
871
958
|
|
872
959
|
/* call-seq:
|
@@ -906,19 +993,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
906
993
|
*
|
907
994
|
* Return the file descriptor number for this client.
|
908
995
|
*/
|
996
|
+
#ifndef _WIN32
|
909
997
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
910
998
|
GET_CLIENT(self);
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
REQUIRE_CONNECTED(wrapper);
|
915
|
-
fd_set_fd = wrapper->client->net.fd;
|
916
|
-
return INT2NUM(fd_set_fd);
|
917
|
-
}
|
999
|
+
REQUIRE_CONNECTED(wrapper);
|
1000
|
+
return INT2NUM(wrapper->client->net.fd);
|
1001
|
+
}
|
918
1002
|
#else
|
1003
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
919
1004
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
920
|
-
#endif
|
921
1005
|
}
|
1006
|
+
#endif
|
922
1007
|
|
923
1008
|
/* call-seq:
|
924
1009
|
* client.last_id
|
@@ -987,7 +1072,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
|
987
1072
|
REQUIRE_CONNECTED(wrapper);
|
988
1073
|
|
989
1074
|
args.mysql = wrapper->client;
|
990
|
-
args.db =
|
1075
|
+
args.db = StringValueCStr(db);
|
991
1076
|
|
992
1077
|
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
993
1078
|
rb_raise_mysql2_error(wrapper);
|
@@ -1012,7 +1097,7 @@ static void *nogvl_ping(void *ptr) {
|
|
1012
1097
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
1013
1098
|
GET_CLIENT(self);
|
1014
1099
|
|
1015
|
-
if (!wrapper
|
1100
|
+
if (!CONNECTED(wrapper)) {
|
1016
1101
|
return Qfalse;
|
1017
1102
|
} else {
|
1018
1103
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
@@ -1027,10 +1112,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1027
1112
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1028
1113
|
{
|
1029
1114
|
GET_CLIENT(self);
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1115
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1116
|
+
return Qfalse;
|
1117
|
+
else
|
1118
|
+
return Qtrue;
|
1034
1119
|
}
|
1035
1120
|
|
1036
1121
|
/* call-seq:
|
@@ -1078,9 +1163,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1078
1163
|
}
|
1079
1164
|
|
1080
1165
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1081
|
-
RB_GC_GUARD(current);
|
1166
|
+
(void)RB_GC_GUARD(current);
|
1082
1167
|
Check_Type(current, T_HASH);
|
1083
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1168
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1084
1169
|
|
1085
1170
|
return resultObj;
|
1086
1171
|
}
|
@@ -1097,6 +1182,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1097
1182
|
}
|
1098
1183
|
#endif
|
1099
1184
|
|
1185
|
+
/* call-seq:
|
1186
|
+
* client.automatic_close?
|
1187
|
+
*
|
1188
|
+
* @return [Boolean]
|
1189
|
+
*/
|
1190
|
+
static VALUE get_automatic_close(VALUE self) {
|
1191
|
+
GET_CLIENT(self);
|
1192
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1193
|
+
}
|
1194
|
+
|
1195
|
+
/* call-seq:
|
1196
|
+
* client.automatic_close = false
|
1197
|
+
*
|
1198
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1199
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1200
|
+
* call +close+ when the connection is no longer needed.
|
1201
|
+
*
|
1202
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1203
|
+
*/
|
1204
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1205
|
+
GET_CLIENT(self);
|
1206
|
+
if (RTEST(value)) {
|
1207
|
+
wrapper->automatic_close = 1;
|
1208
|
+
} else {
|
1209
|
+
#ifndef _WIN32
|
1210
|
+
wrapper->automatic_close = 0;
|
1211
|
+
#else
|
1212
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1213
|
+
#endif
|
1214
|
+
}
|
1215
|
+
return value;
|
1216
|
+
}
|
1217
|
+
|
1100
1218
|
/* call-seq:
|
1101
1219
|
* client.reconnect = true
|
1102
1220
|
*
|
@@ -1149,18 +1267,17 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1149
1267
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1150
1268
|
char *charset_name;
|
1151
1269
|
#ifdef HAVE_RUBY_ENCODING_H
|
1152
|
-
size_t charset_name_len;
|
1153
1270
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1154
1271
|
rb_encoding *enc;
|
1155
1272
|
VALUE rb_enc;
|
1156
1273
|
#endif
|
1157
1274
|
GET_CLIENT(self);
|
1158
1275
|
|
1276
|
+
Check_Type(value, T_STRING);
|
1159
1277
|
charset_name = RSTRING_PTR(value);
|
1160
1278
|
|
1161
1279
|
#ifdef HAVE_RUBY_ENCODING_H
|
1162
|
-
|
1163
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1280
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1164
1281
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1165
1282
|
VALUE inspect = rb_inspect(value);
|
1166
1283
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1183,11 +1300,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1183
1300
|
GET_CLIENT(self);
|
1184
1301
|
|
1185
1302
|
mysql_ssl_set(wrapper->client,
|
1186
|
-
NIL_P(key)
|
1187
|
-
NIL_P(cert)
|
1188
|
-
NIL_P(ca)
|
1189
|
-
NIL_P(capath) ? NULL :
|
1190
|
-
NIL_P(cipher) ? NULL :
|
1303
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1304
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1305
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1306
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1307
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1191
1308
|
|
1192
1309
|
return self;
|
1193
1310
|
}
|
@@ -1208,19 +1325,35 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1208
1325
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1209
1326
|
}
|
1210
1327
|
|
1328
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1329
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1330
|
+
}
|
1331
|
+
|
1211
1332
|
static VALUE initialize_ext(VALUE self) {
|
1212
1333
|
GET_CLIENT(self);
|
1213
1334
|
|
1214
1335
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1215
1336
|
/* TODO: warning - not enough memory? */
|
1216
|
-
|
1337
|
+
rb_raise_mysql2_error(wrapper);
|
1217
1338
|
}
|
1218
1339
|
|
1219
1340
|
wrapper->initialized = 1;
|
1220
1341
|
return self;
|
1221
1342
|
}
|
1222
1343
|
|
1344
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1345
|
+
*
|
1346
|
+
* Create a new prepared statement.
|
1347
|
+
*/
|
1348
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1349
|
+
GET_CLIENT(self);
|
1350
|
+
REQUIRE_CONNECTED(wrapper);
|
1351
|
+
|
1352
|
+
return rb_mysql_stmt_new(self, sql);
|
1353
|
+
}
|
1354
|
+
|
1223
1355
|
void init_mysql2_client() {
|
1356
|
+
#ifdef _WIN32
|
1224
1357
|
/* verify the libmysql we're about to use was the version we were built against
|
1225
1358
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1226
1359
|
int i;
|
@@ -1235,15 +1368,14 @@ void init_mysql2_client() {
|
|
1235
1368
|
}
|
1236
1369
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1237
1370
|
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
1371
|
}
|
1240
1372
|
}
|
1373
|
+
#endif
|
1241
1374
|
|
1242
1375
|
/* Initializing mysql library, so different threads could call Client.new */
|
1243
1376
|
/* without race condition in the library */
|
1244
1377
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1245
1378
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1246
|
-
return;
|
1247
1379
|
}
|
1248
1380
|
|
1249
1381
|
#if 0
|
@@ -1254,26 +1386,30 @@ void init_mysql2_client() {
|
|
1254
1386
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1255
1387
|
|
1256
1388
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1389
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1257
1390
|
|
1258
1391
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1259
|
-
rb_define_method(cMysql2Client, "
|
1392
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1260
1393
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1261
1394
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1262
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1263
1395
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
1264
1396
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
1265
1397
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1266
1398
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1267
1399
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1400
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1268
1401
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1269
1402
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1270
1403
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1271
1404
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1272
1405
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1273
1406
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1407
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1408
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1274
1409
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1275
1410
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1276
1411
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1412
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1277
1413
|
#ifdef HAVE_RUBY_ENCODING_H
|
1278
1414
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1279
1415
|
#endif
|
@@ -1288,25 +1424,33 @@ void init_mysql2_client() {
|
|
1288
1424
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1289
1425
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1290
1426
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1427
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1428
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1291
1429
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1292
1430
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1431
|
+
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
1293
1432
|
|
1294
1433
|
sym_id = ID2SYM(rb_intern("id"));
|
1295
1434
|
sym_version = ID2SYM(rb_intern("version"));
|
1435
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
1296
1436
|
sym_async = ID2SYM(rb_intern("async"));
|
1297
1437
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1298
1438
|
sym_as = ID2SYM(rb_intern("as"));
|
1299
1439
|
sym_array = ID2SYM(rb_intern("array"));
|
1300
1440
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1301
1441
|
|
1442
|
+
intern_brackets = rb_intern("[]");
|
1302
1443
|
intern_merge = rb_intern("merge");
|
1303
1444
|
intern_merge_bang = rb_intern("merge!");
|
1304
|
-
|
1305
|
-
intern_sql_state_eql = rb_intern("sql_state=");
|
1445
|
+
intern_new_with_args = rb_intern("new_with_args");
|
1306
1446
|
|
1307
1447
|
#ifdef CLIENT_LONG_PASSWORD
|
1308
1448
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1309
1449
|
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1450
|
+
#else
|
1451
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1452
|
+
* but we're using it in our default connection flags. */
|
1453
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
1310
1454
|
#endif
|
1311
1455
|
|
1312
1456
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1413,4 +1557,31 @@ void init_mysql2_client() {
|
|
1413
1557
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1414
1558
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1415
1559
|
#endif
|
1560
|
+
|
1561
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1562
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1563
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1564
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1565
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1566
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1567
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1568
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1569
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1570
|
+
#endif
|
1571
|
+
|
1572
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1573
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1574
|
+
#endif
|
1575
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1576
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1577
|
+
#endif
|
1578
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1579
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1580
|
+
#endif
|
1581
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1582
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1583
|
+
#endif
|
1584
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1585
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1586
|
+
#endif
|
1416
1587
|
}
|