mysql2 0.4.4 → 0.4.10
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/README.md +72 -47
- data/ext/mysql2/client.c +165 -30
- data/ext/mysql2/client.h +1 -8
- data/ext/mysql2/extconf.rb +26 -2
- data/ext/mysql2/mysql2_ext.h +0 -4
- data/ext/mysql2/result.c +12 -3
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +106 -15
- data/lib/mysql2/client.rb +19 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +1 -0
- data/spec/mysql2/client_spec.rb +152 -104
- data/spec/mysql2/error_spec.rb +4 -6
- data/spec/mysql2/result_spec.rb +62 -36
- data/spec/mysql2/statement_spec.rb +125 -52
- data/spec/spec_helper.rb +73 -59
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- metadata +12 -11
data/ext/mysql2/result.c
CHANGED
@@ -262,8 +262,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
|
|
262
262
|
if (wrapper->result_buffers != NULL) return;
|
263
263
|
|
264
264
|
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
265
|
-
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(
|
266
|
-
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(
|
265
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
266
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
267
267
|
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
268
268
|
|
269
269
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
@@ -405,6 +405,13 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
405
405
|
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
406
406
|
}
|
407
407
|
break;
|
408
|
+
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
409
|
+
if (args->castBool && fields[i].length == 1) {
|
410
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
411
|
+
}else{
|
412
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
413
|
+
}
|
414
|
+
break;
|
408
415
|
case MYSQL_TYPE_SHORT: // short int
|
409
416
|
if (result_buffer->is_unsigned) {
|
410
417
|
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
@@ -494,7 +501,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
494
501
|
case MYSQL_TYPE_BLOB: // char[]
|
495
502
|
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
496
503
|
case MYSQL_TYPE_LONG_BLOB: // char[]
|
497
|
-
case MYSQL_TYPE_BIT: // char[]
|
498
504
|
case MYSQL_TYPE_SET: // char[]
|
499
505
|
case MYSQL_TYPE_ENUM: // char[]
|
500
506
|
case MYSQL_TYPE_GEOMETRY: // char[]
|
@@ -920,6 +926,9 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
920
926
|
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
921
927
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
922
928
|
} else if (wrapper->rows && !cacheRows) {
|
929
|
+
if (wrapper->resultFreed) {
|
930
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
931
|
+
}
|
923
932
|
mysql_data_seek(wrapper->result, 0);
|
924
933
|
wrapper->lastRowProcessed = 0;
|
925
934
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
data/ext/mysql2/result.h
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#ifndef MYSQL2_RESULT_H
|
2
2
|
#define MYSQL2_RESULT_H
|
3
|
+
#include <stdbool.h>
|
3
4
|
|
4
5
|
void init_mysql2_result(void);
|
5
6
|
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement);
|
@@ -21,8 +22,8 @@ typedef struct {
|
|
21
22
|
mysql_client_wrapper *client_wrapper;
|
22
23
|
/* statement result bind buffers */
|
23
24
|
MYSQL_BIND *result_buffers;
|
24
|
-
|
25
|
-
|
25
|
+
bool *is_null;
|
26
|
+
bool *error;
|
26
27
|
unsigned long *length;
|
27
28
|
} mysql2_result_wrapper;
|
28
29
|
|
data/ext/mysql2/statement.c
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
VALUE cMysql2Statement;
|
4
4
|
extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
|
5
|
-
static VALUE sym_stream, intern_new_with_args, intern_each;
|
6
|
-
static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year
|
5
|
+
static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s;
|
6
|
+
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
|
7
|
+
#ifndef HAVE_RB_BIG_CMP
|
8
|
+
static ID id_cmp;
|
9
|
+
#endif
|
7
10
|
|
8
11
|
#define GET_STATEMENT(self) \
|
9
12
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -121,7 +124,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
121
124
|
|
122
125
|
// set STMT_ATTR_UPDATE_MAX_LENGTH attr
|
123
126
|
{
|
124
|
-
|
127
|
+
bool truth = 1;
|
125
128
|
if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
|
126
129
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
|
127
130
|
}
|
@@ -180,7 +183,6 @@ static void *nogvl_execute(void *ptr) {
|
|
180
183
|
static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
|
181
184
|
unsigned long length;
|
182
185
|
|
183
|
-
bind_buffer->buffer_type = MYSQL_TYPE_STRING;
|
184
186
|
bind_buffer->buffer = RSTRING_PTR(string);
|
185
187
|
|
186
188
|
length = RSTRING_LEN(string);
|
@@ -204,6 +206,50 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
204
206
|
xfree(length_buffers); \
|
205
207
|
}
|
206
208
|
|
209
|
+
/* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
|
210
|
+
static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
211
|
+
{
|
212
|
+
unsigned LONG_LONG num;
|
213
|
+
size_t len;
|
214
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
215
|
+
int nlz_bits = 0;
|
216
|
+
len = rb_absint_size(bignum, &nlz_bits);
|
217
|
+
#else
|
218
|
+
len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS;
|
219
|
+
#endif
|
220
|
+
if (len > sizeof(LONG_LONG)) goto overflow;
|
221
|
+
if (RBIGNUM_POSITIVE_P(bignum)) {
|
222
|
+
num = rb_big2ull(bignum);
|
223
|
+
if (num > LLONG_MAX)
|
224
|
+
goto overflow;
|
225
|
+
*ptr = num;
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
if (len == 8 &&
|
229
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
230
|
+
nlz_bits == 0 &&
|
231
|
+
#endif
|
232
|
+
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
233
|
+
/* Optimized to avoid object allocation for Ruby 2.1+
|
234
|
+
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
235
|
+
*/
|
236
|
+
!rb_absint_singlebit_p(bignum)
|
237
|
+
#elif defined(HAVE_RB_BIG_CMP)
|
238
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
239
|
+
#else
|
240
|
+
/* Ruby 1.8.7 and REE doesn't have rb_big_cmp */
|
241
|
+
rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
242
|
+
#endif
|
243
|
+
) {
|
244
|
+
goto overflow;
|
245
|
+
}
|
246
|
+
*ptr = rb_big2ll(bignum);
|
247
|
+
}
|
248
|
+
return 0;
|
249
|
+
overflow:
|
250
|
+
return 1;
|
251
|
+
}
|
252
|
+
|
207
253
|
/* call-seq: stmt.execute
|
208
254
|
*
|
209
255
|
* Executes the current prepared statement, returns +result+.
|
@@ -265,9 +311,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
265
311
|
#endif
|
266
312
|
break;
|
267
313
|
case T_BIGNUM:
|
268
|
-
|
269
|
-
|
270
|
-
|
314
|
+
{
|
315
|
+
LONG_LONG num;
|
316
|
+
if (my_big2ll(argv[i], &num) == 0) {
|
317
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
318
|
+
bind_buffers[i].buffer = xmalloc(sizeof(long long int));
|
319
|
+
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
320
|
+
} else {
|
321
|
+
/* The bignum was larger than we can fit in LONG_LONG, send it as a string */
|
322
|
+
VALUE rb_val_as_string = rb_big2str(argv[i], 10);
|
323
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
324
|
+
params_enc[i] = rb_val_as_string;
|
325
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
326
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
327
|
+
#endif
|
328
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
|
+
}
|
330
|
+
}
|
271
331
|
break;
|
272
332
|
case T_FLOAT:
|
273
333
|
bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
|
@@ -275,13 +335,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
275
335
|
*(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
|
276
336
|
break;
|
277
337
|
case T_STRING:
|
278
|
-
|
279
|
-
|
338
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
339
|
+
|
340
|
+
params_enc[i] = argv[i];
|
280
341
|
#ifdef HAVE_RUBY_ENCODING_H
|
281
|
-
|
342
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
282
343
|
#endif
|
283
|
-
|
284
|
-
|
344
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
345
|
+
break;
|
346
|
+
case T_TRUE:
|
347
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
348
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
349
|
+
*(signed char*)(bind_buffers[i].buffer) = 1;
|
350
|
+
break;
|
351
|
+
case T_FALSE:
|
352
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
353
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
354
|
+
*(signed char*)(bind_buffers[i].buffer) = 0;
|
285
355
|
break;
|
286
356
|
default:
|
287
357
|
// TODO: what Ruby type should support MYSQL_TYPE_TIME
|
@@ -294,7 +364,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
294
364
|
|
295
365
|
memset(&t, 0, sizeof(MYSQL_TIME));
|
296
366
|
t.neg = 0;
|
297
|
-
|
367
|
+
|
368
|
+
if (CLASS_OF(argv[i]) == rb_cTime) {
|
369
|
+
t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
|
370
|
+
} else if (CLASS_OF(argv[i]) == cDateTime) {
|
371
|
+
t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000;
|
372
|
+
}
|
373
|
+
|
298
374
|
t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
|
299
375
|
t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
|
300
376
|
t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
|
@@ -402,6 +478,7 @@ static VALUE fields(VALUE self) {
|
|
402
478
|
rb_encoding *default_internal_enc, *conn_enc;
|
403
479
|
#endif
|
404
480
|
GET_STATEMENT(self);
|
481
|
+
GET_CLIENT(stmt_wrapper->client);
|
405
482
|
stmt = stmt_wrapper->stmt;
|
406
483
|
|
407
484
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -412,12 +489,22 @@ static VALUE fields(VALUE self) {
|
|
412
489
|
}
|
413
490
|
#endif
|
414
491
|
|
415
|
-
metadata
|
492
|
+
metadata = mysql_stmt_result_metadata(stmt);
|
493
|
+
if (metadata == NULL) {
|
494
|
+
if (mysql_stmt_errno(stmt) != 0) {
|
495
|
+
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
496
|
+
wrapper->active_thread = Qnil;
|
497
|
+
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
498
|
+
}
|
499
|
+
// no data and no error, so query was not a SELECT
|
500
|
+
return Qnil;
|
501
|
+
}
|
502
|
+
|
416
503
|
fields = mysql_fetch_fields(metadata);
|
417
504
|
field_count = mysql_stmt_field_count(stmt);
|
418
505
|
field_list = rb_ary_new2((long)field_count);
|
419
506
|
|
420
|
-
for(i = 0; i < field_count; i++) {
|
507
|
+
for (i = 0; i < field_count; i++) {
|
421
508
|
VALUE rb_field;
|
422
509
|
|
423
510
|
rb_field = rb_str_new(fields[i].name, fields[i].name_length);
|
@@ -492,6 +579,7 @@ void init_mysql2_statement() {
|
|
492
579
|
intern_new_with_args = rb_intern("new_with_args");
|
493
580
|
intern_each = rb_intern("each");
|
494
581
|
|
582
|
+
intern_sec_fraction = rb_intern("sec_fraction");
|
495
583
|
intern_usec = rb_intern("usec");
|
496
584
|
intern_sec = rb_intern("sec");
|
497
585
|
intern_min = rb_intern("min");
|
@@ -501,4 +589,7 @@ void init_mysql2_statement() {
|
|
501
589
|
intern_year = rb_intern("year");
|
502
590
|
|
503
591
|
intern_to_s = rb_intern("to_s");
|
592
|
+
#ifndef HAVE_RB_BIG_CMP
|
593
|
+
id_cmp = rb_intern("<=>");
|
594
|
+
#endif
|
504
595
|
}
|
data/lib/mysql2/client.rb
CHANGED
@@ -19,6 +19,7 @@ module Mysql2
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
|
+
fail Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
22
23
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
23
24
|
@read_timeout = nil
|
24
25
|
@query_options = self.class.default_query_options.dup
|
@@ -30,13 +31,13 @@ module Mysql2
|
|
30
31
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
31
32
|
|
32
33
|
# TODO: stricter validation rather than silent massaging
|
33
|
-
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
|
34
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close, :enable_cleartext_plugin].each do |key|
|
34
35
|
next unless opts.key?(key)
|
35
36
|
case key
|
36
|
-
when :reconnect, :local_infile, :secure_auth, :automatic_close
|
37
|
+
when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
|
37
38
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
38
39
|
when :connect_timeout, :read_timeout, :write_timeout
|
39
|
-
send(:"#{key}=", opts[key]) unless opts[key].nil?
|
40
|
+
send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
|
40
41
|
else
|
41
42
|
send(:"#{key}=", opts[key])
|
42
43
|
end
|
@@ -46,7 +47,8 @@ module Mysql2
|
|
46
47
|
self.charset_name = opts[:encoding] || 'utf8'
|
47
48
|
|
48
49
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
49
|
-
ssl_set(*ssl_options) if ssl_options.any?
|
50
|
+
ssl_set(*ssl_options) if ssl_options.any? || opts.key?(:sslverify)
|
51
|
+
self.ssl_mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
50
52
|
|
51
53
|
case opts[:flags]
|
52
54
|
when Array
|
@@ -60,11 +62,11 @@ module Mysql2
|
|
60
62
|
end
|
61
63
|
|
62
64
|
# SSL verify is a connection flag rather than a mysql_ssl_set option
|
63
|
-
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
65
|
+
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
64
66
|
|
65
67
|
if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) }
|
66
68
|
warn "============= WARNING FROM mysql2 ============="
|
67
|
-
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be
|
69
|
+
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future."
|
68
70
|
warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options."
|
69
71
|
warn "============= END WARNING FROM mysql2 ========="
|
70
72
|
end
|
@@ -87,6 +89,17 @@ module Mysql2
|
|
87
89
|
connect user, pass, host, port, database, socket, flags
|
88
90
|
end
|
89
91
|
|
92
|
+
def parse_ssl_mode(mode)
|
93
|
+
m = mode.to_s.upcase
|
94
|
+
if m.start_with?('SSL_MODE_')
|
95
|
+
return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
|
96
|
+
else
|
97
|
+
x = 'SSL_MODE_' + m
|
98
|
+
return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
|
99
|
+
end
|
100
|
+
warn "Unknown MySQL ssl_mode flag: #{mode}"
|
101
|
+
end
|
102
|
+
|
90
103
|
def parse_flags_array(flags, initial = 0)
|
91
104
|
flags.reduce(initial) do |memo, f|
|
92
105
|
fneg = f.start_with?('-') ? f[1..-1] : nil
|
data/lib/mysql2/version.rb
CHANGED
data/spec/em/em_spec.rb
CHANGED
@@ -70,6 +70,7 @@ begin
|
|
70
70
|
let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
|
71
71
|
let(:error) { StandardError.new('some error') }
|
72
72
|
before { allow(client).to receive(:async_result).and_raise(error) }
|
73
|
+
after { client.close }
|
73
74
|
|
74
75
|
it "should swallow exceptions raised in by the client" do
|
75
76
|
errors = []
|