mysql2 0.5.2 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +76 -34
  3. data/ext/mysql2/client.c +123 -25
  4. data/ext/mysql2/extconf.rb +23 -6
  5. data/ext/mysql2/mysql2_ext.c +6 -1
  6. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
  7. data/ext/mysql2/mysql_enc_to_ruby.h +56 -5
  8. data/ext/mysql2/result.c +230 -15
  9. data/ext/mysql2/result.h +1 -0
  10. data/ext/mysql2/statement.c +14 -4
  11. data/lib/mysql2/client.rb +20 -3
  12. data/lib/mysql2/error.rb +4 -3
  13. data/lib/mysql2/statement.rb +1 -3
  14. data/lib/mysql2/version.rb +1 -1
  15. data/lib/mysql2.rb +7 -3
  16. data/support/3A79BD29.asc +49 -0
  17. data/support/5072E1F5.asc +5 -5
  18. data/support/mysql_enc_to_ruby.rb +6 -1
  19. data/support/ruby_enc_to_mysql.rb +2 -0
  20. metadata +10 -55
  21. data/examples/eventmachine.rb +0 -19
  22. data/examples/threaded.rb +0 -16
  23. data/spec/configuration.yml.example +0 -11
  24. data/spec/em/em_spec.rb +0 -135
  25. data/spec/my.cnf.example +0 -9
  26. data/spec/mysql2/client_spec.rb +0 -1072
  27. data/spec/mysql2/error_spec.rb +0 -78
  28. data/spec/mysql2/result_spec.rb +0 -485
  29. data/spec/mysql2/statement_spec.rb +0 -712
  30. data/spec/rcov.opts +0 -3
  31. data/spec/spec_helper.rb +0 -112
  32. data/spec/ssl/ca-cert.pem +0 -17
  33. data/spec/ssl/ca-key.pem +0 -27
  34. data/spec/ssl/ca.cnf +0 -22
  35. data/spec/ssl/cert.cnf +0 -22
  36. data/spec/ssl/client-cert.pem +0 -17
  37. data/spec/ssl/client-key.pem +0 -27
  38. data/spec/ssl/client-req.pem +0 -15
  39. data/spec/ssl/gen_certs.sh +0 -48
  40. data/spec/ssl/pkcs8-client-key.pem +0 -28
  41. data/spec/ssl/pkcs8-server-key.pem +0 -28
  42. data/spec/ssl/server-cert.pem +0 -17
  43. data/spec/ssl/server-key.pem +0 -27
  44. data/spec/ssl/server-req.pem +0 -15
  45. data/spec/test_data +0 -1
data/ext/mysql2/result.c CHANGED
@@ -1,6 +1,7 @@
1
1
  #include <mysql2_ext.h>
2
2
 
3
3
  #include "mysql_enc_to_ruby.h"
4
+ #define MYSQL2_CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0]))
4
5
 
5
6
  static rb_encoding *binaryEncoding;
6
7
 
@@ -16,6 +17,20 @@ static rb_encoding *binaryEncoding;
16
17
  */
17
18
  #define MYSQL2_MIN_TIME 2678400ULL
18
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
+
19
34
  #define GET_RESULT(self) \
20
35
  mysql2_result_wrapper *wrapper; \
21
36
  Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
@@ -29,14 +44,15 @@ typedef struct {
29
44
  int streaming;
30
45
  ID db_timezone;
31
46
  ID app_timezone;
32
- VALUE block_given;
47
+ int block_given; /* boolean */
33
48
  } result_each_args;
34
49
 
35
50
  extern VALUE mMysql2, cMysql2Client, cMysql2Error;
36
51
  static VALUE cMysql2Result, cDateTime, cDate;
37
52
  static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
38
53
  static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
39
- intern_civil, intern_new_offset, intern_merge, intern_BigDecimal;
54
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
55
+ intern_query_options;
40
56
  static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
41
57
  sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
42
58
  sym_cache_rows, sym_cast, sym_stream, sym_name;
@@ -155,11 +171,18 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
155
171
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
156
172
  rb_field = ID2SYM(rb_field);
