Compare commits

..

No commits in common. "master" and "with_async_validation" have entirely different histories.

18 changed files with 702 additions and 1028 deletions

17
README
View File

@ -2,8 +2,6 @@
An enhanced MySQL database driver. With support for async operations and threaded database access. An enhanced MySQL database driver. With support for async operations and threaded database access.
Added Encoding awareness for Ruby 1.9. Convert all data to default external encoding ( merged from http://github.com/lsegal/mysql-ruby )
== Building == Building
gem build mysqlplus.gemspec gem build mysqlplus.gemspec
@ -22,18 +20,6 @@ Added Encoding awareness for Ruby 1.9. Convert all data to default external enc
--with-mysql-dir=/usr/local/mysql \ --with-mysql-dir=/usr/local/mysql \
--with-mysql-lib=/usr/local/mysql/lib \ --with-mysql-lib=/usr/local/mysql/lib \
--with-mysql-include=/usr/local/mysql/include --with-mysql-include=/usr/local/mysql/include
== Using
to use within rails
add require 'mysqlplus' to the top of environment.rb
this instantiates the Mysql class that you can use.
and it will automagically use async_query instead of query, so it's a drop in replacement.
Same with other scripts that want to use it--just require 'mysqlplus' BEFORE you require 'mysql' and it will
load the asynchronous version, then ignore the sequent require 'mysql' call.
== Other helpful mysql utilities:
slim attributes http://slim-attributes.rubyforge.org/ boosts mysql speed by using arrays instead of hashed lookup.
Hash extension gem also results in speedups when used: http://blog.chak.org/2008/02/09/speeding-up-activerecord-with-hashes-take-2/
=== Credits === Credits
@ -44,6 +30,3 @@ Lourens Naude for 1.9 integration help.
=== License === License
Ruby License, http://www.ruby-lang.org/en/LICENSE.txt. Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
== Mailing list
http://groups.google.com/group/never-block?hl=en

View File

@ -1,12 +0,0 @@
TODO list:
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?
Some of the tests currently might "think" they are using the ruby select but in reality be using the C select.
gc_disabled is unused
if they call get_result twice consecutively it should blow (and maybe already does).
mingw support
add slim attributes right in there :)

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +1,22 @@
require 'mkmf' require 'mkmf'
def exec_command(command, flag_raise=false)
output = `#{command}`
return output.chomp if $? == 0
msg = "failed: #{command}"
raise msg if flag_raise
die msg
end
def die(message)
$stderr.puts "*** ERROR: #{message}"
exit 1
end
if /mswin32/ =~ RUBY_PLATFORM if /mswin32/ =~ RUBY_PLATFORM
inc, lib = dir_config('mysql') inc, lib = dir_config('mysql')
#exit 1 unless have_library("libmysql") exit 1 unless have_library("libmysql")
have_library("libmysql") or die "can't find libmysql."
elsif mc = with_config('mysql-config') then elsif mc = with_config('mysql-config') then
mc = 'mysql_config' if mc == true mc = 'mysql_config' if mc == true
#cflags = `#{mc} --cflags`.chomp cflags = `#{mc} --cflags`.chomp
#exit 1 if $? != 0 exit 1 if $? != 0
cflags = exec_command("#{mc} --cflags") libs = `#{mc} --libs`.chomp
#libs = `#{mc} --libs`.chomp exit 1 if $? != 0
#exit 1 if $? != 0
libs = exec_command("#{mc} --libs")
$CPPFLAGS += ' ' + cflags $CPPFLAGS += ' ' + cflags
$libs = libs + " " + $libs $libs = libs + " " + $libs
else else
puts "Trying to detect MySQL configuration with mysql_config command..." inc, lib = dir_config('mysql', '/usr/local')
begin libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
cflags = libs = nil while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
exit 1 if libs.empty?
dirs = ENV['PATH'].split(':') + %w[ have_library(libs.shift)
/opt
/opt/local
/opt/local/mysql
/opt/local/lib/mysql5
/usr
/usr/local
/usr/local/mysql
/usr/local/mysql-*
/usr/local/lib/mysql5
].map{|dir| "#{dir}/bin" }
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
if /mswin32/ =~ RUBY_PLATFORM
inc, lib = dir_config('mysql')
exit 1 unless have_library("libmysql")
elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
mc = Dir[GLOB].first if mc == true
puts "Succeeded to detect MySQL configuration: #{mc}"
cflags = `#{mc} --cflags`.chomp
exit 1 if $? != 0
libs = `#{mc} --libs`.chomp
exit 1 if $? != 0
$CPPFLAGS += ' ' + cflags
$libs = libs + " " + $libs
else
puts "Failed to detect MySQL configuration with mysql_config command."
puts "Trying to detect MySQL client library..."
inc, lib = dir_config('mysql', '/usr/local')
libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
#exit 1 if libs.empty?
!libs.empty? or die "can't find mysql client library."
have_library(libs.shift)
end
end
end end
end end
@ -81,14 +28,11 @@ if have_header('mysql.h') then
elsif have_header('mysql/mysql.h') then elsif have_header('mysql/mysql.h') then
src = "#include <mysql/errmsg.h>\n#include <mysql/mysqld_error.h>\n" src = "#include <mysql/errmsg.h>\n#include <mysql/mysqld_error.h>\n"
else else
#exit 1 exit 1
die "can't find 'mysql.h'."
end end
# check for 1.9 if have_func('rb_thread_blocking_region') and have_macro('RB_UBF_DFL', 'ruby.h')
if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h') cflags << "-DHAVE_TBR"
$CFLAGS += " -DHAVE_TBR "
$CPPFLAGS << " -DHAVE_TBR "
end end
# make mysql constant # make mysql constant
@ -103,10 +47,9 @@ end
if /mswin32/ =~ RUBY_PLATFORM && !/-E/.match(cpp) if /mswin32/ =~ RUBY_PLATFORM && !/-E/.match(cpp)
cpp << " -E" cpp << " -E"
end end
#unless system "#{cpp} > confout" then unless system "#{cpp} > confout" then
# exit 1 exit 1
#end end
exec_command("#{cpp} > confout")
File.unlink "conftest.c" File.unlink "conftest.c"
error_syms = [] error_syms = []
@ -128,10 +71,4 @@ File.open('error_const.h', 'w') do |f|
end end
end end
$CPPFLAGS += " -DRUBY19" if RUBY_VERSION =~ /1.9/ create_makefile("mysql")
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
$LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
end
create_makefile("mysql")

