mysql2 0.4.4 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
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 = []