157
173
  } else {
158
- rb_field = rb_str_new(field->name, field->name_length);
159
- rb_enc_associate(rb_field, conn_enc);
160
- if (default_internal_enc) {
174
+ #ifdef HAVE_RB_ENC_INTERNED_STR
175
+ rb_field = rb_enc_interned_str(field->name, field->name_length, conn_enc);
176
+ if (default_internal_enc && default_internal_enc != conn_enc) {
177
+ rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
178
+ }
179
+ #else
180
+ rb_field = rb_enc_str_new(field->name, field->name_length, conn_enc);
181
+ if (default_internal_enc && default_internal_enc != conn_enc) {
161
182
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
162
183
  }
184
+ rb_obj_freeze(rb_field);
185
+ #endif
163
186
  }
164
187
  rb_ary_store(wrapper->fields, idx, rb_field);
165
188
  }
@@ -167,9 +190,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
167
190
  return rb_field;
168
191
  }
169
192
 
193
+ static VALUE rb_mysql_result_fetch_field_type(VALUE self, unsigned int idx) {
194
+ VALUE rb_field_type;
195
+ GET_RESULT(self);
196
+
197
+ if (wrapper->fieldTypes == Qnil) {
198
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
199
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
200
+ }
201
+
202
+ rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
203
+ if (rb_field_type == Qnil) {
204
+ MYSQL_FIELD *field = NULL;
205
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
206
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
207
+ int precision;
208
+
209
+ field = mysql_fetch_field_direct(wrapper->result, idx);
210
+
211
+ switch(field->type) {
212
+ case MYSQL_TYPE_NULL: // NULL
213
+ rb_field_type = rb_str_new_cstr("null");
214
+ break;
215
+ case MYSQL_TYPE_TINY: // signed char
216
+ rb_field_type = rb_sprintf("tinyint(%ld)", field->length);
217
+ break;
218
+ case MYSQL_TYPE_SHORT: // short int
219
+ rb_field_type = rb_sprintf("smallint(%ld)", field->length);
220
+ break;
221
+ case MYSQL_TYPE_YEAR: // short int
222
+ rb_field_type = rb_sprintf("year(%ld)", field->length);
223
+ break;
224
+ case MYSQL_TYPE_INT24: // int
225
+ rb_field_type = rb_sprintf("mediumint(%ld)", field->length);
226
+ break;
227
+ case MYSQL_TYPE_LONG: // int
228
+ rb_field_type = rb_sprintf("int(%ld)", field->length);
229
+ break;
230
+ case MYSQL_TYPE_LONGLONG: // long long int
231
+ rb_field_type = rb_sprintf("bigint(%ld)", field->length);
232
+ break;
233
+ case MYSQL_TYPE_FLOAT: // float
234
+ rb_field_type = rb_sprintf("float(%ld,%d)", field->length, field->decimals);
235
+ break;
236
+ case MYSQL_TYPE_DOUBLE: // double
237
+ rb_field_type = rb_sprintf("double(%ld,%d)", field->length, field->decimals);
238
+ break;
239
+ case MYSQL_TYPE_TIME: // MYSQL_TIME
240
+ rb_field_type = rb_str_new_cstr("time");
241
+ break;
242
+ case MYSQL_TYPE_DATE: // MYSQL_TIME
243
+ case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
244
+ rb_field_type = rb_str_new_cstr("date");
245
+ break;
246
+ case MYSQL_TYPE_DATETIME: // MYSQL_TIME
247
+ rb_field_type = rb_str_new_cstr("datetime");
248
+ break;
249
+ case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
250
+ rb_field_type = rb_str_new_cstr("timestamp");
251
+ break;
252
+ case MYSQL_TYPE_DECIMAL: // char[]
253
+ case MYSQL_TYPE_NEWDECIMAL: // char[]
254
+ /*
255
+ Handle precision similar to this line from mysql's code:
256
+ https://github.com/mysql/mysql-server/blob/ea7d2e2d16ac03afdd9cb72a972a95981107bf51/sql/field.cc#L2246
257
+ */
258
+ precision = field->length - (field->decimals > 0 ? 2 : 1);
259
+ rb_field_type = rb_sprintf("decimal(%d,%d)", precision, field->decimals);
260
+ break;
261
+ case MYSQL_TYPE_STRING: // char[]
262
+ if (field->flags & ENUM_FLAG) {
263
+ rb_field_type = rb_str_new_cstr("enum");
264
+ } else if (field->flags & SET_FLAG) {
265
+ rb_field_type = rb_str_new_cstr("set");
266
+ } else {
267
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
268
+ rb_field_type = rb_sprintf("binary(%ld)", field->length);
269
+ } else {
270
+ rb_field_type = rb_sprintf("char(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
271
+ }
272
+ }
273
+ break;
274
+ case MYSQL_TYPE_VAR_STRING: // char[]
275
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
276
+ rb_field_type = rb_sprintf("varbinary(%ld)", field->length);
277
+ } else {
278
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
279
+ }
280
+ break;
281
+ case MYSQL_TYPE_VARCHAR: // char[]
282
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
283
+ break;
284
+ case MYSQL_TYPE_TINY_BLOB: // char[]
285
+ rb_field_type = rb_str_new_cstr("tinyblob");
286
+ break;
287
+ case MYSQL_TYPE_BLOB: // char[]
288
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
289
+ switch(field->length) {
290
+ case 255:
291
+ rb_field_type = rb_str_new_cstr("tinyblob");
292
+ break;
293
+ case 65535:
294
+ rb_field_type = rb_str_new_cstr("blob");
295
+ break;
296
+ case 16777215:
297
+ rb_field_type = rb_str_new_cstr("mediumblob");
298
+ break;
299
+ case 4294967295:
300
+ rb_field_type = rb_str_new_cstr("longblob");
301
+ default:
302
+ break;
303
+ }
304
+ } else {
305
+ if (field->length == (255 * MYSQL2_MAX_BYTES_PER_CHAR)) {
306
+ rb_field_type = rb_str_new_cstr("tinytext");
307
+ } else if (field->length == (65535 * MYSQL2_MAX_BYTES_PER_CHAR)) {
308
+ rb_field_type = rb_str_new_cstr("text");
309
+ } else if (field->length == (16777215 * MYSQL2_MAX_BYTES_PER_CHAR)) {
310
+ rb_field_type = rb_str_new_cstr("mediumtext");
311
+ } else if (field->length == 4294967295) {
312
+ rb_field_type = rb_str_new_cstr("longtext");
313
+ } else {
314
+ rb_field_type = rb_sprintf("text(%ld)", field->length);
315
+ }
316
+ }
317
+ break;
318
+ case MYSQL_TYPE_MEDIUM_BLOB: // char[]
319
+ rb_field_type = rb_str_new_cstr("mediumblob");
320
+ break;
321
+ case MYSQL_TYPE_LONG_BLOB: // char[]
322
+ rb_field_type = rb_str_new_cstr("longblob");
323
+ break;
324
+ case MYSQL_TYPE_BIT: // char[]
325
+ rb_field_type = rb_sprintf("bit(%ld)", field->length);
326
+ break;
327
+ case MYSQL_TYPE_SET: // char[]
328
+ rb_field_type = rb_str_new_cstr("set");
329
+ break;
330
+ case MYSQL_TYPE_ENUM: // char[]
331
+ rb_field_type = rb_str_new_cstr("enum");
332
+ break;
333
+ case MYSQL_TYPE_GEOMETRY: // char[]
334
+ rb_field_type = rb_str_new_cstr("geometry");
335
+ break;
336
+ case MYSQL_TYPE_JSON: // json
337
+ rb_field_type = rb_str_new_cstr("json");
338
+ break;
339
+ default:
340
+ rb_field_type = rb_str_new_cstr("unknown");
341
+ break;
342
+ }
343
+
344
+ rb_enc_associate(rb_field_type, conn_enc);
345
+ if (default_internal_enc) {
346
+ rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
347
+ }
348
+
349
+ rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
350
+ }
351
+
352
+ return rb_field_type;
353
+ }
354
+
170
355
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
171
356
  /* if binary flag is set, respect its wishes */
