add cache_rows option to enable/disable internal row caching for results
This commit is contained in:
parent
10222fb455
commit
ae6c33a13f
16
README.rdoc
16
README.rdoc
|
@ -100,7 +100,7 @@ The default result type is set to :hash, but you can override a previous setting
|
||||||
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
|
||||||
|
|
|
@ -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,6 +459,7 @@ 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
|
rb_global_variable(&opt_decimal_zero); //never GC
|
||||||
opt_decimal_zero = rb_str_new2("0.0");
|
opt_decimal_zero = rb_str_new2("0.0");
|
||||||
|
|
|
@ -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 = {})
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue