Compare commits
50 Commits
static-lin
...
master
Author | SHA1 | Date |
---|---|---|
John Bintz | b99e0a057a | |
Brian Lopez | 52f62c8b44 | |
Brian Lopez | 66f595a406 | |
dan sinclair | 5f6ff0d4a8 | |
Brian Lopez | 80697d11b2 | |
dan sinclair | dac2f02c9b | |
dan sinclair | 9d49728c30 | |
dj2 | c2e2f0c46c | |
Youhei Kondou | 550c68cade | |
Brian Lopez | d48846f13b | |
Brian Lopez | 99766a2303 | |
Brian Lopez | badc5e04e3 | |
Brian Lopez | 974a5658d6 | |
Brian Lopez | 60b8d061ac | |
Brian Lopez | 2b37ace3dc | |
Brian Lopez | 1057f976d0 | |
Brian Lopez | 90ddb63e52 | |
Brian Lopez | 426cff8adc | |
Brian Lopez | f79bf6261e | |
Brian Lopez | 05df9e312d | |
Brian Lopez | 60fd02ebe6 | |
Brian Lopez | 7e75f5ed4c | |
Anko painting | e7dcf37bd4 | |
Brian Lopez | 0b3b63305e | |
Brian Lopez | 47405ae1f0 | |
Brian Lopez | a8041fed21 | |
Brian Lopez | e823b9ec0d | |
Brian Lopez | c8020f29ac | |
Brian Lopez | 3c6959a8bb | |
Brian Lopez | 410e914411 | |
Brian Lopez | 3b6229771a | |
Brian Lopez | 0ae583fe64 | |
Brian Lopez | 22c9ff48a8 | |
Brian Lopez | 0a7e7ee475 | |
Brian Lopez | ba4f3612b6 | |
Brian Lopez | 841ee2bba4 | |
Brian Lopez | 6e5cda6a52 | |
Brian Lopez | 832eb2d247 | |
Brian Lopez | c394122fd9 | |
Brian Lopez | a6b5e9c28c | |
Brian Lopez | 7169649857 | |
Brian Lopez | 225ddadaf7 | |
Brian Lopez | 0d1e9916bf | |
Brian Lopez | a17ba07c75 | |
Brian Lopez | f9d30e8f85 | |
Brian Lopez | 40f0cd012c | |
Anton Mironov | 687487d5a5 | |
Brian Lopez | 2ae908c512 | |
Brian Lopez | 4383885634 | |
Brian Lopez | 307b92b966 |
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,17 @@
|
|||
# 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
|
||||
|
|
|
@ -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 informative explanations of) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
|
||||
* Eric Wong - for the contribution (and the informative explanations) 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)
|
||||
|
|
37
Rakefile
37
Rakefile
|
@ -1,42 +1,5 @@
|
|||
# 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 }
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# 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
|
|
@ -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 || !wrapper->client->net.vio) { \
|
||||
if(wrapper->closed) { \
|
||||
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, INT2NUM(mysql_errno(client)));
|
||||
rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(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 **)ptr;
|
||||
MYSQL *client;
|
||||
|
||||
/* may initialize embedded server and read /etc/services off disk */
|
||||
*client = mysql_init(NULL);
|
||||
return *client ? Qtrue : Qfalse;
|
||||
client = mysql_init((MYSQL *)ptr);
|
||||
return client ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static VALUE nogvl_connect(void *ptr) {
|
||||
|
@ -104,45 +104,42 @@ static VALUE nogvl_connect(void *ptr) {
|
|||
return client ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static void rb_mysql_client_free(void * ptr) {
|
||||
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
||||
static VALUE nogvl_close(void *ptr) {
|
||||
mysql_client_wrapper *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,
|
||||
* so ignore any potential fcntl errors since they don't matter
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
int flags = fcntl(wrapper->client->net.fd, F_GETFL);
|
||||
if (flags > 0 && !(flags & O_NONBLOCK))
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
|
||||
#else
|
||||
u_long iMode = 1;
|
||||
ioctlsocket(fd, FIONBIO, &iMode);
|
||||
ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode);
|
||||
#endif
|
||||
|
||||
mysql_close(wrapper->client);
|
||||
free(wrapper->client);
|
||||
}
|
||||
|
||||
/* It's safe to call mysql_close() on an already closed connection. */
|
||||
if (!wrapper->closed) {
|
||||
mysql_close(wrapper->client);
|
||||
}
|
||||
xfree(ptr);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE nogvl_close(void * ptr) {
|
||||
mysql_client_wrapper *wrapper = ptr;
|
||||
if (!wrapper->closed) {
|
||||
mysql_close(wrapper->client);
|
||||
wrapper->closed = 1;
|
||||
}
|
||||
return Qnil;
|
||||
static void rb_mysql_client_free(void * ptr) {
|
||||
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
||||
|
||||
nogvl_close(wrapper);
|
||||
|
||||
xfree(ptr);
|
||||
}
|
||||
|
||||
static VALUE allocate(VALUE klass) {
|
||||
|
@ -151,7 +148,8 @@ 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 = 0;
|
||||
wrapper->closed = 1;
|
||||
wrapper->client = (MYSQL*)malloc(sizeof(MYSQL));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -166,7 +164,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 = NUM2INT(flags);
|
||||
args.client_flag = NUM2ULONG(flags);
|
||||
|
||||
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
||||
// unable to connect
|
||||
|
@ -185,7 +183,9 @@ 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);
|
||||
|
||||
rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
||||
if (!wrapper->closed) {
|
||||
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;
|
||||
VALUE opts, defaults, read_timeout;
|
||||
GET_CLIENT(self);
|
||||
|
||||
REQUIRE_OPEN_DB(wrapper);
|
||||
|
@ -290,6 +290,7 @@ 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
|
||||
|
@ -302,6 +303,23 @@ 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
|
||||
|
@ -310,7 +328,11 @@ 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, NULL);
|
||||
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));
|
||||
}
|
||||
|
||||
if (retval < 0) {
|
||||
rb_sys_fail(0);
|
||||
|
@ -334,6 +356,7 @@ 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();
|
||||
|
@ -345,7 +368,6 @@ 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
|
||||
|
@ -365,6 +387,7 @@ 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);
|
||||
|
@ -385,13 +408,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));
|
||||
|
@ -419,8 +442,14 @@ 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);
|
||||
return ULL2NUM(mysql_affected_rows(wrapper->client));
|
||||
retVal = 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) {
|
||||
|
@ -500,11 +529,12 @@ 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, ((void *) &wrapper->client), RUBY_UBF_IO, 0) == Qfalse) {
|
||||
if (rb_thread_blocking_region(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
||||
/* TODO: warning - not enough memory? */
|
||||
return rb_raise_mysql2_error(wrapper->client);
|
||||
}
|
||||
|
||||
wrapper->closed = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ void init_mysql2_client();
|
|||
|
||||
typedef struct {
|
||||
VALUE encoding;
|
||||
short int active;
|
||||
short int closed;
|
||||
char active;
|
||||
char closed;
|
||||
MYSQL *client;
|
||||
} mysql_client_wrapper;
|
||||
|
||||
|
|
|
@ -57,9 +57,13 @@ end
|
|||
asplode h unless have_header h
|
||||
end
|
||||
|
||||
unless RUBY_PLATFORM =~ /mswin/
|
||||
unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/
|
||||
$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')
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
#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>
|
||||
|
|
|
@ -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) {
|
||||
if (fields[i].flags & BINARY_FLAG && fields[i].charsetnr == 63) {
|
||||
rb_enc_associate(val, binaryEncoding);
|
||||
} else {
|
||||
// lookup the encoding configured on this field
|
||||
|
|
|
@ -8,10 +8,10 @@ typedef struct {
|
|||
VALUE fields;
|
||||
VALUE rows;
|
||||
VALUE encoding;
|
||||
long numberOfFields;
|
||||
unsigned int numberOfFields;
|
||||
unsigned long numberOfRows;
|
||||
unsigned long lastRowProcessed;
|
||||
short int resultFreed;
|
||||
char resultFreed;
|
||||
MYSQL_RES *result;
|
||||
} mysql2_result_wrapper;
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ module Mysql2
|
|||
results = @client.async_result
|
||||
@deferable.succeed(results)
|
||||
rescue Exception => e
|
||||
puts e.backtrace.join("\n\t")
|
||||
@deferable.fail(e)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,9 @@ 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)
|
||||
|
@ -447,7 +450,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 << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], [])
|
||||
indexes << Mysql2IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, [], [])
|
||||
end
|
||||
|
||||
indexes.last.columns << row[:Column_name]
|
||||
|
@ -617,8 +620,15 @@ 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
|
||||
|
||||
|
|
|
@ -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.4"
|
||||
VERSION = "0.2.6"
|
||||
end
|
||||
|
|
|
@ -8,7 +8,8 @@ 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
|
||||
: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 = {})
|
||||
|
@ -23,6 +24,11 @@ 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]
|
||||
|
@ -31,7 +37,7 @@ module Mysql2
|
|||
port = opts[:port] || 3306
|
||||
database = opts[:database]
|
||||
socket = opts[:socket]
|
||||
flags = opts[:flags] || 0
|
||||
flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags]
|
||||
|
||||
connect user, pass, host, port, database, socket, flags
|
||||
end
|
||||
|
|
|
@ -13,20 +13,24 @@ 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={})
|
||||
super(sql, opts.merge(:async => true))
|
||||
deferable = ::EM::DefaultDeferrable.new
|
||||
::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
|
||||
deferable
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{mysql2}
|
||||
s.version = "0.2.4"
|
||||
s.version = "0.2.6"
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Brian Lopez"]
|
||||
s.date = %q{2010-09-17}
|
||||
s.date = %q{2010-10-19}
|
||||
s.email = %q{seniorlopez@gmail.com}
|
||||
s.extensions = ["ext/mysql2/extconf.rb"]
|
||||
s.extra_rdoc_files = [
|
||||
|
@ -17,6 +17,7 @@ Gem::Specification.new do |s|
|
|||
]
|
||||
s.files = [
|
||||
".gitignore",
|
||||
".rspec",
|
||||
"CHANGELOG.md",
|
||||
"MIT-LICENSE",
|
||||
"README.rdoc",
|
||||
|
@ -29,7 +30,6 @@ 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,6 +46,7 @@ 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",
|
||||
|
@ -54,10 +55,11 @@ 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}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# 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
|
|
@ -1,26 +1,49 @@
|
|||
# encoding: UTF-8
|
||||
require 'spec_helper'
|
||||
require 'mysql2/em'
|
||||
if defined? EventMachine
|
||||
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.05) as first_query"
|
||||
defer1.callback do |result|
|
||||
results << result.first
|
||||
EM.stop_event_loop
|
||||
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
|
||||
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("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
|
||||
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
|
||||
end
|
||||
else
|
||||
puts "EventMachine not installed, skipping the specs that use it"
|
||||
end
|
|
@ -23,10 +23,10 @@ describe Mysql2::Client do
|
|||
end
|
||||
end
|
||||
client = klient.new :flags => Mysql2::Client::FOUND_ROWS
|
||||
client.connect_args.last.last.should == Mysql2::Client::FOUND_ROWS
|
||||
(client.connect_args.last.last & Mysql2::Client::FOUND_ROWS).should be_true
|
||||
end
|
||||
|
||||
it "should default flags to 0" do
|
||||
it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
|
||||
klient = Class.new(Mysql2::Client) do
|
||||
attr_reader :connect_args
|
||||
def connect *args
|
||||
|
@ -35,7 +35,12 @@ describe Mysql2::Client do
|
|||
end
|
||||
end
|
||||
client = klient.new
|
||||
client.connect_args.last.last.should == 0
|
||||
(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
|
||||
end
|
||||
|
||||
it "should have a global default_query_options hash" do
|
||||
|
@ -71,13 +76,28 @@ 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))
|
||||
|
@ -103,6 +123,20 @@ 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/
|
||||
|
@ -137,25 +171,34 @@ describe Mysql2::Client do
|
|||
@client.should respond_to(:escape)
|
||||
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
|
||||
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 the passed string if nothing was escaped" do
|
||||
str = "plain"
|
||||
@client.escape(str).object_id.should eql(str.object_id)
|
||||
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 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 thread stack" do
|
||||
lambda {
|
||||
Thread.new { @client.escape("'" * 256 * 1024) }.join
|
||||
}.should_not raise_error(SystemStackError)
|
||||
end
|
||||
|
||||
it "#escape should not overflow the process stack" do
|
||||
lambda {
|
||||
Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
|
||||
}.should_not raise_error(SystemStackError)
|
||||
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
|
||||
end
|
||||
|
||||
it "should respond to #info" do
|
||||
|
@ -203,6 +246,13 @@ 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
|
||||
|
@ -231,6 +281,13 @@ 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'
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
--format specdoc
|
||||
--colour
|
|
@ -1,10 +1,11 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
require 'rubygems'
|
||||
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
||||
require 'rspec'
|
||||
require 'mysql2'
|
||||
require 'timeout'
|
||||
|
||||
Spec::Runner.configure do |config|
|
||||
RSpec.configure do |config|
|
||||
config.before(:all) do
|
||||
client = Mysql2::Client.new :database => 'test'
|
||||
client.query %[
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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
|
|
@ -0,0 +1,16 @@
|
|||
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
|
Loading…
Reference in New Issue