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.
|
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
|
|
||||||
|
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,75 +1,23 @@
|
|||||||
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 +29,12 @@ 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
|
# check for 1.9
|
||||||
if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', '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 +49,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 +73,4 @@ File.open('error_const.h', 'w') do |f|
|
|||||||
end
|
end
|
||||||
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")
|
create_makefile("mysql")
|
||||||
|
524
ext/mysql.c
524
ext/mysql.c
@ -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>
|
||||||
@ -93,13 +60,9 @@ struct mysql {
|
|||||||
MYSQL handler;
|
MYSQL handler;
|
||||||
char connection;
|
char connection;
|
||||||
char query_with_result;
|
char query_with_result;
|
||||||
char gc_disabled;
|
|
||||||
char blocking;
|
char blocking;
|
||||||
int async_in_progress;
|
|
||||||
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,12 +175,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE mysqlres2obj(MYSQL_RES* res, VALUE gc_disabled)
|
static VALUE mysqlres2obj(MYSQL_RES* res)
|
||||||
{
|
{
|
||||||
VALUE obj;
|
VALUE obj;
|
||||||
struct mysql_res* resp;
|
struct mysql_res* resp;
|
||||||
@ -227,10 +190,8 @@ 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)
|
if (++store_result_count > GC_STORE_RESULT_LIMIT)
|
||||||
rb_gc();
|
rb_gc();
|
||||||
*/
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,9 +202,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,135 +227,10 @@ 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;
|
|
||||||
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 *) ¶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) */
|
/* 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)
|
static VALUE real_connect(int argc, VALUE* argv, VALUE klass)
|
||||||
{
|
{
|
||||||
@ -422,28 +258,27 @@ 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
|
||||||
if (mysql_real_connect(&myp->handler, h, u, p, pp, s) == NULL)
|
if (mysql_real_connect(&myp->handler, h, u, p, pp, s) == NULL)
|
||||||
#endif
|
#endif
|
||||||
mysql_raise(&myp->handler);
|
mysql_raise(&myp->handler);
|
||||||
|
|
||||||
myp->handler.reconnect = 0;
|
myp->handler.reconnect = 0;
|
||||||
myp->connection = Qtrue;
|
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;
|
myp->query_with_result = Qtrue;
|
||||||
rb_obj_call_init(obj, argc, argv);
|
rb_obj_call_init(obj, argc, argv);
|
||||||
|
|
||||||
//schedule_connect(obj);
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -453,7 +288,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 +296,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
|
||||||
@ -506,9 +341,6 @@ static VALUE real_connect2(int argc, VALUE* argv, VALUE obj)
|
|||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
m->reconnect = 0;
|
m->reconnect = 0;
|
||||||
GetMysqlStruct(obj)->connection = Qtrue;
|
GetMysqlStruct(obj)->connection = Qtrue;
|
||||||
|
|
||||||
optimize_for_async(obj);
|
|
||||||
//schedule_connect(obj);
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -590,7 +422,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 +461,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 +526,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 +538,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 +580,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;
|
||||||
}
|
}
|
||||||
@ -763,7 +595,7 @@ static VALUE list_fields(int argc, VALUE* argv, VALUE obj)
|
|||||||
res = mysql_list_fields(m, StringValuePtr(table), NILorSTRING(field));
|
res = mysql_list_fields(m, StringValuePtr(table), NILorSTRING(field));
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
return mysqlres2obj(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* list_processes() */
|
/* list_processes() */
|
||||||
@ -773,7 +605,7 @@ static VALUE list_processes(VALUE obj)
|
|||||||
MYSQL_RES* res = mysql_list_processes(m);
|
MYSQL_RES* res = mysql_list_processes(m);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
return mysqlres2obj(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* list_tables(table=nil) */
|
/* list_tables(table=nil) */
|
||||||
@ -793,7 +625,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,10 +689,9 @@ 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
|
typedef struct
|
||||||
{
|
{
|
||||||
MYSQL *mysql_instance;
|
MYSQL *mysql_instance;
|
||||||
@ -872,7 +703,7 @@ typedef struct
|
|||||||
static VALUE store_result_to_location(void *settings_in)
|
static VALUE store_result_to_location(void *settings_in)
|
||||||
{
|
{
|
||||||
mysql_result_to_here_t *settings = (mysql_result_to_here_t *) 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;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,13 +718,13 @@ static VALUE store_result(VALUE obj)
|
|||||||
mysql_result_to_here_t linker;
|
mysql_result_to_here_t linker;
|
||||||
linker.mysql_instance = m;
|
linker.mysql_instance = m;
|
||||||
linker.store_it_here = &res;
|
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
|
#endif
|
||||||
|
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
|
|
||||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
return mysqlres2obj(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* thread_id() */
|
/* thread_id() */
|
||||||
@ -909,54 +740,29 @@ static VALUE use_result(VALUE obj)
|
|||||||
MYSQL_RES* res = mysql_use_result(m);
|
MYSQL_RES* res = mysql_use_result(m);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
return mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
return mysqlres2obj(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
if (mysql_field_count(m) != 0)
|
if (mysql_field_count(m) != 0)
|
||||||
mysql_raise(m);
|
mysql_raise(m);
|
||||||
} else {
|
} else {
|
||||||
VALUE robj = mysqlres2obj(res, GetMysqlStruct(obj)->gc_disabled);
|
VALUE robj = mysqlres2obj(res);
|
||||||
rb_ensure(rb_yield, robj, res_free, robj);
|
rb_ensure(rb_yield, robj, res_free, robj);
|
||||||
}
|
}
|
||||||
#if MYSQL_VERSION_ID >= 40101
|
#if MYSQL_VERSION_ID >= 40101
|
||||||
@ -968,16 +774,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;
|
||||||
@ -992,46 +789,17 @@ static VALUE socket(VALUE obj)
|
|||||||
MYSQL* m = GetHandler(obj);
|
MYSQL* m = GetHandler(obj);
|
||||||
return INT2NUM(m->net.fd);
|
return INT2NUM(m->net.fd);
|
||||||
}
|
}
|
||||||
|
/* socket_type */
|
||||||
/* socket_type --currently returns true or false, needs some work */
|
|
||||||
static VALUE socket_type(VALUE obj)
|
static VALUE socket_type(VALUE obj)
|
||||||
{
|
{
|
||||||
MYSQL* m = GetHandler(obj);
|
MYSQL* m = GetHandler(obj);
|
||||||
if(vio_description(m->net.vio))
|
VALUE description = vio_description( m->net.vio );
|
||||||
return Qtrue; // TODO return a ruby string
|
return NILorSTRING( description );
|
||||||
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 );
|
||||||
}
|
|
||||||
|
|
||||||
/* 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) */
|
/* readable(timeout=nil) */
|
||||||
@ -1046,177 +814,71 @@ 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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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) */
|
/* send_query(sql) */
|
||||||
static VALUE send_query(VALUE obj, VALUE sql)
|
static VALUE send_query(VALUE obj, VALUE sql)
|
||||||
{
|
{
|
||||||
MYSQL* m = GetHandler(obj);
|
MYSQL* m = GetHandler(obj);
|
||||||
|
|
||||||
Check_Type(sql, T_STRING);
|
Check_Type(sql, T_STRING);
|
||||||
|
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
||||||
if (GetMysqlStruct(obj)->connection == Qfalse && async_in_progress(obj) == Qtrue ) {
|
rb_raise(eMysql, "query: not connected");
|
||||||
idle( obj );
|
|
||||||
rb_raise(eMysql, "query: not connected");
|
|
||||||
}
|
}
|
||||||
|
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0)
|
||||||
validate_async_query(obj);
|
mysql_raise(m);
|
||||||
|
return Qnil;
|
||||||
if (mysql_send_query(m, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0){
|
|
||||||
idle( obj );
|
|
||||||
mysql_raise(m);
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
|
||||||
async_in_progress_set( obj, Qfalse );
|
|
||||||
|
|
||||||
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
if (GetMysqlStruct(obj)->connection == Qfalse) {
|
||||||
idle( obj );
|
rb_raise(eMysql, "query: not connected");
|
||||||
rb_raise(eMysql, "query: not connected");
|
|
||||||
}
|
}
|
||||||
if (mysql_read_query_result(m) != 0){
|
if (mysql_read_query_result(m) != 0)
|
||||||
idle( obj );
|
mysql_raise(m);
|
||||||
mysql_raise(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VALUE schedule(VALUE obj, VALUE timeout)
|
||||||
{
|
{
|
||||||
MYSQL* m = GetHandler(obj);
|
MYSQL* m = GetHandler(obj);
|
||||||
fd_set read;
|
fd_set read;
|
||||||
int ret;
|
|
||||||
|
|
||||||
timeout = ( NIL_P(timeout) ? m->net.read_timeout : INT2NUM(timeout) );
|
timeout = ( NIL_P(timeout) ? m->net.read_timeout : INT2NUM(timeout) );
|
||||||
|
|
||||||
struct timeval tv = { tv_sec: timeout, tv_usec: 0 };
|
struct timeval tv = { tv_sec: timeout, tv_usec: 0 };
|
||||||
|
|
||||||
for(;;){
|
FD_ZERO(&read);
|
||||||
FD_ZERO(&read);
|
FD_SET(m->net.fd, &read);
|
||||||
FD_SET(m->net.fd, &read);
|
|
||||||
|
|
||||||
ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv);
|
|
||||||
if (ret < 0) {
|
|
||||||
idle( obj );
|
|
||||||
rb_raise(eMysql, "query: timeout");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m->status == MYSQL_STATUS_READY){
|
if (rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv) < 0) {
|
||||||
break;
|
rb_raise(eMysql, "query: timeout");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int should_schedule_query(){
|
/* async_query(sql,timeout=nil) */
|
||||||
return rb_thread_alone() != 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
||||||
VALUE sql, timeout;
|
VALUE sql, timeout;
|
||||||
|
|
||||||
rb_scan_args(argc, argv, "11", &sql, &timeout);
|
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 );
|
return get_result(obj);
|
||||||
|
|
||||||
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
|
#if MYSQL_VERSION_ID >= 40100
|
||||||
@ -1317,7 +979,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 +1144,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 +1174,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 +1187,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 +1203,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 +1220,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 +1237,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 +1255,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 +1265,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 +1397,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 +1456,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 +1603,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 +1770,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);
|
||||||
@ -2251,7 +1911,7 @@ static VALUE stmt_result_metadata(VALUE obj)
|
|||||||
mysql_stmt_raise(s->stmt);
|
mysql_stmt_raise(s->stmt);
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
return mysqlres2obj(res, Qfalse);
|
return mysqlres2obj(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* row_seek(offset) */
|
/* 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)
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------
|
/*-------------------------------
|
||||||
@ -2509,22 +2169,12 @@ void Init_mysql(void)
|
|||||||
rb_define_method(cMysql, "query", query, 1);
|
rb_define_method(cMysql, "query", query, 1);
|
||||||
rb_define_method(cMysql, "real_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, "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, "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, "get_result", get_result, 0);
|
||||||
rb_define_method(cMysql, "readable?", readable, -1);
|
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, "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", socket, 0);
|
||||||
|
rb_define_method(cMysql, "socket_type", socket_type, 0);
|
||||||
rb_define_method(cMysql, "refresh", refresh, 1);
|
rb_define_method(cMysql, "refresh", refresh, 1);
|
||||||
rb_define_method(cMysql, "reload", reload, 0);
|
rb_define_method(cMysql, "reload", reload, 0);
|
||||||
rb_define_method(cMysql, "select_db", select_db, 1);
|
rb_define_method(cMysql, "select_db", select_db, 1);
|
||||||
@ -2825,4 +2475,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"
|
||||||
}
|
}
|
||||||
|
@ -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
41
mysqlplus.gemspec
Normal file → Executable 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
|
||||||
|
|
||||||
|
@ -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'
|
require 'mysqlplus'
|
||||||
|
|
||||||
class MysqlTest
|
class MysqlTest
|
||||||
@ -195,4 +196,4 @@ class ThreadedMysqlTest < MysqlTest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -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)
|
@ -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)
|
Loading…
Reference in New Issue
Block a user