Compare commits

..

1 Commits

Author SHA1 Message Date
Luis Lavena
7787f25f3f Attempt to enable static linking for Windows 2010-09-26 17:26:05 -03:00
27 changed files with 165 additions and 344 deletions

2
.rspec
View File

@ -1,2 +0,0 @@
--format documentation
--colour

View File

@ -1,17 +1,5 @@
# Changelog # Changelog
## 0.2.6 (October 19th, 2010)
* version bump since the 0.2.5 win32 binary gems were broken
## 0.2.5 (October 19th, 2010)
* fixes for easier Win32 binary gem deployment for targeting 1.8 and 1.9 in the same gem
* refactor of connection checks and management to avoid race conditions with the GC/threading to prevent the unexpected loss of connections
* update the default flags during connection
* add support for setting wait_timeout on AR adapter
* upgrade to rspec2
* bugfix for an edge case where the GC would clean up a Mysql2::Client object before the underlying MYSQL pointer had been initialized
* fix to CFLAGS to allow compilation on SPARC with sunstudio compiler - Anko painting <anko.com+github@gmail.com>
## 0.2.4 (September 17th, 2010) ## 0.2.4 (September 17th, 2010)
* a few patches for win32 support from Luis Lavena - thanks man! * a few patches for win32 support from Luis Lavena - thanks man!
* bugfix from Eric Wong to avoid a potential stack overflow during Mysql2::Client#escape * bugfix from Eric Wong to avoid a potential stack overflow during Mysql2::Client#escape

View File

