Compare commits
No commits in common. "master" and "19_full_thread_no_gil" have entirely different histories.
master
...
19_full_th
17
README
17
README
@ -2,8 +2,6 @@
|
||||
|
||||
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
|
||||
|
||||
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-lib=/usr/local/mysql/lib \
|
||||
--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
|
||||
|
||||
@ -44,6 +30,3 @@ Lourens Naude for 1.9 integration help.
|
||||
|
||||
=== License
|
||||
Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
|
||||
|
||||
== Mailing list
|
||||
http://groups.google.com/group/never-block?hl=en
|
||||
|
12
TODO_LIST
12
TODO_LIST
@ -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 :)
|
1075
ext/error_const.h
1075
ext/error_const.h
File diff suppressed because it is too large
Load Diff
@ -1,76 +1,24 @@
|
||||
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
|
||||
inc, lib = dir_config('mysql')
|
||||
#exit 1 unless have_library("libmysql")
|
||||
have_library("libmysql") or die "can't find libmysql."
|
||||
exit 1 unless have_library("libmysql")
|
||||
elsif mc = with_config('mysql-config') then
|
||||
mc = 'mysql_config' if mc == true
|
||||
#cflags = `#{mc} --cflags`.chomp
|
||||
#exit 1 if $? != 0
|
||||
cflags = exec_command("#{mc} --cflags")
|
||||
#libs = `#{mc} --libs`.chomp
|
||||
#exit 1 if $? != 0
|
||||
libs = exec_command("#{mc} --libs")
|
||||
$CPPFLAGS += ' ' + cflags
|
||||
$libs = libs + " " + $libs
|
||||
else
|
||||
puts "Trying to detect MySQL configuration with mysql_config command..."
|
||||
begin
|
||||
cflags = libs = nil
|
||||
|
||||
dirs = ENV['PATH'].split(':') + %w[
|
||||
/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..."
|
||||
else
|
||||
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."
|
||||
exit 1 if libs.empty?
|
||||
have_library(libs.shift)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
have_func('mysql_ssl_set')
|
||||
@ -81,14 +29,12 @@ if have_header('mysql.h') then
|
||||
elsif have_header('mysql/mysql.h') then
|
||||
src = "#include <mysql/errmsg.h>\n#include <mysql/mysqld_error.h>\n"
|
||||
else
|
||||
#exit 1
|
||||
die "can't find 'mysql.h'."
|
||||
exit 1
|
||||
end
|
||||
|
||||
# check for 1.9
|
||||
if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
|
||||
$CFLAGS += " -DHAVE_TBR "
|
||||
$CPPFLAGS << " -DHAVE_TBR "
|
||||
end
|
||||
|
||||
# make mysql constant
|
||||
@ -103,10 +49,9 @@ end
|
||||
if /mswin32/ =~ RUBY_PLATFORM && !/-E/.match(cpp)
|
||||
cpp << " -E"
|
||||
end
|
||||
#unless system "#{cpp} > confout" then
|
||||
# exit 1
|
||||
#end
|
||||
exec_command("#{cpp} > confout")
|
||||
unless system "#{cpp} > confout" then
|
||||
exit 1
|
||||
end
|
||||
File.unlink "conftest.c"
|
||||
|
||||
error_syms = []
|
||||
@ -128,10 +73,4 @@ File.open('error_const.h', 'w') do |f|
|
||||
end
|
||||
end
|
||||
|
||||
$CPPFLAGS += " -DRUBY19" if RUBY_VERSION =~ /1.9/
|
||||
|
||||
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
|
||||
$LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
|
||||
end
|
||||
|
||||
create_makefile("mysql")
|
||||
|
486
ext/mysql.c
486
ext/mysql.c
@ -6,49 +6,16 @@
|
||||
|
||||
#include <ruby.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(str) RSTRING(str)->ptr
|
||||
#endif
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(str) RSTRING(str)->len
|
||||
#endif
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(ary) RARRAY(ary)->ptr
|
||||
#endif
|
||||
#ifndef HAVE_RB_STR_SET_LEN
|
||||
#define rb_str_set_len(str, length) (RSTRING_LEN(str) = (length))
|
||||
#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
|
||||
#include <mysql.h>
|
||||
#include <mysql_com.h>
|
||||
@ -93,13 +60,9 @@ struct mysql {
|
||||
MYSQL handler;
|
||||
char connection;
|
||||
char query_with_result;
|
||||
char gc_disabled;
|
||||
char blocking;
|
||||
int async_in_progress;
|
||||
char busy;
|
||||
};
|
||||
|
||||
// a wrapper for mysql_res's so we can detect double frees
|
||||
struct mysql_res {
|
||||
MYSQL_RES* res;
|
||||
char freed;
|
||||
@ -212,12 +175,12 @@ static void mysql_raise(MYSQL* m)
|
||||
VALUE e = rb_exc_new2(eMysql, mysql_error(m));
|
||||
rb_iv_set(e, "errno", INT2FIX(mysql_errno(m)));
|
||||
#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
|
||||
rb_exc_raise(e);
|
||||
}
|
||||
|
||||
static VALUE mysqlres2obj(MYSQL_RES* res, VALUE gc_disabled)
|
||||
static VALUE mysqlres2obj(MYSQL_RES* res)
|
||||
{
|
||||
VALUE obj;
|
||||
struct mysql_res* resp;
|
||||
@ -227,10 +190,8 @@ static VALUE mysqlres2obj(MYSQL_RES* res, VALUE gc_disabled)
|
||||
resp->res = res;
|
||||
resp->freed = Qfalse;
|
||||
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)
|
||||
rb_gc();
|
||||
*/
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -241,9 +202,9 @@ static VALUE make_field_obj(MYSQL_FIELD* f)
|
||||
if (f == NULL)
|
||||
return Qnil;
|
||||
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, "table", f->table? rb_str_freeze(rb_enc_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, "name", f->name? rb_str_freeze(rb_tainted_str_new2(f->name)): 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_tainted_str_new2(f->def)): Qnil);
|
||||
rb_iv_set(obj, "type", INT2NUM(f->type));
|
||||
rb_iv_set(obj, "length", INT2NUM(f->length));
|
||||
rb_iv_set(obj, "max_length", INT2NUM(f->max_length));
|
||||
@ -266,135 +227,10 @@ static VALUE init(VALUE klass)
|
||||
mysql_init(&myp->handler);
|
||||
myp->connection = Qfalse;
|
||||
myp->query_with_result = Qtrue;
|
||||
myp->gc_disabled = Qtrue;
|
||||
rb_obj_call_init(obj, 0, NULL);
|
||||
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 *) ¶m_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 )
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
return mysql_thread_id( m );
|
||||
}
|
||||
|
||||
static VALUE async_in_progress( VALUE obj )
|
||||
{
|
||||
struct mysql* m = GetMysqlStruct(obj);
|
||||
return ( m->async_in_progress == connection_identifier(obj) ) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static VALUE async_in_progress_set( VALUE obj, VALUE flag )
|
||||
{
|
||||
struct mysql* m = GetMysqlStruct(obj);
|
||||
m->async_in_progress = (flag == Qnil || flag == Qfalse) ? 0 : connection_identifier(obj);
|
||||
return flag;
|
||||
}
|
||||
|
||||
// does this actually really do anything helpful? Not sure.
|
||||
static void optimize_for_async( VALUE obj )
|
||||
{
|
||||
struct mysql* m = GetMysqlStruct(obj);
|
||||
my_bool was_blocking;
|
||||
vio_blocking(m->handler.net.vio, 0, &was_blocking);
|
||||
m->blocking = vio_is_blocking( m->handler.net.vio );
|
||||
|
||||
vio_fastsend( m->handler.net.vio );
|
||||
async_in_progress_set( obj, Qfalse );
|
||||
}
|
||||
|
||||
// TODO does nothing currently
|
||||
static void schedule_connect(VALUE obj )
|
||||
{
|
||||
/* TODO is this old?
|
||||
MYSQL* m = GetHandler(obj);
|
||||
fd_set read;
|
||||
|
||||
struct timeval tv = { tv_sec: m->options.connect_timeout, tv_usec: 0 };
|
||||
if (rb_thread_select(0, NULL, NULL, NULL, &tv) < 0) {
|
||||
rb_raise(eMysql, "connect: timeout");
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
FD_ZERO(&read);
|
||||
FD_SET(m->net.fd, &read);
|
||||
|
||||
if (rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv) < 0) {
|
||||
rb_raise(eMysql, "connect: timeout");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/* real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, sock=nil, flag=nil) */
|
||||
static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
||||
{
|
||||
@ -422,12 +258,8 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
||||
|
||||
obj = Data_Make_Struct(klass, struct mysql, 0, free_mysql, myp);
|
||||
#if MYSQL_VERSION_ID >= 32200
|
||||
mysql_init(&myp->handler); /* we get here */
|
||||
# ifdef HAVE_TBR
|
||||
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
|
||||
mysql_init(&myp->handler);
|
||||
if (mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL)
|
||||
#elif MYSQL_VERSION_ID >= 32115
|
||||
if (mysql_real_connect(&myp->handler, h, u, p, pp, s, f) == NULL)
|
||||
#else
|
||||
@ -438,13 +270,16 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
||||
myp->handler.reconnect = 0;
|
||||
myp->connection = Qtrue;
|
||||
|
||||
optimize_for_async(obj);
|
||||
my_bool was_blocking;
|
||||
|
||||
vio_blocking(myp->handler.net.vio, 0, &was_blocking);
|
||||
myp->blocking = vio_is_blocking( myp->handler.net.vio );
|
||||
|
||||
vio_fastsend( myp->handler.net.vio );
|
||||
|
||||
myp->query_with_result = Qtrue;
|
||||
rb_obj_call_init(obj, argc, argv);
|
||||
|
||||
//schedule_connect(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -453,7 +288,7 @@ static VALUE escape_string(VALUE klass, VALUE str)
|
||||
{
|
||||
VALUE ret;
|
||||
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)));
|
||||
return ret;
|
||||
}
|
||||
@ -461,7 +296,7 @@ static VALUE escape_string(VALUE klass, VALUE str)
|
||||
/* client_info() */
|
||||
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
|
||||
@ -507,9 +342,6 @@ static VALUE real_connect2(int argc, VALUE* argv, VALUE obj)
|
||||
m->reconnect = 0;
|
||||
GetMysqlStruct(obj)->connection = Qtrue;
|
||||
|
||||
optimize_for_async(obj);
|
||||
//schedule_connect(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -590,7 +422,7 @@ static VALUE real_escape_string(VALUE obj, VALUE str)
|
||||
MYSQL* m = GetHandler(obj);
|
||||
VALUE ret;
|
||||
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)));
|
||||
return ret;
|
||||
}
|
||||
@ -629,7 +461,7 @@ static VALUE change_user(int argc, VALUE* argv, VALUE obj)
|
||||
/* character_set_name() */
|
||||
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
|
||||
|
||||
@ -694,7 +526,7 @@ static VALUE field_count(VALUE obj)
|
||||
/* host_info() */
|
||||
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() */
|
||||
@ -706,14 +538,14 @@ static VALUE proto_info(VALUE obj)
|
||||
/* server_info() */
|
||||
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() */
|
||||
static VALUE info(VALUE 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() */
|
||||
@ -748,7 +580,7 @@ static VALUE list_dbs(int argc, VALUE* argv, VALUE obj)
|
||||
n = mysql_num_rows(res);
|
||||
ret = rb_ary_new2(n);
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
@ -763,7 +595,7 @@ static VALUE list_fields(int argc, VALUE* argv, VALUE obj)
|
||||
res = mysql_list_fields(m, StringValuePtr(table), NILorSTRING(field));
|
||||
if (res == NULL)
|
||||
mysql_raise(m);
|
||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
||||
return mysqlres2obj(res);
|
||||
}
|
||||
|
||||
/* list_processes() */
|
||||
@ -773,7 +605,7 @@ static VALUE list_processes(VALUE obj)
|
||||
MYSQL_RES* res = mysql_list_processes(m);
|
||||
if (res == NULL)
|
||||
mysql_raise(m);
|
||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
||||
return mysqlres2obj(res);
|
||||
}
|
||||
|
||||
/* list_tables(table=nil) */
|
||||
@ -793,7 +625,7 @@ static VALUE list_tables(int argc, VALUE* argv, VALUE obj)
|
||||
n = mysql_num_rows(res);
|
||||
ret = rb_ary_new2(n);
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
@ -857,10 +689,9 @@ static VALUE my_stat(VALUE obj)
|
||||
const char* s = mysql_stat(m);
|
||||
if (s == NULL)
|
||||
mysql_raise(m);
|
||||
return rb_enc_tainted_str_new2(s);
|
||||
return rb_tainted_str_new2(s);
|
||||
}
|
||||
|
||||
// 1.9 friendly
|
||||
typedef struct
|
||||
{
|
||||
MYSQL *mysql_instance;
|
||||
@ -872,7 +703,7 @@ typedef struct
|
||||
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
|
||||
*(settings->store_it_here) = mysql_store_result(settings->mysql_instance); // this one runs a good long while for very large queries
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
@ -887,13 +718,13 @@ static VALUE store_result(VALUE obj)
|
||||
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);
|
||||
rb_thread_blocking_region(store_result_to_location, (void *) &linker, RUBY_UBF_IO, 0); /* not sure if this should be RUBY_UBF_IO or RUBY_UBF_PROCESS here -- see Ruby 1.9 ChangeLog */
|
||||
#endif
|
||||
|
||||
if (res == NULL)
|
||||
mysql_raise(m);
|
||||
|
||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
||||
return mysqlres2obj(res);
|
||||
}
|
||||
|
||||
/* thread_id() */
|
||||
@ -909,54 +740,29 @@ static VALUE use_result(VALUE obj)
|
||||
MYSQL_RES* res = mysql_use_result(m);
|
||||
if (res == NULL)
|
||||
mysql_raise(m);
|
||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
||||
return mysqlres2obj(res);
|
||||
}
|
||||
|
||||
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) */
|
||||
static VALUE query(VALUE obj, VALUE sql)
|
||||
{
|
||||
int loop = 0;
|
||||
MYSQL* m = GetHandler(obj);
|
||||
QueryArgs args;
|
||||
int result;
|
||||
|
||||
Check_Type(sql, T_STRING);
|
||||
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
||||
rb_raise(eMysql, "query: not connected");
|
||||
}
|
||||
if (rb_block_given_p()) {
|
||||
#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)
|
||||
if (mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
|
||||
mysql_raise(m);
|
||||
|
||||
do {
|
||||
MYSQL_RES* res = mysql_store_result(m);
|
||||
if (res == NULL) {
|
||||
if (mysql_field_count(m) != 0)
|
||||
mysql_raise(m);
|
||||
} else {
|
||||
VALUE robj = mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
||||
VALUE robj = mysqlres2obj(res);
|
||||
rb_ensure(rb_yield, robj, res_free, robj);
|
||||
}
|
||||
#if MYSQL_VERSION_ID >= 40101
|
||||
@ -968,16 +774,7 @@ static VALUE query(VALUE obj, VALUE sql)
|
||||
#endif
|
||||
return obj;
|
||||
}
|
||||
|
||||
#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)
|
||||
if (mysql_real_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
|
||||
mysql_raise(m);
|
||||
if (GetMysqlStruct(obj)->query_with_result == Qfalse)
|
||||
return obj;
|
||||
@ -992,15 +789,12 @@ static VALUE socket(VALUE obj)
|
||||
MYSQL* m = GetHandler(obj);
|
||||
return INT2NUM(m->net.fd);
|
||||
}
|
||||
|
||||
/* socket_type --currently returns true or false, needs some work */
|
||||
/* socket_type */
|
||||
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;
|
||||
VALUE description = vio_description( m->net.vio );
|
||||
return NILorSTRING( description );
|
||||
}
|
||||
|
||||
/* blocking */
|
||||
@ -1008,32 +802,6 @@ static VALUE blocking(VALUE obj){
|
||||
return ( GetMysqlStruct(obj)->blocking ? Qtrue : Qfalse );
|
||||
}
|
||||
|
||||
/* is_busy */
|
||||
static VALUE is_busy(VALUE obj){
|
||||
return ( GetMysqlStruct(obj)->busy ? Qtrue : Qfalse );
|
||||
}
|
||||
|
||||
static VALUE is_idle(VALUE obj){
|
||||
return ( is_busy(obj) == Qtrue ) ? Qfalse : Qtrue;
|
||||
}
|
||||
|
||||
/* busy(true|false) */
|
||||
static VALUE busy_set(VALUE obj, VALUE flag)
|
||||
{
|
||||
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
||||
rb_raise(rb_eTypeError, "invalid type, required true or false.");
|
||||
GetMysqlStruct(obj)->busy = flag;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static void busy( VALUE obj ){
|
||||
busy_set( obj, Qtrue );
|
||||
}
|
||||
|
||||
static void idle( VALUE obj ){
|
||||
busy_set( obj, Qfalse );
|
||||
}
|
||||
|
||||
/* readable(timeout=nil) */
|
||||
static VALUE readable( int argc, VALUE* argv, VALUE obj )
|
||||
{
|
||||
@ -1046,152 +814,59 @@ static VALUE readable( int argc, VALUE* argv, VALUE obj )
|
||||
if ( NIL_P( 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 );
|
||||
}
|
||||
|
||||
/* retry */
|
||||
static VALUE retry( VALUE obj )
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
return ( vio_should_retry( m->net.vio ) == 1 ? Qtrue : Qfalse );
|
||||
}
|
||||
|
||||
/* interrupted */
|
||||
static VALUE interrupted( VALUE obj )
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
return ( vio_was_interrupted( m->net.vio ) == 1 ? Qtrue : Qfalse );
|
||||
}
|
||||
|
||||
/* reconnected */
|
||||
static VALUE reconnected( VALUE obj ){
|
||||
MYSQL* m = GetHandler(obj);
|
||||
int current_connection_id = mysql_thread_id( m );
|
||||
mysql_ping(m);
|
||||
return ( current_connection_id == mysql_thread_id( m ) ) ? Qfalse : Qtrue;
|
||||
}
|
||||
|
||||
/* disable_gc(true|false) */
|
||||
static VALUE disable_gc_set(VALUE obj, VALUE flag)
|
||||
{
|
||||
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
||||
rb_raise(rb_eTypeError, "invalid type, required true or false.");
|
||||
GetMysqlStruct(obj)->gc_disabled = flag;
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* gc_disabled */
|
||||
static VALUE gc_disabled( VALUE obj ){
|
||||
return GetMysqlStruct(obj)->gc_disabled ? Qtrue: Qfalse;
|
||||
}
|
||||
|
||||
static void validate_async_query( VALUE obj )
|
||||
{
|
||||
if( async_in_progress(obj) == Qtrue ){
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/* for testing */
|
||||
static VALUE simulate_disconnect( VALUE obj )
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
mysql_library_end();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
/* send_query(sql) */
|
||||
static VALUE send_query(VALUE obj, VALUE sql)
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
|
||||
Check_Type(sql, T_STRING);
|
||||
|
||||
if (GetMysqlStruct(obj)->connection == Qfalse && async_in_progress(obj) == Qtrue ) {
|
||||
idle( obj );
|
||||
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
||||
rb_raise(eMysql, "query: not connected");
|
||||
}
|
||||
|
||||
validate_async_query(obj);
|
||||
|
||||
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0){
|
||||
idle( obj );
|
||||
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
|
||||
mysql_raise(m);
|
||||
}
|
||||
async_in_progress_set( obj, Qtrue );
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
get_result
|
||||
returns the mysql_result set (default) [i.e. all rows in said said]
|
||||
or nil if query_with_result == false
|
||||
*/
|
||||
/* get_result */
|
||||
static VALUE get_result(VALUE obj)
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
|
||||
async_in_progress_set( obj, Qfalse );
|
||||
|
||||
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
||||
idle( obj );
|
||||
rb_raise(eMysql, "query: not connected");
|
||||
}
|
||||
if (mysql_read_query_result(m) != 0){
|
||||
idle( obj );
|
||||
if (mysql_read_query_result(m) != 0)
|
||||
mysql_raise(m);
|
||||
}
|
||||
|
||||
if (GetMysqlStruct(obj)->query_with_result == Qfalse)
|
||||
return obj;
|
||||
|
||||
if (mysql_field_count(m) == 0)
|
||||
return Qnil;
|
||||
|
||||
return store_result(obj);
|
||||
}
|
||||
|
||||
static void schedule_query(VALUE obj, VALUE timeout)
|
||||
static VALUE schedule(VALUE obj, VALUE timeout)
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
fd_set read;
|
||||
int ret;
|
||||
|
||||
timeout = ( NIL_P(timeout) ? m->net.read_timeout : INT2NUM(timeout) );
|
||||
|
||||
struct timeval tv = { tv_sec: timeout, tv_usec: 0 };
|
||||
|
||||
for(;;){
|
||||
FD_ZERO(&read);
|
||||
FD_SET(m->net.fd, &read);
|
||||
|
||||
ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv);
|
||||
if (ret < 0) {
|
||||
idle( obj );
|
||||
if (rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv) < 0) {
|
||||
rb_raise(eMysql, "query: timeout");
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m->status == MYSQL_STATUS_READY){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int should_schedule_query(){
|
||||
return rb_thread_alone() != 1;
|
||||
}
|
||||
|
||||
/* async_query(sql,timeout=nil)
|
||||
optionally take a block
|
||||
*/
|
||||
/* async_query(sql,timeout=nil) */
|
||||
static VALUE async_query(int argc, VALUE* argv, VALUE obj)
|
||||
{
|
||||
MYSQL* m = GetHandler(obj);
|
||||
@ -1199,24 +874,11 @@ static VALUE async_query(int argc, VALUE* argv, VALUE obj)
|
||||
|
||||
rb_scan_args(argc, argv, "11", &sql, &timeout);
|
||||
|
||||
async_in_progress_set( obj, Qfalse );
|
||||
send_query(obj,sql);
|
||||
|
||||
busy(obj);
|
||||
schedule(obj, timeout);
|
||||
|
||||
send_query( obj, sql );
|
||||
|
||||
if ( should_schedule_query() ){
|
||||
schedule_query(obj, timeout);
|
||||
}
|
||||
|
||||
if (rb_block_given_p()) {
|
||||
rb_yield( get_result(obj) );
|
||||
idle( obj );
|
||||
return obj;
|
||||
}else{
|
||||
idle( obj );
|
||||
return get_result(obj);
|
||||
}
|
||||
}
|
||||
|
||||
#if MYSQL_VERSION_ID >= 40100
|
||||
@ -1317,7 +979,7 @@ static VALUE set_server_option(VALUE obj, VALUE option)
|
||||
static VALUE sqlstate(VALUE obj)
|
||||
{
|
||||
MYSQL *m = GetHandler(obj);
|
||||
return rb_enc_tainted_str_new2(mysql_sqlstate(m));
|
||||
return rb_tainted_str_new2(mysql_sqlstate(m));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1482,16 +1144,16 @@ static VALUE fetch_row(VALUE obj)
|
||||
return Qnil;
|
||||
ary = rb_ary_new2(n);
|
||||
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;
|
||||
}
|
||||
|
||||
/* process_all_hashes (internal helper) */
|
||||
/* process_all_hashes (internal) */
|
||||
static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, int yield)
|
||||
{
|
||||
MYSQL_RES* res = GetMysqlRes(obj);
|
||||
unsigned int n = mysql_num_fields(res);
|
||||
VALUE ary = Qnil;
|
||||
VALUE ary;
|
||||
if(build_array)
|
||||
ary = rb_ary_new();
|
||||
MYSQL_ROW row = mysql_fetch_row(res); // grab one off the top, to determine the rows
|
||||
@ -1512,7 +1174,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
|
||||
if (colname == Qnil) {
|
||||
colname = rb_ary_new2(n);
|
||||
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_ary_store(colname, i, s);
|
||||
}
|
||||
@ -1525,7 +1187,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
|
||||
colname = rb_ary_new2(n);
|
||||
for (i=0; i<n; i++) {
|
||||
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);
|
||||
rb_obj_freeze(s);
|
||||
rb_ary_store(colname, i, s);
|
||||
@ -1541,7 +1203,7 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
|
||||
hash = rb_hash_new();
|
||||
lengths = mysql_fetch_lengths(res);
|
||||
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)
|
||||
rb_ary_push(ary, hash);
|
||||
@ -1558,8 +1220,6 @@ static VALUE process_all_hashes(VALUE obj, VALUE with_table, int build_array, in
|
||||
|
||||
if(yield)
|
||||
return obj;
|
||||
|
||||
return Qnil; /* we should never get here -- this takes out a compiler warning */
|
||||
}
|
||||
|
||||
/* fetch_hash2 (internal) */
|
||||
@ -1577,12 +1237,12 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
|
||||
return Qnil;
|
||||
hash = rb_hash_new();
|
||||
|
||||
if (with_table == Qnil || with_table == Qfalse) {
|
||||
if (with_table == Qfalse) {
|
||||
colname = rb_iv_get(obj, "colname");
|
||||
if (colname == Qnil) {
|
||||
colname = rb_ary_new2(n);
|
||||
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_ary_store(colname, i, s);
|
||||
}
|
||||
@ -1595,7 +1255,7 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
|
||||
colname = rb_ary_new2(n);
|
||||
for (i=0; i<n; i++) {
|
||||
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);
|
||||
rb_obj_freeze(s);
|
||||
rb_ary_store(colname, i, s);
|
||||
@ -1605,7 +1265,7 @@ static VALUE fetch_hash2(VALUE obj, VALUE with_table)
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -1737,7 +1397,7 @@ static VALUE field_hash(VALUE obj)
|
||||
static VALUE field_inspect(VALUE obj)
|
||||
{
|
||||
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));
|
||||
return s;
|
||||
}
|
||||
@ -1796,7 +1456,7 @@ static void mysql_stmt_raise(MYSQL_STMT* 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, "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);
|
||||
}
|
||||
|
||||
@ -1943,12 +1603,12 @@ static VALUE stmt_execute(int argc, VALUE *argv, VALUE obj)
|
||||
s->param.bind[i].buffer = &(s->param.buffer[i]);
|
||||
t.second_part = 0;
|
||||
t.neg = 0;
|
||||
t.second = FIX2INT(rb_ary_entry(a, 0));
|
||||
t.minute = FIX2INT(rb_ary_entry(a, 1));
|
||||
t.hour = FIX2INT(rb_ary_entry(a, 2));
|
||||
t.day = FIX2INT(rb_ary_entry(a, 3));
|
||||
t.month = FIX2INT(rb_ary_entry(a, 4));
|
||||
t.year = FIX2INT(rb_ary_entry(a, 5));
|
||||
t.second = FIX2INT(RARRAY(a)->ptr[0]);
|
||||
t.minute = FIX2INT(RARRAY(a)->ptr[1]);
|
||||
t.hour = FIX2INT(RARRAY(a)->ptr[2]);
|
||||
t.day = FIX2INT(RARRAY(a)->ptr[3]);
|
||||
t.month = FIX2INT(RARRAY(a)->ptr[4]);
|
||||
t.year = FIX2INT(RARRAY(a)->ptr[5]);
|
||||
*(MYSQL_TIME*)&(s->param.buffer[i]) = t;
|
||||
} else if (CLASS_OF(argv[i]) == cMysqlTime) {
|
||||
MYSQL_TIME t;
|
||||
@ -2110,7 +1770,7 @@ static VALUE stmt_fetch(VALUE obj)
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_BIT:
|
||||
#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;
|
||||
default:
|
||||
rb_raise(rb_eTypeError, "unknown buffer_type: %d", s->result.bind[i].buffer_type);
|
||||
@ -2251,7 +1911,7 @@ static VALUE stmt_result_metadata(VALUE obj)
|
||||
mysql_stmt_raise(s->stmt);
|
||||
return Qnil;
|
||||
}
|
||||
return mysqlres2obj(res, Qfalse);
|
||||
return mysqlres2obj(res);
|
||||
}
|
||||
|
||||
/* row_seek(offset) */
|
||||
@ -2299,7 +1959,7 @@ static VALUE stmt_send_long_data(VALUE obj, VALUE col, VALUE data)
|
||||
static VALUE stmt_sqlstate(VALUE 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));
|
||||
}
|
||||
|
||||
/*-------------------------------
|
||||
@ -2509,22 +2169,12 @@ void Init_mysql(void)
|
||||
rb_define_method(cMysql, "query", query, 1);
|
||||
rb_define_method(cMysql, "real_query", query, 1);
|
||||
rb_define_method(cMysql, "c_async_query", async_query, -1);
|
||||
rb_define_method(cMysql, "async_in_progress?", async_in_progress, 0);
|
||||
rb_define_method(cMysql, "async_in_progress=", async_in_progress_set, 1);
|
||||
rb_define_method(cMysql, "send_query", send_query, 1);
|
||||
rb_define_method(cMysql, "simulate_disconnect", simulate_disconnect, 0);
|
||||
rb_define_method(cMysql, "reconnected?", reconnected, 0);
|
||||
rb_define_method(cMysql, "get_result", get_result, 0);
|
||||
rb_define_method(cMysql, "readable?", readable, -1);
|
||||
rb_define_method(cMysql, "retry?", retry, 0);
|
||||
rb_define_method(cMysql, "interrupted?", interrupted, 0);
|
||||
rb_define_method(cMysql, "blocking?", blocking, 0);
|
||||
rb_define_method(cMysql, "gc_disabled?", gc_disabled, 0);
|
||||
rb_define_method(cMysql, "disable_gc=", disable_gc_set, 1);
|
||||
rb_define_method(cMysql, "busy?", is_busy, 0);
|
||||
rb_define_method(cMysql, "idle?", is_idle, 0);
|
||||
rb_define_method(cMysql, "busy=", busy_set, 1);
|
||||
rb_define_method(cMysql, "socket", socket, 0);
|
||||
rb_define_method(cMysql, "socket_type", socket_type, 0);
|
||||
rb_define_method(cMysql, "refresh", refresh, 1);
|
||||
rb_define_method(cMysql, "reload", reload, 0);
|
||||
rb_define_method(cMysql, "select_db", select_db, 1);
|
||||
|
@ -1,23 +1,19 @@
|
||||
require File.dirname(__FILE__) + '/mysql' # load our version of mysql--note
|
||||
# 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.
|
||||
require 'mysql'
|
||||
|
||||
#
|
||||
# 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
|
||||
|
||||
def ruby_async_query(sql, timeout = nil) # known to deadlock TODO
|
||||
def async_query(sql, timeout = nil)
|
||||
send_query(sql)
|
||||
select [ (@sockets ||= {})[socket] ||= IO.new(socket) ], nil, nil, nil
|
||||
get_result
|
||||
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
|
||||
|
||||
class Mysql::Result
|
||||
def all_hashes
|
||||
rows = []
|
||||
each_hash { |row| rows << row }
|
||||
rows
|
||||
end
|
||||
end
|
||||
|
41
mysqlplus.gemspec
Normal file → Executable file
41
mysqlplus.gemspec
Normal file → Executable file
@ -1,30 +1,29 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "mysqlplus"
|
||||
s.version = "0.1.4"
|
||||
s.date = "2010-07-04"
|
||||
s.summary = "Enhanced Ruby MySQL driver with Ruby 1.9 encoding awareness"
|
||||
s.email = "jeremysuriel@gmail.com"
|
||||
s.homepage = "http://github.com/jrmey/mysqlplus"
|
||||
s.version = "0.1.0"
|
||||
s.date = "2008-08-13"
|
||||
s.summary = "Enhanced Ruby MySQL driver"
|
||||
s.email = "oldmoe@gmail.com"
|
||||
s.homepage = "http://github.com/oldmoe/mysqlplus"
|
||||
s.description = "Enhanced Ruby MySQL driver"
|
||||
s.has_rdoc = true
|
||||
s.authors = ["Muhammad A. Ali", "Jeremy Suriel", "John Bintz"]
|
||||
s.authors = ["Muhammad A. Ali"]
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.files = %w[
|
||||
README
|
||||
Rakefile
|
||||
TODO_LIST
|
||||
ext/error_const.h
|
||||
ext/extconf.rb
|
||||
ext/mysql.c
|
||||
lib/mysqlplus.rb
|
||||
mysqlplus.gemspec
|
||||
] + Dir.glob('test/*')
|
||||
s.files = [
|
||||
"mysqlplus.gemspec",
|
||||
"README",
|
||||
"Rakefile",
|
||||
"lib/mysqlplus.rb",
|
||||
"test/test_helper.rb",
|
||||
"test/native_threaded_test.rb",
|
||||
"test/c_threaded_test.rb",
|
||||
"test/evented_test.rb",
|
||||
"ext/error_const.h",
|
||||
"ext/extconf.rb",
|
||||
"ext/mysql.c"
|
||||
]
|
||||
s.rdoc_options = ["--main", "README"]
|
||||
s.extra_rdoc_files = ["README"]
|
||||
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
|
||||
|
||||
|
@ -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'
|
@ -1,37 +0,0 @@
|
||||
require 'create_test_db'
|
||||
|
||||
use_the_all_hashes_method = true
|
||||
|
||||
$count = 5
|
||||
|
||||
$start = Time.now
|
||||
|
||||
$connections = []
|
||||
$count.times do
|
||||
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
||||
end
|
||||
|
||||
|
||||
$threads = []
|
||||
$count.times do |i|
|
||||
$threads << Thread.new do
|
||||
|
||||
query = "select * from test_table"
|
||||
puts "sending query on connection #{i}"
|
||||
conn = $connections[i]
|
||||
result = conn.async_query(query)
|
||||
if use_the_all_hashes_method
|
||||
saved = result.all_hashes
|
||||
else
|
||||
saved = []
|
||||
result.each_hash {|h| saved << h }
|
||||
end
|
||||
result.free
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
puts 'waiting on threads'
|
||||
$threads.each{|t| t.join }
|
||||
|
||||
puts Time.now - $start
|
@ -1,7 +0,0 @@
|
||||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
m = Mysql.real_connect('localhost','root','','mysql')
|
||||
|
||||
m.c_async_query( 'SELECT * FROM user' ) do |result|
|
||||
puts result.inspect
|
||||
end
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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'
|
@ -1,40 +0,0 @@
|
||||
require 'mysqlplus'
|
||||
require 'benchmark'
|
||||
|
||||
with_gc = Mysql.real_connect('localhost','root','','mysql')
|
||||
without_gc = Mysql.real_connect('localhost','root','','mysql')
|
||||
without_gc.disable_gc = true
|
||||
|
||||
$gc_stats = []
|
||||
|
||||
def countable_gc?
|
||||
GC.respond_to? :count
|
||||
end
|
||||
|
||||
def gc_counts( label, scope )
|
||||
$gc_stats << "Objects #{scope} ( #{label} ) #{GC.count}"
|
||||
end
|
||||
|
||||
def with_gc_counts( label )
|
||||
gc_counts( label, 'before' ) if countable_gc?
|
||||
yield
|
||||
gc_counts( label, 'after' ) if countable_gc?
|
||||
end
|
||||
|
||||
n = 1000
|
||||
|
||||
Benchmark.bmbm do |x|
|
||||
x.report( 'With GC' ) do
|
||||
with_gc_counts( 'With GC' ) do
|
||||
n.times{ with_gc.c_async_query( 'SELECT * FROM user' ) }
|
||||
end
|
||||
end
|
||||
GC.start
|
||||
x.report( 'Without GC' ) do
|
||||
with_gc_counts( 'Without GC' ) do
|
||||
n.times{ without_gc.c_async_query( 'SELECT * FROM user' ) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts $gc_stats.join( ' | ' )
|
@ -1,6 +0,0 @@
|
||||
require 'mysqlplus'
|
||||
a = Mysql.real_connect('localhost','root')
|
||||
100.times { a.query("select sleep(0)") }
|
||||
print "pass"
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
m = Mysql.real_connect('localhost','root')
|
||||
m.reconnect = true
|
||||
$count = 0
|
||||
class << m
|
||||
def safe_query( query )
|
||||
begin
|
||||
send_query( query )
|
||||
rescue => e
|
||||
$count += 1
|
||||
puts e.message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
m.safe_query( 'select sleep(1)' )
|
||||
m.safe_query( 'select sleep(1)' )#raises
|
||||
m.simulate_disconnect #fires mysql_library_end
|
||||
m.safe_query( 'select sleep(1)' )
|
||||
m.safe_query( 'select sleep(1)' )#raises
|
||||
m.close
|
||||
m.connect('localhost','root')
|
||||
m.safe_query( 'select sleep(1)' )
|
||||
m.safe_query( 'select sleep(1)' )#raises
|
||||
m.simulate_disconnect
|
||||
raise unless $count == 3
|
||||
m.safe_query( 'BEGIN' )
|
||||
m.safe_query( 'select sleep(1)' ) # raises
|
||||
m.get_result()
|
||||
m.safe_query( 'COMMIT' )
|
||||
m.get_result
|
||||
raise unless $count == 4
|
@ -1,18 +0,0 @@
|
||||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
$m = Mysql.real_connect('localhost','root')
|
||||
#$m.reconnect = true
|
||||
|
||||
def assert_reconnected
|
||||
puts $m.reconnected?().inspect
|
||||
sleep 1
|
||||
yield
|
||||
puts $m.reconnected?().inspect
|
||||
end
|
||||
|
||||
assert_reconnected do
|
||||
$m.simulate_disconnect
|
||||
end
|
||||
assert_reconnected do
|
||||
$m.close
|
||||
end
|
43
test/test_all_hashes.rb
Normal file
43
test/test_all_hashes.rb
Normal file
@ -0,0 +1,43 @@
|
||||
# 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'
|
||||
|
||||
use_the_all_hashes_method = true
|
||||
|
||||
$count = 5
|
||||
|
||||
$start = Time.now
|
||||
|
||||
$connections = []
|
||||
$count.times do
|
||||
$connections << Mysql.real_connect('localhost','root', '', 'local_leadgen_dev')
|
||||
end
|
||||
|
||||
puts 'connection pool ready'
|
||||
|
||||
$threads = []
|
||||
$count.times do |i|
|
||||
$threads << Thread.new do
|
||||
|
||||
query = "select * from campus_zips"
|
||||
puts "sending query on connection #{i}"
|
||||
conn = $connections[i]
|
||||
result = conn.async_query(query)
|
||||
if use_the_all_hashes_method
|
||||
saved = result.all_hashes
|
||||
else
|
||||
saved = []
|
||||
result.each_hash {|h| saved << h }
|
||||
end
|
||||
result.free
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
puts 'waiting on threads'
|
||||
$threads.each{|t| t.join }
|
||||
|
||||
puts Time.now - $start
|
@ -1,3 +1,4 @@
|
||||
require 'rubygems'
|
||||
require 'mysqlplus'
|
||||
|
||||
class MysqlTest
|
||||
|
@ -5,7 +5,8 @@
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@ -15,7 +16,7 @@ $start = Time.now
|
||||
|
||||
$connections = []
|
||||
$count.times do
|
||||
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
||||
$connections << Mysql.real_connect('localhost','root', '', 'local_leadgen_dev')
|
||||
end
|
||||
|
||||
puts 'connection pool ready'
|
||||
@ -27,7 +28,7 @@ $count.times do |i|
|
||||
puts "sending query on connection #{i}"
|
||||
conn = $connections[i]
|
||||
saved = []
|
||||
query = "select * from test_table"
|
||||
query = "select * from campus_zips"
|
||||
if do_the_use_query_optimization
|
||||
conn.query_with_result=false
|
||||
result = conn.async_query(query)
|
@ -1,7 +1,7 @@
|
||||
require 'mysqlplus'
|
||||
require 'rubygems'
|
||||
require 'sequel'
|
||||
|
||||
require 'mysqlplus'
|
||||
class Mysql
|
||||
unless method_defined? :sync_query
|
||||
alias :sync_query :query
|
Loading…
Reference in New Issue
Block a user