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.
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(my_bool));
266
- wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
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
- my_bool *is_null;
25
- my_bool *error;
25
+ bool *is_null;
26
+ bool *error;
26
27
  unsigned long *length;
27
28
  } mysql2_result_wrapper;
28
29
 
@@ -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, intern_to_s;
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
- my_bool truth = 1;
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
- bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
269
- bind_buffers[i].buffer = xmalloc(sizeof(long long int));
270
- *(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[i]);
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
- params_enc[i] = argv[i];
338
+ bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
339
+
340
+ params_enc[i] = argv[i];
280
341
  #ifdef HAVE_RUBY_ENCODING_H
281
- params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
342
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
282
343
  #endif
283
- set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
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
- t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
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 = mysql_stmt_result_metadata(stmt);
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] && ssl_options.any?
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 deprecated at some point in the future."
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
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.4.4"
2
+ VERSION = "0.4.10"
3
3
  end
@@ -9,9 +9,3 @@ user:
9
9
  username: LOCALUSERNAME
10
10
  password:
11
11
  database: mysql2_test
12
-
13
- numericuser:
14
- host: localhost
15
- username: LOCALUSERNAME
16
- password:
17
- database: 12345
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 = []