mysql2 0.3.18 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/LICENSE +21 -0
- data/README.md +63 -12
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +170 -175
- data/ext/mysql2/client.h +21 -1
- data/ext/mysql2/extconf.rb +95 -35
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +5 -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 +494 -132
- data/ext/mysql2/result.h +12 -6
- data/ext/mysql2/statement.c +494 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +68 -22
- 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/em/em_spec.rb +21 -21
- data/spec/mysql2/client_spec.rb +393 -351
- data/spec/mysql2/error_spec.rb +37 -36
- data/spec/mysql2/result_spec.rb +213 -208
- data/spec/mysql2/statement_spec.rb +684 -0
- data/spec/spec_helper.rb +7 -0
- 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 +41 -46
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,12 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
30
30
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
31
|
}
|
32
32
|
|
33
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
34
|
-
REQUIRE_INITIALIZED(wrapper) \
|
35
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
36
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
37
|
-
}
|
38
|
-
|
39
33
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
40
34
|
REQUIRE_INITIALIZED(wrapper) \
|
41
35
|
if (wrapper->connected) { \
|
42
36
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
43
37
|
}
|
44
38
|
|
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
39
|
/*
|
53
40
|
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
54
41
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
@@ -136,16 +123,17 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
136
123
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
137
124
|
#endif
|
138
125
|
|
139
|
-
e = rb_funcall(cMysql2Error,
|
140
|
-
|
141
|
-
|
126
|
+
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
127
|
+
rb_error_msg,
|
128
|
+
LONG2FIX(wrapper->server_version),
|
129
|
+
UINT2NUM(mysql_errno(wrapper->client)),
|
130
|
+
rb_sql_state);
|
142
131
|
rb_exc_raise(e);
|
143
|
-
return Qnil;
|
144
132
|
}
|
145
133
|
|
146
134
|
static void *nogvl_init(void *ptr) {
|
147
135
|
MYSQL *client;
|
148
|
-
mysql_client_wrapper *wrapper =
|
136
|
+
mysql_client_wrapper *wrapper = ptr;
|
149
137
|
|
150
138
|
/* may initialize embedded server and read /etc/services off disk */
|
151
139
|
client = mysql_init(wrapper->client);
|
@@ -182,23 +170,31 @@ static void *nogvl_connect(void *ptr) {
|
|
182
170
|
*/
|
183
171
|
static VALUE invalidate_fd(int clientfd)
|
184
172
|
{
|
185
|
-
#ifdef
|
173
|
+
#ifdef O_CLOEXEC
|
186
174
|
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
187
175
|
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
176
|
#else
|
194
|
-
/* Well we don't have
|
195
|
-
int sockfd =
|
196
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
177
|
+
/* Well we don't have O_CLOEXEC, trigger the fallback code below */
|
178
|
+
int sockfd = -1;
|
197
179
|
#endif
|
198
180
|
|
199
181
|
if (sockfd < 0) {
|
200
|
-
/*
|
201
|
-
*
|
182
|
+
/* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
|
183
|
+
* compile time, but isn't available at run-time. So we'll just be quick
|
184
|
+
* about setting FD_CLOEXEC now.
|
185
|
+
*/
|
186
|
+
int flags;
|
187
|
+
sockfd = open("/dev/null", O_RDWR);
|
188
|
+
flags = fcntl(sockfd, F_GETFD);
|
189
|
+
/* Do the flags dance in case there are more defined flags in the future */
|
190
|
+
if (flags != -1) {
|
191
|
+
flags |= FD_CLOEXEC;
|
192
|
+
fcntl(sockfd, F_SETFD, flags);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
if (sockfd < 0) {
|
197
|
+
/* Cannot raise here, because one or both of the following may be true:
|
202
198
|
* a) we have no GVL (in C Ruby)
|
203
199
|
* b) are running as a GC finalizer
|
204
200
|
*/
|
@@ -213,43 +209,47 @@ static VALUE invalidate_fd(int clientfd)
|
|
213
209
|
#endif /* _WIN32 */
|
214
210
|
|
215
211
|
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
|
212
|
+
mysql_client_wrapper *wrapper = ptr;
|
235
213
|
|
236
|
-
|
214
|
+
if (wrapper->client) {
|
215
|
+
mysql_close(wrapper->client);
|
216
|
+
xfree(wrapper->client);
|
217
|
+
wrapper->client = NULL;
|
218
|
+
wrapper->connected = 0;
|
219
|
+
wrapper->active_thread = Qnil;
|
237
220
|
}
|
238
221
|
|
239
222
|
return NULL;
|
240
223
|
}
|
241
224
|
|
225
|
+
/* this is called during GC */
|
242
226
|
static void rb_mysql_client_free(void *ptr) {
|
243
|
-
mysql_client_wrapper *wrapper =
|
227
|
+
mysql_client_wrapper *wrapper = ptr;
|
244
228
|
decr_mysql2_client(wrapper);
|
245
229
|
}
|
246
230
|
|
247
231
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
248
232
|
{
|
249
233
|
wrapper->refcount--;
|
234
|
+
|
250
235
|
if (wrapper->refcount == 0) {
|
236
|
+
#ifndef _WIN32
|
237
|
+
if (wrapper->connected) {
|
238
|
+
/* The client is being garbage collected while connected. Prevent
|
239
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
240
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
241
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
242
|
+
* would render the underlying connection unusable, interrupting other
|
243
|
+
* processes which share this object across a fork().
|
244
|
+
*/
|
245
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
246
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
247
|
+
close(wrapper->client->net.fd);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
#endif
|
251
|
+
|
251
252
|
nogvl_close(wrapper);
|
252
|
-
xfree(wrapper->client);
|
253
253
|
xfree(wrapper);
|
254
254
|
}
|
255
255
|
}
|
@@ -259,7 +259,7 @@ static VALUE allocate(VALUE klass) {
|
|
259
259
|
mysql_client_wrapper * wrapper;
|
260
260
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
261
261
|
wrapper->encoding = Qnil;
|
262
|
-
|
262
|
+
MARK_CONN_INACTIVE(self);
|
263
263
|
wrapper->server_version = 0;
|
264
264
|
wrapper->reconnect_enabled = 0;
|
265
265
|
wrapper->connect_timeout = 0;
|
@@ -267,6 +267,7 @@ static VALUE allocate(VALUE klass) {
|
|
267
267
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
268
268
|
wrapper->refcount = 1;
|
269
269
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
270
|
+
|
270
271
|
return obj;
|
271
272
|
}
|
272
273
|
|
@@ -287,7 +288,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
287
288
|
oldLen = RSTRING_LEN(str);
|
288
289
|
newStr = xmalloc(oldLen*2+1);
|
289
290
|
|
290
|
-
newLen = mysql_escape_string((char *)newStr,
|
291
|
+
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
|
291
292
|
if (newLen == oldLen) {
|
292
293
|
/* no need to return a new ruby string if nothing changed */
|
293
294
|
xfree(newStr);
|
@@ -332,18 +333,17 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
332
333
|
|
333
334
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
334
335
|
struct nogvl_connect_args args;
|
335
|
-
time_t start_time, end_time;
|
336
|
-
unsigned int elapsed_time, connect_timeout;
|
336
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
337
337
|
VALUE rv;
|
338
338
|
GET_CLIENT(self);
|
339
339
|
|
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
|
340
|
+
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
|
341
|
+
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
|
342
|
+
args.port = NIL_P(port) ? 0 : NUM2INT(port);
|
343
|
+
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
|
344
|
+
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
|
345
|
+
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
|
346
|
+
args.mysql = wrapper->client;
|
347
347
|
args.client_flag = NUM2ULONG(flags);
|
348
348
|
|
349
349
|
if (wrapper->connect_timeout)
|
@@ -360,7 +360,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
360
360
|
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
361
361
|
if (elapsed_time > 0)
|
362
362
|
elapsed_time--;
|
363
|
-
if (elapsed_time >= wrapper->connect_timeout)
|
363
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
364
364
|
break;
|
365
365
|
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
366
366
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
@@ -381,10 +381,13 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
381
381
|
}
|
382
382
|
|
383
383
|
/*
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
387
|
-
*
|
384
|
+
* Terminate the connection; call this when the connection is no longer needed.
|
385
|
+
* The garbage collector can close the connection, but doing so emits an
|
386
|
+
* "Aborted connection" error on the server and increments the Aborted_clients
|
387
|
+
* status variable.
|
388
|
+
*
|
389
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
390
|
+
* @return [void]
|
388
391
|
*/
|
389
392
|
static VALUE rb_mysql_client_close(VALUE self) {
|
390
393
|
GET_CLIENT(self);
|
@@ -434,10 +437,9 @@ static void *nogvl_read_query_result(void *ptr) {
|
|
434
437
|
}
|
435
438
|
|
436
439
|
static void *nogvl_do_result(void *ptr, char use_result) {
|
437
|
-
mysql_client_wrapper *wrapper;
|
440
|
+
mysql_client_wrapper *wrapper = ptr;
|
438
441
|
MYSQL_RES *result;
|
439
442
|
|
440
|
-
wrapper = (mysql_client_wrapper *)ptr;
|
441
443
|
if (use_result) {
|
442
444
|
result = mysql_use_result(wrapper->client);
|
443
445
|
} else {
|
@@ -446,7 +448,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
446
448
|
|
447
449
|
/* once our result is stored off, this connection is
|
448
450
|
ready for another command to be issued */
|
449
|
-
|
451
|
+
MARK_CONN_INACTIVE(self);
|
450
452
|
|
451
453
|
return result;
|
452
454
|
}
|
@@ -499,9 +501,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
499
501
|
}
|
500
502
|
|
501
503
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
502
|
-
RB_GC_GUARD(current);
|
504
|
+
(void)RB_GC_GUARD(current);
|
503
505
|
Check_Type(current, T_HASH);
|
504
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
506
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
505
507
|
|
506
508
|
return resultObj;
|
507
509
|
}
|
@@ -515,7 +517,7 @@ struct async_query_args {
|
|
515
517
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
516
518
|
GET_CLIENT(self);
|
517
519
|
|
518
|
-
|
520
|
+
MARK_CONN_INACTIVE(self);
|
519
521
|
wrapper->connected = 0;
|
520
522
|
|
521
523
|
/* Invalidate the MySQL socket to prevent further communication.
|
@@ -527,19 +529,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
527
529
|
}
|
528
530
|
|
529
531
|
rb_exc_raise(error);
|
530
|
-
|
531
|
-
return Qnil;
|
532
532
|
}
|
533
533
|
|
534
534
|
static VALUE do_query(void *args) {
|
535
|
-
struct async_query_args *async_args;
|
535
|
+
struct async_query_args *async_args = args;
|
536
536
|
struct timeval tv;
|
537
|
-
struct timeval*
|
537
|
+
struct timeval *tvp;
|
538
538
|
long int sec;
|
539
539
|
int retval;
|
540
540
|
VALUE read_timeout;
|
541
541
|
|
542
|
-
async_args = (struct async_query_args *)args;
|
543
542
|
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
544
543
|
|
545
544
|
tvp = NULL;
|
@@ -577,11 +576,9 @@ static VALUE do_query(void *args) {
|
|
577
576
|
}
|
578
577
|
#else
|
579
578
|
static VALUE finish_and_mark_inactive(void *args) {
|
580
|
-
VALUE self;
|
579
|
+
VALUE self = args;
|
581
580
|
MYSQL_RES *result;
|
582
581
|
|
583
|
-
self = (VALUE)args;
|
584
|
-
|
585
582
|
GET_CLIENT(self);
|
586
583
|
|
587
584
|
if (!NIL_P(wrapper->active_thread)) {
|
@@ -591,13 +588,31 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
591
588
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
592
589
|
mysql_free_result(result);
|
593
590
|
|
594
|
-
|
591
|
+
MARK_CONN_INACTIVE(self);
|
595
592
|
}
|
596
593
|
|
597
594
|
return Qnil;
|
598
595
|
}
|
599
596
|
#endif
|
600
597
|
|
598
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
599
|
+
VALUE thread_current = rb_thread_current();
|
600
|
+
GET_CLIENT(self);
|
601
|
+
|
602
|
+
// see if this connection is still waiting on a result from a previous query
|
603
|
+
if (NIL_P(wrapper->active_thread)) {
|
604
|
+
// mark this connection active
|
605
|
+
wrapper->active_thread = thread_current;
|
606
|
+
} else if (wrapper->active_thread == thread_current) {
|
607
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
608
|
+
} else {
|
609
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
610
|
+
const char *thr = StringValueCStr(inspect);
|
611
|
+
|
612
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
613
|
+
}
|
614
|
+
}
|
615
|
+
|
601
616
|
/* call-seq:
|
602
617
|
* client.abandon_results!
|
603
618
|
*
|
@@ -632,74 +647,47 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
632
647
|
* client.query(sql, options = {})
|
633
648
|
*
|
634
649
|
* Query the database with +sql+, with optional +options+. For the possible
|
635
|
-
* options, see
|
650
|
+
* options, see default_query_options on the Mysql2::Client class.
|
636
651
|
*/
|
637
|
-
static VALUE
|
652
|
+
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
638
653
|
#ifndef _WIN32
|
639
654
|
struct async_query_args async_args;
|
640
655
|
#endif
|
641
656
|
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
657
|
GET_CLIENT(self);
|
649
658
|
|
650
659
|
REQUIRE_CONNECTED(wrapper);
|
651
660
|
args.mysql = wrapper->client;
|
652
661
|
|
653
|
-
|
654
|
-
RB_GC_GUARD(current);
|
662
|
+
(void)RB_GC_GUARD(current);
|
655
663
|
Check_Type(current, T_HASH);
|
656
664
|
rb_iv_set(self, "@current_query_options", current);
|
657
665
|
|
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);
|
666
|
+
Check_Type(sql, T_STRING);
|
667
667
|
#ifdef HAVE_RUBY_ENCODING_H
|
668
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
669
668
|
/* ensure the string is in the encoding the connection is expecting */
|
670
|
-
args.sql = rb_str_export_to_enc(
|
669
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
670
|
+
#else
|
671
|
+
args.sql = sql;
|
671
672
|
#endif
|
672
|
-
args.sql_ptr =
|
673
|
+
args.sql_ptr = RSTRING_PTR(args.sql);
|
673
674
|
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
675
|
args.wrapper = wrapper;
|
690
676
|
|
677
|
+
rb_mysql_client_set_active_thread(self);
|
678
|
+
|
691
679
|
#ifndef _WIN32
|
692
680
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
693
681
|
|
694
|
-
if (
|
682
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
683
|
+
return Qnil;
|
684
|
+
} else {
|
695
685
|
async_args.fd = wrapper->client->net.fd;
|
696
686
|
async_args.self = self;
|
697
687
|
|
698
688
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
699
689
|
|
700
690
|
return rb_mysql_client_async_result(self);
|
701
|
-
} else {
|
702
|
-
return Qnil;
|
703
691
|
}
|
704
692
|
#else
|
705
693
|
do_send_query(&args);
|
@@ -736,9 +724,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
736
724
|
oldLen = RSTRING_LEN(str);
|
737
725
|
newStr = xmalloc(oldLen*2+1);
|
738
726
|
|
739
|
-
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr,
|
727
|
+
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
740
728
|
if (newLen == oldLen) {
|
741
729
|
/* no need to return a new ruby string if nothing changed */
|
730
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
731
|
+
if (default_internal_enc) {
|
732
|
+
str = rb_str_export_to_enc(str, default_internal_enc);
|
733
|
+
}
|
734
|
+
#endif
|
742
735
|
xfree(newStr);
|
743
736
|
return str;
|
744
737
|
} else {
|
@@ -800,17 +793,17 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
800
793
|
break;
|
801
794
|
|
802
795
|
case MYSQL_READ_DEFAULT_FILE:
|
803
|
-
charval = (const char *)
|
796
|
+
charval = (const char *)StringValueCStr(value);
|
804
797
|
retval = charval;
|
805
798
|
break;
|
806
799
|
|
807
800
|
case MYSQL_READ_DEFAULT_GROUP:
|
808
|
-
charval = (const char *)
|
801
|
+
charval = (const char *)StringValueCStr(value);
|
809
802
|
retval = charval;
|
810
803
|
break;
|
811
804
|
|
812
805
|
case MYSQL_INIT_COMMAND:
|
813
|
-
charval = (const char *)
|
806
|
+
charval = (const char *)StringValueCStr(value);
|
814
807
|
retval = charval;
|
815
808
|
break;
|
816
809
|
|
@@ -843,30 +836,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
843
836
|
*
|
844
837
|
* Returns a string that represents the client library version.
|
845
838
|
*/
|
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();
|
839
|
+
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
840
|
+
VALUE version_info, version, header_version;
|
841
|
+
version_info = rb_hash_new();
|
854
842
|
|
855
|
-
|
856
|
-
|
857
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
858
|
-
#endif
|
843
|
+
version = rb_str_new2(mysql_get_client_info());
|
844
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
859
845
|
|
860
|
-
rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
|
861
|
-
client_info = rb_str_new2(mysql_get_client_info());
|
862
846
|
#ifdef HAVE_RUBY_ENCODING_H
|
863
|
-
rb_enc_associate(
|
864
|
-
|
865
|
-
client_info = rb_str_export_to_enc(client_info, default_internal_enc);
|
866
|
-
}
|
847
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
848
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
867
849
|
#endif
|
868
|
-
|
869
|
-
|
850
|
+
|
851
|
+
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
852
|
+
rb_hash_aset(version_info, sym_version, version);
|
853
|
+
rb_hash_aset(version_info, sym_header_version, header_version);
|
854
|
+
|
855
|
+
return version_info;
|
870
856
|
}
|
871
857
|
|
872
858
|
/* call-seq:
|
@@ -906,19 +892,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
906
892
|
*
|
907
893
|
* Return the file descriptor number for this client.
|
908
894
|
*/
|
895
|
+
#ifndef _WIN32
|
909
896
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
910
897
|
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
|
-
}
|
898
|
+
REQUIRE_CONNECTED(wrapper);
|
899
|
+
return INT2NUM(wrapper->client->net.fd);
|
900
|
+
}
|
918
901
|
#else
|
902
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
919
903
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
920
|
-
#endif
|
921
904
|
}
|
905
|
+
#endif
|
922
906
|
|
923
907
|
/* call-seq:
|
924
908
|
* client.last_id
|
@@ -987,7 +971,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
|
987
971
|
REQUIRE_CONNECTED(wrapper);
|
988
972
|
|
989
973
|
args.mysql = wrapper->client;
|
990
|
-
args.db =
|
974
|
+
args.db = StringValueCStr(db);
|
991
975
|
|
992
976
|
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
993
977
|
rb_raise_mysql2_error(wrapper);
|
@@ -1078,9 +1062,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1078
1062
|
}
|
1079
1063
|
|
1080
1064
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1081
|
-
RB_GC_GUARD(current);
|
1065
|
+
(void)RB_GC_GUARD(current);
|
1082
1066
|
Check_Type(current, T_HASH);
|
1083
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1067
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1084
1068
|
|
1085
1069
|
return resultObj;
|
1086
1070
|
}
|
@@ -1149,7 +1133,6 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1149
1133
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1150
1134
|
char *charset_name;
|
1151
1135
|
#ifdef HAVE_RUBY_ENCODING_H
|
1152
|
-
size_t charset_name_len;
|
1153
1136
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1154
1137
|
rb_encoding *enc;
|
1155
1138
|
VALUE rb_enc;
|
@@ -1159,8 +1142,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1159
1142
|
charset_name = RSTRING_PTR(value);
|
1160
1143
|
|
1161
1144
|
#ifdef HAVE_RUBY_ENCODING_H
|
1162
|
-
|
1163
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1145
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1164
1146
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1165
1147
|
VALUE inspect = rb_inspect(value);
|
1166
1148
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1183,11 +1165,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1183
1165
|
GET_CLIENT(self);
|
1184
1166
|
|
1185
1167
|
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 :
|
1168
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1169
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1170
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1171
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1172
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1191
1173
|
|
1192
1174
|
return self;
|
1193
1175
|
}
|
@@ -1220,7 +1202,19 @@ static VALUE initialize_ext(VALUE self) {
|
|
1220
1202
|
return self;
|
1221
1203
|
}
|
1222
1204
|
|
1205
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1206
|
+
*
|
1207
|
+
* Create a new prepared statement.
|
1208
|
+
*/
|
1209
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1210
|
+
GET_CLIENT(self);
|
1211
|
+
REQUIRE_CONNECTED(wrapper);
|
1212
|
+
|
1213
|
+
return rb_mysql_stmt_new(self, sql);
|
1214
|
+
}
|
1215
|
+
|
1223
1216
|
void init_mysql2_client() {
|
1217
|
+
#ifdef _WIN32
|
1224
1218
|
/* verify the libmysql we're about to use was the version we were built against
|
1225
1219
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1226
1220
|
int i;
|
@@ -1235,15 +1229,14 @@ void init_mysql2_client() {
|
|
1235
1229
|
}
|
1236
1230
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1237
1231
|
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
1232
|
}
|
1240
1233
|
}
|
1234
|
+
#endif
|
1241
1235
|
|
1242
1236
|
/* Initializing mysql library, so different threads could call Client.new */
|
1243
1237
|
/* without race condition in the library */
|
1244
1238
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1245
1239
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1246
|
-
return;
|
1247
1240
|
}
|
1248
1241
|
|
1249
1242
|
#if 0
|
@@ -1254,17 +1247,17 @@ void init_mysql2_client() {
|
|
1254
1247
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1255
1248
|
|
1256
1249
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1250
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1257
1251
|
|
1258
1252
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1259
|
-
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
1260
1253
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1261
1254
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1262
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1263
1255
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
1264
1256
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
1265
1257
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1266
1258
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1267
1259
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1260
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1268
1261
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1269
1262
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1270
1263
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
@@ -1290,19 +1283,21 @@ void init_mysql2_client() {
|
|
1290
1283
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1291
1284
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1292
1285
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1286
|
+
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
1293
1287
|
|
1294
1288
|
sym_id = ID2SYM(rb_intern("id"));
|
1295
1289
|
sym_version = ID2SYM(rb_intern("version"));
|
1290
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
1296
1291
|
sym_async = ID2SYM(rb_intern("async"));
|
1297
1292
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1298
1293
|
sym_as = ID2SYM(rb_intern("as"));
|
1299
1294
|
sym_array = ID2SYM(rb_intern("array"));
|
1300
1295
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1301
1296
|
|
1297
|
+
intern_brackets = rb_intern("[]");
|
1302
1298
|
intern_merge = rb_intern("merge");
|
1303
1299
|
intern_merge_bang = rb_intern("merge!");
|
1304
|
-
|
1305
|
-
intern_sql_state_eql = rb_intern("sql_state=");
|
1300
|
+
intern_new_with_args = rb_intern("new_with_args");
|
1306
1301
|
|
1307
1302
|
#ifdef CLIENT_LONG_PASSWORD
|
1308
1303
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|