View File

@ -6,49 +6,16 @@
#include <ruby.h> #include <ruby.h>
#include <errno.h> #include <errno.h>
#include <stdarg.h>
#ifndef RSTRING_PTR #ifndef RSTRING_PTR
#define RSTRING_PTR(str) RSTRING(str)->ptr #define RSTRING_PTR(str) RSTRING(str)->ptr
#endif #endif
#ifndef RSTRING_LEN #ifndef RSTRING_LEN
#define RSTRING_LEN(str) RSTRING(str)->len #define RSTRING_LEN(str) RSTRING(str)->len
#endif #endif
#ifndef RARRAY_PTR
#define RARRAY_PTR(ary) RARRAY(ary)->ptr
#endif
#ifndef HAVE_RB_STR_SET_LEN #ifndef HAVE_RB_STR_SET_LEN
#define rb_str_set_len(str, length) (RSTRING_LEN(str) = (length)) #define rb_str_set_len(str, length) (RSTRING_LEN(str) = (length))
#endif #endif
#ifdef RUBY19
#include <ruby/encoding.h>
#define DEFAULT_ENCODING (rb_enc_get(rb_enc_default_external()))
#else
#define DEFAULT_ENCODING NULL
#define rb_enc_str_new(ptr, len, enc) rb_str_new(ptr, len)
#endif
VALUE
rb_enc_tainted_str_new(const char *ptr, long len)
{
VALUE str = rb_enc_str_new(ptr, len, DEFAULT_ENCODING);
OBJ_TAINT(str);
return str;
}
VALUE
rb_enc_tainted_str_new2(const char *ptr)
{
VALUE str = rb_enc_str_new(ptr, strlen(ptr), DEFAULT_ENCODING);
OBJ_TAINT(str);
return str;
}
#ifdef HAVE_MYSQL_H #ifdef HAVE_MYSQL_H
#include <mysql.h> #include <mysql.h>
#include <mysql_com.h> #include <mysql_com.h>
@ -99,7 +66,6 @@ struct mysql {
char busy; char busy;
}; };
// a wrapper for mysql_res's so we can detect double frees
struct mysql_res { struct mysql_res {
MYSQL_RES* res; MYSQL_RES* res;
char freed; char freed;
@ -212,7 +178,7 @@ static void mysql_raise(MYSQL* m)
VALUE e = rb_exc_new2(eMysql, mysql_error(m)); VALUE e = rb_exc_new2(eMysql, mysql_error(m));
rb_iv_set(e, "errno", INT2FIX(mysql_errno(m))); rb_iv_set(e, "errno", INT2FIX(mysql_errno(m)));
#if MYSQL_VERSION_ID >= 40101 #if MYSQL_VERSION_ID >= 40101
rb_iv_set(e, "sqlstate", rb_enc_tainted_str_new2(mysql_sqlstate(m))); rb_iv_set(e, "sqlstate", rb_tainted_str_new2(mysql_sqlstate(m)));
#endif #endif
rb_exc_raise(e); rb_exc_raise(e);
} }
@ -227,10 +193,9 @@ static VALUE mysqlres2obj(MYSQL_RES* res, VALUE gc_disabled)
resp->res = res; resp->res = res;
resp->freed = Qfalse; resp->freed = Qfalse;
rb_obj_call_init(obj, 0, NULL); rb_obj_call_init(obj, 0, NULL);
/* disabled until it can be reviewed further--rely on the normal GC for now. if (++store_result_count > GC_STORE_RESULT_LIMIT && gc_disabled == Qfalse){
if (++store_result_count > GC_STORE_RESULT_LIMIT) rb_gc();
rb_gc(); }
*/
return obj; return obj;
} }
@ -241,9 +206,9 @@ static VALUE make_field_obj(MYSQL_FIELD* f)
if (f == NULL) if (f == NULL)
return Qnil; return Qnil;
obj = rb_obj_alloc(cMysqlField); obj = rb_obj_alloc(cMysqlField);
rb_iv_set(obj, "name", f->name? rb_str_freeze(rb_enc_tainted_str_new2(f->name)): Qnil); rb_iv_set(obj, "name", f->name? rb_str_freeze(rb_tainted_str_new2(f->name)): Qnil);
rb_iv_set(obj, "table", f->table? rb_str_freeze(rb_enc_tainted_str_new2(f->table)): Qnil); rb_iv_set(obj, "table", f->table? rb_str_freeze(rb_tainted_str_new2(f->table)): Qnil);
rb_iv_set(obj, "def", f->def? rb_str_freeze(rb_enc_tainted_str_new2(f->def)): Qnil); rb_iv_set(obj, "def", f->def? rb_str_freeze(rb_tainted_str_new2(f->def)): Qnil);
rb_iv_set(obj, "type", INT2NUM(f->type)); rb_iv_set(obj, "type", INT2NUM(f->type));
rb_iv_set(obj, "length", INT2NUM(f->length)); rb_iv_set(obj, "length", INT2NUM(f->length));
rb_iv_set(obj, "max_length", INT2NUM(f->max_length)); rb_iv_set(obj, "max_length", INT2NUM(f->max_length));
@ -266,81 +231,11 @@ static VALUE init(VALUE klass)
mysql_init(&myp->handler); mysql_init(&myp->handler);
myp->connection = Qfalse; myp->connection = Qfalse;
myp->query_with_result = Qtrue; myp->query_with_result = Qtrue;
myp->gc_disabled = Qtrue; myp->gc_disabled = Qfalse;
rb_obj_call_init(obj, 0, NULL); rb_obj_call_init(obj, 0, NULL);
return obj; return obj;
} }
// =========== a 1.9 rb_thread_blocking_region simplifier attempt
#ifdef HAVE_TBR
typedef struct
{
void *func_pointer;
int param_count;
void *args[10];
} arg_holder, *arg_holder2;
// here's how to make rb_thread_blocking_region much cleaner and easier
// syntax: param_count+2, func_pointer to call, [RUBY_UBF_IO or RUBY_UBF_PROCESS], param1, param2...
// 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
// ex: (int) returned_this = rb_thread_blocking_region_variable_params(10, &method_name, RUBY_UBF_IO, param1, param2, param3, param4, param5, param6, param7, param8)
static void *call_single_function_rb_thread_blocking_region(void *arg_holder_in);
void *rb_thread_blocking_region_variable_params(int number, ...)
{
va_list param_pt;
va_start(param_pt, number);
int index;
arg_holder param_storer;
void *func_pointer = va_arg(param_pt, void *);
void *interrupter = va_arg(param_pt, void *);
param_storer.func_pointer = func_pointer;
int real_param_count = number - 2;
param_storer.param_count = real_param_count;
for(index = 0 ; index < real_param_count ; index++)
{
void *arg = va_arg(param_pt, void *);
param_storer.args[index] = arg;
}
va_end(param_pt);
return (void *) rb_thread_blocking_region((rb_blocking_function_t *)call_single_function_rb_thread_blocking_region, (void *) &param_storer, interrupter, 0);
}
// used internally
static void * call_single_function_rb_thread_blocking_region(void *arg_holder_in)
{
arg_holder *params_and_func = (arg_holder *) arg_holder_in;
int param_count = params_and_func->param_count;
void *result;
switch(param_count)
{
case 3:;
void * (*pt3Func)(void *, void *, void *) = params_and_func->func_pointer;
result = (*pt3Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2]);
break;
case 6:;
void * (*pt6Func)(void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer;
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]);
break;
case 8:;
void * (*pt8Func)(void *, void *, void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer;
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]);
break;
default:;
printf("UNknown param count--please add it! %d\n", param_count);
result = (void *) Qnil;
}
return result;
}
#endif
static VALUE connection_identifier( VALUE obj ) static VALUE connection_identifier( VALUE obj )
{ {
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
@ -360,7 +255,6 @@ static VALUE async_in_progress_set( VALUE obj, VALUE flag )
return flag; return flag;
} }
// does this actually really do anything helpful? Not sure.
static void optimize_for_async( VALUE obj ) static void optimize_for_async( VALUE obj )
{ {
struct mysql* m = GetMysqlStruct(obj); struct mysql* m = GetMysqlStruct(obj);
@ -372,19 +266,16 @@ static void optimize_for_async( VALUE obj )
async_in_progress_set( obj, Qfalse ); async_in_progress_set( obj, Qfalse );
} }
// TODO does nothing currently
static void schedule_connect(VALUE obj ) static void schedule_connect(VALUE obj )
{ {
/* TODO is this old?
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
fd_set read; fd_set read;
struct timeval tv = { tv_sec: m->options.connect_timeout, tv_usec: 0 }; struct timeval tv = { tv_sec: m->options.connect_timeout, tv_usec: 0 };
if (rb_thread_select(0, NULL, NULL, NULL, &tv) < 0) { if (rb_thread_select(0, NULL, NULL, NULL, &tv) < 0) {
rb_raise(eMysql, "connect: timeout"); rb_raise(eMysql, "connect: timeout");
} }
*/
/* /*
FD_ZERO(&read); FD_ZERO(&read);
FD_SET(m->net.fd, &read); FD_SET(m->net.fd, &read);
@ -422,12 +313,8 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
obj = Data_Make_Struct(klass, struct mysql, 0, free_mysql, myp); obj = Data_Make_Struct(klass, struct mysql, 0, free_mysql, myp);
#if MYSQL_VERSION_ID >= 32200 #if MYSQL_VERSION_ID >= 32200
mysql_init(&myp->handler); /* we get here */ mysql_init(&myp->handler);
# ifdef HAVE_TBR if (mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL)
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)
# else
if(mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL)
# endif
#elif MYSQL_VERSION_ID >= 32115 #elif MYSQL_VERSION_ID >= 32115
if (mysql_real_connect(&myp->handler, h, u, p, pp, s, f) == NULL) if (mysql_real_connect(&myp->handler, h, u, p, pp, s, f) == NULL)
#else #else
@ -443,7 +330,7 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
myp->query_with_result = Qtrue; myp->query_with_result = Qtrue;
rb_obj_call_init(obj, argc, argv); rb_obj_call_init(obj, argc, argv);
//schedule_connect(obj); schedule_connect(obj);
return obj; return obj;
} }
@ -453,7 +340,7 @@ static VALUE escape_string(VALUE klass, VALUE str)
{ {
VALUE ret; VALUE ret;
Check_Type(str, T_STRING); Check_Type(str, T_STRING);
ret = rb_enc_str_new(0, (RSTRING_LEN(str))*2+1, DEFAULT_ENCODING); ret = rb_str_new(0, (RSTRING_LEN(str))*2+1);
rb_str_set_len(ret, mysql_escape_string(RSTRING_PTR(ret), RSTRING_PTR(str), RSTRING_LEN(str))); rb_str_set_len(ret, mysql_escape_string(RSTRING_PTR(ret), RSTRING_PTR(str), RSTRING_LEN(str)));
return ret; return ret;
} }
@ -461,7 +348,7 @@ static VALUE escape_string(VALUE klass, VALUE str)
/* client_info() */ /* client_info() */
static VALUE client_info(VALUE klass) static VALUE client_info(VALUE klass)
{ {
return rb_enc_tainted_str_new2(mysql_get_client_info()); return rb_tainted_str_new2(mysql_get_client_info());
} }
#if MYSQL_VERSION_ID >= 32332 #if MYSQL_VERSION_ID >= 32332
@ -508,7 +395,7 @@ static VALUE real_connect2(int argc, VALUE* argv, VALUE obj)
GetMysqlStruct(obj)->connection = Qtrue; GetMysqlStruct(obj)->connection = Qtrue;
optimize_for_async(obj); optimize_for_async(obj);
//schedule_connect(obj); schedule_connect(obj);
return obj; return obj;
} }
@ -590,7 +477,7 @@ static VALUE real_escape_string(VALUE obj, VALUE str)
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
VALUE ret; VALUE ret;
Check_Type(str, T_STRING); Check_Type(str, T_STRING);
ret = rb_enc_str_new(0, (RSTRING_LEN(str))*2+1, DEFAULT_ENCODING); ret = rb_str_new(0, (RSTRING_LEN(str))*2+1);
rb_str_set_len(ret, mysql_real_escape_string(m, RSTRING_PTR(ret), RSTRING_PTR(str), RSTRING_LEN(str))); rb_str_set_len(ret, mysql_real_escape_string(m, RSTRING_PTR(ret), RSTRING_PTR(str), RSTRING_LEN(str)));
return ret; return ret;
} }
@ -629,7 +516,7 @@ static VALUE change_user(int argc, VALUE* argv, VALUE obj)
/* character_set_name() */ /* character_set_name() */
static VALUE character_set_name(VALUE obj) static VALUE character_set_name(VALUE obj)
{ {
return rb_enc_tainted_str_new2(mysql_character_set_name(GetHandler(obj))); return rb_tainted_str_new2(mysql_character_set_name(GetHandler(obj)));
} }
#endif #endif
@ -694,7 +581,7 @@ static VALUE field_count(VALUE obj)
/* host_info() */ /* host_info() */
static VALUE host_info(VALUE obj) static VALUE host_info(VALUE obj)
{ {
return rb_enc_tainted_str_new2(mysql_get_host_info(GetHandler(obj))); return rb_tainted_str_new2(mysql_get_host_info(GetHandler(obj)));
} }
/* proto_info() */ /* proto_info() */
@ -706,14 +593,14 @@ static VALUE proto_info(VALUE obj)
/* server_info() */ /* server_info() */
static VALUE server_info(VALUE obj) static VALUE server_info(VALUE obj)
{ {
return rb_enc_tainted_str_new2(mysql_get_server_info(GetHandler(obj))); return rb_tainted_str_new2(mysql_get_server_info(GetHandler(obj)));
} }
/* info() */ /* info() */
static VALUE info(VALUE obj) static VALUE info(VALUE obj)
{ {
const char* p = mysql_info(GetHandler(obj)); const char* p = mysql_info(GetHandler(obj));
return p? rb_enc_tainted_str_new2(p): Qnil; return p? rb_tainted_str_new2(p): Qnil;
} }
/* insert_id() */ /* insert_id() */
@ -748,7 +635,7 @@ static VALUE list_dbs(int argc, VALUE* argv, VALUE obj)
n = mysql_num_rows(res); n = mysql_num_rows(res);
ret = rb_ary_new2(n); ret = rb_ary_new2(n);
for (i=0; i<n; i++) for (i=0; i<n; i++)
rb_ary_store(ret, i, rb_enc_tainted_str_new2(mysql_fetch_row(res)[0])); rb_ary_store(ret, i, rb_tainted_str_new2(mysql_fetch_row(res)[0]));
mysql_free_result(res); mysql_free_result(res);
return ret; return ret;
} }
@ -793,7 +680,7 @@ static VALUE list_tables(int argc, VALUE* argv, VALUE obj)
n = mysql_num_rows(res); n = mysql_num_rows(res);
ret = rb_ary_new2(n); ret = rb_ary_new2(n);
for (i=0; i<n; i++) for (i=0; i<n; i++)
rb_ary_store(ret, i, rb_enc_tainted_str_new2(mysql_fetch_row(res)[0])); rb_ary_store(ret, i, rb_tainted_str_new2(mysql_fetch_row(res)[0]));
mysql_free_result(res); mysql_free_result(res);
return ret; return ret;
} }
@ -857,42 +744,16 @@ static VALUE my_stat(VALUE obj)
const char* s = mysql_stat(m); const char* s = mysql_stat(m);
if (s == NULL) if (s == NULL)
mysql_raise(m); mysql_raise(m);
return rb_enc_tainted_str_new2(s); return rb_tainted_str_new2(s);
}
// 1.9 friendly
typedef struct
{
MYSQL *mysql_instance;
MYSQL_RES **store_it_here;
} mysql_result_to_here_t,
*shared_stuff_p;
static VALUE store_result_to_location(void *settings_in)
{
mysql_result_to_here_t *settings = (mysql_result_to_here_t *) settings_in;
*(settings->store_it_here) = mysql_store_result(settings->mysql_instance); // this one line runs a good long while for very large queries
return Qnil;
} }
/* store_result() */ /* store_result() */
static VALUE store_result(VALUE obj) static VALUE store_result(VALUE obj)
{ {
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
MYSQL_RES* res = NULL; MYSQL_RES* res = mysql_store_result(m);
#ifndef HAVE_TBR
res = mysql_store_result(m);
#else
mysql_result_to_here_t linker;
linker.mysql_instance = m;
linker.store_it_here = &res;
rb_thread_blocking_region(store_result_to_location, (void *) &linker, RUBY_UBF_IO, 0);
#endif
if (res == NULL) if (res == NULL)
mysql_raise(m); mysql_raise(m);
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled); return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
} }
@ -913,43 +774,18 @@ static VALUE use_result(VALUE obj)
} }
static VALUE res_free(VALUE); static VALUE res_free(VALUE);
typedef struct {
MYSQL *m;
const char *data;
unsigned long len;
} QueryArgs;
static VALUE blocking_query(void *data)
{
QueryArgs *args = (QueryArgs *) data;
return (VALUE) mysql_real_query(args->m, args->data, args->len);
}
/* query(sql) */ /* query(sql) */
static VALUE query(VALUE obj, VALUE sql) static VALUE query(VALUE obj, VALUE sql)
{ {
int loop = 0; int loop = 0;
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
QueryArgs args;
int result;
Check_Type(sql, T_STRING); Check_Type(sql, T_STRING);
if (GetMysqlStruct(obj)->connection == Qfalse) { if (GetMysqlStruct(obj)->connection == Qfalse) {
rb_raise(eMysql, "query: not connected"); rb_raise(eMysql, "query: not connected");
} }
if (rb_block_given_p()) { if (rb_block_given_p()) {
#ifdef RUBY_VM if (mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
args.m = m;
args.data = RSTRING_PTR(sql);
args.len = RSTRING_LEN(sql);
result = (int) rb_thread_blocking_region(blocking_query, &args, RUBY_UBF_PROCESS, 0);
#else
result = mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql));
#endif
if (result != 0)
mysql_raise(m); mysql_raise(m);
do { do {
MYSQL_RES* res = mysql_store_result(m); MYSQL_RES* res = mysql_store_result(m);
if (res == NULL) { if (res == NULL) {
@ -968,16 +804,7 @@ static VALUE query(VALUE obj, VALUE sql)
#endif #endif
return obj; return obj;
} }
if (mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
#ifdef RUBY_VM
args.m = m;
args.data = RSTRING_PTR(sql);
args.len = RSTRING_LEN(sql);
result = (int) rb_thread_blocking_region(blocking_query, &args, RUBY_UBF_PROCESS, 0);
#else
result = mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql));
#endif
if (result != 0)
mysql_raise(m); mysql_raise(m);
if (GetMysqlStruct(obj)->query_with_result == Qfalse) if (GetMysqlStruct(obj)->query_with_result == Qfalse)
return obj; return obj;
@ -993,16 +820,6 @@ static VALUE socket(VALUE obj)
return INT2NUM(m->net.fd); return INT2NUM(m->net.fd);
} }
/* socket_type --currently returns true or false, needs some work */
static VALUE socket_type(VALUE obj)
{
MYSQL* m = GetHandler(obj);
if(vio_description(m->net.vio))
return Qtrue; // TODO return a ruby string
else
return Qnil;
}
/* blocking */ /* blocking */
static VALUE blocking(VALUE obj){ static VALUE blocking(VALUE obj){
return ( GetMysqlStruct(obj)->blocking ? Qtrue : Qfalse ); return ( GetMysqlStruct(obj)->blocking ? Qtrue : Qfalse );
@ -1046,7 +863,7 @@ static VALUE readable( int argc, VALUE* argv, VALUE obj )
if ( NIL_P( timeout ) ){ if ( NIL_P( timeout ) ){
timeout = m->net.read_timeout; timeout = m->net.read_timeout;
} }
// todo could do a rb_blocking_region here
return ( vio_poll_read( m->net.vio, INT2NUM(timeout) ) == 0 ? Qtrue : Qfalse ); return ( vio_poll_read( m->net.vio, INT2NUM(timeout) ) == 0 ? Qtrue : Qfalse );
} }
@ -1088,20 +905,42 @@ static VALUE gc_disabled( VALUE obj ){
static void validate_async_query( VALUE obj ) static void validate_async_query( VALUE obj )
{ {
MYSQL* m = GetHandler(obj);
if( async_in_progress(obj) == Qtrue ){ if( async_in_progress(obj) == Qtrue ){
async_in_progress_set(obj, Qfalse); async_in_progress_set(obj, Qfalse);
rb_raise(eMysql, "Query out of sequence: Each call to Mysql#send_query requires a successive Mysql#get_result."); rb_raise(eMysql, "Query out of sequence: Each call to Mysql#send_query requires a successive Mysql#get_result.");
} }
} }
/* for testing */ static void simulate_disconnect( VALUE obj )
static VALUE simulate_disconnect( VALUE obj )
{ {
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
mysql_library_end(); mysql_library_end();
return Qnil;
} }
static int begins_with_insensitive(char *candidate, char *check_for_in_upper_case)
{
/* skip opening whitespace --tab is 11, newline is 12, cr is 15, space 32 */
char *where_at = candidate;
while( ((*where_at >= 11 && *where_at <= 15) || (*where_at == 32)) && (where_at != 0))
where_at++;
char *where_at_in_test = check_for_in_upper_case;
while(*where_at_in_test)
{
int candidate_char = *where_at;
if(candidate_char == 0)
return 0; /* end of line */
if(candidate_char >= 97 && candidate_char < 122) /* then it's upper case --lower case ify it */
candidate_char -= 32;
if(candidate_char != *where_at_in_test)
return 0;
where_at_in_test++;
where_at++;
}
return 1;
}
/* send_query(sql) */ /* send_query(sql) */
static VALUE send_query(VALUE obj, VALUE sql) static VALUE send_query(VALUE obj, VALUE sql)
@ -1120,17 +959,29 @@ static VALUE send_query(VALUE obj, VALUE sql)
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0){ if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0){
idle( obj ); idle( obj );
mysql_raise(m); mysql_raise(m);
} }
async_in_progress_set( obj, Qtrue );
return Qnil; /* what about http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html and more? */
if(
begins_with_insensitive(RSTRING_PTR(sql), "SET ") ||
begins_with_insensitive(RSTRING_PTR(sql), "BEGIN") ||
begins_with_insensitive(RSTRING_PTR(sql), "START TRANSACTION") ||
begins_with_insensitive(RSTRING_PTR(sql), "ROLLBACK") ||
begins_with_insensitive(RSTRING_PTR(sql), "LOCK ") ||
begins_with_insensitive(RSTRING_PTR(sql), "UNLOCK ") ||
begins_with_insensitive(RSTRING_PTR(sql), "USE ") ||
begins_with_insensitive(RSTRING_PTR(sql), "COMMIT") )
{
/* do not mark an async in progress --they used send_query for something that doesn't necessarily have a result--is this allowable? */
async_in_progress_set( obj, Qfalse );
} else {
async_in_progress_set( obj, Qtrue );
}
return Qnil;
} }
/* /* get_result */
get_result
returns the mysql_result set (default) [i.e. all rows in said said]
or nil if query_with_result == false
*/
static VALUE get_result(VALUE obj) static VALUE get_result(VALUE obj)
{ {
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
@ -1148,11 +999,9 @@ static VALUE get_result(VALUE obj)
if (GetMysqlStruct(obj)->query_with_result == Qfalse) if (GetMysqlStruct(obj)->query_with_result == Qfalse)
return obj; return obj;
if (mysql_field_count(m) == 0) if (mysql_field_count(m) == 0)
return Qnil; return Qnil;
return store_result(obj);
return store_result(obj);
} }
static void schedule_query(VALUE obj, VALUE timeout) static void schedule_query(VALUE obj, VALUE timeout)
@ -1189,9 +1038,7 @@ static int should_schedule_query(){
return rb_thread_alone() != 1; return rb_thread_alone() != 1;
} }
/* async_query(sql,timeout=nil) /* async_query(sql,timeout=nil) */
optionally take a block
*/
static VALUE async_query(int argc, VALUE* argv, VALUE obj) static VALUE async_query(int argc, VALUE* argv, VALUE obj)
{ {
MYSQL* m = GetHandler(obj); MYSQL* m = GetHandler(obj);
@ -1317,7 +1164,7 @@ static VALUE set_server_option(VALUE obj, VALUE option)
static VALUE sqlstate(VALUE obj) static VALUE sqlstate(VALUE obj)
{ {
MYSQL *m = GetHandler(obj); MYSQL *m = GetHandler(obj);
return rb_enc_tainted_str_new2(mysql_sqlstate(m)); return rb_tainted_str_new2(mysql_sqlstate(m));
} }
#endif #endif
@ -1482,16 +1329,16 @@ static VALUE fetch_row(VALUE obj)
return Qnil; return Qnil;
ary = rb_ary_new2(n); ary = rb_ary_new2(n);
for (i=0; i<n; i++) for (i=0; i<n; i++)
rb_ary_store(ary, i, row[i]? rb_enc_tainted_str_new(row[i], lengths[i]): Qnil); rb_ary_store(ary, i, row[i]? rb_tainted_str_new(row[i], lengths[i]): Qnil);
return ary; return ary;
} }
/* process_all_hashes (internal helper) */ /* process_all_hashes (internal) */
static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, int yield) static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, int yield)
{ {
MYSQL_RES* res = GetMysqlRes(obj); MYSQL_RES* res = GetMysqlRes(obj);
unsigned int n = mysql_num_fields(res); unsigned int n = mysql_num_fields(res);
VALUE ary = Qnil; VALUE ary;
if(build_array) if(build_array)
ary = rb_ary_new(); ary = rb_ary_new();
MYSQL_ROW row = mysql_fetch_row(res); // grab one off the top, to determine the rows MYSQL_ROW row = mysql_fetch_row(res); // grab one off the top, to determine the rows
@ -1512,7 +1359,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
if (colname == Qnil) { if (colname == Qnil) {
colname = rb_ary_new2(n); colname = rb_ary_new2(n);
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
VALUE s = rb_enc_tainted_str_new2(fields[i].name); VALUE s = rb_tainted_str_new2(fields[i].name);
rb_obj_freeze(s); rb_obj_freeze(s);
rb_ary_store(colname, i, s); rb_ary_store(colname, i, s);
} }
@ -1525,7 +1372,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
colname = rb_ary_new2(n); colname = rb_ary_new2(n);
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
int len = strlen(fields[i].table)+strlen(fields[i].name)+1; int len = strlen(fields[i].table)+strlen(fields[i].name)+1;
VALUE s = rb_enc_tainted_str_new(NULL, len); VALUE s = rb_tainted_str_new(NULL, len);
snprintf(RSTRING_PTR(s), len+1, "%s.%s", fields[i].table, fields[i].name); snprintf(RSTRING_PTR(s), len+1, "%s.%s", fields[i].table, fields[i].name);
rb_obj_freeze(s); rb_obj_freeze(s);
rb_ary_store(colname, i, s); rb_ary_store(colname, i, s);
@ -1541,7 +1388,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
hash = rb_hash_new(); hash = rb_hash_new();
lengths = mysql_fetch_lengths(res); lengths = mysql_fetch_lengths(res);
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
rb_hash_aset(hash, rb_ary_entry(colname, i), row[i]? rb_enc_tainted_str_new(row[i], lengths[i]): Qnil); rb_hash_aset(hash, rb_ary_entry(colname, i), row[i]? rb_tainted_str_new(row[i], lengths[i]): Qnil);
} }
if(build_array) if(build_array)
rb_ary_push(ary, hash); rb_ary_push(ary, hash);
@ -1558,8 +1405,6 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
if(yield) if(yield)
return obj; return obj;
return Qnil; /* we should never get here -- this takes out a compiler warning */
} }
/* fetch_hash2 (internal) */ /* fetch_hash2 (internal) */
@ -1577,12 +1422,12 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
return Qnil; return Qnil;
hash = rb_hash_new(); hash = rb_hash_new();
if (with_table == Qnil || with_table == Qfalse) { if (with_table == Qfalse) {
colname = rb_iv_get(obj, "colname"); colname = rb_iv_get(obj, "colname");
if (colname == Qnil) { if (colname == Qnil) {
colname = rb_ary_new2(n); colname = rb_ary_new2(n);
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
VALUE s = rb_enc_tainted_str_new2(fields[i].name); VALUE s = rb_tainted_str_new2(fields[i].name);
rb_obj_freeze(s); rb_obj_freeze(s);
rb_ary_store(colname, i, s); rb_ary_store(colname, i, s);
} }
@ -1595,7 +1440,7 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
colname = rb_ary_new2(n); colname = rb_ary_new2(n);
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
int len = strlen(fields[i].table)+strlen(fields[i].name)+1; int len = strlen(fields[i].table)+strlen(fields[i].name)+1;
VALUE s = rb_enc_tainted_str_new(NULL, len); VALUE s = rb_tainted_str_new(NULL, len);
snprintf(RSTRING_PTR(s), len+1, "%s.%s", fields[i].table, fields[i].name); snprintf(RSTRING_PTR(s), len+1, "%s.%s", fields[i].table, fields[i].name);
rb_obj_freeze(s); rb_obj_freeze(s);
rb_ary_store(colname, i, s); rb_ary_store(colname, i, s);
@ -1605,7 +1450,7 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
} }
} }
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
rb_hash_aset(hash, rb_ary_entry(colname, i), row[i]? rb_enc_tainted_str_new(row[i], lengths[i]): Qnil); rb_hash_aset(hash, rb_ary_entry(colname, i), row[i]? rb_tainted_str_new(row[i], lengths[i]): Qnil);
} }
return hash; return hash;
} }
@ -1737,7 +1582,7 @@ static VALUE field_hash(VALUE obj)
static VALUE field_inspect(VALUE obj) static VALUE field_inspect(VALUE obj)
{ {
VALUE n = rb_iv_get(obj, "name"); VALUE n = rb_iv_get(obj, "name");
VALUE s = rb_enc_str_new(0, RSTRING_LEN(n) + 16, DEFAULT_ENCODING); VALUE s = rb_str_new(0, RSTRING_LEN(n) + 16);
sprintf(RSTRING_PTR(s), "#<Mysql::Field:%s>", RSTRING_PTR(n)); sprintf(RSTRING_PTR(s), "#<Mysql::Field:%s>", RSTRING_PTR(n));
return s; return s;
} }
@ -1796,7 +1641,7 @@ static void mysql_stmt_raise(MYSQL_STMT* s)
{ {
VALUE e = rb_exc_new2(eMysql, mysql_stmt_error(s)); VALUE e = rb_exc_new2(eMysql, mysql_stmt_error(s));
rb_iv_set(e, "errno", INT2FIX(mysql_stmt_errno(s))); rb_iv_set(e, "errno", INT2FIX(mysql_stmt_errno(s)));
rb_iv_set(e, "sqlstate", rb_enc_tainted_str_new2(mysql_stmt_sqlstate(s))); rb_iv_set(e, "sqlstate", rb_tainted_str_new2(mysql_stmt_sqlstate(s)));
rb_exc_raise(e); rb_exc_raise(e);
} }
@ -1943,12 +1788,12 @@ static VALUE stmt_execute(int argc, VALUE *argv, VALUE obj)
s->param.bind[i].buffer = &(s->param.buffer[i]); s->param.bind[i].buffer = &(s->param.buffer[i]);
t.second_part = 0; t.second_part = 0;
t.neg = 0; t.neg = 0;
t.second = FIX2INT(rb_ary_entry(a, 0)); t.second = FIX2INT(RARRAY(a)->ptr[0]);
t.minute = FIX2INT(rb_ary_entry(a, 1)); t.minute = FIX2INT(RARRAY(a)->ptr[1]);
t.hour = FIX2INT(rb_ary_entry(a, 2)); t.hour = FIX2INT(RARRAY(a)->ptr[2]);
t.day = FIX2INT(rb_ary_entry(a, 3)); t.day = FIX2INT(RARRAY(a)->ptr[3]);
t.month = FIX2INT(rb_ary_entry(a, 4)); t.month = FIX2INT(RARRAY(a)->ptr[4]);
t.year = FIX2INT(rb_ary_entry(a, 5)); t.year = FIX2INT(RARRAY(a)->ptr[5]);
*(MYSQL_TIME*)&(s->param.buffer[i]) = t; *(MYSQL_TIME*)&(s->param.buffer[i]) = t;
} else if (CLASS_OF(argv[i]) == cMysqlTime) { } else if (CLASS_OF(argv[i]) == cMysqlTime) {
MYSQL_TIME t; MYSQL_TIME t;
@ -2110,7 +1955,7 @@ static VALUE stmt_fetch(VALUE obj)
case MYSQL_TYPE_NEWDECIMAL: case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_BIT: case MYSQL_TYPE_BIT:
#endif #endif
v = rb_enc_tainted_str_new(s->result.bind[i].buffer, s->result.length[i]); v = rb_tainted_str_new(s->result.bind[i].buffer, s->result.length[i]);
break; break;
default: default:
rb_raise(rb_eTypeError, "unknown buffer_type: %d", s->result.bind[i].buffer_type); rb_raise(rb_eTypeError, "unknown buffer_type: %d", s->result.bind[i].buffer_type);
@ -2299,7 +2144,7 @@ static VALUE stmt_send_long_data(VALUE obj, VALUE col, VALUE data)
static VALUE stmt_sqlstate(VALUE obj) static VALUE stmt_sqlstate(VALUE obj)
{ {
struct mysql_stmt* s = DATA_PTR(obj); struct mysql_stmt* s = DATA_PTR(obj);
return rb_enc_tainted_str_new2(mysql_stmt_sqlstate(s->stmt)); return rb_tainted_str_new2(mysql_stmt_sqlstate(s->stmt));
} }
/*------------------------------- /*-------------------------------
@ -2825,4 +2670,4 @@ void Init_mysql(void)
/* Mysql::Error constant */ /* Mysql::Error constant */
#define rb_define_mysql_const(s) rb_define_const(eMysql, #s, INT2NUM(s)) #define rb_define_mysql_const(s) rb_define_const(eMysql, #s, INT2NUM(s))
#include "error_const.h" #include "error_const.h"
} }

