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.
|
||||
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:
|
||||
|
||||
|
@ -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
|
||||
|
||||
== 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.
|
||||
|
||||
client = Mysql2::Client.new
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
== Compatibility
|
||||
|
|
|
@ -12,7 +12,7 @@ static VALUE intern_encoding_from_charset;
|
|||
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
|
||||
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,
|
||||
sym_local, sym_utc, sym_cast_booleans;
|
||||
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows;
|
||||
static ID intern_merge;
|
||||
|
||||
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;
|
||||
mysql2_result_wrapper * wrapper;
|
||||
unsigned long i;
|
||||
int symbolizeKeys = 0, asArray = 0, castBool = 0;
|
||||
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1;
|
||||
|
||||
GetMysql2Result(self, wrapper);
|
||||
|
||||
|
@ -339,6 +339,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||
castBool = 1;
|
||||
}
|
||||
|
||||
if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
|
||||
cacheRows = 0;
|
||||
}
|
||||
|
||||
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
||||
if (dbTz == sym_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);
|
||||
}
|
||||
|
||||
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
||||
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
||||
// 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
|
||||
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);
|
||||
for (i = 0; i < wrapper->numberOfRows; i++) {
|
||||
VALUE row;
|
||||
if (i < rowsProcessed) {
|
||||
if (cacheRows && i < rowsProcessed) {
|
||||
row = rb_ary_entry(wrapper->rows, i);
|
||||
} else {
|
||||
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++;
|
||||
}
|
||||
|
||||
|
@ -453,6 +459,7 @@ void init_mysql2_result() {
|
|||
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
|
||||
sym_database_timezone = ID2SYM(rb_intern("database_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");
|
||||
|
|
|
@ -2,12 +2,13 @@ module Mysql2
|
|||
class Client
|
||||
attr_reader :query_options
|
||||
@@default_query_options = {
|
||||
:as => :hash,
|
||||
:async => false,
|
||||
:cast_booleans => false,
|
||||
:symbolize_keys => false,
|
||||
: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
|
||||
:as => :hash, # the type of object you want each row back as; also supports :array (an array of values)
|
||||
: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 tinyint(1) fields as true/false in ruby
|
||||
: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
|
||||
}
|
||||
|
||||
def initialize(opts = {})
|
||||
|
|
|
@ -47,8 +47,13 @@ describe Mysql2::Result do
|
|||
end
|
||||
end
|
||||
|
||||
it "should cache previously yielded results" do
|
||||
@result.first.should eql(@result.first)
|
||||
it "should cache previously yielded results by default" do
|
||||
@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
|
||||
|
||||
|
|
Loading…
Reference in New Issue