Merge branch 'master' into stmt
This commit is contained in:
commit
39a28c4a91
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,3 +9,4 @@ mkmf.log
|
|||||||
pkg/
|
pkg/
|
||||||
tmp
|
tmp
|
||||||
vendor
|
vendor
|
||||||
|
lib/mysql2/mysql2.rb
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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
|
||||||
|
* added the ability to turn internal row caching on/off via the :cache_rows => true/false option
|
||||||
|
* a couple of small patches for rbx compatibility
|
||||||
|
* set IndexDefinition#length in AR adapter - Kouhei Yanagita <yanagi@shakenbu.org>
|
||||||
|
* fix a long-standing data corruption bug - thank you thank you thank you to @joedamato (http://github.com/ice799)
|
||||||
|
* bugfix from calling mysql_close on a closed/freed connection surfaced by the above fix
|
||||||
|
|
||||||
## 0.2.3 (August 20th, 2010)
|
## 0.2.3 (August 20th, 2010)
|
||||||
* connection flags can now be passed to the constructor via the :flags key
|
* connection flags can now be passed to the constructor via the :flags key
|
||||||
* switch AR adapter connection over to use FOUND_ROWS option
|
* switch AR adapter connection over to use FOUND_ROWS option
|
||||||
|
22
README.rdoc
22
README.rdoc
@ -89,18 +89,18 @@ or
|
|||||||
|
|
||||||
=== Array of Arrays
|
=== Array of Arrays
|
||||||
|
|
||||||
Pass the {:as => :array} option to any of the above methods of configuration
|
Pass the :as => :array option to any of the above methods of configuration
|
||||||
|
|
||||||
=== Array of Hashes
|
=== Array of Hashes
|
||||||
|
|
||||||
The default result type is set to :hash, but you can override a previous setting to something else with {:as => :hash}
|
The default result type is set to :hash, but you can override a previous setting to something else with :as => :hash
|
||||||
|
|
||||||
=== Others...
|
=== Others...
|
||||||
|
|
||||||
I may add support for {:as => :csv} or even {:as => :json} to allow for *much* more efficient generation of those data types from result sets.
|
I may add support for :as => :csv or even :as => :json to allow for *much* more efficient generation of those data types from result sets.
|
||||||
If you'd like to see either of these (or others), open an issue and start bugging me about it ;)
|
If you'd like to see either of these (or others), open an issue and start bugging me about it ;)
|
||||||
|
|
||||||
== Timezones
|
=== Timezones
|
||||||
|
|
||||||
Mysql2 now supports two timezone options:
|
Mysql2 now supports two timezone options:
|
||||||
|
|
||||||
@ -112,14 +112,14 @@ Then, if :application_timezone is set to say - :local - Mysql2 will then convert
|
|||||||
|
|
||||||
Both options only allow two values - :local or :utc - with the exception that :application_timezone can be [and defaults to] nil
|
Both options only allow two values - :local or :utc - with the exception that :application_timezone can be [and defaults to] nil
|
||||||
|
|
||||||
== Casting "boolean" columns
|
=== Casting "boolean" columns
|
||||||
|
|
||||||
You can now tell Mysql2 to cast tinyint(1) fields to boolean values in Ruby with the :cast_booleans option.
|
You can now tell Mysql2 to cast tinyint(1) fields to boolean values in Ruby with the :cast_booleans option.
|
||||||
|
|
||||||
client = Mysql2::Client.new
|
client = Mysql2::Client.new
|
||||||
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
||||||
|
|
||||||
== Async
|
=== Async
|
||||||
|
|
||||||
Mysql2::Client takes advantage of the MySQL C API's (undocumented) non-blocking function mysql_send_query for *all* queries.
|
Mysql2::Client takes advantage of the MySQL C API's (undocumented) non-blocking function mysql_send_query for *all* queries.
|
||||||
But, in order to take full advantage of it in your Ruby code, you can do:
|
But, in order to take full advantage of it in your Ruby code, you can do:
|
||||||
@ -136,6 +136,14 @@ NOTE: Because of the way MySQL's query API works, this method will block until t
|
|||||||
So if you really need things to stay async, it's best to just monitor the socket with something like EventMachine.
|
So if you really need things to stay async, it's best to just monitor the socket with something like EventMachine.
|
||||||
If you need multiple query concurrency take a look at using a connection pool.
|
If you need multiple query concurrency take a look at using a connection pool.
|
||||||
|
|
||||||
|
=== Row Caching
|
||||||
|
|
||||||
|
By default, Mysql2 will cache rows that have been created in Ruby (since this happens lazily).
|
||||||
|
This is especially helpful since it saves the cost of creating the row in Ruby if you were to iterate over the collection again.
|
||||||
|
|
||||||
|
If you only plan on using each row once, then it's much more efficient to disable this behavior by setting the :cache_rows option to false.
|
||||||
|
This would be helpful if you wanted to iterate over the results in a streaming manner. Meaning the GC would cleanup rows you don't need anymore as you're iterating over the result set.
|
||||||
|
|
||||||
== ActiveRecord
|
== ActiveRecord
|
||||||
|
|
||||||
To use the ActiveRecord driver, all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
|
To use the ActiveRecord driver, all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
|
||||||
@ -182,6 +190,8 @@ For example, if you were to yield 4 rows from a 100 row dataset, only 4 hashes w
|
|||||||
Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
|
Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
|
||||||
Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
|
Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
|
||||||
|
|
||||||
|
This caching behavior can be disabled by setting the :cache_rows option to false.
|
||||||
|
|
||||||
As for field values themselves, I'm workin on it - but expect that soon.
|
As for field values themselves, I'm workin on it - but expect that soon.
|
||||||
|
|
||||||
== Compatibility
|
== Compatibility
|
||||||
|
@ -70,7 +70,7 @@ Benchmark.bmbm do |x|
|
|||||||
end
|
end
|
||||||
|
|
||||||
do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
|
do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
|
||||||
command = DataObjects::Mysql::Command.new do_mysql, sql
|
command = do_mysql.create_command sql
|
||||||
x.report do
|
x.report do
|
||||||
puts "do_mysql"
|
puts "do_mysql"
|
||||||
number_of.times do
|
number_of.times do
|
||||||
|
@ -8,20 +8,18 @@ static VALUE intern_encoding_from_charset;
|
|||||||
static ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
|
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(_ctxt) \
|
#define REQUIRE_OPEN_DB(wrapper) \
|
||||||
if(!_ctxt->net.vio) { \
|
if(wrapper->closed || !wrapper->client->net.vio) { \
|
||||||
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
||||||
return Qnil; \
|
return Qnil; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MARK_CONN_INACTIVE(conn) \
|
#define MARK_CONN_INACTIVE(conn) \
|
||||||
wrapper->active = 0;
|
wrapper->active = 0
|
||||||
|
|
||||||
#define GET_CLIENT(self) \
|
#define GET_CLIENT(self) \
|
||||||
mysql_client_wrapper *wrapper; \
|
mysql_client_wrapper *wrapper; \
|
||||||
MYSQL *client; \
|
Data_Get_Struct(self, mysql_client_wrapper, wrapper)
|
||||||
Data_Get_Struct(self, mysql_client_wrapper, wrapper); \
|
|
||||||
client = &wrapper->client;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* used to pass all arguments to mysql_real_connect while inside
|
* used to pass all arguments to mysql_real_connect while inside
|
||||||
@ -85,12 +83,11 @@ static VALUE rb_raise_mysql2_error(MYSQL *client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE nogvl_init(void *ptr) {
|
static VALUE nogvl_init(void *ptr) {
|
||||||
MYSQL * client = (MYSQL *)ptr;
|
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(NULL);
|
*client = mysql_init(NULL);
|
||||||
|
return *client ? Qtrue : Qfalse;
|
||||||
return client ? Qtrue : Qfalse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE nogvl_connect(void *ptr) {
|
static VALUE nogvl_connect(void *ptr) {
|
||||||
@ -108,36 +105,43 @@ static VALUE nogvl_connect(void *ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void rb_mysql_client_free(void * ptr) {
|
static void rb_mysql_client_free(void * ptr) {
|
||||||
mysql_client_wrapper * wrapper = (mysql_client_wrapper *)ptr;
|
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
||||||
MYSQL * client = &wrapper->client;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we'll send a QUIT message to the server, but that message is more of a
|
* 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
|
* formality than a hard requirement since the socket is getting shutdown
|
||||||
* anyways, so ensure the socket write does not block our interpreter
|
* anyways, so ensure the socket write does not block our interpreter
|
||||||
*/
|
*/
|
||||||
int fd = client->net.fd;
|
int fd = wrapper->client->net.fd;
|
||||||
int flags;
|
|
||||||
|
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
flags = fcntl(fd, F_GETFL);
|
#ifndef _WIN32
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
if (flags > 0 && !(flags & O_NONBLOCK))
|
if (flags > 0 && !(flags & O_NONBLOCK))
|
||||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
#else
|
||||||
|
u_long iMode = 1;
|
||||||
|
ioctlsocket(fd, FIONBIO, &iMode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's safe to call mysql_close() on an already closed connection. */
|
/* It's safe to call mysql_close() on an already closed connection. */
|
||||||
mysql_close(client);
|
if (!wrapper->closed) {
|
||||||
|
mysql_close(wrapper->client);
|
||||||
|
}
|
||||||
xfree(ptr);
|
xfree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE nogvl_close(void * ptr) {
|
static VALUE nogvl_close(void * ptr) {
|
||||||
MYSQL *client = (MYSQL *)ptr;
|
mysql_client_wrapper *wrapper = ptr;
|
||||||
mysql_close(client);
|
if (!wrapper->closed) {
|
||||||
client->net.fd = -1;
|
mysql_close(wrapper->client);
|
||||||
|
wrapper->closed = 1;
|
||||||
|
}
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,12 +151,13 @@ 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 = 0;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
||||||
struct nogvl_connect_args args;
|
struct nogvl_connect_args args;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
args.host = NIL_P(host) ? "localhost" : StringValuePtr(host);
|
args.host = NIL_P(host) ? "localhost" : StringValuePtr(host);
|
||||||
args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
|
args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
|
||||||
@ -160,12 +165,12 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|||||||
args.user = NIL_P(user) ? NULL : StringValuePtr(user);
|
args.user = NIL_P(user) ? NULL : StringValuePtr(user);
|
||||||
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 = client;
|
args.mysql = wrapper->client;
|
||||||
args.client_flag = NUM2INT(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
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(wrapper->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -178,9 +183,9 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|||||||
* for the garbage collector.
|
* for the garbage collector.
|
||||||
*/
|
*/
|
||||||
static VALUE rb_mysql_client_close(VALUE self) {
|
static VALUE rb_mysql_client_close(VALUE self) {
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
rb_thread_blocking_region(nogvl_close, client, RUBY_UBF_IO, 0);
|
rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
||||||
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
@ -221,30 +226,30 @@ static VALUE nogvl_store_result(void *ptr) {
|
|||||||
|
|
||||||
static VALUE rb_mysql_client_async_result(VALUE self) {
|
static VALUE rb_mysql_client_async_result(VALUE self) {
|
||||||
MYSQL_RES * result;
|
MYSQL_RES * result;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) {
|
if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
||||||
// an error occurred, mark this connection inactive
|
// an error occurred, mark this connection inactive
|
||||||
MARK_CONN_INACTIVE(self);
|
MARK_CONN_INACTIVE(self);
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(wrapper->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0);
|
||||||
|
|
||||||
// we have our result, mark this connection inactive
|
// we have our result, mark this connection inactive
|
||||||
MARK_CONN_INACTIVE(self);
|
MARK_CONN_INACTIVE(self);
|
||||||
|
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
if (mysql_field_count(client) != 0) {
|
if (mysql_field_count(wrapper->client) != 0) {
|
||||||
rb_raise_mysql2_error(client);
|
rb_raise_mysql2_error(wrapper->client);
|
||||||
}
|
}
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE resultObj = rb_mysql_result_to_obj(result);
|
VALUE resultObj = rb_mysql_result_to_obj(result);
|
||||||
// pass-through query options for result construction later
|
// pass-through query options for result construction later
|
||||||
rb_iv_set(resultObj, "@query_options", rb_obj_dup(rb_iv_get(self, "@query_options")));
|
rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0));
|
||||||
|
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
mysql2_result_wrapper * result_wrapper;
|
mysql2_result_wrapper * result_wrapper;
|
||||||
@ -260,10 +265,10 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
int fd, retval;
|
int fd, retval;
|
||||||
int async = 0;
|
int async = 0;
|
||||||
VALUE opts, defaults;
|
VALUE opts, defaults;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
args.mysql = client;
|
args.mysql = wrapper->client;
|
||||||
|
|
||||||
// see if this connection is still waiting on a result from a previous query
|
// see if this connection is still waiting on a result from a previous query
|
||||||
if (wrapper->active == 0) {
|
if (wrapper->active == 0) {
|
||||||
@ -294,13 +299,13 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
||||||
// an error occurred, we're not active anymore
|
// an error occurred, we're not active anymore
|
||||||
MARK_CONN_INACTIVE(self);
|
MARK_CONN_INACTIVE(self);
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(wrapper->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
fd = client->net.fd;
|
fd = wrapper->client->net.fd;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
FD_ZERO(&fdset);
|
FD_ZERO(&fdset);
|
||||||
FD_SET(fd, &fdset);
|
FD_SET(fd, &fdset);
|
||||||
@ -327,7 +332,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
||||||
VALUE newStr;
|
VALUE newStr;
|
||||||
unsigned long newLen, oldLen;
|
unsigned long newLen, oldLen;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
Check_Type(str, T_STRING);
|
Check_Type(str, T_STRING);
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
@ -338,15 +343,15 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
oldLen = RSTRING_LEN(str);
|
oldLen = RSTRING_LEN(str);
|
||||||
char escaped[(oldLen*2)+1];
|
newStr = rb_str_new(0, oldLen*2+1);
|
||||||
|
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
newLen = mysql_real_escape_string(client, escaped, 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
|
||||||
return str;
|
return str;
|
||||||
} else {
|
} else {
|
||||||
newStr = rb_str_new(escaped, newLen);
|
rb_str_resize(newStr, newLen);
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
rb_enc_associate(newStr, conn_enc);
|
rb_enc_associate(newStr, conn_enc);
|
||||||
if (default_internal_enc) {
|
if (default_internal_enc) {
|
||||||
@ -379,17 +384,17 @@ 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);
|
||||||
#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(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
|
|
||||||
version = rb_hash_new();
|
version = rb_hash_new();
|
||||||
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client)));
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
|
||||||
server_info = rb_str_new2(mysql_get_server_info(client));
|
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
rb_enc_associate(server_info, conn_enc);
|
rb_enc_associate(server_info, conn_enc);
|
||||||
if (default_internal_enc) {
|
if (default_internal_enc) {
|
||||||
@ -401,34 +406,34 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_mysql_client_socket(VALUE self) {
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
return INT2NUM(client->net.fd);
|
return INT2NUM(wrapper->client->net.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_mysql_client_last_id(VALUE self) {
|
static VALUE rb_mysql_client_last_id(VALUE self) {
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
return ULL2NUM(mysql_insert_id(client));
|
return ULL2NUM(mysql_insert_id(wrapper->client));
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(wrapper);
|
||||||
return ULL2NUM(mysql_affected_rows(client));
|
return ULL2NUM(mysql_affected_rows(wrapper->client));
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE set_reconnect(VALUE self, VALUE value) {
|
static VALUE set_reconnect(VALUE self, VALUE value) {
|
||||||
my_bool reconnect;
|
my_bool reconnect;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
if(!NIL_P(value)) {
|
if(!NIL_P(value)) {
|
||||||
reconnect = value == Qfalse ? 0 : 1;
|
reconnect = value == Qfalse ? 0 : 1;
|
||||||
|
|
||||||
/* set default reconnect behavior */
|
/* set default reconnect behavior */
|
||||||
if (mysql_options(client, MYSQL_OPT_RECONNECT, &reconnect)) {
|
if (mysql_options(wrapper->client, MYSQL_OPT_RECONNECT, &reconnect)) {
|
||||||
/* TODO: warning - unable to set reconnect behavior */
|
/* TODO: warning - unable to set reconnect behavior */
|
||||||
rb_warn("%s\n", mysql_error(client));
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -436,16 +441,16 @@ static VALUE set_reconnect(VALUE self, VALUE value) {
|
|||||||
|
|
||||||
static VALUE set_connect_timeout(VALUE self, VALUE value) {
|
static VALUE set_connect_timeout(VALUE self, VALUE value) {
|
||||||
unsigned int connect_timeout = 0;
|
unsigned int connect_timeout = 0;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
if(!NIL_P(value)) {
|
if(!NIL_P(value)) {
|
||||||
connect_timeout = NUM2INT(value);
|
connect_timeout = NUM2INT(value);
|
||||||
if(0 == connect_timeout) return value;
|
if(0 == connect_timeout) return value;
|
||||||
|
|
||||||
/* set default connection timeout behavior */
|
/* set default connection timeout behavior */
|
||||||
if (mysql_options(client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
|
if (mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
|
||||||
/* TODO: warning - unable to set connection timeout */
|
/* TODO: warning - unable to set connection timeout */
|
||||||
rb_warn("%s\n", mysql_error(client));
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -453,7 +458,7 @@ static VALUE set_connect_timeout(VALUE self, VALUE value) {
|
|||||||
|
|
||||||
static VALUE set_charset_name(VALUE self, VALUE value) {
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
||||||
char * charset_name;
|
char * charset_name;
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
VALUE new_encoding;
|
VALUE new_encoding;
|
||||||
@ -469,19 +474,19 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|||||||
|
|
||||||
charset_name = StringValuePtr(value);
|
charset_name = StringValuePtr(value);
|
||||||
|
|
||||||
if (mysql_options(client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
||||||
/* TODO: warning - unable to set charset */
|
/* TODO: warning - unable to set charset */
|
||||||
rb_warn("%s\n", mysql_error(client));
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
||||||
GET_CLIENT(self)
|
GET_CLIENT(self);
|
||||||
|
|
||||||
if(!NIL_P(ca) || !NIL_P(key)) {
|
if(!NIL_P(ca) || !NIL_P(key)) {
|
||||||
mysql_ssl_set(client,
|
mysql_ssl_set(wrapper->client,
|
||||||
NIL_P(key) ? NULL : StringValuePtr(key),
|
NIL_P(key) ? NULL : StringValuePtr(key),
|
||||||
NIL_P(cert) ? NULL : StringValuePtr(cert),
|
NIL_P(cert) ? NULL : StringValuePtr(cert),
|
||||||
NIL_P(ca) ? NULL : StringValuePtr(ca),
|
NIL_P(ca) ? NULL : StringValuePtr(ca),
|
||||||
@ -493,11 +498,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, 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(client);
|
return rb_raise_mysql2_error(wrapper->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -34,7 +34,8 @@ void init_mysql2_client();
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
VALUE encoding;
|
VALUE encoding;
|
||||||
short int active;
|
short int active;
|
||||||
MYSQL client;
|
short int closed;
|
||||||
|
MYSQL *client;
|
||||||
} mysql_client_wrapper;
|
} mysql_client_wrapper;
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -12,7 +12,7 @@ static VALUE intern_encoding_from_charset;
|
|||||||
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
|
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
|
||||||
intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
|
intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
|
||||||
static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
|
static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
|
||||||
sym_local, sym_utc, sym_cast_booleans;
|
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows;
|
||||||
static ID intern_merge;
|
static ID intern_merge;
|
||||||
|
|
||||||
static void rb_mysql_result_mark(void * wrapper) {
|
static void rb_mysql_result_mark(void * wrapper) {
|
||||||
@ -316,7 +316,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||||||
ID db_timezone, app_timezone, dbTz, appTz;
|
ID db_timezone, app_timezone, dbTz, appTz;
|
||||||
mysql2_result_wrapper * wrapper;
|
mysql2_result_wrapper * wrapper;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
int symbolizeKeys = 0, asArray = 0, castBool = 0;
|
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1;
|
||||||
|
|
||||||
GetMysql2Result(self, wrapper);
|
GetMysql2Result(self, wrapper);
|
||||||
|
|
||||||
@ -339,6 +339,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||||||
castBool = 1;
|
castBool = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
|
||||||
|
cacheRows = 0;
|
||||||
|
}
|
||||||
|
|
||||||
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
||||||
if (dbTz == sym_local) {
|
if (dbTz == sym_local) {
|
||||||
db_timezone = intern_local;
|
db_timezone = intern_local;
|
||||||
@ -369,7 +373,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||||||
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
||||||
// we've already read the entire dataset from the C result into our
|
// we've already read the entire dataset from the C result into our
|
||||||
// internal array. Lets hand that over to the user since it's ready to go
|
// internal array. Lets hand that over to the user since it's ready to go
|
||||||
for (i = 0; i < wrapper->numberOfRows; i++) {
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
||||||
@ -380,11 +384,13 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||||||
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
||||||
for (i = 0; i < wrapper->numberOfRows; i++) {
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
||||||
VALUE row;
|
VALUE row;
|
||||||
if (i < rowsProcessed) {
|
if (cacheRows && i < rowsProcessed) {
|
||||||
row = rb_ary_entry(wrapper->rows, i);
|
row = rb_ary_entry(wrapper->rows, i);
|
||||||
} else {
|
} else {
|
||||||
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
|
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
|
||||||
rb_ary_store(wrapper->rows, i, row);
|
if (cacheRows) {
|
||||||
|
rb_ary_store(wrapper->rows, i, row);
|
||||||
|
}
|
||||||
wrapper->lastRowProcessed++;
|
wrapper->lastRowProcessed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,11 +459,12 @@ void init_mysql2_result() {
|
|||||||
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
|
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
|
||||||
sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
|
sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
|
||||||
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
|
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
|
||||||
|
sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
|
||||||
|
|
||||||
rb_global_variable(&opt_decimal_zero); //never GC
|
|
||||||
opt_decimal_zero = rb_str_new2("0.0");
|
opt_decimal_zero = rb_str_new2("0.0");
|
||||||
rb_global_variable(&opt_float_zero);
|
rb_global_variable(&opt_decimal_zero); //never GC
|
||||||
opt_float_zero = rb_float_new((double)0);
|
opt_float_zero = rb_float_new((double)0);
|
||||||
|
rb_global_variable(&opt_float_zero);
|
||||||
opt_time_year = INT2NUM(2000);
|
opt_time_year = INT2NUM(2000);
|
||||||
opt_time_month = INT2NUM(1);
|
opt_time_month = INT2NUM(1);
|
||||||
opt_utc_offset = INT2NUM(0);
|
opt_utc_offset = INT2NUM(0);
|
||||||
|
@ -8,7 +8,7 @@ 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;
|
||||||
short int resultFreed;
|
short int resultFreed;
|
||||||
|
@ -447,10 +447,11 @@ 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 << IndexDefinition.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]
|
||||||
|
indexes.last.lengths << row[:Sub_part]
|
||||||
end
|
end
|
||||||
indexes
|
indexes
|
||||||
end
|
end
|
||||||
|
@ -13,5 +13,5 @@ require 'mysql2/field'
|
|||||||
#
|
#
|
||||||
# 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.3"
|
VERSION = "0.2.4"
|
||||||
end
|
end
|
||||||
|
@ -2,12 +2,13 @@ module Mysql2
|
|||||||
class Client
|
class Client
|
||||||
attr_reader :query_options
|
attr_reader :query_options
|
||||||
@@default_query_options = {
|
@@default_query_options = {
|
||||||
:as => :hash,
|
:as => :hash, # the type of object you want each row back as; also supports :array (an array of values)
|
||||||
:async => false,
|
:async => false, # don't wait for a result after sending the query, you'll have to monitor the socket yourself then eventually call Mysql2::Client#async_result
|
||||||
:cast_booleans => false,
|
:cast_booleans => false, # cast tinyint(1) fields as true/false in ruby
|
||||||
:symbolize_keys => false,
|
: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
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize(opts = {})
|
def initialize(opts = {})
|
||||||
|
@ -7,5 +7,9 @@ module Mysql2
|
|||||||
@error_number = nil
|
@error_number = nil
|
||||||
@sql_state = nil
|
@sql_state = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Mysql gem compatibility
|
||||||
|
alias_method :errno, :error_number
|
||||||
|
alias_method :error, :message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -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.3"
|
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-08-20}
|
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 = [
|
||||||
|
@ -105,30 +105,32 @@ describe Mysql2::Client do
|
|||||||
|
|
||||||
# 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
|
||||||
it "should run signal handlers while waiting for a response" do
|
if RUBY_PLATFORM !~ /mingw|mswin/
|
||||||
mark = {}
|
it "should run signal handlers while waiting for a response" do
|
||||||
trap(:USR1) { mark[:USR1] = Time.now }
|
mark = {}
|
||||||
begin
|
trap(:USR1) { mark[:USR1] = Time.now }
|
||||||
mark[:START] = Time.now
|
begin
|
||||||
pid = fork do
|
mark[:START] = Time.now
|
||||||
sleep 1 # wait for client "SELECT sleep(2)" query to start
|
pid = fork do
|
||||||
Process.kill(:USR1, Process.ppid)
|
sleep 1 # wait for client "SELECT sleep(2)" query to start
|
||||||
sleep # wait for explicit kill to prevent GC disconnect
|
Process.kill(:USR1, Process.ppid)
|
||||||
|
sleep # wait for explicit kill to prevent GC disconnect
|
||||||
|
end
|
||||||
|
@client.query("SELECT sleep(2)")
|
||||||
|
mark[:END] = Time.now
|
||||||
|
mark.include?(:USR1).should be_true
|
||||||
|
(mark[:USR1] - mark[:START]).should >= 1
|
||||||
|
(mark[:USR1] - mark[:START]).should < 1.1
|
||||||
|
(mark[:END] - mark[:USR1]).should > 0.9
|
||||||
|
(mark[:END] - mark[:START]).should >= 2
|
||||||
|
(mark[:END] - mark[:START]).should < 2.1
|
||||||
|
Process.kill(:TERM, pid)
|
||||||
|
Process.waitpid2(pid)
|
||||||
|
ensure
|
||||||
|
trap(:USR1, 'DEFAULT')
|
||||||
end
|
end
|
||||||
@client.query("SELECT sleep(2)")
|
|
||||||
mark[:END] = Time.now
|
|
||||||
mark.include?(:USR1).should be_true
|
|
||||||
(mark[:USR1] - mark[:START]).should >= 1
|
|
||||||
(mark[:USR1] - mark[:START]).should < 1.1
|
|
||||||
(mark[:END] - mark[:USR1]).should > 0.9
|
|
||||||
(mark[:END] - mark[:START]).should >= 2
|
|
||||||
(mark[:END] - mark[:START]).should < 2.1
|
|
||||||
Process.kill(:TERM, pid)
|
|
||||||
Process.waitpid2(pid)
|
|
||||||
ensure
|
|
||||||
trap(:USR1, 'DEFAULT')
|
|
||||||
end
|
end
|
||||||
end if RUBY_PLATFORM !~ /mingw|mswin/
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respond to #escape" do
|
it "should respond to #escape" do
|
||||||
@ -144,6 +146,18 @@ describe Mysql2::Client do
|
|||||||
@client.escape(str).object_id.should eql(str.object_id)
|
@client.escape(str).object_id.should eql(str.object_id)
|
||||||
end
|
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 "#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
|
it "should respond to #info" do
|
||||||
@client.should respond_to(:info)
|
@client.should respond_to(:info)
|
||||||
end
|
end
|
||||||
|
@ -13,4 +13,13 @@ describe Mysql2::Error do
|
|||||||
it "should respond to #sql_state" do
|
it "should respond to #sql_state" do
|
||||||
@error.should respond_to(:sql_state)
|
@error.should respond_to(:sql_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Mysql gem compatibility
|
||||||
|
it "should alias #error_number to #errno" do
|
||||||
|
@error.should respond_to(:errno)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should alias #message to #error" do
|
||||||
|
@error.should respond_to(:error)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -47,8 +47,13 @@ describe Mysql2::Result do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should cache previously yielded results" do
|
it "should cache previously yielded results by default" do
|
||||||
@result.first.should eql(@result.first)
|
@result.first.object_id.should eql(@result.first.object_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not cache previously yielded results if cache_rows is disabled" do
|
||||||
|
result = @client.query "SELECT 1", :cache_rows => false
|
||||||
|
result.first.object_id.should_not eql(result.first.object_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
gem 'rake-compiler', '~> 0.7.1'
|
gem 'rake-compiler', '~> 0.7.1'
|
||||||
require "rake/extensiontask"
|
require "rake/extensiontask"
|
||||||
|
|
||||||
MYSQL_VERSION = "5.1.49"
|
MYSQL_VERSION = "5.1.50"
|
||||||
MYSQL_MIRROR = ENV['MYSQL_MIRROR'] || "http://mysql.localhost.net.ar"
|
MYSQL_MIRROR = ENV['MYSQL_MIRROR'] || "http://mysql.mirrors.pair.com"
|
||||||
|
|
||||||
Rake::ExtensionTask.new("mysql2", JEWELER.gemspec) do |ext|
|
|
||||||
|
def gemspec
|
||||||
|
@clean_gemspec ||= eval(File.read(File.expand_path('../../mysql2.gemspec', __FILE__)))
|
||||||
|
end
|
||||||
|
|
||||||
|
Rake::ExtensionTask.new("mysql2", gemspec) do |ext|
|
||||||
# reference where the vendored MySQL got extracted
|
# reference where the vendored MySQL got extracted
|
||||||
mysql_lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', "mysql-#{MYSQL_VERSION}-win32"))
|
mysql_lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', "mysql-#{MYSQL_VERSION}-win32"))
|
||||||
|
|
||||||
@ -12,8 +17,39 @@ Rake::ExtensionTask.new("mysql2", JEWELER.gemspec) do |ext|
|
|||||||
if RUBY_PLATFORM =~ /mswin|mingw/ then
|
if RUBY_PLATFORM =~ /mswin|mingw/ then
|
||||||
ext.config_options << "--with-mysql-include=#{mysql_lib}/include"
|
ext.config_options << "--with-mysql-include=#{mysql_lib}/include"
|
||||||
ext.config_options << "--with-mysql-lib=#{mysql_lib}/lib/opt"
|
ext.config_options << "--with-mysql-lib=#{mysql_lib}/lib/opt"
|
||||||
|
else
|
||||||
|
ext.cross_compile = true
|
||||||
|
ext.cross_platform = ['x86-mingw32', 'x86-mswin32-60']
|
||||||
|
ext.cross_config_options << "--with-mysql-include=#{mysql_lib}/include"
|
||||||
|
ext.cross_config_options << "--with-mysql-lib=#{mysql_lib}/lib/opt"
|
||||||
end
|
end
|
||||||
|
|
||||||
ext.lib_dir = File.join 'lib', 'mysql2'
|
ext.lib_dir = File.join 'lib', 'mysql2'
|
||||||
|
|
||||||
|
# clean compiled extension
|
||||||
|
CLEAN.include "#{ext.lib_dir}/*.#{RbConfig::CONFIG['DLEXT']}"
|
||||||
end
|
end
|
||||||
Rake::Task[:spec].prerequisites << :compile
|
Rake::Task[:spec].prerequisites << :compile
|
||||||
|
|
||||||
|
namespace :cross do
|
||||||
|
task :file_list do
|
||||||
|
gemspec.extensions = []
|
||||||
|
gemspec.files += Dir["lib/#{gemspec.name}/#{gemspec.name}.rb"]
|
||||||
|
gemspec.files += Dir["lib/#{gemspec.name}/1.{8,9}/#{gemspec.name}.so"]
|
||||||
|
# gemspec.files += Dir["ext/mysql2/*.dll"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
file 'lib/mysql2/mysql2.rb' do
|
||||||
|
name = gemspec.name
|
||||||
|
File.open("lib/#{name}/#{name}.rb", 'wb') do |f|
|
||||||
|
f.write <<-eoruby
|
||||||
|
require "#{name}/\#{RUBY_VERSION.sub(/\\.\\d+$/, '')}/#{name}"
|
||||||
|
eoruby
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Rake::Task.task_defined?(:cross)
|
||||||
|
Rake::Task[:cross].prerequisites << "lib/mysql2/mysql2.rb"
|
||||||
|
Rake::Task[:cross].prerequisites << "cross:file_list"
|
||||||
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user