View File

@ -1,23 +1,19 @@
require File.dirname(__FILE__) + '/mysql' # load our version of mysql--note require 'mysql'
# if someone does a require 'mysql' after a require 'mysqlplus' then their screen will be littered with warnings
# and the "old" mysql will override the "new" mysqlplus, so be careful.
#
# The mysqlplus library is a [slightly updated] fork of the Mysql class, with asynchronous capability added
# See http://www.kitebird.com/articles/ruby-mysql.html for details, as well as the test directory within the gem
#
class Mysql class Mysql
def ruby_async_query(sql, timeout = nil) # known to deadlock TODO def async_query(sql, timeout = nil)
send_query(sql) send_query(sql)
select [ (@sockets ||= {})[socket] ||= IO.new(socket) ], nil, nil, nil select [ (@sockets ||= {})[socket] ||= IO.new(socket) ], nil, nil, nil
get_result get_result
end end
begin
alias_method :async_query, :c_async_query
rescue NameError => e
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")
end
end end
class Mysql::Result
def all_hashes
rows = []
each_hash { |row| rows << row }
rows
end
end

41
mysqlplus.gemspec Normal file → Executable file
View File

@ -1,30 +1,29 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "mysqlplus" s.name = "mysqlplus"
s.version = "0.1.4" s.version = "0.1.0"
s.date = "2010-07-04" s.date = "2008-08-13"
s.summary = "Enhanced Ruby MySQL driver with Ruby 1.9 encoding awareness" s.summary = "Enhanced Ruby MySQL driver"
s.email = "jeremysuriel@gmail.com" s.email = "oldmoe@gmail.com"
s.homepage = "http://github.com/jrmey/mysqlplus" s.homepage = "http://github.com/oldmoe/mysqlplus"
s.description = "Enhanced Ruby MySQL driver" s.description = "Enhanced Ruby MySQL driver"
s.has_rdoc = true s.has_rdoc = true
s.authors = ["Muhammad A. Ali", "Jeremy Suriel", "John Bintz"] s.authors = ["Muhammad A. Ali"]
s.platform = Gem::Platform::RUBY s.platform = Gem::Platform::RUBY
s.files = %w[ s.files = [
README "mysqlplus.gemspec",
Rakefile "README",
TODO_LIST "Rakefile",
ext/error_const.h "lib/mysqlplus.rb",
ext/extconf.rb "test/test_helper.rb",
ext/mysql.c "test/native_threaded_test.rb",
lib/mysqlplus.rb "test/c_threaded_test.rb",
mysqlplus.gemspec "test/evented_test.rb",
] + Dir.glob('test/*') "ext/error_const.h",
"ext/extconf.rb",
"ext/mysql.c"
]
s.rdoc_options = ["--main", "README"] s.rdoc_options = ["--main", "README"]
s.extra_rdoc_files = ["README"] s.extra_rdoc_files = ["README"]
s.extensions << "ext/extconf.rb" s.extensions << "ext/extconf.rb"
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
end
end end

