mysql2 0.5.3 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mysql2/result.c CHANGED
@@ -17,9 +17,27 @@ static rb_encoding *binaryEncoding;
17
17
  */
18
18
  #define MYSQL2_MIN_TIME 2678400ULL
19
19
 
20
+ #define MYSQL2_MAX_BYTES_PER_CHAR 3
21
+
22
+ /* From Mysql documentations:
23
+ * To distinguish between binary and nonbinary data for string data types,
24
+ * check whether the charsetnr value is 63. If so, the character set is binary,
25
+ * which indicates binary rather than nonbinary data. This enables you to distinguish BINARY
26
+ * from CHAR, VARBINARY from VARCHAR, and the BLOB types from the TEXT types.
27
+ */
28
+ #define MYSQL2_BINARY_CHARSET 63
29
+
30
+ #ifndef MYSQL_TYPE_JSON
31
+ #define MYSQL_TYPE_JSON 245
32
+ #endif
33
+
34
+ #ifndef NEW_TYPEDDATA_WRAPPER
35
+ #define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
36
+ #endif
37
+
20
38
  #define GET_RESULT(self) \
21
39
  mysql2_result_wrapper *wrapper; \
22
- Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
40
+ TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
23
41
 
24
42
  typedef struct {
25
43
  int symbolizeKeys;
@@ -47,11 +65,11 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
47
65
  static void rb_mysql_result_mark(void * wrapper) {
48
66
  mysql2_result_wrapper * w = wrapper;
49
67
  if (w) {
50
- rb_gc_mark(w->fields);
51
- rb_gc_mark(w->rows);
52
- rb_gc_mark(w->encoding);
53
- rb_gc_mark(w->client);
54
- rb_gc_mark(w->statement);
68
+ rb_gc_mark_movable(w->fields);
69
+ rb_gc_mark_movable(w->rows);
70
+ rb_gc_mark_movable(w->encoding);
71
+ rb_gc_mark_movable(w->client);
72
+ rb_gc_mark_movable(w->statement);
55
73
  }
56
74
  }
57
75
 
@@ -113,6 +131,48 @@ static void rb_mysql_result_free(void *ptr) {
113
131
  xfree(wrapper);
114
132
  }
115
133
 
134
+ static size_t rb_mysql_result_memsize(const void * wrapper) {
135
+ const mysql2_result_wrapper * w = wrapper;
136
+ size_t memsize = sizeof(*w);
137
+ if (w->stmt_wrapper) {
138
+ memsize += sizeof(*w->stmt_wrapper);
139
+ }
140
+ if (w->client_wrapper) {
141
+ memsize += sizeof(*w->client_wrapper);
142
+ }
143
+ return memsize;
144
+ }
145
+
146
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
147
+ static void rb_mysql_result_compact(void * wrapper) {
148
+ mysql2_result_wrapper * w = wrapper;
149
+ if (w) {
150
+ rb_mysql2_gc_location(w->fields);
151
+ rb_mysql2_gc_location(w->rows);
152
+ rb_mysql2_gc_location(w->encoding);
153
+ rb_mysql2_gc_location(w->client);
154
+ rb_mysql2_gc_location(w->statement);
155
+ }
156
+ }
157
+ #endif
158
+
159
+ static const rb_data_type_t rb_mysql_result_type = {
160
+ "rb_mysql_result",
161
+ {
162
+ rb_mysql_result_mark,
163
+ rb_mysql_result_free,
164
+ rb_mysql_result_memsize,
165
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
166
+ rb_mysql_result_compact,
167
+ #endif
168
+ },
169
+ 0,
170
+ 0,
171
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
172
+ RUBY_TYPED_FREE_IMMEDIATELY,
173
+ #endif
174
+ };
175
+
116
176
  static VALUE rb_mysql_result_free_(VALUE self) {
117
177
  GET_RESULT(self);
118
178
  rb_mysql_result_free_result(wrapper);
@@ -157,11 +217,18 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
157
217
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
158
218
  rb_field = ID2SYM(rb_field);
159
219
  } else {
160
- rb_field = rb_str_new(field->name, field->name_length);
161
- rb_enc_associate(rb_field, conn_enc);
162
- if (default_internal_enc) {
220
+ #ifdef HAVE_RB_ENC_INTERNED_STR
221
+ rb_field = rb_enc_interned_str(field->name, field->name_length, conn_enc);
222
+ if (default_internal_enc && default_internal_enc != conn_enc) {
223
+ rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
224
+ }
225
+ #else
226
+ rb_field = rb_enc_str_new(field->name, field->name_length, conn_enc);
227
+ if (default_internal_enc && default_internal_enc != conn_enc) {
163
228
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
164
229
  }
230
+ rb_obj_freeze(rb_field);
231
+ #endif
165
232
  }
166
233
  rb_ary_store(wrapper->fields, idx, rb_field);
167
234
  }
@@ -169,9 +236,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
169
236
  return rb_field;
170
237
  }
