mysql2 0.3.18 → 0.4.9
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 +132 -55
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +374 -197
- data/ext/mysql2/client.h +13 -3
- data/ext/mysql2/extconf.rb +119 -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 +595 -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 +530 -388
- data/spec/mysql2/error_spec.rb +38 -39
- data/spec/mysql2/result_spec.rb +223 -214
- data/spec/mysql2/statement_spec.rb +763 -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,27 @@ 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
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
907
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
908
|
+
boolval = (value == Qfalse ? 0 : 1);
|
909
|
+
retval = &boolval;
|
910
|
+
break;
|
911
|
+
#endif
|
912
|
+
|
817
913
|
default:
|
818
914
|
return Qfalse;
|
819
915
|
}
|
@@ -843,30 +939,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
843
939
|
*
|
844
940
|
* Returns a string that represents the client library version.
|
845
941
|
*/
|
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();
|
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();
|
854
945
|
|
855
|
-
|
856
|
-
|
857
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
858
|
-
#endif
|
946
|
+
version = rb_str_new2(mysql_get_client_info());
|
947
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
859
948
|
|
860
|
-
rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
|
861
|
-
client_info = rb_str_new2(mysql_get_client_info());
|
862
949
|
#ifdef HAVE_RUBY_ENCODING_H
|
863
|
-
rb_enc_associate(
|
864
|
-
|
865
|
-
client_info = rb_str_export_to_enc(client_info, default_internal_enc);
|
866
|
-
}
|
950
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
951
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
867
952
|
#endif
|
868
|
-
|
869
|
-
|
953
|
+
|
954
|
+
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
955
|
+
rb_hash_aset(version_info, sym_version, version);
|
956
|
+
rb_hash_aset(version_info, sym_header_version, header_version);
|
957
|
+
|
958
|
+
return version_info;
|
870
959
|
}
|
871
960
|
|
872
961
|
/* call-seq:
|
@@ -906,19 +995,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
906
995
|
*
|
907
996
|
* Return the file descriptor number for this client.
|
908
997
|
*/
|
998
|
+
#ifndef _WIN32
|
909
999
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
910
1000
|
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
|
-
}
|
1001
|
+
REQUIRE_CONNECTED(wrapper);
|
1002
|
+
return INT2NUM(wrapper->client->net.fd);
|
1003
|
+
}
|
918
1004
|
#else
|
1005
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
919
1006
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
920
|
-
#endif
|
921
1007
|
}
|
1008
|
+
#endif
|
922
1009
|
|
923
1010
|
/* call-seq:
|
924
1011
|
* client.last_id
|
@@ -987,7 +1074,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
|
987
1074
|
REQUIRE_CONNECTED(wrapper);
|
988
1075
|
|
989
1076
|
args.mysql = wrapper->client;
|
990
|
-
args.db =
|
1077
|
+
args.db = StringValueCStr(db);
|
991
1078
|
|
992
1079
|
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
993
1080
|
rb_raise_mysql2_error(wrapper);
|
@@ -1012,7 +1099,7 @@ static void *nogvl_ping(void *ptr) {
|
|
1012
1099
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
1013
1100
|
GET_CLIENT(self);
|
1014
1101
|
|
1015
|
-
if (!wrapper
|
1102
|
+
if (!CONNECTED(wrapper)) {
|
1016
1103
|
return Qfalse;
|
1017
1104
|
} else {
|
1018
1105
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
@@ -1027,10 +1114,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1027
1114
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1028
1115
|
{
|
1029
1116
|
GET_CLIENT(self);
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1117
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1118
|
+
return Qfalse;
|
1119
|
+
else
|
1120
|
+
return Qtrue;
|
1034
1121
|
}
|
1035
1122
|
|
1036
1123
|
/* call-seq:
|
@@ -1078,9 +1165,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1078
1165
|
}
|
1079
1166
|
|
1080
1167
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1081
|
-
RB_GC_GUARD(current);
|
1168
|
+
(void)RB_GC_GUARD(current);
|
1082
1169
|
Check_Type(current, T_HASH);
|
1083
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1170
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1084
1171
|
|
1085
1172
|
return resultObj;
|
1086
1173
|
}
|
@@ -1097,6 +1184,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1097
1184
|
}
|
1098
1185
|
#endif
|
1099
1186
|
|
1187
|
+
/* call-seq:
|
1188
|
+
* client.automatic_close?
|
1189
|
+
*
|
1190
|
+
* @return [Boolean]
|
1191
|
+
*/
|
1192
|
+
static VALUE get_automatic_close(VALUE self) {
|
1193
|
+
GET_CLIENT(self);
|
1194
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
/* call-seq:
|
1198
|
+
* client.automatic_close = false
|
1199
|
+
*
|
1200
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1201
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1202
|
+
* call +close+ when the connection is no longer needed.
|
1203
|
+
*
|
1204
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1205
|
+
*/
|
1206
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1207
|
+
GET_CLIENT(self);
|
1208
|
+
if (RTEST(value)) {
|
1209
|
+
wrapper->automatic_close = 1;
|
1210
|
+
} else {
|
1211
|
+
#ifndef _WIN32
|
1212
|
+
wrapper->automatic_close = 0;
|
1213
|
+
#else
|
1214
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1215
|
+
#endif
|
1216
|
+
}
|
1217
|
+
return value;
|
1218
|
+
}
|
1219
|
+
|
1100
1220
|
/* call-seq:
|
1101
1221
|
* client.reconnect = true
|
1102
1222
|
*
|
@@ -1149,18 +1269,17 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1149
1269
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1150
1270
|
char *charset_name;
|
1151
1271
|
#ifdef HAVE_RUBY_ENCODING_H
|
1152
|
-
size_t charset_name_len;
|
1153
1272
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1154
1273
|
rb_encoding *enc;
|
1155
1274
|
VALUE rb_enc;
|
1156
1275
|
#endif
|
1157
1276
|
GET_CLIENT(self);
|
1158
1277
|
|
1278
|
+
Check_Type(value, T_STRING);
|
1159
1279
|
charset_name = RSTRING_PTR(value);
|
1160
1280
|
|
1161
1281
|
#ifdef HAVE_RUBY_ENCODING_H
|
1162
|
-
|
1163
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1282
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1164
1283
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1165
1284
|
VALUE inspect = rb_inspect(value);
|
1166
1285
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1183,11 +1302,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1183
1302
|
GET_CLIENT(self);
|
1184
1303
|
|
1185
1304
|
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 :
|
1305
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1306
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1307
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1308
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1309
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1191
1310
|
|
1192
1311
|
return self;
|
1193
1312
|
}
|
@@ -1208,19 +1327,39 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1208
1327
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1209
1328
|
}
|
1210
1329
|
|
1330
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1331
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1332
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1333
|
+
#else
|
1334
|
+
rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
|
1335
|
+
#endif
|
1336
|
+
}
|
1337
|
+
|
1211
1338
|
static VALUE initialize_ext(VALUE self) {
|
1212
1339
|
GET_CLIENT(self);
|
1213
1340
|
|
1214
1341
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1215
1342
|
/* TODO: warning - not enough memory? */
|
1216
|
-
|
1343
|
+
rb_raise_mysql2_error(wrapper);
|
1217
1344
|
}
|
1218
1345
|
|
1219
1346
|
wrapper->initialized = 1;
|
1220
1347
|
return self;
|
1221
1348
|
}
|
1222
1349
|
|
1350
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1351
|
+
*
|
1352
|
+
* Create a new prepared statement.
|
1353
|
+
*/
|
1354
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1355
|
+
GET_CLIENT(self);
|
1356
|
+
REQUIRE_CONNECTED(wrapper);
|
1357
|
+
|
1358
|
+
return rb_mysql_stmt_new(self, sql);
|
1359
|
+
}
|
1360
|
+
|
1223
1361
|
void init_mysql2_client() {
|
1362
|
+
#ifdef _WIN32
|
1224
1363
|
/* verify the libmysql we're about to use was the version we were built against
|
1225
1364
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1226
1365
|
int i;
|
@@ -1235,15 +1374,14 @@ void init_mysql2_client() {
|
|
1235
1374
|
}
|
1236
1375
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1237
1376
|
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
1377
|
}
|
1240
1378
|
}
|
1379
|
+
#endif
|
1241
1380
|
|
1242
1381
|
/* Initializing mysql library, so different threads could call Client.new */
|
1243
1382
|
/* without race condition in the library */
|
1244
1383
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1245
1384
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1246
|
-
return;
|
1247
1385
|
}
|
1248
1386
|
|
1249
1387
|
#if 0
|
@@ -1254,26 +1392,30 @@ void init_mysql2_client() {
|
|
1254
1392
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1255
1393
|
|
1256
1394
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1395
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1257
1396
|
|
1258
1397
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1259
|
-
rb_define_method(cMysql2Client, "
|
1398
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1260
1399
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1261
1400
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1262
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1263
1401
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
1264
1402
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
1265
1403
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1266
1404
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1267
1405
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1406
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1268
1407
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1269
1408
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1270
1409
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1271
1410
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1272
1411
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1273
1412
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1413
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1414
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1274
1415
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1275
1416
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1276
1417
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1418
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1277
1419
|
#ifdef HAVE_RUBY_ENCODING_H
|
1278
1420
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1279
1421
|
#endif
|
@@ -1288,25 +1430,33 @@ void init_mysql2_client() {
|
|
1288
1430
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1289
1431
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1290
1432
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1433
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1434
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1291
1435
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1292
1436
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1437
|
+
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
1293
1438
|
|
1294
1439
|
sym_id = ID2SYM(rb_intern("id"));
|
1295
1440
|
sym_version = ID2SYM(rb_intern("version"));
|
1441
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
1296
1442
|
sym_async = ID2SYM(rb_intern("async"));
|
1297
1443
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1298
1444
|
sym_as = ID2SYM(rb_intern("as"));
|
1299
1445
|
sym_array = ID2SYM(rb_intern("array"));
|
1300
1446
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1301
1447
|
|
1448
|
+
intern_brackets = rb_intern("[]");
|
1302
1449
|
intern_merge = rb_intern("merge");
|
1303
1450
|
intern_merge_bang = rb_intern("merge!");
|
1304
|
-
|
1305
|
-
intern_sql_state_eql = rb_intern("sql_state=");
|
1451
|
+
intern_new_with_args = rb_intern("new_with_args");
|
1306
1452
|
|
1307
1453
|
#ifdef CLIENT_LONG_PASSWORD
|
1308
1454
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1309
1455
|
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1456
|
+
#else
|
1457
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1458
|
+
* but we're using it in our default connection flags. */
|
1459
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
1310
1460
|
#endif
|
1311
1461
|
|
1312
1462
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1413,4 +1563,31 @@ void init_mysql2_client() {
|
|
1413
1563
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1414
1564
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1415
1565
|
#endif
|
1566
|
+
|
1567
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1568
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1569
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1570
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1571
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1572
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1573
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1574
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1575
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1576
|
+
#endif
|
1577
|
+
|
1578
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1579
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1580
|
+
#endif
|
1581
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1582
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1583
|
+
#endif
|
1584
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1585
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1586
|
+
#endif
|
1587
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1588
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1589
|
+
#endif
|
1590
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1591
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1592
|
+
#endif
|
1416
1593
|
}
|