View File

@ -1,9 +0,0 @@
# I suppose if all the tests don't blow up, that probably means pass
require 'mysqlplus'
for file in Dir.glob('*_test.rb') do
puts 'testing ' + file
# fork so we don't run out of connections to the mysql db, as few tests ever clean up their old processes
pid = Process.fork { load file }
Process.wait(pid)
end
puts 'successful'

View File

@ -1,22 +0,0 @@
# If this script returns without the word pass
# you may have compiled mysqlplus using ruby and
# run it using a different version of ruby
if RUBY_VERSION >= "1.9.1"
require 'mysqlplus'
require 'socket'
require 'timeout'
TCPServer.new '0.0.0.0', 8002
Thread.new {
sleep 2
print "pass"
system("kill -9 #{Process.pid}")
}
Timeout::timeout(1) {
# uncomment this line to do the 'real' test
# which hangs otherwise (blows up if code is bad, otherwise hangs)
Mysql.real_connect '127.0.0.1', 'root', 'pass', 'db', 8002
}
raise 'should never get here'
end

View File

@ -1,17 +0,0 @@
require 'mysqlplus'
begin
Mysql.real_connect('fakehost','root', '', 'local_leadgen_dev')
rescue Mysql::Error
end
begin
Mysql.real_connect('localhost','root', '', 'faketable')
rescue Mysql::Error
end
begin
Mysql.real_connect('localhost', 'root', 'pass', 'db', 3307)# bad port
rescue Mysql::Error
end
print "pass"

