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