@ -234,7 +234,7 @@ then iterating over every row using an #each like method yielding a block:
== Special Thanks == Special Thanks
* Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude * Eric Wong - for the contribution (and informative explanations of) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
* Yury Korolev (http://github.com/yury) - for TONS of help testing the ActiveRecord adapter * Yury Korolev (http://github.com/yury) - for TONS of help testing the ActiveRecord adapter
* Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
* Mike Perham (http://github.com/mperham) - Async ActiveRecord adapter (uses Fibers and EventMachine) * Mike Perham (http://github.com/mperham) - Async ActiveRecord adapter (uses Fibers and EventMachine)

View File

@ -1,5 +1,42 @@
# encoding: UTF-8 # encoding: UTF-8
begin
require 'jeweler'
JEWELER = Jeweler::Tasks.new do |gem|
gem.name = "mysql2"
gem.summary = "A simple, fast Mysql library for Ruby, binding to libmysql"
gem.email = "seniorlopez@gmail.com"
gem.homepage = "http://github.com/brianmario/mysql2"
gem.authors = ["Brian Lopez"]
gem.require_paths = ["lib", "ext"]
gem.extra_rdoc_files = `git ls-files *.rdoc`.split("\n")
gem.files = `git ls-files`.split("\n")
gem.extensions = ["ext/mysql2/extconf.rb"]
gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore)
# gem.rubyforge_project = "mysql2"
end
rescue LoadError
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler -s http://gems.github.com"
end
require 'rake' require 'rake'
require 'spec/rake/spectask'
desc "Run all examples with RCov"
Spec::Rake::SpecTask.new('spec:rcov') do |t|
t.spec_files = FileList['spec/']
t.rcov = true
t.rcov_opts = lambda do
IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
end
end
Spec::Rake::SpecTask.new('spec') do |t|
t.spec_files = FileList['spec/']
t.spec_opts << '--options' << 'spec/spec.opts'
t.verbose = true
t.warning = true
end
task :default => :spec
# Load custom tasks # Load custom tasks
Dir['tasks/*.rake'].sort.each { |f| load f } Dir['tasks/*.rake'].sort.each { |f| load f }

View File

@ -1 +1 @@
0.2.6 0.2.4

20
benchmark/thread_alone.rb Normal file
View File

@ -0,0 +1,20 @@
# encoding: UTF-8
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
require 'rubygems'
require 'benchmark'
require 'mysql2'
iterations = 1000
client = Mysql2::Client.new(:host => "localhost", :username => "root", :database => "test")
query = lambda{ iterations.times{ client.query("SELECT mysql2_test.* FROM mysql2_test") } }
Benchmark.bmbm do |x|
x.report('select') do
query.call
end
x.report('rb_thread_select') do
thread = Thread.new{ sleep(10) }
query.call
thread.kill
end
end

View File

@ -9,7 +9,7 @@ static ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
static ID intern_merge, intern_error_number_eql, intern_sql_state_eql; static ID intern_merge, intern_error_number_eql, intern_sql_state_eql;
#define REQUIRE_OPEN_DB(wrapper) \ #define REQUIRE_OPEN_DB(wrapper) \
if(wrapper->closed) { \ if(wrapper->closed || !wrapper->client->net.vio) { \
rb_raise(cMysql2Error, "closed MySQL connection"); \ rb_raise(cMysql2Error, "closed MySQL connection"); \
return Qnil; \ return Qnil; \
} }
@ -76,18 +76,18 @@ static void rb_mysql_client_mark(void * wrapper) {
static VALUE rb_raise_mysql2_error(MYSQL *client) { static VALUE rb_raise_mysql2_error(MYSQL *client) {
VALUE e = rb_exc_new2(cMysql2Error, mysql_error(client)); VALUE e = rb_exc_new2(cMysql2Error, mysql_error(client));
rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(client))); rb_funcall(e, intern_error_number_eql, 1, INT2NUM(mysql_errno(client)));
rb_funcall(e, intern_sql_state_eql, 1, rb_tainted_str_new2(mysql_sqlstate(client))); rb_funcall(e, intern_sql_state_eql, 1, rb_tainted_str_new2(mysql_sqlstate(client)));
rb_exc_raise(e); rb_exc_raise(e);
return Qnil; return Qnil;
} }
static VALUE nogvl_init(void *ptr) { static VALUE nogvl_init(void *ptr) {
MYSQL *client; MYSQL **client = (MYSQL **)ptr;
/* may initialize embedded server and read /etc/services off disk */ /* may initialize embedded server and read /etc/services off disk */
client = mysql_init((MYSQL *)ptr); *client = mysql_init(NULL);
return client ? Qtrue : Qfalse; return *client ? Qtrue : Qfalse;
} }
static VALUE nogvl_connect(void *ptr) { static VALUE nogvl_connect(void *ptr) {
@ -104,42 +104,45 @@ static VALUE nogvl_connect(void *ptr) {
return client ? Qtrue : Qfalse; return client ? Qtrue : Qfalse;
} }
static VALUE nogvl_close(void *ptr) { static void rb_mysql_client_free(void * ptr) {
mysql_client_wrapper *wrapper = ptr; mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
if (!wrapper->closed) {
wrapper->closed = 1;
/*
* we'll send a QUIT message to the server, but that message is more of a
* formality than a hard requirement since the socket is getting shutdown
* anyways, so ensure the socket write does not block our interpreter
*/
int fd = wrapper->client->net.fd;
if (fd >= 0) {
/* /*
* we'll send a QUIT message to the server, but that message is more of a
* formality than a hard requirement since the socket is getting shutdown
* anyways, so ensure the socket write does not block our interpreter
*
*
* if the socket is dead we have no chance of blocking, * if the socket is dead we have no chance of blocking,
* so ignore any potential fcntl errors since they don't matter * so ignore any potential fcntl errors since they don't matter
*/ */
#ifndef _WIN32 #ifndef _WIN32
int flags = fcntl(wrapper->client->net.fd, F_GETFL); int flags = fcntl(fd, F_GETFL);
if (flags > 0 && !(flags & O_NONBLOCK)) if (flags > 0 && !(flags & O_NONBLOCK))
fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK); fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else #else
u_long iMode = 1; u_long iMode = 1;
ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode); ioctlsocket(fd, FIONBIO, &iMode);
#endif #endif
mysql_close(wrapper->client);
free(wrapper->client);
} }
return Qnil; /* It's safe to call mysql_close() on an already closed connection. */
if (!wrapper->closed) {
mysql_close(wrapper->client);
}
xfree(ptr);
} }
static void rb_mysql_client_free(void * ptr) { static VALUE nogvl_close(void * ptr) {
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr; mysql_client_wrapper *wrapper = ptr;
if (!wrapper->closed) {
nogvl_close(wrapper); mysql_close(wrapper->client);
wrapper->closed = 1;
xfree(ptr); }
return Qnil;
} }
static VALUE allocate(VALUE klass) { static VALUE allocate(VALUE klass) {
@ -148,8 +151,7 @@ static VALUE allocate(VALUE klass) {
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper); obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
wrapper->encoding = Qnil; wrapper->encoding = Qnil;
wrapper->active = 0; wrapper->active = 0;
wrapper->closed = 1; wrapper->closed = 0;
wrapper->client = (MYSQL*)malloc(sizeof(MYSQL));
return obj; return obj;
} }
@ -164,7 +166,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass); args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
args.db = NIL_P(database) ? NULL : StringValuePtr(database); args.db = NIL_P(database) ? NULL : StringValuePtr(database);
args.mysql = wrapper->client; args.mysql = wrapper->client;
args.client_flag = NUM2ULONG(flags); args.client_flag = NUM2INT(flags);
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) { if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
// unable to connect // unable to connect
@ -183,9 +185,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
static VALUE rb_mysql_client_close(VALUE self) { static VALUE rb_mysql_client_close(VALUE self) {
GET_CLIENT(self); GET_CLIENT(self);
if (!wrapper->closed) { rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
}
return Qnil; return Qnil;
} }
@ -264,7 +264,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
fd_set fdset; fd_set fdset;
int fd, retval; int fd, retval;
int async = 0; int async = 0;
VALUE opts, defaults, read_timeout; VALUE opts, defaults;
GET_CLIENT(self); GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper); REQUIRE_OPEN_DB(wrapper);
@ -290,7 +290,6 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
opts = defaults; opts = defaults;
} }
Check_Type(args.sql, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
// ensure the string is in the encoding the connection is expecting // ensure the string is in the encoding the connection is expecting
@ -303,23 +302,6 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
return rb_raise_mysql2_error(wrapper->client); return rb_raise_mysql2_error(wrapper->client);
} }
read_timeout = rb_iv_get(self, "@read_timeout");
struct timeval tv;
struct timeval* tvp = NULL;
if (!NIL_P(read_timeout)) {
Check_Type(read_timeout, T_FIXNUM);
tvp = &tv;
long int sec = FIX2INT(read_timeout);
// TODO: support partial seconds?
// also, this check is here for sanity, we also check up in Ruby
if (sec >= 0) {
tvp->tv_sec = sec;
} else {
rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
}
tvp->tv_usec = 0;
}
if (!async) { if (!async) {
// the below code is largely from do_mysql // the below code is largely from do_mysql
// http://github.com/datamapper/do // http://github.com/datamapper/do
@ -328,11 +310,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
FD_ZERO(&fdset); FD_ZERO(&fdset);
FD_SET(fd, &fdset); FD_SET(fd, &fdset);
retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, tvp); retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
if (retval == 0) {
rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
}
if (retval < 0) { if (retval < 0) {
rb_sys_fail(0); rb_sys_fail(0);
@ -356,7 +334,6 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
unsigned long newLen, oldLen; unsigned long newLen, oldLen;
GET_CLIENT(self); GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper);
Check_Type(str, T_STRING); Check_Type(str, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *default_internal_enc = rb_default_internal_encoding();
@ -368,6 +345,7 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
oldLen = RSTRING_LEN(str); oldLen = RSTRING_LEN(str);
newStr = rb_str_new(0, oldLen*2+1); newStr = rb_str_new(0, oldLen*2+1);
REQUIRE_OPEN_DB(wrapper);
newLen = mysql_real_escape_string(wrapper->client, RSTRING_PTR(newStr), StringValuePtr(str), oldLen); newLen = mysql_real_escape_string(wrapper->client, RSTRING_PTR(newStr), StringValuePtr(str), oldLen);
if (newLen == oldLen) { if (newLen == oldLen) {
// no need to return a new ruby string if nothing changed // no need to return a new ruby string if nothing changed
@ -387,7 +365,6 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
static VALUE rb_mysql_client_info(VALUE self) { static VALUE rb_mysql_client_info(VALUE self) {
VALUE version = rb_hash_new(), client_info; VALUE version = rb_hash_new(), client_info;
GET_CLIENT(self); GET_CLIENT(self);
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *default_internal_enc = rb_default_internal_encoding();
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
@ -408,13 +385,13 @@ static VALUE rb_mysql_client_info(VALUE self) {
static VALUE rb_mysql_client_server_info(VALUE self) { static VALUE rb_mysql_client_server_info(VALUE self) {
VALUE version, server_info; VALUE version, server_info;
GET_CLIENT(self); GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper);
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *default_internal_enc = rb_default_internal_encoding();
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
#endif #endif
REQUIRE_OPEN_DB(wrapper);
version = rb_hash_new(); version = rb_hash_new();
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client))); rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
server_info = rb_str_new2(mysql_get_server_info(wrapper->client)); server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
@ -442,14 +419,8 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
static VALUE rb_mysql_client_affected_rows(VALUE self) { static VALUE rb_mysql_client_affected_rows(VALUE self) {
GET_CLIENT(self); GET_CLIENT(self);
my_ulonglong retVal;
REQUIRE_OPEN_DB(wrapper); REQUIRE_OPEN_DB(wrapper);
retVal = mysql_affected_rows(wrapper->client); return ULL2NUM(mysql_affected_rows(wrapper->client));
if (retVal == (my_ulonglong)-1) {
rb_raise_mysql2_error(wrapper->client);
}
return ULL2NUM(retVal);
} }
static VALUE set_reconnect(VALUE self, VALUE value) { static VALUE set_reconnect(VALUE self, VALUE value) {
@ -529,12 +500,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
static VALUE init_connection(VALUE self) { static VALUE init_connection(VALUE self) {
GET_CLIENT(self); GET_CLIENT(self);
if (rb_thread_blocking_region(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) { if (rb_thread_blocking_region(nogvl_init, ((void *) &wrapper->client), RUBY_UBF_IO, 0) == Qfalse) {
/* TODO: warning - not enough memory? */ /* TODO: warning - not enough memory? */
return rb_raise_mysql2_error(wrapper->client); return rb_raise_mysql2_error(wrapper->client);
} }
wrapper->closed = 0;
return self; return self;
} }

View File

@ -33,8 +33,8 @@ void init_mysql2_client();
typedef struct { typedef struct {
VALUE encoding; VALUE encoding;
char active; short int active;
char closed; short int closed;
MYSQL *client; MYSQL *client;
} mysql_client_wrapper; } mysql_client_wrapper;

View File

@ -26,7 +26,8 @@ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
if RUBY_PLATFORM =~ /mswin|mingw/ if RUBY_PLATFORM =~ /mswin|mingw/
inc, lib = dir_config('mysql') inc, lib = dir_config('mysql')
exit 1 unless have_library("libmysql") library = enable_config('static') ? 'mysqlclient' : 'libmysql'
exit 1 unless have_library(library)
elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
mc = Dir[GLOB].first if mc == true mc = Dir[GLOB].first if mc == true
cflags = `#{mc} --cflags`.chomp cflags = `#{mc} --cflags`.chomp
@ -57,13 +58,9 @@ end
asplode h unless have_header h asplode h unless have_header h
end end
unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/ unless RUBY_PLATFORM =~ /mswin/
$CFLAGS << ' -Wall -funroll-loops' $CFLAGS << ' -Wall -funroll-loops'
end end
# $CFLAGS << ' -O0 -ggdb3 -Wextra' # $CFLAGS << ' -O0 -ggdb3 -Wextra'
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
$LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
end
create_makefile('mysql2/mysql2') create_makefile('mysql2/mysql2')

View File

@ -4,12 +4,6 @@
#include <ruby.h> #include <ruby.h>
#include <fcntl.h> #include <fcntl.h>
#ifndef HAVE_UINT
#define HAVE_UINT
typedef unsigned short ushort;
typedef unsigned int uint;
#endif
#ifdef HAVE_MYSQL_H #ifdef HAVE_MYSQL_H
#include <mysql.h> #include <mysql.h>
#include <mysql_com.h> #include <mysql_com.h>

View File

@ -248,7 +248,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
val = rb_str_new(row[i], fieldLengths[i]); val = rb_str_new(row[i], fieldLengths[i]);
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
// if binary flag is set, respect it's wishes // if binary flag is set, respect it's wishes
if (fields[i].flags & BINARY_FLAG && fields[i].charsetnr == 63) { if (fields[i].flags & BINARY_FLAG) {
rb_enc_associate(val, binaryEncoding); rb_enc_associate(val, binaryEncoding);
} else { } else {
// lookup the encoding configured on this field // lookup the encoding configured on this field

View File

@ -8,10 +8,10 @@ typedef struct {
VALUE fields; VALUE fields;
VALUE rows; VALUE rows;
VALUE encoding; VALUE encoding;
unsigned int numberOfFields; long numberOfFields;
unsigned long numberOfRows; unsigned long numberOfRows;
unsigned long lastRowProcessed; unsigned long lastRowProcessed;
char resultFreed; short int resultFreed;
MYSQL_RES *result; MYSQL_RES *result;
} mysql2_result_wrapper; } mysql2_result_wrapper;

View File

@ -35,6 +35,7 @@ module Mysql2
results = @client.async_result results = @client.async_result
@deferable.succeed(results) @deferable.succeed(results)
rescue Exception => e rescue Exception => e
puts e.backtrace.join("\n\t")
@deferable.fail(e) @deferable.fail(e)
end end
end end

View File

@ -18,9 +18,6 @@ module ActiveRecord
end end
module ConnectionAdapters module ConnectionAdapters
class Mysql2IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
end
class Mysql2Column < Column class Mysql2Column < Column
BOOL = "tinyint(1)" BOOL = "tinyint(1)"
def extract_default(default) def extract_default(default)
@ -450,7 +447,7 @@ module ActiveRecord
if current_index != row[:Key_name] if current_index != row[:Key_name]
next if row[:Key_name] == PRIMARY # skip the primary key next if row[:Key_name] == PRIMARY # skip the primary key
current_index = row[:Key_name] current_index = row[:Key_name]
indexes << Mysql2IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], []) indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], [])
end end
indexes.last.columns << row[:Column_name] indexes.last.columns << row[:Column_name]
@ -620,15 +617,8 @@ module ActiveRecord
# Turn this off. http://dev.rubyonrails.org/ticket/6778 # Turn this off. http://dev.rubyonrails.org/ticket/6778
variable_assignments = ['SQL_AUTO_IS_NULL=0'] variable_assignments = ['SQL_AUTO_IS_NULL=0']
encoding = @config[:encoding] encoding = @config[:encoding]
# make sure we set the encoding
variable_assignments << "NAMES '#{encoding}'" if encoding variable_assignments << "NAMES '#{encoding}'" if encoding
# increase timeout so mysql server doesn't disconnect us
wait_timeout = @config[:wait_timeout]
wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
variable_assignments << "@@wait_timeout = #{wait_timeout}"
execute("SET #{variable_assignments.join(', ')}", :skip_logging) execute("SET #{variable_assignments.join(', ')}", :skip_logging)
end end

View File

@ -12,5 +12,5 @@ require 'mysql2/result'
# #
# A modern, simple and very fast Mysql library for Ruby - binding to libmysql # A modern, simple and very fast Mysql library for Ruby - binding to libmysql
module Mysql2 module Mysql2
VERSION = "0.2.6" VERSION = "0.2.4"
end end

View File

@ -8,8 +8,7 @@ module Mysql2
:symbolize_keys => false, # return field names as symbols instead of strings :symbolize_keys => false, # return field names as symbols instead of strings
:database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in :database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in
:application_timezone => nil, # timezone Mysql2 will convert to before handing the object back to the caller :application_timezone => nil, # timezone Mysql2 will convert to before handing the object back to the caller
:cache_rows => true, # tells Mysql2 to use it's internal row cache for results :cache_rows => true # tells Mysql2 to use it's internal row cache for results
:connect_flags => REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION
} }
def initialize(opts = {}) def initialize(opts = {})
@ -24,11 +23,6 @@ module Mysql2
# force the encoding to utf8 # force the encoding to utf8
self.charset_name = opts[:encoding] || 'utf8' self.charset_name = opts[:encoding] || 'utf8'
@read_timeout = opts[:read_timeout]
if @read_timeout and @read_timeout < 0
raise Mysql2::Error, "read_timeout must be a positive integer, you passed #{@read_timeout}"
end
ssl_set(*opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslciper)) ssl_set(*opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslciper))
user = opts[:username] user = opts[:username]
@ -37,7 +31,7 @@ module Mysql2
port = opts[:port] || 3306 port = opts[:port] || 3306
database = opts[:database] database = opts[:database]
socket = opts[:socket] socket = opts[:socket]
flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags] flags = opts[:flags] || 0
connect user, pass, host, port, database, socket, flags connect user, pass, host, port, database, socket, flags
end end

View File

@ -13,24 +13,20 @@ module Mysql2
end end
def notify_readable def notify_readable
detach
begin begin
@deferable.succeed(@client.async_result) @deferable.succeed(@client.async_result)
rescue Exception => e rescue Exception => e
@deferable.fail(e) @deferable.fail(e)
end end
detach
end end
end end
def query(sql, opts={}) def query(sql, opts={})
if ::EM.reactor_running? super(sql, opts.merge(:async => true))
super(sql, opts.merge(:async => true)) deferable = ::EM::DefaultDeferrable.new
deferable = ::EM::DefaultDeferrable.new ::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true deferable
deferable
else
super(sql, opts)
end
end end
end end
end end

View File

@ -1,29 +0,0 @@
# encoding: utf-8
require 'mysql2/em'
require 'fiber'
module Mysql2
module EM
module Fiber
class Client < ::Mysql2::EM::Client
def query(sql, opts={})
if ::EM.reactor_running?
deferable = super(sql, opts)
fiber = ::Fiber.current
deferable.callback do |result|
fiber.resume(result)
end
deferable.errback do |err|
fiber.resume(err)
end
::Fiber.yield
else
super(sql, opts)
end
end
end
end
end
end

View File

@ -5,11 +5,11 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = %q{mysql2} s.name = %q{mysql2}
s.version = "0.2.6" s.version = "0.2.4"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Brian Lopez"] s.authors = ["Brian Lopez"]
s.date = %q{2010-10-19} s.date = %q{2010-09-17}
s.email = %q{seniorlopez@gmail.com} s.email = %q{seniorlopez@gmail.com}
s.extensions = ["ext/mysql2/extconf.rb"] s.extensions = ["ext/mysql2/extconf.rb"]
s.extra_rdoc_files = [ s.extra_rdoc_files = [
@ -17,7 +17,6 @@ Gem::Specification.new do |s|
] ]
s.files = [ s.files = [
".gitignore", ".gitignore",
".rspec",
"CHANGELOG.md", "CHANGELOG.md",
"MIT-LICENSE", "MIT-LICENSE",
"README.rdoc", "README.rdoc",
@ -30,6 +29,7 @@ Gem::Specification.new do |s|
"benchmark/query_without_mysql_casting.rb", "benchmark/query_without_mysql_casting.rb",
"benchmark/sequel.rb", "benchmark/sequel.rb",
"benchmark/setup_db.rb", "benchmark/setup_db.rb",
"benchmark/thread_alone.rb",
"examples/eventmachine.rb", "examples/eventmachine.rb",
"examples/threaded.rb", "examples/threaded.rb",
"ext/mysql2/client.c", "ext/mysql2/client.c",
@ -46,7 +46,6 @@ Gem::Specification.new do |s|
"lib/mysql2.rb", "lib/mysql2.rb",
"lib/mysql2/client.rb", "lib/mysql2/client.rb",
"lib/mysql2/em.rb", "lib/mysql2/em.rb",
"lib/mysql2/em_fiber.rb",
"lib/mysql2/error.rb", "lib/mysql2/error.rb",
"lib/mysql2/result.rb", "lib/mysql2/result.rb",
"mysql2.gemspec", "mysql2.gemspec",
@ -55,11 +54,10 @@ Gem::Specification.new do |s|
"spec/mysql2/error_spec.rb", "spec/mysql2/error_spec.rb",
"spec/mysql2/result_spec.rb", "spec/mysql2/result_spec.rb",
"spec/rcov.opts", "spec/rcov.opts",
"spec/spec.opts",
"spec/spec_helper.rb", "spec/spec_helper.rb",
"tasks/benchmarks.rake", "tasks/benchmarks.rake",
"tasks/compile.rake", "tasks/compile.rake",
"tasks/jeweler.rake",
"tasks/rspec.rake",
"tasks/vendor_mysql.rake" "tasks/vendor_mysql.rake"
] ]
s.homepage = %q{http://github.com/brianmario/mysql2} s.homepage = %q{http://github.com/brianmario/mysql2}

View File

@ -1,22 +0,0 @@
# encoding: UTF-8
if defined? EventMachine && defined? Fiber
require 'spec_helper'
require 'mysql2/em_fiber'
describe Mysql2::EM::Fiber::Client do
it 'should support queries' do
results = []
EM.run do
Fiber.new {
client1 = Mysql2::EM::Fiber::Client.new
results = client1.query "SELECT sleep(0.1) as first_query"
EM.stop_event_loop
}.resume
end
results.first.keys.should include("first_query")
end
end
else
puts "Either EventMachine or Fibers not available. Skipping tests that use them."
end

View File

@ -1,49 +1,26 @@
# encoding: UTF-8 # encoding: UTF-8
if defined? EventMachine require 'spec_helper'
require 'spec_helper' require 'mysql2/em'
require 'mysql2/em'
describe Mysql2::EM::Client do describe Mysql2::EM::Client do
it "should support async queries" do it "should support async queries" do
results = [] results = []
EM.run do EM.run do
client1 = Mysql2::EM::Client.new client1 = Mysql2::EM::Client.new
defer1 = client1.query "SELECT sleep(0.1) as first_query" defer1 = client1.query "SELECT sleep(0.05) as first_query"
defer1.callback do |result| defer1.callback do |result|
results << result.first results << result.first
EM.stop_event_loop EM.stop_event_loop
end
client2 = Mysql2::EM::Client.new
defer2 = client2.query "SELECT sleep(0.025) second_query"
defer2.callback do |result|
results << result.first
end
end end
results[0].keys.should include("second_query") client2 = Mysql2::EM::Client.new
results[1].keys.should include("first_query") defer2 = client2.query "SELECT sleep(0.025) second_query"
end defer2.callback do |result|
results << result.first
it "should support queries in callbacks" do
results = []
EM.run do
client = Mysql2::EM::Client.new
defer1 = client.query "SELECT sleep(0.025) as first_query"
defer1.callback do |result|
results << result.first
defer2 = client.query "SELECT sleep(0.025) as second_query"
defer2.callback do |result|
results << result.first
EM.stop_event_loop
end
end
end end
results[0].keys.should include("first_query")
results[1].keys.should include("second_query")
end end
results[0].keys.should include("second_query")
results[1].keys.should include("first_query")
end end
else end
puts "EventMachine not installed, skipping the specs that use it"
end

View File

@ -23,10 +23,10 @@ describe Mysql2::Client do
end end
end end
client = klient.new :flags => Mysql2::Client::FOUND_ROWS client = klient.new :flags => Mysql2::Client::FOUND_ROWS
(client.connect_args.last.last & Mysql2::Client::FOUND_ROWS).should be_true client.connect_args.last.last.should == Mysql2::Client::FOUND_ROWS
end end
it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do it "should default flags to 0" do
klient = Class.new(Mysql2::Client) do klient = Class.new(Mysql2::Client) do
attr_reader :connect_args attr_reader :connect_args
def connect *args def connect *args
@ -35,12 +35,7 @@ describe Mysql2::Client do
end end
end end
client = klient.new client = klient.new
(client.connect_args.last.last & (Mysql2::Client::REMEMBER_OPTIONS | client.connect_args.last.last.should == 0
Mysql2::Client::LONG_PASSWORD |
Mysql2::Client::LONG_FLAG |
Mysql2::Client::TRANSACTIONS |
Mysql2::Client::PROTOCOL_41 |
Mysql2::Client::SECURE_CONNECTION)).should be_true
end end
it "should have a global default_query_options hash" do it "should have a global default_query_options hash" do
@ -76,28 +71,13 @@ describe Mysql2::Client do
it "should be able to close properly" do it "should be able to close properly" do
@client.close.should be_nil @client.close.should be_nil
lambda {
@client.query "SELECT 1"
}.should raise_error(Mysql2::Error)
end end
it "should respond to #query" do it "should respond to #query" do
@client.should respond_to(:query) @client.should respond_to(:query)
end end
it "should expect read_timeout to be a positive integer" do
lambda {
Mysql2::Client.new(:read_timeout => -1)
}.should raise_error(Mysql2::Error)
end
context "#query" do context "#query" do
it "should only accept strings as the query parameter" do
lambda {
@client.query ["SELECT 'not right'"]
}.should raise_error(TypeError)
end
it "should accept an options hash that inherits from Mysql2::Client.default_query_options" do it "should accept an options hash that inherits from Mysql2::Client.default_query_options" do
@client.query "SELECT 1", :something => :else @client.query "SELECT 1", :something => :else
@client.query_options.should eql(@client.query_options.merge(:something => :else)) @client.query_options.should eql(@client.query_options.merge(:something => :else))
@ -123,20 +103,6 @@ describe Mysql2::Client do
}.should raise_error(Mysql2::Error) }.should raise_error(Mysql2::Error)
end end
it "should require an open connection" do
@client.close
lambda {
@client.query "SELECT 1"
}.should raise_error(Mysql2::Error)
end
it "should timeout if we wait longer than :read_timeout" do
client = Mysql2::Client.new(:read_timeout => 1)
lambda {
client.query("SELECT sleep(2)")
}.should raise_error(Mysql2::Error)
end
# XXX this test is not deterministic (because Unix signal handling is not) # XXX this test is not deterministic (because Unix signal handling is not)
# and may fail on a loaded system # and may fail on a loaded system
if RUBY_PLATFORM !~ /mingw|mswin/ if RUBY_PLATFORM !~ /mingw|mswin/
@ -171,34 +137,25 @@ describe Mysql2::Client do
@client.should respond_to(:escape) @client.should respond_to(:escape)
end end
context "#escape" do it "#escape should return a new SQL-escape version of the passed string" do
it "should return a new SQL-escape version of the passed string" do @client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
@client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno") end
end
it "should return the passed string if nothing was escaped" do it "#escape should return the passed string if nothing was escaped" do
str = "plain" str = "plain"
@client.escape(str).object_id.should eql(str.object_id) @client.escape(str).object_id.should eql(str.object_id)
end end
it "should not overflow the thread stack" do it "#escape should not overflow the thread stack" do
lambda { lambda {
Thread.new { @client.escape("'" * 256 * 1024) }.join Thread.new { @client.escape("'" * 256 * 1024) }.join
}.should_not raise_error(SystemStackError) }.should_not raise_error(SystemStackError)
end end
it "should not overflow the process stack" do it "#escape should not overflow the process stack" do
lambda { lambda {
Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
}.should_not raise_error(SystemStackError) }.should_not raise_error(SystemStackError)
end
it "should require an open connection" do
@client.close
lambda {
@client.escape ""
}.should raise_error(Mysql2::Error)
end
end end
it "should respond to #info" do it "should respond to #info" do
@ -246,13 +203,6 @@ describe Mysql2::Client do
server_info[:version].class.should eql(String) server_info[:version].class.should eql(String)
end end
it "#server_info should require an open connection" do
@client.close
lambda {
@client.server_info
}.should raise_error(Mysql2::Error)
end
if defined? Encoding if defined? Encoding
context "strings returned by #server_info" do context "strings returned by #server_info" do
it "should default to the connection's encoding if Encoding.default_internal is nil" do it "should default to the connection's encoding if Encoding.default_internal is nil" do
@ -281,13 +231,6 @@ describe Mysql2::Client do
@client.socket.should_not eql(0) @client.socket.should_not eql(0)
end end
it "#socket should require an open connection" do
@client.close
lambda {
@client.socket
}.should raise_error(Mysql2::Error)
end
it "should raise a Mysql2::Error exception upon connection failure" do it "should raise a Mysql2::Error exception upon connection failure" do
lambda { lambda {
bad_client = Mysql2::Client.new :host => "dfjhdi9wrhw", :username => 'asdfasdf8d2h' bad_client = Mysql2::Client.new :host => "dfjhdi9wrhw", :username => 'asdfasdf8d2h'

2
spec/spec.opts Normal file
View File

@ -0,0 +1,2 @@
--format specdoc
--colour

View File

@ -1,11 +1,10 @@
# encoding: UTF-8 # encoding: UTF-8
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) require 'rubygems'
require 'rspec'
require 'mysql2' require 'mysql2'
require 'timeout' require 'timeout'
RSpec.configure do |config| Spec::Runner.configure do |config|
config.before(:all) do config.before(:all) do
client = Mysql2::Client.new :database => 'test' client = Mysql2::Client.new :database => 'test'
client.query %[ client.query %[

View File

@ -15,7 +15,8 @@ Rake::ExtensionTask.new("mysql2", gemspec) do |ext|
# DRY options feed into compile or cross-compile process # DRY options feed into compile or cross-compile process
windows_options = [ windows_options = [
"--with-mysql-include=#{mysql_lib}/include", "--with-mysql-include=#{mysql_lib}/include",
"--with-mysql-lib=#{mysql_lib}/lib/opt" "--with-mysql-lib=#{mysql_lib}/lib/opt",
'--enable-static'
] ]
# automatically add build options to avoid need of manual input # automatically add build options to avoid need of manual input

View File

@ -1,17 +0,0 @@
begin
require 'jeweler'
JEWELER = Jeweler::Tasks.new do |gem|
gem.name = "mysql2"
gem.summary = "A simple, fast Mysql library for Ruby, binding to libmysql"
gem.email = "seniorlopez@gmail.com"
gem.homepage = "http://github.com/brianmario/mysql2"
gem.authors = ["Brian Lopez"]
gem.require_paths = ["lib", "ext"]
gem.extra_rdoc_files = `git ls-files *.rdoc`.split("\n")
gem.files = `git ls-files`.split("\n")
gem.extensions = ["ext/mysql2/extconf.rb"]
gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore)
end
rescue LoadError
puts "jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
end

View File

@ -1,16 +0,0 @@
begin
require 'rspec'
require 'rspec/core/rake_task'
desc "Run all examples with RCov"
RSpec::Core::RakeTask.new('spec:rcov') do |t|
t.rcov = true
end
RSpec::Core::RakeTask.new('spec') do |t|
t.verbose = true
end
task :default => :spec
rescue LoadError
puts "rspec, or one of its dependencies, is not available. Install it with: sudo gem install rspec"
end