View File

@ -1,22 +0,0 @@
# To run first execute:
=begin
create database local_test_db;
use local_test_db;
CREATE TABLE test_table (
c1 INT,
c2 VARCHAR(20)
);
=end
# This script shows the effect of using .all_hashes instead of looping on each hash
# run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top
# note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes]
# 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
# almost as fast, now, as .all_hashes--they've both been optimized]
require 'mysqlplus'
puts 'initing db'
# init the DB
conn = Mysql.real_connect('localhost', 'root', '', 'local_test_db')
conn.query("delete from test_table")
200_000.times {conn.query(" insert into test_table (c1, c2) values (3, 'ABCDEFG')")}
puts 'connection pool ready'

View File

@ -1,3 +1,4 @@
require 'rubygems'
require 'mysqlplus' require 'mysqlplus'
require 'benchmark' require 'benchmark'
@ -37,4 +38,4 @@ Benchmark.bmbm do |x|
end end
end end
puts $gc_stats.join( ' | ' ) puts $gc_stats.join( ' | ' )

View File

@ -1,6 +0,0 @@
require 'mysqlplus'
a = Mysql.real_connect('localhost','root')
100.times { a.query("select sleep(0)") }
print "pass"

View File

@ -2,13 +2,13 @@ require File.dirname(__FILE__) + '/test_helper'
m = Mysql.real_connect('localhost','root') m = Mysql.real_connect('localhost','root')
m.reconnect = true m.reconnect = true
$count = 0
class << m class << m
def safe_query( query ) def safe_query( query )
begin begin
send_query( query ) send_query( query )
rescue => e rescue => e
$count += 1
puts e.message puts e.message
end end
end end
@ -25,10 +25,7 @@ m.connect('localhost','root')
m.safe_query( 'select sleep(1)' ) m.safe_query( 'select sleep(1)' )
m.safe_query( 'select sleep(1)' )#raises m.safe_query( 'select sleep(1)' )#raises
m.simulate_disconnect m.simulate_disconnect
raise unless $count == 3
m.safe_query( 'BEGIN' ) m.safe_query( 'BEGIN' )
m.safe_query( 'select sleep(1)' ) # raises m.safe_query( 'select sleep(1)' )
m.get_result() m.get_result()
m.safe_query( 'COMMIT' ) m.safe_query( 'COMMIT' )
m.get_result
raise unless $count == 4