171
238
 
239
+ static VALUE rb_mysql_result_fetch_field_type(VALUE self, unsigned int idx) {
240
+ VALUE rb_field_type;
241
+ GET_RESULT(self);
242
+
243
+ if (wrapper->fieldTypes == Qnil) {
244
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
245
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
246
+ }
247
+
248
+ rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
249
+ if (rb_field_type == Qnil) {
250
+ MYSQL_FIELD *field = NULL;
251
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
252
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
253
+ int precision;
254
+
255
+ field = mysql_fetch_field_direct(wrapper->result, idx);
256
+
257
+ switch(field->type) {
258
+ case MYSQL_TYPE_NULL: // NULL
259
+ rb_field_type = rb_str_new_cstr("null");
260
+ break;
261
+ case MYSQL_TYPE_TINY: // signed char
262
+ rb_field_type = rb_sprintf("tinyint(%ld)", field->length);
263
+ break;
264
+ case MYSQL_TYPE_SHORT: // short int
265
+ rb_field_type = rb_sprintf("smallint(%ld)", field->length);
266
+ break;
267
+ case MYSQL_TYPE_YEAR: // short int
268
+ rb_field_type = rb_sprintf("year(%ld)", field->length);
269
+ break;
270
+ case MYSQL_TYPE_INT24: // int
271
+ rb_field_type = rb_sprintf("mediumint(%ld)", field->length);
272
+ break;
273
+ case MYSQL_TYPE_LONG: // int
274
+ rb_field_type = rb_sprintf("int(%ld)", field->length);
275
+ break;
276
+ case MYSQL_TYPE_LONGLONG: // long long int
277
+ rb_field_type = rb_sprintf("bigint(%ld)", field->length);
278
+ break;
279
+ case MYSQL_TYPE_FLOAT: // float
280
+ rb_field_type = rb_sprintf("float(%ld,%d)", field->length, field->decimals);
281
+ break;
282
+ case MYSQL_TYPE_DOUBLE: // double
283
+ rb_field_type = rb_sprintf("double(%ld,%d)", field->length, field->decimals);
284
+ break;
285
+ case MYSQL_TYPE_TIME: // MYSQL_TIME
286
+ rb_field_type = rb_str_new_cstr("time");
287
+ break;
288
+ case MYSQL_TYPE_DATE: // MYSQL_TIME
289
+ case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
290
+ rb_field_type = rb_str_new_cstr("date");
291
+ break;
292
+ case MYSQL_TYPE_DATETIME: // MYSQL_TIME
293
+ rb_field_type = rb_str_new_cstr("datetime");
294
+ break;
295
+ case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
296
+ rb_field_type = rb_str_new_cstr("timestamp");
297
+ break;
298
+ case MYSQL_TYPE_DECIMAL: // char[]
299
+ case MYSQL_TYPE_NEWDECIMAL: // char[]
300
+ /*
301
+ Handle precision similar to this line from mysql's code:
302
+ https://github.com/mysql/mysql-server/blob/ea7d2e2d16ac03afdd9cb72a972a95981107bf51/sql/field.cc#L2246
303
+ */
304
+ precision = field->length - (field->decimals > 0 ? 2 : 1);
305
+ rb_field_type = rb_sprintf("decimal(%d,%d)", precision, field->decimals);
306
+ break;
307
+ case MYSQL_TYPE_STRING: // char[]
308
+ if (field->flags & ENUM_FLAG) {
309
+ rb_field_type = rb_str_new_cstr("enum");
310
+ } else if (field->flags & SET_FLAG) {
311
+ rb_field_type = rb_str_new_cstr("set");
312
+ } else {
313
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
314
+ rb_field_type = rb_sprintf("binary(%ld)", field->length);
315
+ } else {
316
+ rb_field_type = rb_sprintf("char(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
317
+ }
318
+ }
319
+ break;
320
+ case MYSQL_TYPE_VAR_STRING: // char[]
321
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
322
+ rb_field_type = rb_sprintf("varbinary(%ld)", field->length);
323
+ } else {
324
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
325
+ }
326
+ break;
327
+ case MYSQL_TYPE_VARCHAR: // char[]
328
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
329
+ break;
330
+ case MYSQL_TYPE_TINY_BLOB: // char[]
331
+ rb_field_type = rb_str_new_cstr("tinyblob");
332
+ break;
333
+ case MYSQL_TYPE_BLOB: // char[]
334
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
335
+ switch(field->length) {
336
+ case 255:
337
+ rb_field_type = rb_str_new_cstr("tinyblob");
338
+ break;
339
+ case 65535:
340
+ rb_field_type = rb_str_new_cstr("blob");
341
+ break;
342
+ case 16777215:
343
+ rb_field_type = rb_str_new_cstr("mediumblob");
344
+ break;
345
+ case 4294967295:
346
+ rb_field_type = rb_str_new_cstr("longblob");
347
+ default:
348
+ break;
349
+ }
350
+ } else {
351
+ if (field->length == (255 * MYSQL2_MAX_BYTES_PER_CHAR)) {
352
+ rb_field_type = rb_str_new_cstr("tinytext");
353
+ } else if (field->length == (65535 * MYSQL2_MAX_BYTES_PER_CHAR)) {
354
+ rb_field_type = rb_str_new_cstr("text");
355
+ } else if (field->length == (16777215 * MYSQL2_MAX_BYTES_PER_CHAR)) {
356
+ rb_field_type = rb_str_new_cstr("mediumtext");
357
+ } else if (field->length == 4294967295) {
358
+ rb_field_type = rb_str_new_cstr("longtext");
359
+ } else {
360
+ rb_field_type = rb_sprintf("text(%ld)", field->length);
361
+ }
362
+ }
363
+ break;
364
+ case MYSQL_TYPE_MEDIUM_BLOB: // char[]
365
+ rb_field_type = rb_str_new_cstr("mediumblob");
366
+ break;
367
+ case MYSQL_TYPE_LONG_BLOB: // char[]
368
+ rb_field_type = rb_str_new_cstr("longblob");
369
+ break;
370
+ case MYSQL_TYPE_BIT: // char[]
371
+ rb_field_type = rb_sprintf("bit(%ld)", field->length);
372
+ break;
373
+ case MYSQL_TYPE_SET: // char[]
374
+ rb_field_type = rb_str_new_cstr("set");
375
+ break;
376
+ case MYSQL_TYPE_ENUM: // char[]
377
+ rb_field_type = rb_str_new_cstr("enum");
378
+ break;
379
+ case MYSQL_TYPE_GEOMETRY: // char[]
380
+ rb_field_type = rb_str_new_cstr("geometry");
381
+ break;
382
+ case MYSQL_TYPE_JSON: // json
383
+ rb_field_type = rb_str_new_cstr("json");
384
+ break;
385
+ default:
386
+ rb_field_type = rb_str_new_cstr("unknown");
387
+ break;
388
+ }
389
+
390
+ rb_enc_associate(rb_field_type, conn_enc);
391
+ if (default_internal_enc) {
392
+ rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
393
+ }
394
+
395
+ rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
396
+ }
397
+
398
+ return rb_field_type;
399
+ }
400
+
172
401
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
173
402
  /* if binary flag is set, respect its wishes */
