mysql2 0.2.19b6 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/mysql2/client.c +66 -42
- data/ext/mysql2/client.h +2 -0
- data/ext/mysql2/extconf.rb +16 -2
- data/ext/mysql2/result.c +38 -11
- data/ext/mysql2/result.h +3 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -1
- data/lib/mysql2/client.rb +10 -1
- data/lib/mysql2/console.rb +5 -0
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +42 -4
- data/spec/mysql2/result_spec.rb +20 -1
- data/spec/spec_helper.rb +3 -2
- metadata +8 -12
- data/CHANGELOG.md +0 -221
- data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +0 -64
- data/lib/active_record/fiber_patches.rb +0 -132
- data/lib/mysql2/em_fiber.rb +0 -31
- data/spec/em/em_fiber_spec.rb +0 -22
data/ext/mysql2/client.c
CHANGED
|
@@ -44,6 +44,17 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
|
44
44
|
mysql_client_wrapper *wrapper; \
|
|
45
45
|
Data_Get_Struct(self, mysql_client_wrapper, wrapper)
|
|
46
46
|
|
|
47
|
+
/*
|
|
48
|
+
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
|
49
|
+
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
|
50
|
+
* linking against the server itself
|
|
51
|
+
*/
|
|
52
|
+
#ifdef LIBMYSQL_VERSION
|
|
53
|
+
#define MYSQL_LINK_VERSION LIBMYSQL_VERSION
|
|
54
|
+
#else
|
|
55
|
+
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
|
56
|
+
#endif
|
|
57
|
+
|
|
47
58
|
/*
|
|
48
59
|
* used to pass all arguments to mysql_real_connect while inside
|
|
49
60
|
* rb_thread_blocking_region
|
|
@@ -182,13 +193,15 @@ static VALUE nogvl_close(void *ptr) {
|
|
|
182
193
|
return Qnil;
|
|
183
194
|
}
|
|
184
195
|
|
|
185
|
-
static void rb_mysql_client_free(void *
|
|
196
|
+
static void rb_mysql_client_free(void *ptr) {
|
|
186
197
|
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
|
187
198
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
199
|
+
wrapper->refcount--;
|
|
200
|
+
if (wrapper->refcount == 0) {
|
|
201
|
+
nogvl_close(wrapper);
|
|
202
|
+
xfree(wrapper->client);
|
|
203
|
+
xfree(wrapper);
|
|
204
|
+
}
|
|
192
205
|
}
|
|
193
206
|
|
|
194
207
|
static VALUE allocate(VALUE klass) {
|
|
@@ -200,6 +213,7 @@ static VALUE allocate(VALUE klass) {
|
|
|
200
213
|
wrapper->reconnect_enabled = 0;
|
|
201
214
|
wrapper->connected = 0; /* means that a database connection is open */
|
|
202
215
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
|
216
|
+
wrapper->refcount = 1;
|
|
203
217
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
|
204
218
|
return obj;
|
|
205
219
|
}
|
|
@@ -245,6 +259,25 @@ static VALUE rb_mysql_client_warning_count(VALUE self) {
|
|
|
245
259
|
return UINT2NUM(warning_count);
|
|
246
260
|
}
|
|
247
261
|
|
|
262
|
+
static VALUE rb_mysql_info(VALUE self) {
|
|
263
|
+
const char *info;
|
|
264
|
+
VALUE rb_str;
|
|
265
|
+
GET_CLIENT(self);
|
|
266
|
+
|
|
267
|
+
info = mysql_info(wrapper->client);
|
|
268
|
+
|
|
269
|
+
if (info == NULL) {
|
|
270
|
+
return Qnil;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
rb_str = rb_str_new2(info);
|
|
274
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
275
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
|
276
|
+
#endif
|
|
277
|
+
|
|
278
|
+
return rb_str;
|
|
279
|
+
}
|
|
280
|
+
|
|
248
281
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
|
249
282
|
struct nogvl_connect_args args;
|
|
250
283
|
VALUE rv;
|
|
@@ -361,9 +394,7 @@ static VALUE nogvl_use_result(void *ptr) {
|
|
|
361
394
|
static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
362
395
|
MYSQL_RES * result;
|
|
363
396
|
VALUE resultObj;
|
|
364
|
-
|
|
365
|
-
mysql2_result_wrapper * result_wrapper;
|
|
366
|
-
#endif
|
|
397
|
+
VALUE current, is_streaming;
|
|
367
398
|
GET_CLIENT(self);
|
|
368
399
|
|
|
369
400
|
/* if we're not waiting on a result, do nothing */
|
|
@@ -377,7 +408,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
|
377
408
|
return rb_raise_mysql2_error(wrapper);
|
|
378
409
|
}
|
|
379
410
|
|
|
380
|
-
|
|
411
|
+
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
|
381
412
|
if(is_streaming == Qtrue) {
|
|
382
413
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
|
383
414
|
} else {
|
|
@@ -393,14 +424,11 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
|
393
424
|
return Qnil;
|
|
394
425
|
}
|
|
395
426
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
427
|
+
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
|
428
|
+
RB_GC_GUARD(current);
|
|
429
|
+
Check_Type(current, T_HASH);
|
|
430
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
|
399
431
|
|
|
400
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
|
401
|
-
GetMysql2Result(resultObj, result_wrapper);
|
|
402
|
-
result_wrapper->encoding = wrapper->encoding;
|
|
403
|
-
#endif
|
|
404
432
|
return resultObj;
|
|
405
433
|
}
|
|
406
434
|
|
|
@@ -545,10 +573,13 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
|
545
573
|
REQUIRE_CONNECTED(wrapper);
|
|
546
574
|
args.mysql = wrapper->client;
|
|
547
575
|
|
|
548
|
-
|
|
549
|
-
current
|
|
576
|
+
current = rb_hash_dup(rb_iv_get(self, "@query_options"));
|
|
577
|
+
RB_GC_GUARD(current);
|
|
578
|
+
Check_Type(current, T_HASH);
|
|
579
|
+
rb_iv_set(self, "@current_query_options", current);
|
|
580
|
+
|
|
550
581
|
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
|
551
|
-
|
|
582
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
|
552
583
|
|
|
553
584
|
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
|
554
585
|
async = 1;
|
|
@@ -926,10 +957,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
|
926
957
|
{
|
|
927
958
|
MYSQL_RES * result;
|
|
928
959
|
VALUE resultObj;
|
|
929
|
-
|
|
930
|
-
mysql2_result_wrapper * result_wrapper;
|
|
931
|
-
#endif
|
|
932
|
-
|
|
960
|
+
VALUE current;
|
|
933
961
|
GET_CLIENT(self);
|
|
934
962
|
|
|
935
963
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
|
@@ -942,16 +970,12 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
|
942
970
|
return Qnil;
|
|
943
971
|
}
|
|
944
972
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
973
|
+
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
|
974
|
+
RB_GC_GUARD(current);
|
|
975
|
+
Check_Type(current, T_HASH);
|
|
976
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
|
948
977
|
|
|
949
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
|
950
|
-
GetMysql2Result(resultObj, result_wrapper);
|
|
951
|
-
result_wrapper->encoding = wrapper->encoding;
|
|
952
|
-
#endif
|
|
953
978
|
return resultObj;
|
|
954
|
-
|
|
955
979
|
}
|
|
956
980
|
|
|
957
981
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
@@ -1051,14 +1075,12 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
|
1051
1075
|
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
|
1052
1076
|
GET_CLIENT(self);
|
|
1053
1077
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
NIL_P(cipher) ? NULL : StringValuePtr(cipher));
|
|
1061
|
-
}
|
|
1078
|
+
mysql_ssl_set(wrapper->client,
|
|
1079
|
+
NIL_P(key) ? NULL : StringValuePtr(key),
|
|
1080
|
+
NIL_P(cert) ? NULL : StringValuePtr(cert),
|
|
1081
|
+
NIL_P(ca) ? NULL : StringValuePtr(ca),
|
|
1082
|
+
NIL_P(capath) ? NULL : StringValuePtr(capath),
|
|
1083
|
+
NIL_P(cipher) ? NULL : StringValuePtr(cipher));
|
|
1062
1084
|
|
|
1063
1085
|
return self;
|
|
1064
1086
|
}
|
|
@@ -1081,14 +1103,15 @@ void init_mysql2_client() {
|
|
|
1081
1103
|
int i;
|
|
1082
1104
|
int dots = 0;
|
|
1083
1105
|
const char *lib = mysql_get_client_info();
|
|
1084
|
-
|
|
1106
|
+
|
|
1107
|
+
for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
|
|
1085
1108
|
if (lib[i] == '.') {
|
|
1086
1109
|
dots++;
|
|
1087
1110
|
/* we only compare MAJOR and MINOR */
|
|
1088
1111
|
if (dots == 2) break;
|
|
1089
1112
|
}
|
|
1090
|
-
if (lib[i] !=
|
|
1091
|
-
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.",
|
|
1113
|
+
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
|
1114
|
+
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
|
|
1092
1115
|
return;
|
|
1093
1116
|
}
|
|
1094
1117
|
}
|
|
@@ -1120,6 +1143,7 @@ void init_mysql2_client() {
|
|
|
1120
1143
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
|
1121
1144
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
|
1122
1145
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
|
1146
|
+
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
|
1123
1147
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
1124
1148
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
|
1125
1149
|
#endif
|
data/ext/mysql2/client.h
CHANGED
data/ext/mysql2/extconf.rb
CHANGED
|
@@ -9,6 +9,7 @@ end
|
|
|
9
9
|
have_func('rb_thread_blocking_region')
|
|
10
10
|
have_func('rb_wait_for_single_fd')
|
|
11
11
|
have_func('rb_hash_dup')
|
|
12
|
+
have_func('rb_intern3')
|
|
12
13
|
|
|
13
14
|
# borrowed from mysqlplus
|
|
14
15
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
|
@@ -29,6 +30,14 @@ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
|
|
|
29
30
|
|
|
30
31
|
if RUBY_PLATFORM =~ /mswin|mingw/
|
|
31
32
|
inc, lib = dir_config('mysql')
|
|
33
|
+
|
|
34
|
+
# Ruby versions not incorporating the mkmf fix at
|
|
35
|
+
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
|
|
36
|
+
# do not properly search for lib directories, and must be corrected
|
|
37
|
+
unless lib[-3, 3] == 'lib'
|
|
38
|
+
@libdir_basename = 'lib'
|
|
39
|
+
inc, lib = dir_config('mysql')
|
|
40
|
+
end
|
|
32
41
|
exit 1 unless have_library("libmysql")
|
|
33
42
|
elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
|
|
34
43
|
mc = Dir[GLOB].first if mc == true
|
|
@@ -69,8 +78,13 @@ end
|
|
|
69
78
|
if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
|
|
70
79
|
$CFLAGS << ' -Wall -funroll-loops'
|
|
71
80
|
|
|
72
|
-
if
|
|
73
|
-
|
|
81
|
+
if libdir = $libs[%r{-L(/[^ ]+)}, 1]
|
|
82
|
+
# The following comment and test is borrowed from the Pg gem:
|
|
83
|
+
# Try to use runtime path linker option, even if RbConfig doesn't know about it.
|
|
84
|
+
# The rpath option is usually set implicit by dir_config(), but so far not on Mac OS X.
|
|
85
|
+
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', " -Wl,-rpath,#{libdir}")
|
|
86
|
+
$LDFLAGS << " -Wl,-rpath,#{libdir}"
|
|
87
|
+
end
|
|
74
88
|
end
|
|
75
89
|
end
|
|
76
90
|
|
data/ext/mysql2/result.c
CHANGED
|
@@ -64,22 +64,33 @@ static void rb_mysql_result_mark(void * wrapper) {
|
|
|
64
64
|
rb_gc_mark(w->fields);
|
|
65
65
|
rb_gc_mark(w->rows);
|
|
66
66
|
rb_gc_mark(w->encoding);
|
|
67
|
+
rb_gc_mark(w->client);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/* this may be called manually or during GC */
|
|
71
72
|
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
72
73
|
if (wrapper && wrapper->resultFreed != 1) {
|
|
74
|
+
/* FIXME: this may call flush_use_result, which can hit the socket */
|
|
73
75
|
mysql_free_result(wrapper->result);
|
|
74
76
|
wrapper->resultFreed = 1;
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/* this is called during GC */
|
|
79
|
-
static void rb_mysql_result_free(void *
|
|
80
|
-
mysql2_result_wrapper *
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
static void rb_mysql_result_free(void *ptr) {
|
|
82
|
+
mysql2_result_wrapper * wrapper = ptr;
|
|
83
|
+
rb_mysql_result_free_result(wrapper);
|
|
84
|
+
|
|
85
|
+
// If the GC gets to client first it will be nil
|
|
86
|
+
if (wrapper->client != Qnil) {
|
|
87
|
+
wrapper->client_wrapper->refcount--;
|
|
88
|
+
if (wrapper->client_wrapper->refcount == 0) {
|
|
89
|
+
xfree(wrapper->client_wrapper->client);
|
|
90
|
+
xfree(wrapper->client_wrapper);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
xfree(wrapper);
|
|
84
95
|
}
|
|
85
96
|
|
|
@@ -114,15 +125,18 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
|
114
125
|
|
|
115
126
|
field = mysql_fetch_field_direct(wrapper->result, idx);
|
|
116
127
|
if (symbolize_keys) {
|
|
117
|
-
VALUE colStr;
|
|
118
128
|
char buf[field->name_length+1];
|
|
119
129
|
memcpy(buf, field->name, field->name_length);
|
|
120
130
|
buf[field->name_length] = 0;
|
|
131
|
+
|
|
132
|
+
#ifdef HAVE_RB_INTERN3
|
|
133
|
+
rb_field = rb_intern3(buf, field->name_length, rb_utf8_encoding());
|
|
134
|
+
rb_field = ID2SYM(rb_field);
|
|
135
|
+
#else
|
|
136
|
+
VALUE colStr;
|
|
121
137
|
colStr = rb_str_new2(buf);
|
|
122
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
|
123
|
-
rb_enc_associate(colStr, rb_utf8_encoding());
|
|
124
|
-
#endif
|
|
125
138
|
rb_field = ID2SYM(rb_to_id(colStr));
|
|
139
|
+
#endif
|
|
126
140
|
} else {
|
|
127
141
|
rb_field = rb_str_new(field->name, field->name_length);
|
|
128
142
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
@@ -223,7 +237,11 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
223
237
|
val = Qnil;
|
|
224
238
|
break;
|
|
225
239
|
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
|
226
|
-
|
|
240
|
+
if (castBool && fields[i].length == 1) {
|
|
241
|
+
val = *row[i] == 1 ? Qtrue : Qfalse;
|
|
242
|
+
}else{
|
|
243
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
|
244
|
+
}
|
|
227
245
|
break;
|
|
228
246
|
case MYSQL_TYPE_TINY: /* TINYINT field */
|
|
229
247
|
if (castBool && fields[i].length == 1) {
|
|
@@ -376,6 +394,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
|
376
394
|
GetMysql2Result(self, wrapper);
|
|
377
395
|
|
|
378
396
|
defaults = rb_iv_get(self, "@query_options");
|
|
397
|
+
Check_Type(defaults, T_HASH);
|
|
379
398
|
if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
|
|
380
399
|
symbolizeKeys = 1;
|
|
381
400
|
}
|
|
@@ -405,6 +424,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
|
405
424
|
GetMysql2Result(self, wrapper);
|
|
406
425
|
|
|
407
426
|
defaults = rb_iv_get(self, "@query_options");
|
|
427
|
+
Check_Type(defaults, T_HASH);
|
|
408
428
|
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
|
409
429
|
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
|
410
430
|
} else {
|
|
@@ -558,7 +578,7 @@ static VALUE rb_mysql_result_count(VALUE self) {
|
|
|
558
578
|
}
|
|
559
579
|
|
|
560
580
|
/* Mysql2::Result */
|
|
561
|
-
VALUE rb_mysql_result_to_obj(MYSQL_RES *
|
|
581
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
|
562
582
|
VALUE obj;
|
|
563
583
|
mysql2_result_wrapper * wrapper;
|
|
564
584
|
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
|
@@ -569,9 +589,16 @@ VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
|
|
|
569
589
|
wrapper->result = r;
|
|
570
590
|
wrapper->fields = Qnil;
|
|
571
591
|
wrapper->rows = Qnil;
|
|
572
|
-
wrapper->encoding =
|
|
592
|
+
wrapper->encoding = encoding;
|
|
573
593
|
wrapper->streamingComplete = 0;
|
|
594
|
+
wrapper->client = client;
|
|
595
|
+
wrapper->client_wrapper = DATA_PTR(client);
|
|
596
|
+
wrapper->client_wrapper->refcount++;
|
|
597
|
+
|
|
574
598
|
rb_obj_call_init(obj, 0, NULL);
|
|
599
|
+
|
|
600
|
+
rb_iv_set(obj, "@query_options", options);
|
|
601
|
+
|
|
575
602
|
return obj;
|
|
576
603
|
}
|
|
577
604
|
|
data/ext/mysql2/result.h
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
#define MYSQL2_RESULT_H
|
|
3
3
|
|
|
4
4
|
void init_mysql2_result();
|
|
5
|
-
VALUE rb_mysql_result_to_obj(MYSQL_RES *
|
|
5
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r);
|
|
6
6
|
|
|
7
7
|
typedef struct {
|
|
8
8
|
VALUE fields;
|
|
9
9
|
VALUE rows;
|
|
10
|
+
VALUE client;
|
|
10
11
|
VALUE encoding;
|
|
11
12
|
unsigned int numberOfFields;
|
|
12
13
|
unsigned long numberOfRows;
|
|
@@ -14,6 +15,7 @@ typedef struct {
|
|
|
14
15
|
char streamingComplete;
|
|
15
16
|
char resultFreed;
|
|
16
17
|
MYSQL_RES *result;
|
|
18
|
+
mysql_client_wrapper *client_wrapper;
|
|
17
19
|
} mysql2_result_wrapper;
|
|
18
20
|
|
|
19
21
|
#define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
|
|
@@ -5,13 +5,15 @@ require 'mysql2'
|
|
|
5
5
|
module ActiveRecord
|
|
6
6
|
class Base
|
|
7
7
|
def self.mysql2_connection(config)
|
|
8
|
+
config = config.symbolize_keys
|
|
9
|
+
|
|
8
10
|
config[:username] = 'root' if config[:username].nil?
|
|
9
11
|
|
|
10
12
|
if Mysql2::Client.const_defined? :FOUND_ROWS
|
|
11
13
|
config[:flags] = Mysql2::Client::FOUND_ROWS
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
client = Mysql2::Client.new(config
|
|
16
|
+
client = Mysql2::Client.new(config)
|
|
15
17
|
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
|
16
18
|
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
|
|
17
19
|
end
|
data/lib/mysql2/client.rb
CHANGED
|
@@ -36,7 +36,8 @@ module Mysql2
|
|
|
36
36
|
# force the encoding to utf8
|
|
37
37
|
self.charset_name = opts[:encoding] || 'utf8'
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
|
40
|
+
ssl_set(*ssl_options) if ssl_options.any?
|
|
40
41
|
|
|
41
42
|
if [:user,:pass,:hostname,:dbname,:db,:sock].any?{|k| @query_options.has_key?(k) }
|
|
42
43
|
warn "============= WARNING FROM mysql2 ============="
|
|
@@ -60,6 +61,14 @@ module Mysql2
|
|
|
60
61
|
@@default_query_options
|
|
61
62
|
end
|
|
62
63
|
|
|
64
|
+
def query_info
|
|
65
|
+
info = query_info_string
|
|
66
|
+
return {} unless info
|
|
67
|
+
info_hash = {}
|
|
68
|
+
info.split.each_slice(2) { |s| info_hash[s[0].downcase.delete(':').to_sym] = s[1].to_i }
|
|
69
|
+
info_hash
|
|
70
|
+
end
|
|
71
|
+
|
|
63
72
|
private
|
|
64
73
|
def self.local_offset
|
|
65
74
|
::Time.local(2010).utc_offset.to_r / 86400
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
|
@@ -81,11 +81,13 @@ describe Mysql2::Client do
|
|
|
81
81
|
results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
|
|
82
82
|
results[0]['Variable_name'].should eql('Ssl_cipher')
|
|
83
83
|
results[0]['Value'].should_not be_nil
|
|
84
|
-
results[0]['Value'].
|
|
84
|
+
results[0]['Value'].should be_kind_of(String)
|
|
85
|
+
results[0]['Value'].should_not be_empty
|
|
85
86
|
|
|
86
87
|
results[1]['Variable_name'].should eql('Ssl_version')
|
|
87
88
|
results[1]['Value'].should_not be_nil
|
|
88
|
-
results[1]['Value'].
|
|
89
|
+
results[1]['Value'].should be_kind_of(String)
|
|
90
|
+
results[1]['Value'].should_not be_empty
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
it "should respond to #close" do
|
|
@@ -128,6 +130,42 @@ describe Mysql2::Client do
|
|
|
128
130
|
end
|
|
129
131
|
end
|
|
130
132
|
|
|
133
|
+
it "should respond to #query_info" do
|
|
134
|
+
@client.should respond_to(:query_info)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context "#query_info" do
|
|
138
|
+
context "when no info present" do
|
|
139
|
+
before(:each) do
|
|
140
|
+
@client.query('select 1')
|
|
141
|
+
end
|
|
142
|
+
it "should 0" do
|
|
143
|
+
@client.query_info.should be_empty
|
|
144
|
+
@client.query_info_string.should be_nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
context "when has some info" do
|
|
148
|
+
before(:each) do
|
|
149
|
+
@client.query "USE test"
|
|
150
|
+
@client.query "CREATE TABLE IF NOT EXISTS infoTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
after(:each) do
|
|
154
|
+
@client.query "DROP TABLE infoTest"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
before(:each) do
|
|
158
|
+
# http://dev.mysql.com/doc/refman/5.0/en/mysql-info.html says
|
|
159
|
+
# # Note that mysql_info() returns a non-NULL value for INSERT ... VALUES only for the multiple-row form of the statement (that is, only if multiple value lists are specified).
|
|
160
|
+
@client.query("INSERT INTO infoTest (blah) VALUES (1234),(4535)")
|
|
161
|
+
end
|
|
162
|
+
it "should retrieve it" do
|
|
163
|
+
@client.query_info.should == {:records => 2, :duplicates => 0, :warnings => 0}
|
|
164
|
+
@client.query_info_string.should eq 'Records: 2 Duplicates: 0 Warnings: 0'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
131
169
|
it "should expect connect_timeout to be a positive integer" do
|
|
132
170
|
lambda {
|
|
133
171
|
Mysql2::Client.new(:connect_timeout => -1)
|
|
@@ -255,10 +293,10 @@ describe Mysql2::Client do
|
|
|
255
293
|
mark[:END] = Time.now
|
|
256
294
|
mark.include?(:USR1).should be_true
|
|
257
295
|
(mark[:USR1] - mark[:START]).should >= 1
|
|
258
|
-
(mark[:USR1] - mark[:START]).should < 1.
|
|
296
|
+
(mark[:USR1] - mark[:START]).should < 1.3
|
|
259
297
|
(mark[:END] - mark[:USR1]).should > 0.9
|
|
260
298
|
(mark[:END] - mark[:START]).should >= 2
|
|
261
|
-
(mark[:END] - mark[:START]).should < 2.
|
|
299
|
+
(mark[:END] - mark[:START]).should < 2.3
|
|
262
300
|
Process.kill(:TERM, pid)
|
|
263
301
|
Process.waitpid2(pid)
|
|
264
302
|
ensure
|
data/spec/mysql2/result_spec.rb
CHANGED
|
@@ -160,11 +160,16 @@ describe Mysql2::Result do
|
|
|
160
160
|
@test_result['null_test'].should eql(nil)
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
-
it "should return
|
|
163
|
+
it "should return String for a BIT(64) value" do
|
|
164
164
|
@test_result['bit_test'].class.should eql(String)
|
|
165
165
|
@test_result['bit_test'].should eql("\000\000\000\000\000\000\000\005")
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
+
it "should return String for a BIT(1) value" do
|
|
169
|
+
@test_result['single_bit_test'].class.should eql(String)
|
|
170
|
+
@test_result['single_bit_test'].should eql("\001")
|
|
171
|
+
end
|
|
172
|
+
|
|
168
173
|
it "should return Fixnum for a TINYINT value" do
|
|
169
174
|
[Fixnum, Bignum].should include(@test_result['tiny_int_test'].class)
|
|
170
175
|
@test_result['tiny_int_test'].should eql(1)
|
|
@@ -188,6 +193,20 @@ describe Mysql2::Result do
|
|
|
188
193
|
@client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2},#{id3})"
|
|
189
194
|
end
|
|
190
195
|
|
|
196
|
+
it "should return TrueClass or FalseClass for a BIT(1) value if :cast_booleans is enabled" do
|
|
197
|
+
@client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (1)'
|
|
198
|
+
id1 = @client.last_id
|
|
199
|
+
@client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (0)'
|
|
200
|
+
id2 = @client.last_id
|
|
201
|
+
|
|
202
|
+
result1 = @client.query "SELECT single_bit_test FROM mysql2_test WHERE id = #{id1}", :cast_booleans => true
|
|
203
|
+
result2 = @client.query "SELECT single_bit_test FROM mysql2_test WHERE id = #{id2}", :cast_booleans => true
|
|
204
|
+
result1.first['single_bit_test'].should be_true
|
|
205
|
+
result2.first['single_bit_test'].should be_false
|
|
206
|
+
|
|
207
|
+
@client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})"
|
|
208
|
+
end
|
|
209
|
+
|
|
191
210
|
it "should return Fixnum for a SMALLINT value" do
|
|
192
211
|
[Fixnum, Bignum].should include(@test_result['small_int_test'].class)
|
|
193
212
|
@test_result['small_int_test'].should eql(10)
|
data/spec/spec_helper.rb
CHANGED
|
@@ -14,6 +14,7 @@ RSpec.configure do |config|
|
|
|
14
14
|
id MEDIUMINT NOT NULL AUTO_INCREMENT,
|
|
15
15
|
null_test VARCHAR(10),
|
|
16
16
|
bit_test BIT(64),
|
|
17
|
+
single_bit_test BIT(1),
|
|
17
18
|
tiny_int_test TINYINT,
|
|
18
19
|
bool_cast_test TINYINT(1),
|
|
19
20
|
small_int_test SMALLINT,
|
|
@@ -50,7 +51,7 @@ RSpec.configure do |config|
|
|
|
50
51
|
client.query "DELETE FROM mysql2_test;"
|
|
51
52
|
client.query %[
|
|
52
53
|
INSERT INTO mysql2_test (
|
|
53
|
-
null_test, bit_test, tiny_int_test, bool_cast_test, small_int_test, medium_int_test, int_test, big_int_test,
|
|
54
|
+
null_test, bit_test, single_bit_test, tiny_int_test, bool_cast_test, small_int_test, medium_int_test, int_test, big_int_test,
|
|
54
55
|
float_test, float_zero_test, double_test, decimal_test, decimal_zero_test, date_test, date_time_test, timestamp_test, time_test,
|
|
55
56
|
year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
|
|
56
57
|
tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
|
|
@@ -58,7 +59,7 @@ RSpec.configure do |config|
|
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
VALUES (
|
|
61
|
-
NULL, b'101', 1, 1, 10, 10, 10, 10,
|
|
62
|
+
NULL, b'101', b'1', 1, 1, 10, 10, 10, 10,
|
|
62
63
|
10.3, 0, 10.3, 10.3, 0, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00',
|
|
63
64
|
2009, "test", "test", "test", "test", "test",
|
|
64
65
|
"test", "test", "test", "test", "test",
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mysql2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
5
|
-
prerelease:
|
|
4
|
+
version: 0.2.19
|
|
5
|
+
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
8
|
- Brian Lopez
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-
|
|
12
|
+
date: 2013-07-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: eventmachine
|
|
@@ -82,7 +82,6 @@ extensions:
|
|
|
82
82
|
- ext/mysql2/extconf.rb
|
|
83
83
|
extra_rdoc_files: []
|
|
84
84
|
files:
|
|
85
|
-
- CHANGELOG.md
|
|
86
85
|
- MIT-LICENSE
|
|
87
86
|
- README.md
|
|
88
87
|
- ext/mysql2/client.c
|
|
@@ -95,14 +94,12 @@ files:
|
|
|
95
94
|
- ext/mysql2/result.c
|
|
96
95
|
- ext/mysql2/result.h
|
|
97
96
|
- ext/mysql2/wait_for_single_fd.h
|
|
98
|
-
- lib/active_record/connection_adapters/em_mysql2_adapter.rb
|
|
99
97
|
- lib/active_record/connection_adapters/mysql2_adapter.rb
|
|
100
|
-
- lib/active_record/fiber_patches.rb
|
|
101
98
|
- lib/arel/engines/sql/compilers/mysql2_compiler.rb
|
|
102
99
|
- lib/mysql2.rb
|
|
103
100
|
- lib/mysql2/client.rb
|
|
101
|
+
- lib/mysql2/console.rb
|
|
104
102
|
- lib/mysql2/em.rb
|
|
105
|
-
- lib/mysql2/em_fiber.rb
|
|
106
103
|
- lib/mysql2/error.rb
|
|
107
104
|
- lib/mysql2/result.rb
|
|
108
105
|
- lib/mysql2/version.rb
|
|
@@ -111,7 +108,6 @@ files:
|
|
|
111
108
|
- examples/eventmachine.rb
|
|
112
109
|
- examples/threaded.rb
|
|
113
110
|
- spec/configuration.yml.example
|
|
114
|
-
- spec/em/em_fiber_spec.rb
|
|
115
111
|
- spec/em/em_spec.rb
|
|
116
112
|
- spec/mysql2/client_spec.rb
|
|
117
113
|
- spec/mysql2/error_spec.rb
|
|
@@ -119,7 +115,8 @@ files:
|
|
|
119
115
|
- spec/rcov.opts
|
|
120
116
|
- spec/spec_helper.rb
|
|
121
117
|
homepage: http://github.com/brianmario/mysql2
|
|
122
|
-
licenses:
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
123
120
|
post_install_message:
|
|
124
121
|
rdoc_options:
|
|
125
122
|
- --charset=UTF-8
|
|
@@ -134,9 +131,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
134
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
132
|
none: false
|
|
136
133
|
requirements:
|
|
137
|
-
- - ! '
|
|
134
|
+
- - ! '>='
|
|
138
135
|
- !ruby/object:Gem::Version
|
|
139
|
-
version:
|
|
136
|
+
version: '0'
|
|
140
137
|
requirements: []
|
|
141
138
|
rubyforge_project:
|
|
142
139
|
rubygems_version: 1.8.23
|
|
@@ -147,7 +144,6 @@ test_files:
|
|
|
147
144
|
- examples/eventmachine.rb
|
|
148
145
|
- examples/threaded.rb
|
|
149
146
|
- spec/configuration.yml.example
|
|
150
|
-
- spec/em/em_fiber_spec.rb
|
|
151
147
|
- spec/em/em_spec.rb
|
|
152
148
|
- spec/mysql2/client_spec.rb
|
|
153
149
|
- spec/mysql2/error_spec.rb
|
data/CHANGELOG.md
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.2.19 (not yet released)
|
|
4
|
-
## 0.2.19b5 (not yet released)
|
|
5
|
-
* builds on Ruby 2.0-head and Rubinius 2.0-dev
|
|
6
|
-
* encoding names now stored in a Gperf lookup rather than an array
|
|
7
|
-
* long-standing bug fix: options set on a single query must not be applied to subsequent queries
|
|
8
|
-
* add method warning_count
|
|
9
|
-
* add method abandon_results!
|
|
10
|
-
* add setter for reconnect option
|
|
11
|
-
* remove options method (added in 0.3.12b1)
|
|
12
|
-
* support microsecond Time resolution
|
|
13
|
-
* several INT / UINT fixes
|
|
14
|
-
|
|
15
|
-
## 0.2.19b4 (August 22, 2012)
|
|
16
|
-
* add write_timeout as well
|
|
17
|
-
|
|
18
|
-
## 0.2.19b3 (August 22, 2012)
|
|
19
|
-
* several INT / LONG fixes
|
|
20
|
-
* fix linking to MySQL 5.5
|
|
21
|
-
|
|
22
|
-
## 0.2.19b2 (August 10, 2012)
|
|
23
|
-
* more_results is now more_results?
|
|
24
|
-
|
|
25
|
-
## 0.2.19b1 (August 8, 2012)
|
|
26
|
-
* several threading and async bug fixes
|
|
27
|
-
* better handling of read and write timeouts
|
|
28
|
-
* add :local_infile connection option
|
|
29
|
-
* add MULTI_STATEMENTS connection flag and methods store_result, next_result, more_results
|
|
30
|
-
* add select_db and options methods
|
|
31
|
-
* add :stream query option
|
|
32
|
-
* add support for utf8mb4 encoding
|
|
33
|
-
* deprecation warnings for the :user, :pass, :hostname, :dbname, :db, :sock connection options
|
|
34
|
-
|
|
35
|
-
## 0.2.18 (December 6th, 2011)
|
|
36
|
-
* change mysql error detection strategy from using mysql_field_count to the more explicit mysql_errno
|
|
37
|
-
* bugfix to avoid race condition with active connections that error out
|
|
38
|
-
* revert back to using xmalloc/xfree for allocations
|
|
39
|
-
* avoid potentially unsafe Ruby C API usage w/o GVL
|
|
40
|
-
* reacquire GVL before retrying on EINTR on connect
|
|
41
|
-
|
|
42
|
-
## 0.2.17 (November 9th, 2011)
|
|
43
|
-
|
|
44
|
-
## 0.2.16 (November 9th, 2011)
|
|
45
|
-
|
|
46
|
-
## 0.2.15 (November 9th, 2011)
|
|
47
|
-
|
|
48
|
-
## 0.2.14 (November 9th, 2011)
|
|
49
|
-
* use rb_wait_for_single_fd() if available
|
|
50
|
-
* fixed a bug with inheriting query options
|
|
51
|
-
* remove ext/ from the default loadpath
|
|
52
|
-
* fix build issues on OSX with Xcode 4.2 (gcc-llvm compiler)
|
|
53
|
-
|
|
54
|
-
## 0.2.13 (August 16th, 2011)
|
|
55
|
-
* fix stupid bug around symbol encoding support (thanks coderrr!)
|
|
56
|
-
|
|
57
|
-
## 0.2.12 (August 16th, 2011)
|
|
58
|
-
* ensure symbolized column names support encodings in 1.9
|
|
59
|
-
* plugging sql vulnerability in mysql2 adapter
|
|
60
|
-
|
|
61
|
-
## 0.2.11 (June 17th, 2011)
|
|
62
|
-
* fix bug in Time/DateTime range detection
|
|
63
|
-
* (win32) fix bug where the Mysql2::Client object wasn't cleaned up properly if interrupted during a query
|
|
64
|
-
* add Mysql2::Result#count (aliased as size) to get the row count for the dataset
|
|
65
|
-
this can be especially helpful if you want to get the number of rows without having to inflate
|
|
66
|
-
the entire dataset into ruby (since this happens lazily)
|
|
67
|
-
|
|
68
|
-
## 0.2.10 (June 15th, 2011)
|
|
69
|
-
* bug fix for Time/DateTime usage depending on 32/64bit Ruby
|
|
70
|
-
|
|
71
|
-
## 0.2.9 (June 15th, 2011)
|
|
72
|
-
* fix a long standing bug where a signal would interrupt rb_thread_select and put the connection in a permanently broken state
|
|
73
|
-
* turn on casting in the ActiveRecord again, users can disable it if they need to for performance reasons
|
|
74
|
-
|
|
75
|
-
## 0.2.8 (June 14th, 2011)
|
|
76
|
-
* disable async support, and access to the underlying file descriptor under Windows. It's never worked reliably and ruby-core has a lot of work to do in order to make it possible.
|
|
77
|
-
* added support for turning eager-casting off. This is especially useful in ORMs that will lazily cast values upon access.
|
|
78
|
-
* added a warning if a 0.2.x release is being used with ActiveRecord 3.1 since both the 0.2.x releases and AR 3.1 have mysql2 adapters, we want you to use the one in AR 3.1
|
|
79
|
-
* added Mysql2::Client.escape (class-level method)
|
|
80
|
-
* disabled eager-casting in the bundled ActiveRecord adapter (for Rails 3.0 or less)
|
|
81
|
-
|
|
82
|
-
## 0.2.7 (March 28th, 2011)
|
|
83
|
-
* various fixes for em_mysql2 and fiber usage
|
|
84
|
-
* use our own Mysql2IndexDefinition class for better compatibility across ActiveRecord versions
|
|
85
|
-
* ensure the query is a string earlier in the Mysql2::Client#query codepath for 1.9
|
|
86
|
-
* only set binary ruby encoding on fields that have a binary flag *and* encoding set
|
|
87
|
-
* a few various optimizations
|
|
88
|
-
* add support for :read_timeout to be set on a connection
|
|
89
|
-
* Fix to install with MariDB on Windows
|
|
90
|
-
* add fibered em connection without activerecord
|
|
91
|
-
* fix some 1.9.3 compilation warnings
|
|
92
|
-
* add LD_RUN_PATH when using hard coded mysql paths - this should help users with MySQL installed in non-standard locations
|
|
93
|
-
* for windows support, duplicate the socket from libmysql and create a temporary CRT fd
|
|
94
|
-
* fix for handling years before 1970 on Windows
|
|
95
|
-
* fixes to the Fiber adapter
|
|
96
|
-
* set wait_timeout maximum on Windows to 2147483
|
|
97
|
-
* update supported range for Time objects
|
|
98
|
-
* upon being required, make sure the libmysql we're using is the one we were built against
|
|
99
|
-
* add Mysql2::Client#thread_id
|
|
100
|
-
* add Mysql2::Client#ping
|
|
101
|
-
* switch connection check in AR adapter to use Mysql2::Client#ping for efficiency
|
|
102
|
-
* prefer linking against thread-safe version of libmysqlclient
|
|
103
|
-
* define RSTRING_NOT_MODIFIED for an awesome rbx speed boost
|
|
104
|
-
* expose Mysql2::Client#encoding in 1.9, make sure we set the error message and sqlstate encodings accordingly
|
|
105
|
-
* do not segfault when raising for invalid charset (found in 1.9.3dev)
|
|
106
|
-
|
|
107
|
-
## 0.2.6 (October 19th, 2010)
|
|
108
|
-
* version bump since the 0.2.5 win32 binary gems were broken
|
|
109
|
-
|
|
110
|
-
## 0.2.5 (October 19th, 2010)
|
|
111
|
-
* fixes for easier Win32 binary gem deployment for targeting 1.8 and 1.9 in the same gem
|
|
112
|
-
* refactor of connection checks and management to avoid race conditions with the GC/threading to prevent the unexpected loss of connections
|
|
113
|
-
* update the default flags during connection
|
|
114
|
-
* add support for setting wait_timeout on AR adapter
|
|
115
|
-
* upgrade to rspec2
|
|
116
|
-
* bugfix for an edge case where the GC would clean up a Mysql2::Client object before the underlying MYSQL pointer had been initialized
|
|
117
|
-
* fix to CFLAGS to allow compilation on SPARC with sunstudio compiler - Anko painting <anko.com+github@gmail.com>
|
|
118
|
-
|
|
119
|
-
## 0.2.4 (September 17th, 2010)
|
|
120
|
-
* a few patches for win32 support from Luis Lavena - thanks man!
|
|
121
|
-
* bugfix from Eric Wong to avoid a potential stack overflow during Mysql2::Client#escape
|
|
122
|
-
* added the ability to turn internal row caching on/off via the :cache_rows => true/false option
|
|
123
|
-
* a couple of small patches for rbx compatibility
|
|
124
|
-
* set IndexDefinition#length in AR adapter - Kouhei Yanagita <yanagi@shakenbu.org>
|
|
125
|
-
* fix a long-standing data corruption bug - thank you thank you thank you to @joedamato (http://github.com/ice799)
|
|
126
|
-
* bugfix from calling mysql_close on a closed/freed connection surfaced by the above fix
|
|
127
|
-
|
|
128
|
-
## 0.2.3 (August 20th, 2010)
|
|
129
|
-
* connection flags can now be passed to the constructor via the :flags key
|
|
130
|
-
* switch AR adapter connection over to use FOUND_ROWS option
|
|
131
|
-
* patch to ensure we use DateTime objects in place of Time for timestamps that are out of the supported range on 32bit platforms < 1.9.2
|
|
132
|
-
|
|
133
|
-
## 0.2.2 (August 19th, 2010)
|
|
134
|
-
* Change how AR adapter would send initial commands upon connecting
|
|
135
|
-
** we can make multiple session variable assignments in a single query
|
|
136
|
-
* fix signal handling when waiting on queries
|
|
137
|
-
* retry connect if interrupted by signals
|
|
138
|
-
|
|
139
|
-
## 0.2.1 (August 16th, 2010)
|
|
140
|
-
* bring mysql2 ActiveRecord adapter back into gem
|
|
141
|
-
|
|
142
|
-
## 0.2.0 (August 16th, 2010)
|
|
143
|
-
* switch back to letting libmysql manage all allocation/thread-state/freeing for the connection
|
|
144
|
-
* cache various numeric type conversions in hot-spots of the code for a little speed boost
|
|
145
|
-
* ActiveRecord adapter moved into Rails 3 core
|
|
146
|
-
** Don't worry 2.3.x users! We'll either release the adapter as a separate gem, or try to get it into 2.3.9
|
|
147
|
-
* Fix for the "closed MySQL connection" error (GH #31)
|
|
148
|
-
* Fix for the "can't modify frozen object" error in 1.9.2 (GH #37)
|
|
149
|
-
* Introduce cascading query and result options (more info in README)
|
|
150
|
-
* Sequel adapter pulled into core (will be in the next release - 3.15.0 at the time of writing)
|
|
151
|
-
* add a safety check when attempting to send a query before a result has been fetched
|
|
152
|
-
|
|
153
|
-
## 0.1.9 (July 17th, 2010)
|
|
154
|
-
* Support async ActiveRecord access with fibers and EventMachine (mperham)
|
|
155
|
-
* string encoding support for 1.9, respecting Encoding.default_internal
|
|
156
|
-
* added support for rake-compiler (tenderlove)
|
|
157
|
-
* bugfixes for ActiveRecord driver
|
|
158
|
-
** one minor bugfix for TimeZone support
|
|
159
|
-
** fix the select_rows method to return what it should according to the docs (r-stu31)
|
|
160
|
-
* Mysql2::Client#fields method added - returns the array of field names from a resultset, as strings
|
|
161
|
-
* Sequel adapter
|
|
162
|
-
** bugfix regarding sybolized field names (Eric Wong)
|
|
163
|
-
** fix query logging in Sequel adapter
|
|
164
|
-
* Lots of nice code cleanup (tenderlove)
|
|
165
|
-
** Mysql2::Error definition moved to pure-Ruby
|
|
166
|
-
** Mysql2::client#initialize definition moved to pure-Ruby
|
|
167
|
-
** Mysql2::Result partially moved to pure-Ruby
|
|
168
|
-
|
|
169
|
-
## 0.1.8 (June 2nd, 2010)
|
|
170
|
-
* fixes for AR adapter for timezone juggling
|
|
171
|
-
* fixes to be able to run benchmarks and specs under 1.9.2
|
|
172
|
-
|
|
173
|
-
## 0.1.7 (May 22nd, 2010)
|
|
174
|
-
* fix a bug when using the disconnect! method on a closed connection in the AR driver
|
|
175
|
-
|
|
176
|
-
## 0.1.6 (May 14th, 2010)
|
|
177
|
-
* more fixes to the AR adapter related to casting
|
|
178
|
-
* add missing index creation override method to AR adapter
|
|
179
|
-
* added sql_state and error_number methods to the Mysql2::Error exception class
|
|
180
|
-
|
|
181
|
-
## 0.1.5 (May 12th, 2010)
|
|
182
|
-
* quite a few patches from Eric Wong related to thread-safety, non-blocking I/O and general cleanup
|
|
183
|
-
** wrap mysql_real_connect with rb_thread_blocking_region
|
|
184
|
-
** release GVL for possibly blocking mysql_* library calls
|
|
185
|
-
** [cleanup] quiet down warnings
|
|
186
|
-
** [cleanup] make all C symbols static
|
|
187
|
-
** add Mysql2::Client#close method
|
|
188
|
-
** correctly free the wrapped result in case of EOF
|
|
189
|
-
** Fix memory leak from the result wrapper struct itself
|
|
190
|
-
** make Mysql2::Client destructor safely non-blocking
|
|
191
|
-
* bug fixes for ActiveRecord adapter
|
|
192
|
-
** added casting for default values since they all come back from Mysql as strings (!?!)
|
|
193
|
-
** missing constant was added
|
|
194
|
-
** fixed a typo in the show_variable method
|
|
195
|
-
* switched over sscanf for date/time parsing in C
|
|
196
|
-
* made some specs a little finer-grained
|
|
197
|
-
* initial Sequel adapter added
|
|
198
|
-
* updated query benchmarks to reflect the difference between casting in C and in Ruby
|
|
199
|
-
|
|
200
|
-
## 0.1.4 (April 23rd, 2010)
|
|
201
|
-
* optimization: implemented a local cache for rows that are lazily created in ruby during iteration. The MySQL C result is freed as soon as all the results have been cached
|
|
202
|
-
* optimization: implemented a local cache for field names so every row reuses the same objects as field names/keys
|
|
203
|
-
* refactor the Mysql2 connection adapter for ActiveRecord to not extend the Mysql adapter - now being a free-standing connection adapter
|
|
204
|
-
|
|
205
|
-
## 0.1.3 (April 15th, 2010)
|
|
206
|
-
* added an EventMachine Deferrable API
|
|
207
|
-
* added an ActiveRecord connection adapter
|
|
208
|
-
** should be compatible with 2.3.5 and 3.0 (including Arel)
|
|
209
|
-
|
|
210
|
-
## 0.1.2 (April 9th, 2010)
|
|
211
|
-
* fix a bug (copy/paste fail) around checking for empty TIME values and returning nil (thanks @marius)
|
|
212
|
-
|
|
213
|
-
## 0.1.1 (April 6th, 2010)
|
|
214
|
-
* added affected_rows method (mysql_affected_rows)
|
|
215
|
-
* added last_id method (last_insert_id)
|
|
216
|
-
* enable reconnect option by default
|
|
217
|
-
* added initial async query support
|
|
218
|
-
* updated extconf (thanks to the mysqlplus project) for easier gem building
|
|
219
|
-
|
|
220
|
-
## 0.1.0 (April 6th, 2010)
|
|
221
|
-
* initial release
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
# AR adapter for using a fibered mysql2 connection with EM
|
|
4
|
-
# This adapter should be used within Thin or Unicorn with the rack-fiber_pool middleware.
|
|
5
|
-
# Just update your database.yml's adapter to be 'em_mysql2'
|
|
6
|
-
|
|
7
|
-
module ActiveRecord
|
|
8
|
-
class Base
|
|
9
|
-
def self.em_mysql2_connection(config)
|
|
10
|
-
client = ::Mysql2::Fibered::Client.new(config.symbolize_keys)
|
|
11
|
-
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
|
12
|
-
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
require 'fiber'
|
|
18
|
-
require 'eventmachine'
|
|
19
|
-
require 'mysql2'
|
|
20
|
-
require 'active_record/connection_adapters/mysql2_adapter'
|
|
21
|
-
require 'active_record/fiber_patches'
|
|
22
|
-
|
|
23
|
-
module Mysql2
|
|
24
|
-
module Fibered
|
|
25
|
-
class Client < ::Mysql2::Client
|
|
26
|
-
module Watcher
|
|
27
|
-
def initialize(client, deferable)
|
|
28
|
-
@client = client
|
|
29
|
-
@deferable = deferable
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def notify_readable
|
|
33
|
-
begin
|
|
34
|
-
detach
|
|
35
|
-
results = @client.async_result
|
|
36
|
-
@deferable.succeed(results)
|
|
37
|
-
rescue Exception => e
|
|
38
|
-
@deferable.fail(e)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def query(sql, opts={})
|
|
44
|
-
if ::EM.reactor_running?
|
|
45
|
-
super(sql, opts.merge(:async => true))
|
|
46
|
-
deferrable = ::EM::DefaultDeferrable.new
|
|
47
|
-
::EM.watch(self.socket, Watcher, self, deferrable).notify_readable = true
|
|
48
|
-
fiber = Fiber.current
|
|
49
|
-
deferrable.callback do |result|
|
|
50
|
-
fiber.resume(result)
|
|
51
|
-
end
|
|
52
|
-
deferrable.errback do |err|
|
|
53
|
-
fiber.resume(err)
|
|
54
|
-
end
|
|
55
|
-
Fiber.yield.tap do |result|
|
|
56
|
-
raise result if result.is_a?(Exception)
|
|
57
|
-
end
|
|
58
|
-
else
|
|
59
|
-
super(sql, opts)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Necessary monkeypatching to make AR fiber-friendly.
|
|
2
|
-
|
|
3
|
-
module ActiveRecord
|
|
4
|
-
module ConnectionAdapters
|
|
5
|
-
|
|
6
|
-
def self.fiber_pools
|
|
7
|
-
@fiber_pools ||= []
|
|
8
|
-
end
|
|
9
|
-
def self.register_fiber_pool(fp)
|
|
10
|
-
fiber_pools << fp
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
class FiberedMonitor
|
|
14
|
-
class Queue
|
|
15
|
-
def initialize
|
|
16
|
-
@queue = []
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def wait(timeout)
|
|
20
|
-
t = timeout || 5
|
|
21
|
-
fiber = Fiber.current
|
|
22
|
-
x = EM::Timer.new(t) do
|
|
23
|
-
@queue.delete(fiber)
|
|
24
|
-
fiber.resume(false)
|
|
25
|
-
end
|
|
26
|
-
@queue << fiber
|
|
27
|
-
Fiber.yield.tap do
|
|
28
|
-
x.cancel
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def signal
|
|
33
|
-
fiber = @queue.pop
|
|
34
|
-
fiber.resume(true) if fiber
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def synchronize
|
|
39
|
-
yield
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def new_cond
|
|
43
|
-
Queue.new
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# ActiveRecord's connection pool is based on threads. Since we are working
|
|
48
|
-
# with EM and a single thread, multiple fiber design, we need to provide
|
|
49
|
-
# our own connection pool that keys off of Fiber.current so that different
|
|
50
|
-
# fibers running in the same thread don't try to use the same connection.
|
|
51
|
-
class ConnectionPool
|
|
52
|
-
def initialize(spec)
|
|
53
|
-
@spec = spec
|
|
54
|
-
|
|
55
|
-
# The cache of reserved connections mapped to threads
|
|
56
|
-
@reserved_connections = {}
|
|
57
|
-
|
|
58
|
-
# The mutex used to synchronize pool access
|
|
59
|
-
@connection_mutex = FiberedMonitor.new
|
|
60
|
-
@queue = @connection_mutex.new_cond
|
|
61
|
-
|
|
62
|
-
# default 5 second timeout unless on ruby 1.9
|
|
63
|
-
@timeout = spec.config[:wait_timeout] || 5
|
|
64
|
-
|
|
65
|
-
# default max pool size to 5
|
|
66
|
-
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
|
|
67
|
-
|
|
68
|
-
@connections = []
|
|
69
|
-
@checked_out = []
|
|
70
|
-
@automatic_reconnect = true
|
|
71
|
-
@tables = {}
|
|
72
|
-
|
|
73
|
-
@columns = Hash.new do |h, table_name|
|
|
74
|
-
h[table_name] = with_connection do |conn|
|
|
75
|
-
|
|
76
|
-
# Fetch a list of columns
|
|
77
|
-
conn.columns(table_name, "#{table_name} Columns").tap do |columns|
|
|
78
|
-
|
|
79
|
-
# set primary key information
|
|
80
|
-
columns.each do |column|
|
|
81
|
-
column.primary = column.name == primary_keys[table_name]
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
@columns_hash = Hash.new do |h, table_name|
|
|
88
|
-
h[table_name] = Hash[columns[table_name].map { |col|
|
|
89
|
-
[col.name, col]
|
|
90
|
-
}]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
@primary_keys = Hash.new do |h, table_name|
|
|
94
|
-
h[table_name] = with_connection do |conn|
|
|
95
|
-
table_exists?(table_name) ? conn.primary_key(table_name) : 'id'
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def clear_stale_cached_connections!
|
|
101
|
-
cache = @reserved_connections
|
|
102
|
-
keys = Set.new(cache.keys)
|
|
103
|
-
|
|
104
|
-
ActiveRecord::ConnectionAdapters.fiber_pools.each do |pool|
|
|
105
|
-
pool.busy_fibers.each_pair do |object_id, fiber|
|
|
106
|
-
keys.delete(object_id)
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
keys.each do |key|
|
|
111
|
-
next unless cache.has_key?(key)
|
|
112
|
-
checkin cache[key]
|
|
113
|
-
cache.delete(key)
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
private
|
|
118
|
-
|
|
119
|
-
def current_connection_id #:nodoc:
|
|
120
|
-
Fiber.current.object_id
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def checkout_and_verify(c)
|
|
124
|
-
@checked_out << c
|
|
125
|
-
c.run_callbacks :checkout
|
|
126
|
-
c.verify!
|
|
127
|
-
c
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
end
|
|
132
|
-
end
|
data/lib/mysql2/em_fiber.rb
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
require 'mysql2/em'
|
|
4
|
-
require 'fiber'
|
|
5
|
-
|
|
6
|
-
module Mysql2
|
|
7
|
-
module EM
|
|
8
|
-
module Fiber
|
|
9
|
-
class Client < ::Mysql2::EM::Client
|
|
10
|
-
def query(sql, opts={})
|
|
11
|
-
if ::EM.reactor_running?
|
|
12
|
-
deferable = super(sql, opts)
|
|
13
|
-
|
|
14
|
-
fiber = ::Fiber.current
|
|
15
|
-
deferable.callback do |result|
|
|
16
|
-
fiber.resume(result)
|
|
17
|
-
end
|
|
18
|
-
deferable.errback do |err|
|
|
19
|
-
fiber.resume(err)
|
|
20
|
-
end
|
|
21
|
-
::Fiber.yield.tap do |result|
|
|
22
|
-
raise result if result.is_a?(::Exception)
|
|
23
|
-
end
|
|
24
|
-
else
|
|
25
|
-
super(sql, opts)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
data/spec/em/em_fiber_spec.rb
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
if defined? EventMachine && defined? Fiber
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
require 'mysql2/em_fiber'
|
|
5
|
-
|
|
6
|
-
describe Mysql2::EM::Fiber::Client do
|
|
7
|
-
it 'should support queries' do
|
|
8
|
-
results = []
|
|
9
|
-
EM.run do
|
|
10
|
-
Fiber.new {
|
|
11
|
-
client1 = Mysql2::EM::Fiber::Client.new DatabaseCredentials['root']
|
|
12
|
-
results = client1.query "SELECT sleep(0.1) as first_query"
|
|
13
|
-
EM.stop_event_loop
|
|
14
|
-
}.resume
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
results.first.keys.should include("first_query")
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
puts "Either EventMachine or Fibers not available. Skipping tests that use them."
|
|
22
|
-
end
|