View File

@ -1,4 +1,8 @@
require 'create_test_db' # shows the effect of using .all_hashes instead of looping on each hash
# run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top
# note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes]
# on my machine and a 200_000 row table, it took 3.38s versus 3.65s
require 'mysqlplus'
use_the_all_hashes_method = true use_the_all_hashes_method = true
@ -8,15 +12,16 @@ $start = Time.now
$connections = [] $connections = []
$count.times do $count.times do
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db') $connections << Mysql.real_connect('localhost','root', '', 'local_leadgen_dev')
end end
puts 'connection pool ready'
$threads = [] $threads = []
$count.times do |i| $count.times do |i|
$threads << Thread.new do $threads << Thread.new do
query = "select * from test_table" query = "select * from campus_zips"
puts "sending query on connection #{i}" puts "sending query on connection #{i}"
conn = $connections[i] conn = $connections[i]
result = conn.async_query(query) result = conn.async_query(query)

View File

@ -1,3 +1,4 @@
require 'rubygems'
require 'mysqlplus' require 'mysqlplus'
class MysqlTest class MysqlTest
@ -195,4 +196,4 @@ class ThreadedMysqlTest < MysqlTest
end end
end end
end end

View File

@ -5,7 +5,8 @@
# from .82s to .62s # from .82s to .62s
# you can experiment with it by changing the query here to be a long one, and toggling the do_the_use_query_optimization variable # you can experiment with it by changing the query here to be a long one, and toggling the do_the_use_query_optimization variable
# this also has the interesting property of 'freeing' Ruby to do thread changes mid-query. # this also has the interesting property of 'freeing' Ruby to do thread changes mid-query.
require 'create_test_db'
require 'mysqlplus'
do_the_use_query_optimization = true do_the_use_query_optimization = true
@ -15,7 +16,7 @@ $start = Time.now
$connections = [] $connections = []
$count.times do $count.times do
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db') $connections << Mysql.real_connect('localhost','root', '', 'local_leadgen_dev')
end end
puts 'connection pool ready' puts 'connection pool ready'
@ -27,7 +28,7 @@ $count.times do |i|
puts "sending query on connection #{i}" puts "sending query on connection #{i}"
conn = $connections[i] conn = $connections[i]
saved = [] saved = []
query = "select * from test_table" query = "select * from campus_zips"
if do_the_use_query_optimization if do_the_use_query_optimization
conn.query_with_result=false conn.query_with_result=false
result = conn.async_query(query) result = conn.async_query(query)

View File

@ -1,7 +1,7 @@
require 'mysqlplus'
require 'rubygems' require 'rubygems'
require 'sequel' require 'sequel'
require 'mysqlplus'
class Mysql class Mysql
unless method_defined? :sync_query unless method_defined? :sync_query
alias :sync_query :query alias :sync_query :query
@ -21,4 +21,4 @@ start = Time.now
end end
end.map{|t| t.join } end.map{|t| t.join }
p (Time.now - start) p (Time.now - start)