mysqlplus 0.1.1 → 0.1.2
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/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
|
|