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
## 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)
* 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

View File

@ -234,7 +234,7 @@ then iterating over every row using an #each like method yielding a block:
== 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
* 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)

View File

@ -1,5 +1,42 @@
# 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 '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
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;
#define REQUIRE_OPEN_DB(wrapper) \
if(wrapper->closed) { \
if(wrapper->closed || !wrapper->client->net.vio) { \
rb_raise(cMysql2Error, "closed MySQL connection"); \
return Qnil; \
}
@ -76,18 +76,18 @@ static void rb_mysql_client_mark(void * wrapper) {
static VALUE rb_raise_mysql2_error(MYSQL *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_exc_raise(e);
return Qnil;
}
static VALUE nogvl_init(void *ptr) {
MYSQL *client;
MYSQL **client = (MYSQL **)ptr;
/* may initialize embedded server and read /etc/services off disk */
client = mysql_init((MYSQL *)ptr);
return client ? Qtrue : Qfalse;
*client = mysql_init(NULL);
return *client ? Qtrue : Qfalse;
}
static VALUE nogvl_connect(void *ptr) {
@ -104,42 +104,45 @@ static VALUE nogvl_connect(void *ptr) {
return client ? Qtrue : Qfalse;
}
static VALUE nogvl_close(void *ptr) {
mysql_client_wrapper *wrapper = ptr;
if (!wrapper->closed) {
wrapper->closed = 1;
static void rb_mysql_client_free(void * ptr) {
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
/*
* 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,
* so ignore any potential fcntl errors since they don't matter
*/
#ifndef _WIN32
int flags = fcntl(wrapper->client->net.fd, F_GETFL);
int flags = fcntl(fd, F_GETFL);
if (flags > 0 && !(flags & O_NONBLOCK))
fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
u_long iMode = 1;
ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode);
ioctlsocket(fd, FIONBIO, &iMode);
#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) {
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
nogvl_close(wrapper);
xfree(ptr);
static VALUE nogvl_close(void * ptr) {
mysql_client_wrapper *wrapper = ptr;
if (!wrapper->closed) {
mysql_close(wrapper->client);
wrapper->closed = 1;
}
return Qnil;
}
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);
wrapper->encoding = Qnil;
wrapper->active = 0;
wrapper->closed = 1;
wrapper->client = (MYSQL*)malloc(sizeof(MYSQL));
wrapper->closed = 0;
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.db = NIL_P(database) ? NULL : StringValuePtr(database);
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) {
// 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) {
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;
}
@ -264,7 +264,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
fd_set fdset;
int fd, retval;
int async = 0;
VALUE opts, defaults, read_timeout;
VALUE opts, defaults;
GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper);
@ -290,7 +290,6 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
opts = defaults;
}
Check_Type(args.sql, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
// 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);
}
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) {
// the below code is largely from do_mysql
// 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_SET(fd, &fdset);
retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, tvp);
if (retval == 0) {
rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
}
retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
if (retval < 0) {
rb_sys_fail(0);
@ -356,7 +334,6 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
unsigned long newLen, oldLen;
GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper);
Check_Type(str, T_STRING);
#ifdef HAVE_RUBY_ENCODING_H
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);
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);
if (newLen == oldLen) {
// 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) {
VALUE version = rb_hash_new(), client_info;
GET_CLIENT(self);
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc = rb_default_internal_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) {
VALUE version, server_info;
GET_CLIENT(self);
REQUIRE_OPEN_DB(wrapper);
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *default_internal_enc = rb_default_internal_encoding();
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
#endif
REQUIRE_OPEN_DB(wrapper);
version = rb_hash_new();
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(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) {
GET_CLIENT(self);
my_ulonglong retVal;
REQUIRE_OPEN_DB(wrapper);
retVal = mysql_affected_rows(wrapper->client);
if (retVal == (my_ulonglong)-1) {
rb_raise_mysql2_error(wrapper->client);
}
return ULL2NUM(retVal);
return ULL2NUM(mysql_affected_rows(wrapper->client));
}
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) {
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? */
return rb_raise_mysql2_error(wrapper->client);
}
wrapper->closed = 0;
return self;
}

