mysqlplus 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +7 -0
- data/TODO_LIST +6 -7
- data/ext/error_const.h +3 -0
- data/ext/extconf.rb +4 -2
- data/ext/mysql.c +354 -47
- data/lib/mysqlplus.rb +6 -4
- data/mysqlplus.gemspec +8 -13
- data/test/RUN_ALL_TESTS.RB +9 -0
- data/test/{test_all_hashes.rb → all_hashes_test.rb} +3 -9
- data/test/async_query_with_block_test.rb +7 -0
- data/test/connect_failure2_test.rb +22 -0
- data/test/{test_failure.rb → connect_failure_test.rb} +1 -1
- data/test/create_test_db.rb +22 -0
- data/test/gc_benchmark_test.rb +40 -0
- data/test/{test_many_requests.rb → many_requests_test.rb} +0 -1
- data/test/out_of_sync_test.rb +34 -0
- data/test/{test_parsing_while_response_is_being_read.rb → query_with_result_false_test.rb} +3 -4
- data/test/reconnected_test.rb +18 -0
- data/test/test_helper.rb +1 -2
- data/test/{test_threaded_sequel.rb → threaded_sequel_test.rb} +2 -2
- metadata +28 -12
data/README
CHANGED
@@ -29,6 +29,10 @@ An enhanced MySQL database driver. With support for async operations and threade
|
|
29
29
|
Same with other scripts that want to use it--just require 'mysqlplus' BEFORE you require 'mysql' and it will
|
30
30
|
load the asynchronous version, then ignore the sequent require 'mysql' call.
|
31
31
|
|
32
|
+
== Other helpful mysql utilities:
|
33
|
+
slim attributes http://slim-attributes.rubyforge.org/ boosts mysql speed by using arrays instead of hashed lookup.
|
34
|
+
Hash extension gem also results in speedups when used: http://blog.chak.org/2008/02/09/speeding-up-activerecord-with-hashes-take-2/
|
35
|
+
|
32
36
|
=== Credits
|
33
37
|
|
34
38
|
Aman Gupta, for help in threading support and improved tests
|
@@ -38,3 +42,6 @@ Lourens Naude for 1.9 integration help.
|
|
38
42
|
|
39
43
|
=== License
|
40
44
|
Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
|
45
|
+
|
46
|
+
== Mailing list
|
47
|
+
http://groups.google.com/group/never-block?hl=en
|
data/TODO_LIST
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
TODO list:
|
2
2
|
|
3
|
-
Is there a quick, cheap, easy way to test for writability so we don't have to use select
|
3
|
+
Is there a quick, cheap, easy way to test for writability so we don't have to use select itself? Does select take any time that it's worth looking into this?
|
4
4
|
|
5
|
-
|
5
|
+
Some of the tests currently might "think" they are using the ruby select but in reality be using the C select.
|
6
6
|
|
7
|
-
|
7
|
+
gc_disabled is unused
|
8
8
|
|
9
|
-
|
9
|
+
if they call get_result twice consecutively it should blow (and maybe already does).
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
critical todo list:
|
11
|
+
mingw support
|
12
|
+
add slim attributes right in there :)
|
data/ext/error_const.h
CHANGED
@@ -533,4 +533,7 @@
|
|
533
533
|
rb_define_mysql_const(ER_ADMIN_WRONG_MRG_TABLE);
|
534
534
|
rb_define_mysql_const(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT);
|
535
535
|
rb_define_mysql_const(ER_NAME_BECOMES_EMPTY);
|
536
|
+
rb_define_mysql_const(ER_AMBIGUOUS_FIELD_TERM);
|
537
|
+
rb_define_mysql_const(ER_LOAD_DATA_INVALID_COLUMN);
|
538
|
+
rb_define_mysql_const(ER_LOG_PURGE_NO_FILE);
|
536
539
|
rb_define_mysql_const(ER_ERROR_LAST);
|
data/ext/extconf.rb
CHANGED
@@ -45,8 +45,10 @@ else
|
|
45
45
|
exit 1
|
46
46
|
end
|
47
47
|
|
48
|
+
# check for 1.9
|
48
49
|
if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
|
49
|
-
$
|
50
|
+
$CFLAGS += " -DHAVE_TBR "
|
51
|
+
$CPPFLAGS << " -DHAVE_TBR "
|
50
52
|
end
|
51
53
|
|
52
54
|
# make mysql constant
|
@@ -85,4 +87,4 @@ File.open('error_const.h', 'w') do |f|
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
create_makefile("mysql")
|
90
|
+
create_makefile("mysql")
|
data/ext/mysql.c
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
#include <ruby.h>
|
8
8
|
#include <errno.h>
|
9
|
+
#include <stdarg.h>
|
9
10
|
#ifndef RSTRING_PTR
|
10
11
|
#define RSTRING_PTR(str) RSTRING(str)->ptr
|
11
12
|
#endif
|
@@ -60,9 +61,13 @@ struct mysql {
|
|
60
61
|
MYSQL handler;
|
61
62
|
char connection;
|
62
63
|
char query_with_result;
|
64
|
+
char gc_disabled;
|
63
65
|
char blocking;
|
66
|
+
int async_in_progress;
|
67
|
+
char busy;
|
64
68
|
};
|
65
69
|
|
70
|
+
// a wrapper for mysql_res's so we can detect double frees
|
66
71
|
struct mysql_res {
|
67
72
|
MYSQL_RES* res;
|
68
73
|
char freed;
|
@@ -180,7 +185,7 @@ static void mysql_raise(MYSQL* m)
|
|
180
185
|
rb_exc_raise(e);
|
181
186
|
}
|
182
187
|
|
183
|
-
static VALUE mysqlres2obj(MYSQL_RES* res)
|
188
|
+
static VALUE mysqlres2obj(MYSQL_RES* res, VALUE gc_disabled)
|
184
189
|
{
|
185
190
|
VALUE obj;
|
186
191
|
struct mysql_res* resp;
|
@@ -229,10 +234,135 @@ static VALUE init(VALUE klass)
|
|
229
234
|
mysql_init(&myp->handler);
|
230
235
|
myp->connection = Qfalse;
|
231
236
|
myp->query_with_result = Qtrue;
|
237
|
+
myp->gc_disabled = Qtrue;
|
232
238
|
rb_obj_call_init(obj, 0, NULL);
|
233
239
|
return obj;
|
234
240
|
}
|
235
241
|
|
242
|
+
// =========== a 1.9 rb_thread_blocking_region simplifier attempt
|
243
|
+
#ifdef HAVE_TBR
|
244
|
+
|
245
|
+
typedef struct
|
246
|
+
{
|
247
|
+
void *func_pointer;
|
248
|
+
int param_count;
|
249
|
+
void *args[10];
|
250
|
+
} arg_holder, *arg_holder2;
|
251
|
+
|
252
|
+
// here's how to make rb_thread_blocking_region much cleaner and easier
|
253
|
+
// syntax: param_count+2, func_pointer to call, [RUBY_UBF_IO or RUBY_UBF_PROCESS], param1, param2...
|
254
|
+
// the third parameter is the interuptor--possible values appear to be RUBY_UBF_IO or RUBY_UBF_PROCESS http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/ad8c1326b2a8e404/00447b9aa15979be?lnk=raot
|
255
|
+
// ex: (int) returned_this = rb_thread_blocking_region_variable_params(10, &method_name, RUBY_UBF_IO, param1, param2, param3, param4, param5, param6, param7, param8)
|
256
|
+
|
257
|
+
static void *call_single_function_rb_thread_blocking_region(void *arg_holder_in);
|
258
|
+
|
259
|
+
void *rb_thread_blocking_region_variable_params(int number, ...)
|
260
|
+
{
|
261
|
+
va_list param_pt;
|
262
|
+
va_start(param_pt, number);
|
263
|
+
int index;
|
264
|
+
arg_holder param_storer;
|
265
|
+
void *func_pointer = va_arg(param_pt, void *);
|
266
|
+
void *interrupter = va_arg(param_pt, void *);
|
267
|
+
param_storer.func_pointer = func_pointer;
|
268
|
+
int real_param_count = number - 2;
|
269
|
+
param_storer.param_count = real_param_count;
|
270
|
+
for(index = 0 ; index < real_param_count ; index++)
|
271
|
+
{
|
272
|
+
void *arg = va_arg(param_pt, void *);
|
273
|
+
param_storer.args[index] = arg;
|
274
|
+
|
275
|
+
}
|
276
|
+
va_end(param_pt);
|
277
|
+
|
278
|
+
return (void *) rb_thread_blocking_region((rb_blocking_function_t *)call_single_function_rb_thread_blocking_region, (void *) ¶m_storer, interrupter, 0);
|
279
|
+
|
280
|
+
}
|
281
|
+
|
282
|
+
// used internally
|
283
|
+
static void * call_single_function_rb_thread_blocking_region(void *arg_holder_in)
|
284
|
+
{
|
285
|
+
arg_holder *params_and_func = (arg_holder *) arg_holder_in;
|
286
|
+
int param_count = params_and_func->param_count;
|
287
|
+
void *result;
|
288
|
+
switch(param_count)
|
289
|
+
{
|
290
|
+
case 3:;
|
291
|
+
void * (*pt3Func)(void *, void *, void *) = params_and_func->func_pointer;
|
292
|
+
result = (*pt3Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2]);
|
293
|
+
break;
|
294
|
+
case 6:;
|
295
|
+
void * (*pt6Func)(void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer;
|
296
|
+
result = (*pt6Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2], params_and_func->args[3], params_and_func->args[4], params_and_func->args[5]);
|
297
|
+
break;
|
298
|
+
case 8:;
|
299
|
+
void * (*pt8Func)(void *, void *, void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer;
|
300
|
+
result = (*pt8Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2], params_and_func->args[3], params_and_func->args[4], params_and_func->args[5], params_and_func->args[6], params_and_func->args[7]);
|
301
|
+
break;
|
302
|
+
default:;
|
303
|
+
printf("UNknown param count--please add it! %d\n", param_count);
|
304
|
+
result = (void *) Qnil;
|
305
|
+
}
|
306
|
+
|
307
|
+
return result;
|
308
|
+
}
|
309
|
+
|
310
|
+
#endif
|
311
|
+
|
312
|
+
static VALUE connection_identifier( VALUE obj )
|
313
|
+
{
|
314
|
+
MYSQL* m = GetHandler(obj);
|
315
|
+
return mysql_thread_id( m );
|
316
|
+
}
|
317
|
+
|
318
|
+
static VALUE async_in_progress( VALUE obj )
|
319
|
+
{
|
320
|
+
struct mysql* m = GetMysqlStruct(obj);
|
321
|
+
return ( m->async_in_progress == connection_identifier(obj) ) ? Qtrue : Qfalse;
|
322
|
+
}
|
323
|
+
|
324
|
+
static VALUE async_in_progress_set( VALUE obj, VALUE flag )
|
325
|
+
{
|
326
|
+
struct mysql* m = GetMysqlStruct(obj);
|
327
|
+
m->async_in_progress = (flag == Qnil || flag == Qfalse) ? 0 : connection_identifier(obj);
|
328
|
+
return flag;
|
329
|
+
}
|
330
|
+
|
331
|
+
// does this actually really do anything helpful? Not sure.
|
332
|
+
static void optimize_for_async( VALUE obj )
|
333
|
+
{
|
334
|
+
struct mysql* m = GetMysqlStruct(obj);
|
335
|
+
my_bool was_blocking;
|
336
|
+
vio_blocking(m->handler.net.vio, 0, &was_blocking);
|
337
|
+
m->blocking = vio_is_blocking( m->handler.net.vio );
|
338
|
+
|
339
|
+
vio_fastsend( m->handler.net.vio );
|
340
|
+
async_in_progress_set( obj, Qfalse );
|
341
|
+
}
|
342
|
+
|
343
|
+
// TODO does nothing currently
|
344
|
+
static void schedule_connect(VALUE obj )
|
345
|
+
{
|
346
|
+
/* TODO is this old?
|
347
|
+
MYSQL* m = GetHandler(obj);
|
348
|
+
fd_set read;
|
349
|
+
|
350
|
+
struct timeval tv = { tv_sec: m->options.connect_timeout, tv_usec: 0 };
|
351
|
+
if (rb_thread_select(0, NULL, NULL, NULL, &tv) < 0) {
|
352
|
+
rb_raise(eMysql, "connect: timeout");
|
353
|
+
}
|
354
|
+
*/
|
355
|
+
|
356
|
+
/*
|
357
|
+
FD_ZERO(&read);
|
358
|
+
FD_SET(m->net.fd, &read);
|
359
|
+
|
360
|
+
if (rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv) < 0) {
|
361
|
+
rb_raise(eMysql, "connect: timeout");
|
362
|
+
}
|
363
|
+
*/
|
364
|
+
}
|
365
|
+
|
236
366
|
/* real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, sock=nil, flag=nil) */
|
237
367
|
static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
238
368
|
{
|
@@ -260,27 +390,28 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
|
260
390
|
|
261
391
|
obj = Data_Make_Struct(klass, struct mysql, 0, free_mysql, myp);
|
262
392
|
#if MYSQL_VERSION_ID >= 32200
|
263
|
-
mysql_init(&myp->handler);
|
264
|
-
|
393
|
+
mysql_init(&myp->handler); /* we get here */
|
394
|
+
# ifdef HAVE_TBR
|
395
|
+
if( (MYSQL *) rb_thread_blocking_region_variable_params(10, &mysql_real_connect, RUBY_UBF_IO, &myp->handler, h, u, p, d, pp, s, f) == NULL)
|
396
|
+
# else
|
397
|
+
if(mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL)
|
398
|
+
# endif
|
265
399
|
#elif MYSQL_VERSION_ID >= 32115
|
266
400
|
if (mysql_real_connect(&myp->handler, h, u, p, pp, s, f) == NULL)
|
267
401
|
#else
|
268
402
|
if (mysql_real_connect(&myp->handler, h, u, p, pp, s) == NULL)
|
269
403
|
#endif
|
270
404
|
mysql_raise(&myp->handler);
|
271
|
-
|
405
|
+
|
272
406
|
myp->handler.reconnect = 0;
|
273
407
|
myp->connection = Qtrue;
|
274
408
|
|
275
|
-
|
276
|
-
|
277
|
-
vio_blocking(myp->handler.net.vio, 0, &was_blocking);
|
278
|
-
myp->blocking = vio_is_blocking( myp->handler.net.vio );
|
279
|
-
|
280
|
-
vio_fastsend( myp->handler.net.vio );
|
409
|
+
optimize_for_async(obj);
|
281
410
|
|
282
411
|
myp->query_with_result = Qtrue;
|
283
412
|
rb_obj_call_init(obj, argc, argv);
|
413
|
+
|
414
|
+
//schedule_connect(obj);
|
284
415
|
|
285
416
|
return obj;
|
286
417
|
}
|
@@ -343,6 +474,9 @@ static VALUE real_connect2(int argc, VALUE* argv, VALUE obj)
|
|
343
474
|
mysql_raise(m);
|
344
475
|
m->reconnect = 0;
|
345
476
|
GetMysqlStruct(obj)->connection = Qtrue;
|
477
|
+
|
478
|
+
optimize_for_async(obj);
|
479
|
+
//schedule_connect(obj);
|
346
480
|
|
347
481
|
return obj;
|
348
482
|
}
|
@@ -597,7 +731,7 @@ static VALUE list_fields(int argc, VALUE* argv, VALUE obj)
|
|
597
731
|
res = mysql_list_fields(m, StringValuePtr(table), NILorSTRING(field));
|
598
732
|
if (res == NULL)
|
599
733
|
mysql_raise(m);
|
600
|
-
return mysqlres2obj(res);
|
734
|
+
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
601
735
|
}
|
602
736
|
|
603
737
|
/* list_processes() */
|
@@ -607,7 +741,7 @@ static VALUE list_processes(VALUE obj)
|
|
607
741
|
MYSQL_RES* res = mysql_list_processes(m);
|
608
742
|
if (res == NULL)
|
609
743
|
mysql_raise(m);
|
610
|
-
return mysqlres2obj(res);
|
744
|
+
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
611
745
|
}
|
612
746
|
|
613
747
|
/* list_tables(table=nil) */
|
@@ -694,14 +828,40 @@ static VALUE my_stat(VALUE obj)
|
|
694
828
|
return rb_tainted_str_new2(s);
|
695
829
|
}
|
696
830
|
|
831
|
+
// 1.9 friendly
|
832
|
+
typedef struct
|
833
|
+
{
|
834
|
+
MYSQL *mysql_instance;
|
835
|
+
MYSQL_RES **store_it_here;
|
836
|
+
|
837
|
+
} mysql_result_to_here_t,
|
838
|
+
*shared_stuff_p;
|
839
|
+
|
840
|
+
static VALUE store_result_to_location(void *settings_in)
|
841
|
+
{
|
842
|
+
mysql_result_to_here_t *settings = (mysql_result_to_here_t *) settings_in;
|
843
|
+
*(settings->store_it_here) = mysql_store_result(settings->mysql_instance); // this one line runs a good long while for very large queries
|
844
|
+
return Qnil;
|
845
|
+
}
|
846
|
+
|
697
847
|
/* store_result() */
|
698
848
|
static VALUE store_result(VALUE obj)
|
699
849
|
{
|
700
850
|
MYSQL* m = GetHandler(obj);
|
701
|
-
MYSQL_RES* res =
|
851
|
+
MYSQL_RES* res = NULL;
|
852
|
+
#ifndef HAVE_TBR
|
853
|
+
res = mysql_store_result(m);
|
854
|
+
#else
|
855
|
+
mysql_result_to_here_t linker;
|
856
|
+
linker.mysql_instance = m;
|
857
|
+
linker.store_it_here = &res;
|
858
|
+
rb_thread_blocking_region(store_result_to_location, (void *) &linker, RUBY_UBF_IO, 0);
|
859
|
+
#endif
|
860
|
+
|
702
861
|
if (res == NULL)
|
703
862
|
mysql_raise(m);
|
704
|
-
|
863
|
+
|
864
|
+
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
705
865
|
}
|
706
866
|
|
707
867
|
/* thread_id() */
|
@@ -717,7 +877,7 @@ static VALUE use_result(VALUE obj)
|
|
717
877
|
MYSQL_RES* res = mysql_use_result(m);
|
718
878
|
if (res == NULL)
|
719
879
|
mysql_raise(m);
|
720
|
-
return mysqlres2obj(res);
|
880
|
+
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
721
881
|
}
|
722
882
|
|
723
883
|
static VALUE res_free(VALUE);
|
@@ -764,7 +924,7 @@ static VALUE query(VALUE obj, VALUE sql)
|
|
764
924
|
if (mysql_field_count(m) != 0)
|
765
925
|
mysql_raise(m);
|
766
926
|
} else {
|
767
|
-
VALUE robj = mysqlres2obj(res);
|
927
|
+
VALUE robj = mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
768
928
|
rb_ensure(rb_yield, robj, res_free, robj);
|
769
929
|
}
|
770
930
|
#if MYSQL_VERSION_ID >= 40101
|
@@ -800,17 +960,46 @@ static VALUE socket(VALUE obj)
|
|
800
960
|
MYSQL* m = GetHandler(obj);
|
801
961
|
return INT2NUM(m->net.fd);
|
802
962
|
}
|
803
|
-
|
963
|
+
|
964
|
+
/* socket_type --currently returns true or false, needs some work */
|
804
965
|
static VALUE socket_type(VALUE obj)
|
805
966
|
{
|
806
967
|
MYSQL* m = GetHandler(obj);
|
807
|
-
|
808
|
-
|
968
|
+
if(vio_description(m->net.vio))
|
969
|
+
return Qtrue; // TODO return a ruby string
|
970
|
+
else
|
971
|
+
return Qnil;
|
809
972
|
}
|
810
973
|
|
811
974
|
/* blocking */
|
812
975
|
static VALUE blocking(VALUE obj){
|
813
|
-
|
976
|
+
return ( GetMysqlStruct(obj)->blocking ? Qtrue : Qfalse );
|
977
|
+
}
|
978
|
+
|
979
|
+
/* is_busy */
|
980
|
+
static VALUE is_busy(VALUE obj){
|
981
|
+
return ( GetMysqlStruct(obj)->busy ? Qtrue : Qfalse );
|
982
|
+
}
|
983
|
+
|
984
|
+
static VALUE is_idle(VALUE obj){
|
985
|
+
return ( is_busy(obj) == Qtrue ) ? Qfalse : Qtrue;
|
986
|
+
}
|
987
|
+
|
988
|
+
/* busy(true|false) */
|
989
|
+
static VALUE busy_set(VALUE obj, VALUE flag)
|
990
|
+
{
|
991
|
+
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
992
|
+
rb_raise(rb_eTypeError, "invalid type, required true or false.");
|
993
|
+
GetMysqlStruct(obj)->busy = flag;
|
994
|
+
return flag;
|
995
|
+
}
|
996
|
+
|
997
|
+
static void busy( VALUE obj ){
|
998
|
+
busy_set( obj, Qtrue );
|
999
|
+
}
|
1000
|
+
|
1001
|
+
static void idle( VALUE obj ){
|
1002
|
+
busy_set( obj, Qfalse );
|
814
1003
|
}
|
815
1004
|
|
816
1005
|
/* readable(timeout=nil) */
|
@@ -825,69 +1014,177 @@ static VALUE readable( int argc, VALUE* argv, VALUE obj )
|
|
825
1014
|
if ( NIL_P( timeout ) ){
|
826
1015
|
timeout = m->net.read_timeout;
|
827
1016
|
}
|
828
|
-
|
1017
|
+
// todo could do a rb_blocking_region here
|
829
1018
|
return ( vio_poll_read( m->net.vio, INT2NUM(timeout) ) == 0 ? Qtrue : Qfalse );
|
830
1019
|
}
|
831
1020
|
|
1021
|
+
/* retry */
|
1022
|
+
static VALUE retry( VALUE obj )
|
1023
|
+
{
|
1024
|
+
MYSQL* m = GetHandler(obj);
|
1025
|
+
return ( vio_should_retry( m->net.vio ) == 1 ? Qtrue : Qfalse );
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
/* interrupted */
|
1029
|
+
static VALUE interrupted( VALUE obj )
|
1030
|
+
{
|
1031
|
+
MYSQL* m = GetHandler(obj);
|
1032
|
+
return ( vio_was_interrupted( m->net.vio ) == 1 ? Qtrue : Qfalse );
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
/* reconnected */
|
1036
|
+
static VALUE reconnected( VALUE obj ){
|
1037
|
+
MYSQL* m = GetHandler(obj);
|
1038
|
+
int current_connection_id = mysql_thread_id( m );
|
1039
|
+
mysql_ping(m);
|
1040
|
+
return ( current_connection_id == mysql_thread_id( m ) ) ? Qfalse : Qtrue;
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
/* disable_gc(true|false) */
|
1044
|
+
static VALUE disable_gc_set(VALUE obj, VALUE flag)
|
1045
|
+
{
|
1046
|
+
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
1047
|
+
rb_raise(rb_eTypeError, "invalid type, required true or false.");
|
1048
|
+
GetMysqlStruct(obj)->gc_disabled = flag;
|
1049
|
+
return flag;
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
/* gc_disabled */
|
1053
|
+
static VALUE gc_disabled( VALUE obj ){
|
1054
|
+
return GetMysqlStruct(obj)->gc_disabled ? Qtrue: Qfalse;
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
static void validate_async_query( VALUE obj )
|
1058
|
+
{
|
1059
|
+
if( async_in_progress(obj) == Qtrue ){
|
1060
|
+
async_in_progress_set(obj, Qfalse);
|
1061
|
+
rb_raise(eMysql, "Query out of sequence: Each call to Mysql#send_query requires a successive Mysql#get_result.");
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
/* for testing */
|
1066
|
+
static VALUE simulate_disconnect( VALUE obj )
|
1067
|
+
{
|
1068
|
+
MYSQL* m = GetHandler(obj);
|
1069
|
+
mysql_library_end();
|
1070
|
+
return Qnil;
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
|
832
1074
|
/* send_query(sql) */
|
833
1075
|
static VALUE send_query(VALUE obj, VALUE sql)
|
834
1076
|
{
|
835
1077
|
MYSQL* m = GetHandler(obj);
|
836
|
-
|
1078
|
+
|
837
1079
|
Check_Type(sql, T_STRING);
|
838
|
-
|
839
|
-
|
1080
|
+
|
1081
|
+
if (GetMysqlStruct(obj)->connection == Qfalse && async_in_progress(obj) == Qtrue ) {
|
1082
|
+
idle( obj );
|
1083
|
+
rb_raise(eMysql, "query: not connected");
|
840
1084
|
}
|
841
|
-
|
842
|
-
|
843
|
-
|
1085
|
+
|
1086
|
+
validate_async_query(obj);
|
1087
|
+
|
1088
|
+
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0){
|
1089
|
+
idle( obj );
|
1090
|
+
mysql_raise(m);
|
1091
|
+
}
|
1092
|
+
async_in_progress_set( obj, Qtrue );
|
1093
|
+
|
1094
|
+
return Qnil;
|
844
1095
|
}
|
845
1096
|
|
846
|
-
/*
|
1097
|
+
/*
|
1098
|
+
get_result
|
1099
|
+
returns the mysql_result set (default) [i.e. all rows in said said]
|
1100
|
+
or nil if query_with_result == false
|
1101
|
+
*/
|
847
1102
|
static VALUE get_result(VALUE obj)
|
848
1103
|
{
|
849
1104
|
MYSQL* m = GetHandler(obj);
|
1105
|
+
|
1106
|
+
async_in_progress_set( obj, Qfalse );
|
1107
|
+
|
850
1108
|
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
851
|
-
|
1109
|
+
idle( obj );
|
1110
|
+
rb_raise(eMysql, "query: not connected");
|
852
1111
|
}
|
853
|
-
|
854
|
-
|
1112
|
+
if (mysql_read_query_result(m) != 0){
|
1113
|
+
idle( obj );
|
1114
|
+
mysql_raise(m);
|
1115
|
+
}
|
1116
|
+
|
855
1117
|
if (GetMysqlStruct(obj)->query_with_result == Qfalse)
|
856
1118
|
return obj;
|
1119
|
+
|
857
1120
|
if (mysql_field_count(m) == 0)
|
858
|
-
|
1121
|
+
return Qnil;
|
1122
|
+
|
859
1123
|
return store_result(obj);
|
860
1124
|
}
|
861
1125
|
|
862
|
-
static void
|
1126
|
+
static void schedule_query(VALUE obj, VALUE timeout)
|
863
1127
|
{
|
864
1128
|
MYSQL* m = GetHandler(obj);
|
865
1129
|
fd_set read;
|
866
|
-
|
1130
|
+
int ret;
|
1131
|
+
|
867
1132
|
timeout = ( NIL_P(timeout) ? m->net.read_timeout : INT2NUM(timeout) );
|
868
1133
|
|
869
1134
|
struct timeval tv = { tv_sec: timeout, tv_usec: 0 };
|
870
1135
|
|
871
|
-
|
872
|
-
|
1136
|
+
for(;;){
|
1137
|
+
FD_ZERO(&read);
|
1138
|
+
FD_SET(m->net.fd, &read);
|
1139
|
+
|
1140
|
+
ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv);
|
1141
|
+
if (ret < 0) {
|
1142
|
+
idle( obj );
|
1143
|
+
rb_raise(eMysql, "query: timeout");
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
if (ret == 0) {
|
1147
|
+
continue;
|
1148
|
+
}
|
873
1149
|
|
874
|
-
|
875
|
-
|
1150
|
+
if (m->status == MYSQL_STATUS_READY){
|
1151
|
+
break;
|
1152
|
+
}
|
876
1153
|
}
|
877
1154
|
}
|
878
1155
|
|
879
|
-
|
1156
|
+
static int should_schedule_query(){
|
1157
|
+
return rb_thread_alone() != 1;
|
1158
|
+
}
|
1159
|
+
|
1160
|
+
/* async_query(sql,timeout=nil)
|
1161
|
+
optionally take a block
|
1162
|
+
*/
|
880
1163
|
static VALUE async_query(int argc, VALUE* argv, VALUE obj)
|
881
1164
|
{
|
882
|
-
|
1165
|
+
MYSQL* m = GetHandler(obj);
|
1166
|
+
VALUE sql, timeout;
|
1167
|
+
|
1168
|
+
rb_scan_args(argc, argv, "11", &sql, &timeout);
|
883
1169
|
|
884
|
-
|
1170
|
+
async_in_progress_set( obj, Qfalse );
|
885
1171
|
|
886
|
-
|
1172
|
+
busy(obj);
|
887
1173
|
|
888
|
-
|
1174
|
+
send_query( obj, sql );
|
889
1175
|
|
890
|
-
|
1176
|
+
if ( should_schedule_query() ){
|
1177
|
+
schedule_query(obj, timeout);
|
1178
|
+
}
|
1179
|
+
|
1180
|
+
if (rb_block_given_p()) {
|
1181
|
+
rb_yield( get_result(obj) );
|
1182
|
+
idle( obj );
|
1183
|
+
return obj;
|
1184
|
+
}else{
|
1185
|
+
idle( obj );
|
1186
|
+
return get_result(obj);
|
1187
|
+
}
|
891
1188
|
}
|
892
1189
|
|
893
1190
|
#if MYSQL_VERSION_ID >= 40100
|
@@ -1157,7 +1454,7 @@ static VALUE fetch_row(VALUE obj)
|
|
1157
1454
|
return ary;
|
1158
1455
|
}
|
1159
1456
|
|
1160
|
-
/* process_all_hashes (internal) */
|
1457
|
+
/* process_all_hashes (internal helper) */
|
1161
1458
|
static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, int yield)
|
1162
1459
|
{
|
1163
1460
|
MYSQL_RES* res = GetMysqlRes(obj);
|
@@ -1922,7 +2219,7 @@ static VALUE stmt_result_metadata(VALUE obj)
|
|
1922
2219
|
mysql_stmt_raise(s->stmt);
|
1923
2220
|
return Qnil;
|
1924
2221
|
}
|
1925
|
-
return mysqlres2obj(res);
|
2222
|
+
return mysqlres2obj(res, Qfalse);
|
1926
2223
|
}
|
1927
2224
|
|
1928
2225
|
/* row_seek(offset) */
|
@@ -2180,12 +2477,22 @@ void Init_mysql(void)
|
|
2180
2477
|
rb_define_method(cMysql, "query", query, 1);
|
2181
2478
|
rb_define_method(cMysql, "real_query", query, 1);
|
2182
2479
|
rb_define_method(cMysql, "c_async_query", async_query, -1);
|
2480
|
+
rb_define_method(cMysql, "async_in_progress?", async_in_progress, 0);
|
2481
|
+
rb_define_method(cMysql, "async_in_progress=", async_in_progress_set, 1);
|
2183
2482
|
rb_define_method(cMysql, "send_query", send_query, 1);
|
2483
|
+
rb_define_method(cMysql, "simulate_disconnect", simulate_disconnect, 0);
|
2484
|
+
rb_define_method(cMysql, "reconnected?", reconnected, 0);
|
2184
2485
|
rb_define_method(cMysql, "get_result", get_result, 0);
|
2185
2486
|
rb_define_method(cMysql, "readable?", readable, -1);
|
2487
|
+
rb_define_method(cMysql, "retry?", retry, 0);
|
2488
|
+
rb_define_method(cMysql, "interrupted?", interrupted, 0);
|
2186
2489
|
rb_define_method(cMysql, "blocking?", blocking, 0);
|
2490
|
+
rb_define_method(cMysql, "gc_disabled?", gc_disabled, 0);
|
2491
|
+
rb_define_method(cMysql, "disable_gc=", disable_gc_set, 1);
|
2492
|
+
rb_define_method(cMysql, "busy?", is_busy, 0);
|
2493
|
+
rb_define_method(cMysql, "idle?", is_idle, 0);
|
2494
|
+
rb_define_method(cMysql, "busy=", busy_set, 1);
|
2187
2495
|
rb_define_method(cMysql, "socket", socket, 0);
|
2188
|
-
rb_define_method(cMysql, "socket_type", socket_type, 0);
|
2189
2496
|
rb_define_method(cMysql, "refresh", refresh, 1);
|
2190
2497
|
rb_define_method(cMysql, "reload", reload, 0);
|
2191
2498
|
rb_define_method(cMysql, "select_db", select_db, 1);
|
data/lib/mysqlplus.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
require 'mysql' #
|
1
|
+
require File.dirname(__FILE__) + '/mysql' # load our version of mysql--note
|
2
|
+
# if someone does a require 'mysql' after a require 'mysqlplus' then their screen will be littered with warnings
|
3
|
+
# and the "old" mysql will override the "new" mysqlplus, so be careful.
|
2
4
|
|
3
5
|
#
|
4
|
-
#
|
5
|
-
# See http://www.kitebird.com/articles/ruby-mysql.html for details, as well as the test directory within the
|
6
|
+
# The mysqlplus library is a [slightly updated] fork of the Mysql class, with asynchronous capability added
|
7
|
+
# See http://www.kitebird.com/articles/ruby-mysql.html for details, as well as the test directory within the gem
|
6
8
|
#
|
7
9
|
class Mysql
|
8
10
|
|
@@ -15,7 +17,7 @@ class Mysql
|
|
15
17
|
begin
|
16
18
|
alias_method :async_query, :c_async_query
|
17
19
|
rescue NameError => e
|
18
|
-
raise LoadError.new
|
20
|
+
raise LoadError.new("error loading mysqlplus--this may mean you ran a require 'mysql' before a require 'mysqplus', which must come first -- possibly also run gem uninstall mysql")
|
19
21
|
end
|
20
22
|
|
21
23
|
end
|
data/mysqlplus.gemspec
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "mysqlplus"
|
3
|
-
s.version = "0.1.
|
3
|
+
s.version = "0.1.2"
|
4
4
|
s.date = "2009-03-22"
|
5
5
|
s.summary = "Enhanced Ruby MySQL driver"
|
6
6
|
s.email = "oldmoe@gmail.com"
|
7
7
|
s.homepage = "http://github.com/oldmoe/mysqlplus"
|
8
8
|
s.description = "Enhanced Ruby MySQL driver"
|
9
9
|
s.has_rdoc = true
|
10
|
-
s.authors = ["Muhammad A. Ali"]
|
10
|
+
s.authors = ["Muhammad A. Ali et al"]
|
11
11
|
s.platform = Gem::Platform::RUBY
|
12
12
|
s.files = %w[
|
13
13
|
README
|
@@ -18,18 +18,13 @@ Gem::Specification.new do |s|
|
|
18
18
|
ext/mysql.c
|
19
19
|
lib/mysqlplus.rb
|
20
20
|
mysqlplus.gemspec
|
21
|
-
|
22
|
-
test/evented_test.rb
|
23
|
-
test/native_threaded_test.rb
|
24
|
-
test/test_all_hashes.rb
|
25
|
-
test/test_failure.rb
|
26
|
-
test/test_helper.rb
|
27
|
-
test/test_many_requests.rb
|
28
|
-
test/test_parsing_while_response_is_being_read.rb
|
29
|
-
test/test_threaded_sequel.rb
|
30
|
-
]
|
21
|
+
] + Dir.glob('test/*')
|
31
22
|
s.rdoc_options = ["--main", "README"]
|
32
23
|
s.extra_rdoc_files = ["README"]
|
33
24
|
s.extensions << "ext/extconf.rb"
|
34
|
-
end
|
35
25
|
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# I suppose if all the tests don't blow up, that probably means pass
|
2
|
+
require 'mysqlplus'
|
3
|
+
for file in Dir.glob('*_test.rb') do
|
4
|
+
puts 'testing ' + file
|
5
|
+
# fork so we don't run out of connections to the mysql db, as few tests ever clean up their old processes
|
6
|
+
pid = Process.fork { load file }
|
7
|
+
Process.wait(pid)
|
8
|
+
end
|
9
|
+
puts 'successful'
|
@@ -1,9 +1,4 @@
|
|
1
|
-
|
2
|
-
# run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top
|
3
|
-
# note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes]
|
4
|
-
# on my machine and a 200_000 row table, it took 3.38s versus 3.65s
|
5
|
-
require 'rubygems'
|
6
|
-
require 'mysqlplus'
|
1
|
+
require 'create_test_db'
|
7
2
|
|
8
3
|
use_the_all_hashes_method = true
|
9
4
|
|
@@ -13,16 +8,15 @@ $start = Time.now
|
|
13
8
|
|
14
9
|
$connections = []
|
15
10
|
$count.times do
|
16
|
-
$connections << Mysql.real_connect('localhost','root', '', '
|
11
|
+
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
17
12
|
end
|
18
13
|
|
19
|
-
puts 'connection pool ready'
|
20
14
|
|
21
15
|
$threads = []
|
22
16
|
$count.times do |i|
|
23
17
|
$threads << Thread.new do
|
24
18
|
|
25
|
-
query = "select * from
|
19
|
+
query = "select * from test_table"
|
26
20
|
puts "sending query on connection #{i}"
|
27
21
|
conn = $connections[i]
|
28
22
|
result = conn.async_query(query)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# If this script returns without the word pass
|
2
|
+
# you may have compiled mysqlplus using ruby and
|
3
|
+
# run it using a different version of ruby
|
4
|
+
|
5
|
+
if RUBY_VERSION >= "1.9.1"
|
6
|
+
require 'mysqlplus'
|
7
|
+
require 'socket'
|
8
|
+
require 'timeout'
|
9
|
+
TCPServer.new '0.0.0.0', 8002
|
10
|
+
Thread.new {
|
11
|
+
sleep 2
|
12
|
+
print "pass"
|
13
|
+
system("kill -9 #{Process.pid}")
|
14
|
+
}
|
15
|
+
Timeout::timeout(1) {
|
16
|
+
# uncomment this line to do the 'real' test
|
17
|
+
# which hangs otherwise (blows up if code is bad, otherwise hangs)
|
18
|
+
Mysql.real_connect '127.0.0.1', 'root', 'pass', 'db', 8002
|
19
|
+
}
|
20
|
+
raise 'should never get here'
|
21
|
+
end
|
22
|
+
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'mysqlplus'
|
3
2
|
begin
|
4
3
|
Mysql.real_connect('fakehost','root', '', 'local_leadgen_dev')
|
@@ -12,6 +11,7 @@ begin
|
|
12
11
|
Mysql.real_connect('localhost', 'root', 'pass', 'db', 3307)# bad port
|
13
12
|
rescue Mysql::Error
|
14
13
|
end
|
14
|
+
|
15
15
|
print "pass"
|
16
16
|
|
17
17
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# To run first execute:
|
2
|
+
=begin
|
3
|
+
create database local_test_db;
|
4
|
+
use local_test_db;
|
5
|
+
CREATE TABLE test_table (
|
6
|
+
c1 INT,
|
7
|
+
c2 VARCHAR(20)
|
8
|
+
);
|
9
|
+
=end
|
10
|
+
# This script shows the effect of using .all_hashes instead of looping on each hash
|
11
|
+
# run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top
|
12
|
+
# note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes]
|
13
|
+
# on my machine and a 200_000 row table, it took 3.38s versus 3.65s for the old .each_hash way [note also that .each_hash is
|
14
|
+
# almost as fast, now, as .all_hashes--they've both been optimized]
|
15
|
+
require 'mysqlplus'
|
16
|
+
|
17
|
+
puts 'initing db'
|
18
|
+
# init the DB
|
19
|
+
conn = Mysql.real_connect('localhost', 'root', '', 'local_test_db')
|
20
|
+
conn.query("delete from test_table")
|
21
|
+
200_000.times {conn.query(" insert into test_table (c1, c2) values (3, 'ABCDEFG')")}
|
22
|
+
puts 'connection pool ready'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mysqlplus'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
with_gc = Mysql.real_connect('localhost','root','','mysql')
|
5
|
+
without_gc = Mysql.real_connect('localhost','root','','mysql')
|
6
|
+
without_gc.disable_gc = true
|
7
|
+
|
8
|
+
$gc_stats = []
|
9
|
+
|
10
|
+
def countable_gc?
|
11
|
+
GC.respond_to? :count
|
12
|
+
end
|
13
|
+
|
14
|
+
def gc_counts( label, scope )
|
15
|
+
$gc_stats << "Objects #{scope} ( #{label} ) #{GC.count}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_gc_counts( label )
|
19
|
+
gc_counts( label, 'before' ) if countable_gc?
|
20
|
+
yield
|
21
|
+
gc_counts( label, 'after' ) if countable_gc?
|
22
|
+
end
|
23
|
+
|
24
|
+
n = 1000
|
25
|
+
|
26
|
+
Benchmark.bmbm do |x|
|
27
|
+
x.report( 'With GC' ) do
|
28
|
+
with_gc_counts( 'With GC' ) do
|
29
|
+
n.times{ with_gc.c_async_query( 'SELECT * FROM user' ) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
GC.start
|
33
|
+
x.report( 'Without GC' ) do
|
34
|
+
with_gc_counts( 'Without GC' ) do
|
35
|
+
n.times{ without_gc.c_async_query( 'SELECT * FROM user' ) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
puts $gc_stats.join( ' | ' )
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
m = Mysql.real_connect('localhost','root')
|
4
|
+
m.reconnect = true
|
5
|
+
$count = 0
|
6
|
+
class << m
|
7
|
+
def safe_query( query )
|
8
|
+
begin
|
9
|
+
send_query( query )
|
10
|
+
rescue => e
|
11
|
+
$count += 1
|
12
|
+
puts e.message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
m.safe_query( 'select sleep(1)' )
|
19
|
+
m.safe_query( 'select sleep(1)' )#raises
|
20
|
+
m.simulate_disconnect #fires mysql_library_end
|
21
|
+
m.safe_query( 'select sleep(1)' )
|
22
|
+
m.safe_query( 'select sleep(1)' )#raises
|
23
|
+
m.close
|
24
|
+
m.connect('localhost','root')
|
25
|
+
m.safe_query( 'select sleep(1)' )
|
26
|
+
m.safe_query( 'select sleep(1)' )#raises
|
27
|
+
m.simulate_disconnect
|
28
|
+
raise unless $count == 3
|
29
|
+
m.safe_query( 'BEGIN' )
|
30
|
+
m.safe_query( 'select sleep(1)' ) # raises
|
31
|
+
m.get_result()
|
32
|
+
m.safe_query( 'COMMIT' )
|
33
|
+
m.get_result
|
34
|
+
raise unless $count == 4
|
@@ -5,8 +5,7 @@
|
|
5
5
|
# from .82s to .62s
|
6
6
|
# you can experiment with it by changing the query here to be a long one, and toggling the do_the_use_query_optimization variable
|
7
7
|
# this also has the interesting property of 'freeing' Ruby to do thread changes mid-query.
|
8
|
-
require '
|
9
|
-
require 'mysqlplus'
|
8
|
+
require 'create_test_db'
|
10
9
|
|
11
10
|
do_the_use_query_optimization = true
|
12
11
|
|
@@ -16,7 +15,7 @@ $start = Time.now
|
|
16
15
|
|
17
16
|
$connections = []
|
18
17
|
$count.times do
|
19
|
-
$connections << Mysql.real_connect('localhost','root', '', '
|
18
|
+
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
20
19
|
end
|
21
20
|
|
22
21
|
puts 'connection pool ready'
|
@@ -28,7 +27,7 @@ $count.times do |i|
|
|
28
27
|
puts "sending query on connection #{i}"
|
29
28
|
conn = $connections[i]
|
30
29
|
saved = []
|
31
|
-
query = "select * from
|
30
|
+
query = "select * from test_table"
|
32
31
|
if do_the_use_query_optimization
|
33
32
|
conn.query_with_result=false
|
34
33
|
result = conn.async_query(query)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
$m = Mysql.real_connect('localhost','root')
|
4
|
+
#$m.reconnect = true
|
5
|
+
|
6
|
+
def assert_reconnected
|
7
|
+
puts $m.reconnected?().inspect
|
8
|
+
sleep 1
|
9
|
+
yield
|
10
|
+
puts $m.reconnected?().inspect
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_reconnected do
|
14
|
+
$m.simulate_disconnect
|
15
|
+
end
|
16
|
+
assert_reconnected do
|
17
|
+
$m.close
|
18
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
require 'mysqlplus'
|
1
2
|
require 'rubygems'
|
2
3
|
require 'sequel'
|
3
4
|
|
4
|
-
require 'mysqlplus'
|
5
5
|
class Mysql
|
6
6
|
unless method_defined? :sync_query
|
7
7
|
alias :sync_query :query
|
@@ -21,4 +21,4 @@ start = Time.now
|
|
21
21
|
end
|
22
22
|
end.map{|t| t.join }
|
23
23
|
|
24
|
-
p (Time.now - start)
|
24
|
+
p (Time.now - start)
|
metadata
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysqlplus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
version: 0.1.2
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
|
-
- Muhammad A. Ali
|
12
|
+
- Muhammad A. Ali et al
|
8
13
|
autorequire:
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2009-03-22 00:00:00 -
|
17
|
+
date: 2009-03-22 00:00:00 -06:00
|
13
18
|
default_executable:
|
14
19
|
dependencies: []
|
15
20
|
|
@@ -30,17 +35,26 @@ files:
|
|
30
35
|
- ext/mysql.c
|
31
36
|
- lib/mysqlplus.rb
|
32
37
|
- mysqlplus.gemspec
|
38
|
+
- test/all_hashes_test.rb
|
39
|
+
- test/async_query_with_block_test.rb
|
40
|
+
- test/connect_failure2_test.rb
|
41
|
+
- test/connect_failure_test.rb
|
42
|
+
- test/create_test_db.rb
|
33
43
|
- test/c_threaded_test.rb
|
34
44
|
- test/evented_test.rb
|
45
|
+
- test/gc_benchmark_test.rb
|
46
|
+
- test/many_requests_test.rb
|
35
47
|
- test/native_threaded_test.rb
|
36
|
-
- test/
|
37
|
-
- test/
|
48
|
+
- test/out_of_sync_test.rb
|
49
|
+
- test/query_with_result_false_test.rb
|
50
|
+
- test/reconnected_test.rb
|
51
|
+
- test/RUN_ALL_TESTS.RB
|
38
52
|
- test/test_helper.rb
|
39
|
-
- test/
|
40
|
-
- test/test_parsing_while_response_is_being_read.rb
|
41
|
-
- test/test_threaded_sequel.rb
|
53
|
+
- test/threaded_sequel_test.rb
|
42
54
|
has_rdoc: true
|
43
55
|
homepage: http://github.com/oldmoe/mysqlplus
|
56
|
+
licenses: []
|
57
|
+
|
44
58
|
post_install_message:
|
45
59
|
rdoc_options:
|
46
60
|
- --main
|
@@ -51,20 +65,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
65
|
requirements:
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
54
70
|
version: "0"
|
55
|
-
version:
|
56
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
72
|
requirements:
|
58
73
|
- - ">="
|
59
74
|
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
60
77
|
version: "0"
|
61
|
-
version:
|
62
78
|
requirements: []
|
63
79
|
|
64
80
|
rubyforge_project:
|
65
|
-
rubygems_version: 1.3.
|
81
|
+
rubygems_version: 1.3.6
|
66
82
|
signing_key:
|
67
|
-
specification_version:
|
83
|
+
specification_version: 3
|
68
84
|
summary: Enhanced Ruby MySQL driver
|
69
85
|
test_files: []
|
70
86
|
|