174
- if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
403
+ if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
175
404
  rb_enc_associate(val, binaryEncoding);
176
405
  } else if (!field.charsetnr) {
177
406
  /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
@@ -182,7 +411,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
182
411
  int enc_index;
183
412
 
184
413
  enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
185
-
414
+
186
415
  if (enc_name != NULL) {
187
416
  /* use the field encoding we were able to match */
188
417
  enc_index = rb_enc_find_index(enc_name);
@@ -716,6 +945,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
716
945
  return wrapper->fields;
717
946
  }
718
947
 
948
+ static VALUE rb_mysql_result_fetch_field_types(VALUE self) {
949
+ unsigned int i = 0;
950
+
951
+ GET_RESULT(self);
952
+
953
+ if (wrapper->fieldTypes == Qnil) {
954
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
955
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
956
+ }
957
+
958
+ if ((my_ulonglong)RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
959
+ for (i=0; i<wrapper->numberOfFields; i++) {
960
+ rb_mysql_result_fetch_field_type(self, i);
961
+ }
962
+ }
963
+
964
+ return wrapper->fieldTypes;
965
+ }
966
+
719
967
  static VALUE rb_mysql_result_each_(VALUE self,
720
968
  VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
721
969
  const result_each_args *args)
@@ -927,13 +1175,18 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
927
1175
  VALUE obj;
928
1176
  mysql2_result_wrapper * wrapper;
929
1177
 
1178
+ #ifdef NEW_TYPEDDATA_WRAPPER
1179
+ obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1180
+ #else
930
1181
  obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1182
+ #endif
931
1183
  wrapper->numberOfFields = 0;
932
1184
  wrapper->numberOfRows = 0;
933
1185
  wrapper->lastRowProcessed = 0;
934
1186
  wrapper->resultFreed = 0;
935
1187
  wrapper->result = r;
936
1188
  wrapper->fields = Qnil;
1189
+ wrapper->fieldTypes = Qnil;
937
1190
  wrapper->rows = Qnil;
938
1191
  wrapper->encoding = encoding;
939
1192
  wrapper->streamingComplete = 0;
@@ -966,11 +1219,17 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
966
1219
 
967
1220
  void init_mysql2_result() {
968
1221
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1222
+ rb_global_variable(&cDate);
969
1223
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1224
+ rb_global_variable(&cDateTime);
970
1225
 
971
1226
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1227
+ rb_undef_alloc_func(cMysql2Result);
1228
+ rb_global_variable(&cMysql2Result);
1229
+
972
1230
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
973
1231
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1232
+ rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);
974
1233
  rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
975
1234
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
976
1235
  rb_define_alias(cMysql2Result, "size", "count");
data/ext/mysql2/result.h CHANGED
@@ -6,6 +6,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
6
6
 
7
7
  typedef struct {
8
8
  VALUE fields;
9
+ VALUE fieldTypes;
9
10
  VALUE rows;
10
11
  VALUE client;
11
12
  VALUE encoding;
@@ -6,9 +6,13 @@ static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_
6
6
  static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
7
7
  intern_query_options;
8
8
 
9
+ #ifndef NEW_TYPEDDATA_WRAPPER
10
+ #define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
11
+ #endif
12
+
9
13
  #define GET_STATEMENT(self) \
10
14
  mysql_stmt_wrapper *stmt_wrapper; \
11
- Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper); \
15
+ TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \
12
16
  if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
13
17
  if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
14
18
 
@@ -16,9 +20,45 @@ static void rb_mysql_stmt_mark(void * ptr) {
16
20
  mysql_stmt_wrapper *stmt_wrapper = ptr;
17
21
  if (!stmt_wrapper) return;
18
22
 
19
- rb_gc_mark(stmt_wrapper->client);
23
+ rb_gc_mark_movable(stmt_wrapper->client);
24
+ }
25
+
26
+ static void rb_mysql_stmt_free(void *ptr) {
27
+ mysql_stmt_wrapper *stmt_wrapper = ptr;
28
+ decr_mysql2_stmt(stmt_wrapper);
20
29
  }
21
30
 
31
+ static size_t rb_mysql_stmt_memsize(const void * ptr) {
32
+ const mysql_stmt_wrapper *stmt_wrapper = ptr;
33
+ return sizeof(*stmt_wrapper);
34
+ }
35
+
36
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
37
+ static void rb_mysql_stmt_compact(void * ptr) {
38
+ mysql_stmt_wrapper *stmt_wrapper = ptr;
39
+ if (!stmt_wrapper) return;
40
+
41
+ rb_mysql2_gc_location(stmt_wrapper->client);
42
+ }
43
+ #endif
44
+
45
+ static const rb_data_type_t rb_mysql_statement_type = {
46
+ "rb_mysql_statement",
47
+ {
48
+ rb_mysql_stmt_mark,
49
+ rb_mysql_stmt_free,
50
+ rb_mysql_stmt_memsize,
51
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
52
+ rb_mysql_stmt_compact,
53
+ #endif
54
+ },
55
+ 0,
56
+ 0,
57
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
58
+ RUBY_TYPED_FREE_IMMEDIATELY,
59
+ #endif
60
+ };
61
+
22
62
  static void *nogvl_stmt_close(void *ptr) {
23
63
  mysql_stmt_wrapper *stmt_wrapper = ptr;
24
64
  if (stmt_wrapper->stmt) {
@@ -28,11 +68,6 @@ static void *nogvl_stmt_close(void *ptr) {
28
68
  return NULL;
29
69
  }
30
70
 
31
- static void rb_mysql_stmt_free(void *ptr) {
32
- mysql_stmt_wrapper *stmt_wrapper = ptr;
33
- decr_mysql2_stmt(stmt_wrapper);
34
- }
35
-
36
71
  void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
37
72
  stmt_wrapper->refcount--;
38
73
 
@@ -46,7 +81,7 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
46
81
  VALUE e;
47
82
  GET_CLIENT(stmt_wrapper->client);
48
83
  VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
49
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
84
+ VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
50
85
 
51
86
  rb_encoding *conn_enc;
52
87
  conn_enc = rb_to_encoding(wrapper->encoding);
@@ -96,7 +131,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
96
131
 
97
132
  Check_Type(sql, T_STRING);
98
133
 
134
+ #ifdef NEW_TYPEDDATA_WRAPPER
135
+ rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
136
+ #else
99
137
  rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
138
+ #endif
100
139
  {
101
140
  stmt_wrapper->client = rb_client;
102
141
  stmt_wrapper->refcount = 1;
@@ -448,7 +487,7 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
448
487
  if (metadata == NULL) {
449
488
  if (mysql_stmt_errno(stmt) != 0) {
450
489
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
451
- wrapper->active_thread = Qnil;
490
+ wrapper->active_fiber = Qnil;
452
491
  rb_raise_mysql2_stmt_error(stmt_wrapper);
453
492
  }
454
493
  // no data and no error, so query was not a SELECT
@@ -456,12 +495,12 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
456
495
  }
457
496
 
458
497
  if (!is_streaming) {
459
- // recieve the whole result set from the server
498
+ // receive the whole result set from the server
460
499
  if (mysql_stmt_store_result(stmt)) {
461
500
  mysql_free_result(metadata);
462
501
  rb_raise_mysql2_stmt_error(stmt_wrapper);
463
502
  }
464
- wrapper->active_thread = Qnil;
503
+ wrapper->active_fiber = Qnil;
465
504
  }
466
505
 
467
506
  resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
@@ -502,7 +541,7 @@ static VALUE rb_mysql_stmt_fields(VALUE self) {
502
541
  if (metadata == NULL) {
503
542
  if (mysql_stmt_errno(stmt) != 0) {
504
543
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
505
- wrapper->active_thread = Qnil;
544
+ wrapper->active_fiber = Qnil;
506
545
  rb_raise_mysql2_stmt_error(stmt_wrapper);
507
546
  }
508
547
  // no data and no error, so query was not a SELECT
@@ -572,10 +611,18 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
572
611
 
573
612
  void init_mysql2_statement() {
574
613
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
614
+ rb_global_variable(&cDate);
615
+
575
616
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
617
+ rb_global_variable(&cDateTime);
618
+
576
619
  cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
620
+ rb_global_variable(&cBigDecimal);
577
621
 
578
622
  cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
623
+ rb_undef_alloc_func(cMysql2Statement);
624
+ rb_global_variable(&cMysql2Statement);
625
+
579
626
  rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
580
627
  rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
581
628
  rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
data/lib/mysql2/client.rb CHANGED
@@ -20,6 +20,7 @@ module Mysql2
20
20
 
21
21
  def initialize(opts = {})
22
22
  raise Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
23
+
23
24
  opts = Mysql2::Util.key_hash_as_symbols(opts)
24
25
  @read_timeout = nil
25
26
  @query_options = self.class.default_query_options.dup
@@ -33,6 +34,7 @@ module Mysql2
33
34
  # TODO: stricter validation rather than silent massaging
34
35
  %i[reconnect connect_timeout local_infile read_timeout write_timeout default_file default_group secure_auth init_command automatic_close enable_cleartext_plugin default_auth].each do |key|
35
36
  next unless opts.key?(key)
37
+
36
38
  case key
37
39
  when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
38
40
  send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
@@ -46,9 +48,14 @@ module Mysql2
46
48
  # force the encoding to utf8
47
49
  self.charset_name = opts[:encoding] || 'utf8'
48
50
 
51
+ mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
52
+ if (mode == SSL_MODE_VERIFY_CA || mode == SSL_MODE_VERIFY_IDENTITY) && !opts[:sslca]
53
+ opts[:sslca] = find_default_ca_path
54
+ end
55
+
49
56
  ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
50
57
  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]
58
+ self.ssl_mode = mode if mode
52
59
 
53
60
  flags = case opts[:flags]
54
61
  when Array
@@ -115,10 +122,23 @@ module Mysql2
115
122
  end
116
123
  end
117
124
 
125
+ # Find any default system CA paths to handle system roots
126
+ # by default if stricter validation is requested and no
127
+ # path is provide.
128
+ def find_default_ca_path
129
+ [
130
+ "/etc/ssl/certs/ca-certificates.crt",
131
+ "/etc/pki/tls/certs/ca-bundle.crt",
132
+ "/etc/ssl/ca-bundle.pem",
133
+ "/etc/ssl/cert.pem",
134
+ ].find { |f| File.exist?(f) }
135
+ end
136
+
118
137
  # Set default program_name in performance_schema.session_connect_attrs
119
138
  # and performance_schema.session_account_connect_attrs
120
139
  def parse_connect_attrs(conn_attrs)
121
140
  return {} if Mysql2::Client::CONNECT_ATTRS.zero?
141
+
122
142
  conn_attrs ||= {}
123
143
  conn_attrs[:program_name] ||= $PROGRAM_NAME
124
144
  conn_attrs.each_with_object({}) do |(key, value), hash|
@@ -127,7 +147,7 @@ module Mysql2
127
147
  end
128
148
 
129
149
  def query(sql, options = {})
130
- Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do
150
+ Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
131
151
  _query(sql, @query_options.merge(options))
132
152
  end
133
153
  end
@@ -135,6 +155,7 @@ module Mysql2
135
155
  def query_info
136
156
  info = query_info_string
137
157
  return {} unless info
158
+
138
159
  info_hash = {}
139
160
  info.split.each_slice(2) { |s| info_hash[s[0].downcase.delete(':').to_sym] = s[1].to_i }
140
161
  info_hash
data/lib/mysql2/error.rb CHANGED
@@ -24,6 +24,7 @@ module Mysql2
24
24
  1159 => ConnectionError, # ER_NET_READ_INTERRUPTED
25
25
  1160 => ConnectionError, # ER_NET_ERROR_ON_WRITE
26
26
  1161 => ConnectionError, # ER_NET_WRITE_INTERRUPTED
27
+ 1927 => ConnectionError, # ER_CONNECTION_KILLED
27
28
 
28
29
  2001 => ConnectionError, # CR_SOCKET_CREATE_ERROR
29
30
  2002 => ConnectionError, # CR_CONNECTION_ERROR
@@ -1,9 +1,7 @@
1
1
  module Mysql2
2
2
  class Statement
3
- include Enumerable
4
-
5
3
  def execute(*args, **kwargs)
6
- Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do
4
+ Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
7
5
  _execute(*args, **kwargs)
8
6
  end
9
7
  end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.5.3".freeze
2
+ VERSION = "0.5.5".freeze
3
3
  end
data/lib/mysql2.rb CHANGED
@@ -65,6 +65,7 @@ module Mysql2
65
65
  #
66
66
  def self.key_hash_as_symbols(hash)
67
67
  return nil unless hash
68
+
68
69
  Hash[hash.map { |k, v| [k.to_sym, v] }]
69
70
  end
70
71
 
@@ -82,5 +83,6 @@ module Mysql2
82
83
  else
83
84
  ::Timeout::Error
84
85
  end
86
+ TIMEOUT_ERROR_NEVER = { TIMEOUT_ERROR_CLASS => :never }.freeze
85
87
  end
86
88
  end
@@ -0,0 +1,49 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: SKS 1.1.6
3
+ Comment: Hostname: pgp.mit.edu
4
+
5
+ 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
47
+ sTSKHe+QnnnoFmu4gnmDU31i
48
+ =Xqbo
49
+ -----END PGP PUBLIC KEY BLOCK-----