172
- if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
357
+ if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
173
358
  rb_enc_associate(val, binaryEncoding);
174
359
  } else if (!field.charsetnr) {
175
360
  /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
@@ -179,7 +364,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
179
364
  const char *enc_name;
180
365
  int enc_index;
181
366
 
182
- enc_name = (field.charsetnr-1 < CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
367
+ enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
183
368
 
184
369
  if (enc_name != NULL) {
185
370
  /* use the field encoding we were able to match */
@@ -694,7 +879,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
694
879
 
695
880
  GET_RESULT(self);
696
881
 
697
- defaults = rb_iv_get(self, "@query_options");
882
+ defaults = rb_ivar_get(self, intern_query_options);
698
883
  Check_Type(defaults, T_HASH);
699
884
  if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
700
885
  symbolizeKeys = 1;
@@ -714,6 +899,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
714
899
  return wrapper->fields;
715
900
  }
716
901
 
902
+ static VALUE rb_mysql_result_fetch_field_types(VALUE self) {
903
+ unsigned int i = 0;
904
+
905
+ GET_RESULT(self);
906
+
907
+ if (wrapper->fieldTypes == Qnil) {
908
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
909
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
910
+ }
911
+
912
+ if ((my_ulonglong)RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
913
+ for (i=0; i<wrapper->numberOfFields; i++) {
914
+ rb_mysql_result_fetch_field_type(self, i);
915
+ }
916
+ }
917
+
918
+ return wrapper->fieldTypes;
919
+ }
920
+
717
921
  static VALUE rb_mysql_result_each_(VALUE self,
718
922
  VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
719
923
  const result_each_args *args)
@@ -739,7 +943,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
739
943
  row = fetch_row_func(self, fields, args);
740
944
  if (row != Qnil) {
741
945
  wrapper->numberOfRows++;
742
- if (args->block_given != Qnil) {
946
+ if (args->block_given) {
743
947
  rb_yield(row);
744
948
  }
745
949
  }
@@ -789,7 +993,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
789
993
  return Qnil;
790
994
  }
791
995
 
792
- if (args->block_given != Qnil) {
996
+ if (args->block_given) {
793
997
  rb_yield(row);
794
998
  }
795
999
  }
@@ -807,7 +1011,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
807
1011
 
808
1012
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
809
1013
  result_each_args args;
810
- VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
1014
+ VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
811
1015
  ID db_timezone, app_timezone, dbTz, appTz;
812
1016
  int symbolizeKeys, asArray, castBool, cacheRows, cast;
813
1017
 
@@ -817,9 +1021,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
817
1021
  rb_raise(cMysql2Error, "Statement handle already closed");
818
1022
  }
819
1023
 
820
- defaults = rb_iv_get(self, "@query_options");
1024
+ defaults = rb_ivar_get(self, intern_query_options);
821
1025
  Check_Type(defaults, T_HASH);
822
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
1026
+
1027
+ // A block can be passed to this method, but since we don't call the block directly from C,
1028
+ // we don't need to capture it into a variable here with the "&" scan arg.
1029
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
823
1030
  opts = rb_funcall(defaults, intern_merge, 1, opts);
824
1031
  } else {
825
1032
  opts = defaults;
@@ -885,7 +1092,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
885
1092
  args.cast = cast;
886
1093
  args.db_timezone = db_timezone;
887
1094
  args.app_timezone = app_timezone;
888
- args.block_given = block;
1095
+ args.block_given = rb_block_given_p();
889
1096
 
890
1097
  if (wrapper->stmt_wrapper) {
891
1098
  fetch_row_func = rb_mysql_result_fetch_row_stmt;
@@ -929,6 +1136,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
929
1136
  wrapper->resultFreed = 0;
930
1137
  wrapper->result = r;
931
1138
  wrapper->fields = Qnil;
1139
+ wrapper->fieldTypes = Qnil;
932
1140
  wrapper->rows = Qnil;
933
1141
  wrapper->encoding = encoding;
934
1142
  wrapper->streamingComplete = 0;
@@ -950,7 +1158,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
950
1158
  }
951
1159
 
952
1160
  rb_obj_call_init(obj, 0, NULL);
953
- rb_iv_set(obj, "@query_options", options);
1161
+ rb_ivar_set(obj, intern_query_options, options);
954
1162
 
955
1163
  /* Options that cannot be changed in results.each(...) { |row| }
956
1164
  * should be processed here. */
@@ -961,11 +1169,17 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
961
1169
 
962
1170
  void init_mysql2_result() {
963
1171
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1172
+ rb_global_variable(&cDate);
964
1173
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1174
+ rb_global_variable(&cDateTime);
965
1175
 
966
1176
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1177
+ rb_undef_alloc_func(cMysql2Result);
1178
+ rb_global_variable(&cMysql2Result);
1179
+
967
1180
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
968
1181
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1182
+ rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);
969
1183
  rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
970
1184
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
971
1185
  rb_define_alias(cMysql2Result, "size", "count");
@@ -979,6 +1193,7 @@ void init_mysql2_result() {
979
1193
  intern_civil = rb_intern("civil");
980
1194
  intern_new_offset = rb_intern("new_offset");
981
1195
  intern_BigDecimal = rb_intern("BigDecimal");
1196
+ intern_query_options = rb_intern("@query_options");
982
1197
 
983
1198
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
984
1199
  sym_as = ID2SYM(rb_intern("as"));
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;
@@ -3,7 +3,8 @@
3
3
  extern VALUE mMysql2, cMysql2Error;
4
4
  static VALUE cMysql2Statement, cBigDecimal, cDateTime, cDate;
5
5
  static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_merge_bang;
6
- static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
6
+ static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
7
+ intern_query_options;
7
8
 
8
9
  #define GET_STATEMENT(self) \
9
10
  mysql_stmt_wrapper *stmt_wrapper; \
@@ -45,7 +46,7 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
45
46
  VALUE e;
46
47
  GET_CLIENT(stmt_wrapper->client);
47
48
  VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
48
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
49
+ VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
49
50
 
50
51
  rb_encoding *conn_enc;
51
52
  conn_enc = rb_to_encoding(wrapper->encoding);
@@ -404,7 +405,7 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
404
405
  }
405
406
 
406
407
  // Duplicate the options hash, merge! extra opts, put the copy into the Result object
407
- current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
408
+ current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
408
409
  (void)RB_GC_GUARD(current);
409
410
  Check_Type(current, T_HASH);
410
411
 
@@ -455,7 +456,7 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
455
456
  }
