mysql2 0.5.0 → 0.5.4
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 +5 -5
- data/README.md +81 -34
- data/ext/mysql2/client.c +123 -25
- data/ext/mysql2/extconf.rb +24 -6
- data/ext/mysql2/mysql2_ext.c +6 -1
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
- data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
- data/ext/mysql2/result.c +231 -15
- data/ext/mysql2/result.h +1 -0
- data/ext/mysql2/statement.c +46 -14
- data/lib/mysql2/client.rb +20 -3
- data/lib/mysql2/error.rb +4 -3
- data/lib/mysql2/statement.rb +1 -3
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +7 -3
- data/support/3A79BD29.asc +49 -0
- data/support/5072E1F5.asc +5 -5
- data/support/mysql_enc_to_ruby.rb +6 -1
- data/support/ruby_enc_to_mysql.rb +2 -0
- metadata +10 -55
- data/examples/eventmachine.rb +0 -19
- data/examples/threaded.rb +0 -16
- data/spec/configuration.yml.example +0 -11
- data/spec/em/em_spec.rb +0 -135
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -1072
- data/spec/mysql2/error_spec.rb +0 -78
- data/spec/mysql2/result_spec.rb +0 -485
- data/spec/mysql2/statement_spec.rb +0 -708
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -112
- data/spec/ssl/ca-cert.pem +0 -17
- data/spec/ssl/ca-key.pem +0 -27
- data/spec/ssl/ca.cnf +0 -22
- data/spec/ssl/cert.cnf +0 -22
- data/spec/ssl/client-cert.pem +0 -17
- data/spec/ssl/client-key.pem +0 -27
- data/spec/ssl/client-req.pem +0 -15
- data/spec/ssl/gen_certs.sh +0 -48
- data/spec/ssl/pkcs8-client-key.pem +0 -28
- data/spec/ssl/pkcs8-server-key.pem +0 -28
- data/spec/ssl/server-cert.pem +0 -17
- data/spec/ssl/server-key.pem +0 -27
- data/spec/ssl/server-req.pem +0 -15
- 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
|
-
|
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
|
-
|
159
|
-
|
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 ==
|
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,8 @@ 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 = mysql2_mysql_enc_to_rb[field.charsetnr-1];
|
367
|
+
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
|
368
|
+
|
183
369
|
if (enc_name != NULL) {
|
184
370
|
/* use the field encoding we were able to match */
|
185
371
|
enc_index = rb_enc_find_index(enc_name);
|
@@ -693,7 +879,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
693
879
|
|
694
880
|
GET_RESULT(self);
|
695
881
|
|
696
|
-
defaults =
|
882
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
697
883
|
Check_Type(defaults, T_HASH);
|
698
884
|
if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
|
699
885
|
symbolizeKeys = 1;
|
@@ -713,6 +899,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
713
899
|
return wrapper->fields;
|
714
900
|
}
|
715
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
|
+
|
716
921
|
static VALUE rb_mysql_result_each_(VALUE self,
|
717
922
|
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
718
923
|
const result_each_args *args)
|
@@ -738,7 +943,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
738
943
|
row = fetch_row_func(self, fields, args);
|
739
944
|
if (row != Qnil) {
|
740
945
|
wrapper->numberOfRows++;
|
741
|
-
if (args->block_given
|
946
|
+
if (args->block_given) {
|
742
947
|
rb_yield(row);
|
743
948
|
}
|
744
949
|
}
|
@@ -788,7 +993,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
788
993
|
return Qnil;
|
789
994
|
}
|
790
995
|
|
791
|
-
if (args->block_given
|
996
|
+
if (args->block_given) {
|
792
997
|
rb_yield(row);
|
793
998
|
}
|
794
999
|
}
|
@@ -806,7 +1011,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
806
1011
|
|
807
1012
|
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
808
1013
|
result_each_args args;
|
809
|
-
VALUE defaults, opts,
|
1014
|
+
VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
810
1015
|
ID db_timezone, app_timezone, dbTz, appTz;
|
811
1016
|
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
812
1017
|
|
@@ -816,9 +1021,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
816
1021
|
rb_raise(cMysql2Error, "Statement handle already closed");
|
817
1022
|
}
|
818
1023
|
|
819
|
-
defaults =
|
1024
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
820
1025
|
Check_Type(defaults, T_HASH);
|
821
|
-
|
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) {
|
822
1030
|
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
823
1031
|
} else {
|
824
1032
|
opts = defaults;
|
@@ -884,7 +1092,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
884
1092
|
args.cast = cast;
|
885
1093
|
args.db_timezone = db_timezone;
|
886
1094
|
args.app_timezone = app_timezone;
|
887
|
-
args.block_given =
|
1095
|
+
args.block_given = rb_block_given_p();
|
888
1096
|
|
889
1097
|
if (wrapper->stmt_wrapper) {
|
890
1098
|
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
@@ -928,6 +1136,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
928
1136
|
wrapper->resultFreed = 0;
|
929
1137
|
wrapper->result = r;
|
930
1138
|
wrapper->fields = Qnil;
|
1139
|
+
wrapper->fieldTypes = Qnil;
|
931
1140
|
wrapper->rows = Qnil;
|
932
1141
|
wrapper->encoding = encoding;
|
933
1142
|
wrapper->streamingComplete = 0;
|
@@ -949,7 +1158,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
949
1158
|
}
|
950
1159
|
|
951
1160
|
rb_obj_call_init(obj, 0, NULL);
|
952
|
-
|
1161
|
+
rb_ivar_set(obj, intern_query_options, options);
|
953
1162
|
|
954
1163
|
/* Options that cannot be changed in results.each(...) { |row| }
|
955
1164
|
* should be processed here. */
|
@@ -960,11 +1169,17 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
960
1169
|
|
961
1170
|
void init_mysql2_result() {
|
962
1171
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
1172
|
+
rb_global_variable(&cDate);
|
963
1173
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
1174
|
+
rb_global_variable(&cDateTime);
|
964
1175
|
|
965
1176
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
1177
|
+
rb_undef_alloc_func(cMysql2Result);
|
1178
|
+
rb_global_variable(&cMysql2Result);
|
1179
|
+
|
966
1180
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
967
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);
|
968
1183
|
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
969
1184
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
970
1185
|
rb_define_alias(cMysql2Result, "size", "count");
|
@@ -978,6 +1193,7 @@ void init_mysql2_result() {
|
|
978
1193
|
intern_civil = rb_intern("civil");
|
979
1194
|
intern_new_offset = rb_intern("new_offset");
|
980
1195
|
intern_BigDecimal = rb_intern("BigDecimal");
|
1196
|
+
intern_query_options = rb_intern("@query_options");
|
981
1197
|
|
982
1198
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
983
1199
|
sym_as = ID2SYM(rb_intern("as"));
|
data/ext/mysql2/result.h
CHANGED
data/ext/mysql2/statement.c
CHANGED
@@ -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 =
|
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);
|
@@ -403,6 +404,39 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
|
403
404
|
}
|
404
405
|
}
|
405
406
|
|
407
|
+
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
408
|
+
current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
|
409
|
+
(void)RB_GC_GUARD(current);
|
410
|
+
Check_Type(current, T_HASH);
|
411
|
+
|
412
|
+
// Merge in hash opts/keyword arguments
|
413
|
+
if (!NIL_P(opts)) {
|
414
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
415
|
+
}
|
416
|
+
|
417
|
+
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
418
|
+
|
419
|
+
// From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no
|
420
|
+
// Ruby API calls are allowed so that GC is not invoked. If the connection is
|
421
|
+
// in results-streaming-mode for Statement A, and in the middle Statement B
|
422
|
+
// gets garbage collected, a message will be sent to the server notifying it
|
423
|
+
// to release Statement B, resulting in the following error:
|
424
|
+
// Commands out of sync; you can't run this command now
|
425
|
+
//
|
426
|
+
// In streaming mode, statement execute must return a cursor because we
|
427
|
+
// cannot prevent other Statement objects from being garbage collected
|
428
|
+
// between fetches of each row of the result set. The following error
|
429
|
+
// occurs if cursor mode is not set:
|
430
|
+
// Row retrieval was canceled by mysql_stmt_close
|
431
|
+
|
432
|
+
if (is_streaming) {
|
433
|
+
unsigned long type = CURSOR_TYPE_READ_ONLY;
|
434
|
+
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) {
|
435
|
+
FREE_BINDS;
|
436
|
+
rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY");
|
437
|
+
}
|
438
|
+
}
|
439
|
+
|
406
440
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
|
407
441
|
FREE_BINDS;
|
408
442
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
@@ -421,19 +455,8 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
|
421
455
|
return Qnil;
|
422
456
|
}
|
423
457
|
|
424
|
-
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
425
|
-
current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
|
426
|
-
(void)RB_GC_GUARD(current);
|
427
|
-
Check_Type(current, T_HASH);
|
428
|
-
|
429
|
-
// Merge in hash opts/keyword arguments
|
430
|
-
if (!NIL_P(opts)) {
|
431
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
432
|
-
}
|
433
|
-
|
434
|
-
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
435
458
|
if (!is_streaming) {
|
436
|
-
//
|
459
|
+
// receive the whole result set from the server
|
437
460
|
if (mysql_stmt_store_result(stmt)) {
|
438
461
|
mysql_free_result(metadata);
|
439
462
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
@@ -549,10 +572,18 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
|
|
549
572
|
|
550
573
|
void init_mysql2_statement() {
|
551
574
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
575
|
+
rb_global_variable(&cDate);
|
576
|
+
|
552
577
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
578
|
+
rb_global_variable(&cDateTime);
|
579
|
+
|
553
580
|
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
581
|
+
rb_global_variable(&cBigDecimal);
|
554
582
|
|
555
583
|
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
|
584
|
+
rb_undef_alloc_func(cMysql2Statement);
|
585
|
+
rb_global_variable(&cMysql2Statement);
|
586
|
+
|
556
587
|
rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
|
557
588
|
rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
|
558
589
|
rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
|
@@ -577,4 +608,5 @@ void init_mysql2_statement() {
|
|
577
608
|
|
578
609
|
intern_to_s = rb_intern("to_s");
|
579
610
|
intern_merge_bang = rb_intern("merge!");
|
611
|
+
intern_query_options = rb_intern("@query_options");
|
580
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 =
|
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::
|
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
|
data/lib/mysql2/statement.rb
CHANGED
@@ -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::
|
4
|
+
Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
|
7
5
|
_execute(*args, **kwargs)
|
8
6
|
end
|
9
7
|
end
|
data/lib/mysql2/version.rb
CHANGED
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 '
|
24
|
-
|
25
|
-
|
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-----
|