View File

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

View File

@ -26,7 +26,8 @@ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
if RUBY_PLATFORM =~ /mswin|mingw/
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
mc = Dir[GLOB].first if mc == true
cflags = `#{mc} --cflags`.chomp
@ -57,13 +58,9 @@ end
asplode h unless have_header h
end
unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/
unless RUBY_PLATFORM =~ /mswin/
$CFLAGS << ' -Wall -funroll-loops'
end
# $CFLAGS << ' -O0 -ggdb3 -Wextra'
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
$LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
end
create_makefile('mysql2/mysql2')

View File

@ -4,12 +4,6 @@
#include <ruby.h>
#include <fcntl.h>
#ifndef HAVE_UINT
#define HAVE_UINT
typedef unsigned short ushort;
typedef unsigned int uint;
#endif
#ifdef HAVE_MYSQL_H
#include <mysql.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]);
#ifdef HAVE_RUBY_ENCODING_H
// 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);
} else {
// lookup the encoding configured on this field

View File

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

View File

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

View File

@ -18,9 +18,6 @@ module ActiveRecord
end
module ConnectionAdapters
class Mysql2IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
end
class Mysql2Column < Column
BOOL = "tinyint(1)"
def extract_default(default)
@ -450,7 +447,7 @@ module ActiveRecord
if current_index != row[:Key_name]
next if row[:Key_name] == PRIMARY # skip the primary key
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
indexes.last.columns << row[:Column_name]
@ -620,15 +617,8 @@ module ActiveRecord
# Turn this off. http://dev.rubyonrails.org/ticket/6778
variable_assignments = ['SQL_AUTO_IS_NULL=0']
encoding = @config[:encoding]
# make sure we set the 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)
end

View File

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

View File

@ -8,8 +8,7 @@ module Mysql2
:symbolize_keys => false, # return field names as symbols instead of strings
: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
: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
:cache_rows => true # tells Mysql2 to use it's internal row cache for results
}
def initialize(opts = {})
@ -24,11 +23,6 @@ module Mysql2
# force the encoding to 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))
user = opts[:username]
@ -37,7 +31,7 @@ module Mysql2
port = opts[:port] || 3306
database = opts[:database]
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
end

View File

@ -13,24 +13,20 @@ module Mysql2
end
def notify_readable
detach
begin
@deferable.succeed(@client.async_result)
rescue Exception => e
@deferable.fail(e)
end
detach
end
end
def query(sql, opts={})
if ::EM.reactor_running?
super(sql, opts.merge(:async => true))
deferable = ::EM::DefaultDeferrable.new
::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
deferable
else
super(sql, opts)
end
super(sql, opts.merge(:async => true))
deferable = ::EM::DefaultDeferrable.new
::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
deferable
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|
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.authors = ["Brian Lopez"]
s.date = %q{2010-10-19}
s.date = %q{2010-09-17}
s.email = %q{seniorlopez@gmail.com}
s.extensions = ["ext/mysql2/extconf.rb"]
s.extra_rdoc_files = [
@ -17,7 +17,6 @@ Gem::Specification.new do |s|
]
s.files = [
".gitignore",
".rspec",
"CHANGELOG.md",
"MIT-LICENSE",
"README.rdoc",
@ -30,6 +29,7 @@ Gem::Specification.new do |s|
"benchmark/query_without_mysql_casting.rb",
"benchmark/sequel.rb",
"benchmark/setup_db.rb",
"benchmark/thread_alone.rb",
"examples/eventmachine.rb",
"examples/threaded.rb",
"ext/mysql2/client.c",
@ -46,7 +46,6 @@ Gem::Specification.new do |s|
"lib/mysql2.rb",
"lib/mysql2/client.rb",
"lib/mysql2/em.rb",
"lib/mysql2/em_fiber.rb",
"lib/mysql2/error.rb",
"lib/mysql2/result.rb",
"mysql2.gemspec",
@ -55,11 +54,10 @@ Gem::Specification.new do |s|
"spec/mysql2/error_spec.rb",
"spec/mysql2/result_spec.rb",
"spec/rcov.opts",
"spec/spec.opts",
"spec/spec_helper.rb",
"tasks/benchmarks.rake",
"tasks/compile.rake",
"tasks/jeweler.rake",
"tasks/rspec.rake",
"tasks/vendor_mysql.rake"
]
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
if defined? EventMachine
require 'spec_helper'
require 'mysql2/em'
require 'spec_helper'
require 'mysql2/em'
describe Mysql2::EM::Client do
it "should support async queries" do
results = []
EM.run do
client1 = Mysql2::EM::Client.new
defer1 = client1.query "SELECT sleep(0.1) as first_query"
defer1.callback do |result|
results << result.first
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
describe Mysql2::EM::Client do
it "should support async queries" do
results = []
EM.run do
client1 = Mysql2::EM::Client.new
defer1 = client1.query "SELECT sleep(0.05) as first_query"
defer1.callback do |result|
results << result.first
EM.stop_event_loop
end
results[0].keys.should include("second_query")
results[1].keys.should include("first_query")
end
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
client2 = Mysql2::EM::Client.new
defer2 = client2.query "SELECT sleep(0.025) second_query"
defer2.callback do |result|
results << result.first
end
results[0].keys.should include("first_query")
results[1].keys.should include("second_query")
end
results[0].keys.should include("second_query")
results[1].keys.should include("first_query")
end
else
puts "EventMachine not installed, skipping the specs that use it"
end

View File

@ -23,10 +23,10 @@ describe Mysql2::Client do
end
end
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
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
attr_reader :connect_args
def connect *args
@ -35,12 +35,7 @@ describe Mysql2::Client do
end
end
client = klient.new
(client.connect_args.last.last & (Mysql2::Client::REMEMBER_OPTIONS |
Mysql2::Client::LONG_PASSWORD |
Mysql2::Client::LONG_FLAG |
Mysql2::Client::TRANSACTIONS |
Mysql2::Client::PROTOCOL_41 |
Mysql2::Client::SECURE_CONNECTION)).should be_true
client.connect_args.last.last.should == 0
end
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
@client.close.should be_nil
lambda {
@client.query "SELECT 1"
}.should raise_error(Mysql2::Error)
end
it "should respond to #query" do
@client.should respond_to(:query)
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
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
@client.query "SELECT 1", :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)
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)
# and may fail on a loaded system
if RUBY_PLATFORM !~ /mingw|mswin/
@ -171,34 +137,25 @@ describe Mysql2::Client do
@client.should respond_to(:escape)
end
context "#escape" 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")
end
it "#escape 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")
end
it "should return the passed string if nothing was escaped" do
str = "plain"
@client.escape(str).object_id.should eql(str.object_id)
end
it "#escape should return the passed string if nothing was escaped" do
str = "plain"
@client.escape(str).object_id.should eql(str.object_id)
end
it "should not overflow the thread stack" do
lambda {
Thread.new { @client.escape("'" * 256 * 1024) }.join
}.should_not raise_error(SystemStackError)
end
it "#escape should not overflow the thread stack" do
lambda {
Thread.new { @client.escape("'" * 256 * 1024) }.join
}.should_not raise_error(SystemStackError)
end
it "should not overflow the process stack" do
lambda {
Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
}.should_not raise_error(SystemStackError)
end
it "should require an open connection" do
@client.close
lambda {
@client.escape ""
}.should raise_error(Mysql2::Error)
end
it "#escape should not overflow the process stack" do
lambda {
Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
}.should_not raise_error(SystemStackError)
end
it "should respond to #info" do
@ -246,13 +203,6 @@ describe Mysql2::Client do
server_info[:version].class.should eql(String)
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
context "strings returned by #server_info" 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)
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
lambda {
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
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require 'rspec'
require 'rubygems'
require 'mysql2'
require 'timeout'
RSpec.configure do |config|
Spec::Runner.configure do |config|
config.before(:all) do
client = Mysql2::Client.new :database => 'test'
client.query %[

View File

@ -15,7 +15,8 @@ Rake::ExtensionTask.new("mysql2", gemspec) do |ext|
# DRY options feed into compile or cross-compile process
windows_options = [
"--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

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