456
457
 
457
458
  if (!is_streaming) {
458
- // recieve the whole result set from the server
459
+ // receive the whole result set from the server
459
460
  if (mysql_stmt_store_result(stmt)) {
460
461
  mysql_free_result(metadata);
461
462
  rb_raise_mysql2_stmt_error(stmt_wrapper);
@@ -571,10 +572,18 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
571
572
 
572
573
  void init_mysql2_statement() {
573
574
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
575
+ rb_global_variable(&cDate);
576
+
574
577
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
578
+ rb_global_variable(&cDateTime);
579
+
575
580
  cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
581
+ rb_global_variable(&cBigDecimal);
576
582
 
577
583
  cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
584
+ rb_undef_alloc_func(cMysql2Statement);
585
+ rb_global_variable(&cMysql2Statement);
586
+
578
587
  rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
579
588
  rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
580
589
  rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
@@ -599,4 +608,5 @@ void init_mysql2_statement() {
599
608
 
600
609
  intern_to_s = rb_intern("to_s");
601
610
  intern_merge_bang = rb_intern("merge!");
611
+ intern_query_options = rb_intern("@query_options");
602
612
  }
data/lib/mysql2/client.rb CHANGED
@@ -31,7 +31,7 @@ module Mysql2
31
31
  opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
32
32
 
33
33
  # TODO: stricter validation rather than silent massaging
34
- %i[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
+ %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
35
  next unless opts.key?(key)
36
36
  case key
37
37
  when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
@@ -46,9 +46,14 @@ module Mysql2
46
46
  # force the encoding to utf8
47
47
  self.charset_name = opts[:encoding] || 'utf8'
48
48
 
49
+ mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
50
+ if (mode == SSL_MODE_VERIFY_CA || mode == SSL_MODE_VERIFY_IDENTITY) && !opts[:sslca]
51
+ opts[:sslca] = find_default_ca_path
52
+ end
53
+
49
54
  ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
50
55
  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]
56
+ self.ssl_mode = mode if mode
52
57
 
53
58
  flags = case opts[:flags]
54
59
  when Array
@@ -115,6 +120,18 @@ module Mysql2
115
120
  end
116
121
  end
117
122
 
123
+ # Find any default system CA paths to handle system roots
124
+ # by default if stricter validation is requested and no
125
+ # path is provide.
126
+ def find_default_ca_path
127
+ [
128
+ "/etc/ssl/certs/ca-certificates.crt",
129
+ "/etc/pki/tls/certs/ca-bundle.crt",
130
+ "/etc/ssl/ca-bundle.pem",
131
+ "/etc/ssl/cert.pem",
132
+ ].find { |f| File.exist?(f) }
133
+ end
134
+
118
135
  # Set default program_name in performance_schema.session_connect_attrs
119
136
  # and performance_schema.session_account_connect_attrs
120
137
  def parse_connect_attrs(conn_attrs)
@@ -127,7 +144,7 @@ module Mysql2
127
144
  end
128
145
 
129
146
  def query(sql, options = {})
130
- Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do
147
+ Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
131
148
  _query(sql, @query_options.merge(options))
132
149
  end
133
150
  end
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
@@ -52,7 +53,7 @@ module Mysql2
52
53
  def initialize(msg, server_version = nil, error_number = nil, sql_state = nil)
53
54
  @server_version = server_version
54
55
  @error_number = error_number
55
- @sql_state = sql_state ? sql_state.encode(ENCODE_OPTS) : nil
56
+ @sql_state = sql_state ? sql_state.encode(**ENCODE_OPTS) : nil
56
57
 
57
58
  super(clean_message(msg))
58
59
  end
@@ -91,9 +92,9 @@ module Mysql2
91
92
  # Returns a valid UTF-8 string.
92
93
  def clean_message(message)
93
94
  if @server_version && @server_version > 50500
94
- message.encode(ENCODE_OPTS)
95
+ message.encode(**ENCODE_OPTS)
95
96
  else
96
- message.encode(Encoding::UTF_8, ENCODE_OPTS)
97
+ message.encode(Encoding::UTF_8, **ENCODE_OPTS)
97
98
  end
98
99
  end
99
100
  end
@@ -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.2".freeze
2
+ VERSION = "0.5.4".freeze
3
3
  end
data/lib/mysql2.rb CHANGED
@@ -20,9 +20,12 @@ if RUBY_PLATFORM =~ /mswin|mingw/
20
20
  end
21
21
 
22
22
  if dll_path
23
- require 'Win32API'
24
- LoadLibrary = Win32API.new('Kernel32', 'LoadLibrary', ['P'], 'I')
25
- if LoadLibrary.call(dll_path).zero?
23
+ require 'fiddle'
24
+ kernel32 = Fiddle.dlopen 'kernel32'
25
+ load_library = Fiddle::Function.new(
26
+ kernel32['LoadLibraryW'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT,
27
+ )
28
+ if load_library.call(dll_path.encode('utf-16le')).zero?
26
29
  abort "Failed to load libmysql.dll from #{dll_path}"
27
30
  end
28
31
  end
@@ -79,5 +82,6 @@ module Mysql2
79
82
  else
80
83
  ::Timeout::Error
81
84
  end
85
+ TIMEOUT_ERROR_NEVER = { TIMEOUT_ERROR_CLASS => :never }.freeze
82
86
  end
83
87
  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-----
data/support/5072E1F5.asc CHANGED
@@ -1,5 +1,5 @@
1
1
  -----BEGIN PGP PUBLIC KEY BLOCK-----
2
- Version: GnuPG v1.4.5 (GNU/Linux)
2
+ Version: GnuPG v1
3
3
 
4
4
  mQGiBD4+owwRBAC14GIfUfCyEDSIePvEW3SAFUdJBtoQHH/nJKZyQT7h9bPlUWC3
5
5
  RODjQReyCITRrdwyrKUGku2FmeVGwn2u2WmDMNABLnpprWPkBdCk96+OmSLN9brZ
@@ -11,9 +11,9 @@ kYpXBACmWpP8NJTkamEnPCia2ZoOHODANwpUkP43I7jsDmgtobZX9qnrAXw+uNDI
11
11
  QJEXM6FSbi0LLtZciNlYsafwAPEOMDKpMqAK6IyisNtPvaLd8lH0bPAnWqcyefep
12
12
  rv0sxxqUEMcM3o7wwgfN83POkDasDbs3pjwPhxvhz6//62zQJ7Q2TXlTUUwgUmVs
13
13
  ZWFzZSBFbmdpbmVlcmluZyA8bXlzcWwtYnVpbGRAb3NzLm9yYWNsZS5jb20+iGwE
14
- ExECACwCGyMCHgECF4ACGQEGCwkIBwMCBhUKCQgCAwUWAgMBAAUCWKcFIAUJHirJ
15
- FAAKCRCMcY07UHLh9VcFAJ46pUyVd8BZ2r5CppMC1tmyQ3ceRgCfVPwuVsiS0VER
16
- 5WUqtAQDt+DoetCIaQQTEQIAKQIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAhkB
14
+ ExECACwCGyMCHgECF4ACGQEGCwkIBwMCBhUKCQgCAwUWAgMBAAUCXEBY+wUJI87e
15
+ 5AAKCRCMcY07UHLh9RZPAJ9uvm0zlzfCN+DHxHVaoFLFjdVYTQCfborsC9tmEZYa
16
+ whhogjeBkZkorbyIaQQTEQIAKQIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAhkB
17
17
  BQJTAdRmBQkaZsvLAAoJEIxxjTtQcuH1X4MAoKNLWAbCBUj96637kv6Xa/fJuX5m
18
18
  AJwPtmgDfjUe2iuhXdTrFEPT19SB6ohmBBMRAgAmAhsjBgsJCAcDAgQVAggDBBYC
19
19
  AwECHgECF4AFAk53PioFCRP7AhUACgkQjHGNO1By4fUmzACeJdfqgc9gWTUhgmcM
@@ -428,5 +428,5 @@ GoaU9u41oyZTIiXPiFidJoIZCh7fdurP8pn3X+R5HUNXMr7M+ba8lSNxce/F3kmH
428
428
  0L7rsKqdh9d/aVxhJINJ+inVDnrXWVoXu9GBjT8Nco1iU9SIVAQYEQIADAUCTnc9
429
429
  7QUJE/sBuAASB2VHUEcAAQEJEIxxjTtQcuH1FJsAmwWK9vmwRJ/y9gTnJ8PWf0BV
430
430
  roUTAKClYAhZuX2nUNwH4vlEJQHDqYa5yQ==
431
- =HfUN
431
+ =ghXk
432
432
  -----END PGP PUBLIC KEY BLOCK-----
@@ -43,6 +43,8 @@ mysql_to_rb = {
43
43
  "geostd8" => "NULL",
44
44
  "cp932" => "Windows-31J",
45
45
  "eucjpms" => "eucJP-ms",
46
+ "utf16le" => "UTF-16LE",
47
+ "gb18030" => "GB18030",
46
48
  }
47
49
 
48
50
  client = Mysql2::Client.new(username: user, password: pass, host: host, port: port.to_i)
@@ -52,7 +54,10 @@ encodings_with_nil = Array.new(encodings.size)
52
54
 
53
55
  collations.each do |collation|
54
56
  mysql_col_idx = collation[2].to_i
55
- rb_enc = mysql_to_rb[collation[1]]
57
+ rb_enc = mysql_to_rb.fetch(collation[1]) do |mysql_enc|
58
+ $stderr.puts "WARNING: Missing mapping for collation \"#{collation[0]}\" with encoding \"#{mysql_enc}\" and id #{mysql_col_idx}, assuming NULL"
59
+ "NULL"
60
+ end
56
61
  encodings[mysql_col_idx - 1] = [mysql_col_idx, rb_enc]
57
62
  end
58
63
 
@@ -38,6 +38,8 @@ mysql_to_rb = {
38
38
  "geostd8" => nil,
39
39
  "cp932" => "Windows-31J",
40
40
  "eucjpms" => "eucJP-ms",
41
+ "utf16le" => "UTF-16LE",
42
+ "gb18030" => "GB18030",
41
43
  }
42
44
 
43
45
  puts <<-HEADER