From 81fd9bc11922d5bcd3b6be7289b4838fed448292 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 30 Dec 2012 16:39:02 -0600 Subject: [PATCH 1/6] Factor out result @query_options into a separate function. --- ext/mysql2/result.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 2a1967c1c..01784180e 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -755,6 +755,37 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) { return wrapper->fields; } +static void rb_mysql_row_query_options(VALUE opts, ID *db_timezone, ID *app_timezone, int *symbolizeKeys, int *asArray, int *castBool, int *cast, int *cacheRows) { + ID dbTz, appTz; + + *symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys)); + *asArray = rb_hash_aref(opts, sym_as) == sym_array; + *castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans)); + *cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows)); + *cast = RTEST(rb_hash_aref(opts, sym_cast)); + + dbTz = rb_hash_aref(opts, sym_database_timezone); + if (dbTz == sym_local) { + *db_timezone = intern_local; + } else if (dbTz == sym_utc) { + *db_timezone = intern_utc; + } else { + if (!NIL_P(dbTz)) { + rb_warn(":database_timezone option must be :utc or :local - defaulting to :local"); + } + *db_timezone = intern_local; + } + + appTz = rb_hash_aref(opts, sym_application_timezone); + if (appTz == sym_local) { + *app_timezone = intern_local; + } else if (appTz == sym_utc) { + *app_timezone = intern_utc; + } else { + *app_timezone = Qnil; + } +} + static VALUE rb_mysql_result_each_(VALUE self, VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args), const result_each_args *args) From 887ac0185a6ed5a4a354774aba4a9594ac075e48 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 30 Dec 2012 16:42:15 -0600 Subject: [PATCH 2/6] Feature request #130: add element reference operator #[] to Mysql2::Result using mysql_data_seek. --- ext/mysql2/result.c | 102 +++++++++++++++++++++++-------------- spec/mysql2/result_spec.rb | 31 +++++++++++ 2 files changed, 94 insertions(+), 39 deletions(-) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 01784180e..d6ed05896 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -786,6 +786,66 @@ static void rb_mysql_row_query_options(VALUE opts, ID *db_timezone, ID *app_time } } +static VALUE rb_mysql_result_element(VALUE self, VALUE seek) { + result_each_args args; + MYSQL_FIELD *fields = NULL; + long offset; + ID db_timezone, app_timezone; + int symbolizeKeys, asArray, castBool, cacheRows, cast; + VALUE opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + + GET_RESULT(self); + + offset = NUM2LONG(seek); + + if (!wrapper->numberOfRows) { + wrapper->numberOfRows = mysql_num_rows(wrapper->result); + } + + opts = rb_iv_get(self, "@query_options"); + rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &cacheRows); + + if (wrapper->is_streaming) { + rb_raise(cMysql2Error, "Element reference operator #[] cannot be used in streaming mode."); + } + + /* count back from the end if passed a negative number */ + if (offset < 0) { + offset = wrapper->numberOfRows + offset; + } + + /* negative offset was too big */ + if (offset < 0) { + return Qnil; + /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */ + } + + if (wrapper->numberOfRows <= (unsigned long)offset) { + return Qnil; + /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */ + } + + mysql_data_seek(wrapper->result, offset); + + // Backward compat + args.symbolizeKeys = symbolizeKeys; + args.asArray = asArray; + args.castBool = castBool; + args.cacheRows = cacheRows; + args.cast = cast; + args.db_timezone = db_timezone; + args.app_timezone = app_timezone; + args.block_given = Qnil; + + if (wrapper->stmt) { + fetch_row_func = rb_mysql_result_fetch_row_stmt; + } else { + fetch_row_func = rb_mysql_result_fetch_row; + } + + return fetch_row_func(self, fields, &args); +} + static VALUE rb_mysql_result_each_(VALUE self, VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args), const result_each_args *args) @@ -878,7 +938,7 @@ static VALUE rb_mysql_result_each_(VALUE self, static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { result_each_args args; VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); - ID db_timezone, app_timezone, dbTz, appTz; + ID db_timezone, app_timezone; int symbolizeKeys, asArray, castBool, cacheRows, cast; GET_RESULT(self); @@ -891,44 +951,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { opts = defaults; } - symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys)); - asArray = rb_hash_aref(opts, sym_as) == sym_array; - castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans)); - cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows)); - cast = RTEST(rb_hash_aref(opts, sym_cast)); - - if (wrapper->is_streaming && cacheRows) { - rb_warn(":cache_rows is ignored if :stream is true"); - } - - if (wrapper->stmt && !cacheRows && !wrapper->is_streaming) { - rb_warn(":cache_rows is forced for prepared statements (if not streaming)"); - } - - if (wrapper->stmt && !cast) { - rb_warn(":cast is forced for prepared statements"); - } - - dbTz = rb_hash_aref(opts, sym_database_timezone); - if (dbTz == sym_local) { - db_timezone = intern_local; - } else if (dbTz == sym_utc) { - db_timezone = intern_utc; - } else { - if (!NIL_P(dbTz)) { - rb_warn(":database_timezone option must be :utc or :local - defaulting to :local"); - } - db_timezone = intern_local; - } - - appTz = rb_hash_aref(opts, sym_application_timezone); - if (appTz == sym_local) { - app_timezone = intern_local; - } else if (appTz == sym_utc) { - app_timezone = intern_utc; - } else { - app_timezone = Qnil; - } + rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &cacheRows); if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) { wrapper->numberOfRows = wrapper->stmt ? mysql_stmt_num_rows(wrapper->stmt) : mysql_num_rows(wrapper->result); @@ -1019,6 +1042,7 @@ void init_mysql2_result() { cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime")); cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject); + rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, 1); rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1); rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0); rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0); diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index b32939753..ed73da587 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -98,6 +98,37 @@ end end + context "#[]" do + it "should return results when accessed by [offset]" do + result = @client.query "SELECT 1 UNION SELECT 2" + result[1][result.fields.first].should eql(2) + result[0][result.fields.first].should eql(1) + end + + it "should return results when accessed by negative [offset]" do + result = @client.query "SELECT 1 UNION SELECT 2" + result[-1][result.fields.first].should eql(2) + result[-2][result.fields.first].should eql(1) + end + + it "should return nil if we use too large [offset]" do + result = @client.query "SELECT 1 UNION SELECT 2" + result[2].should be_nil + result[200].should be_nil + end + + it "should return nil if we use too negative [offset]" do + result = @client.query "SELECT 1 UNION SELECT 2" + result[-3].should be_nil + result[-300].should be_nil + end + + it "should throw an exception if we use an [offset] in streaming mode" do + result = @client.query "SELECT 1 UNION SELECT 2", :stream => true + expect { result[0] }.to raise_exception(Mysql2::Error) + end + end + context "#fields" do before(:each) do @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1") From bbab643fb1b6cb1f19a7386113119bf2a0d74e65 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 31 Dec 2012 00:59:50 -0600 Subject: [PATCH 3/6] Result #[] with Array-like [offset, count] plus options in [offset, {opts hash}] and [offset, count, {opts hash}]. --- ext/mysql2/result.c | 73 ++++++++++++++++++++++++++++---------- spec/mysql2/result_spec.rb | 33 ++++++++++++----- 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index d6ed05896..d2b3ea75d 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -786,46 +786,68 @@ static void rb_mysql_row_query_options(VALUE opts, ID *db_timezone, ID *app_time } } -static VALUE rb_mysql_result_element(VALUE self, VALUE seek) { +static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { result_each_args args; MYSQL_FIELD *fields = NULL; - long offset; ID db_timezone, app_timezone; + VALUE seek, count, row, rows; + long i, c_seek, c_count = 0; int symbolizeKeys, asArray, castBool, cacheRows, cast; - VALUE opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + VALUE defaults, block, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); GET_RESULT(self); - offset = NUM2LONG(seek); + defaults = rb_iv_get(self, "@query_options"); + Check_Type(defaults, T_HASH); + rb_scan_args(argc, argv, "12&", &seek, &count, &opts, &block); - if (!wrapper->numberOfRows) { - wrapper->numberOfRows = mysql_num_rows(wrapper->result); + /* If the second arg is a hash, it's the opts and there's no count */ + if (TYPE(count) == T_HASH) { + opts = count; + count = Qnil; + } + + c_seek = NUM2LONG(seek); + if (!NIL_P(count)) { + c_count = NUM2LONG(count); + /* Special case: ary[x, 0] returns []*/ + if (!c_count) return rb_ary_new(); + } + + if (!NIL_P(opts)) { + opts = rb_funcall(defaults, intern_merge, 1, opts); + } else { + opts = defaults; } - opts = rb_iv_get(self, "@query_options"); rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &cacheRows); if (wrapper->is_streaming) { rb_raise(cMysql2Error, "Element reference operator #[] cannot be used in streaming mode."); } + if (!wrapper->numberOfRows) { + wrapper->numberOfRows = mysql_num_rows(wrapper->result); + } + /* count back from the end if passed a negative number */ - if (offset < 0) { - offset = wrapper->numberOfRows + offset; + if (c_seek < 0) { + c_seek = wrapper->numberOfRows + c_seek; } /* negative offset was too big */ - if (offset < 0) { + if (c_seek < 0) { return Qnil; - /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */ + /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", c_seek, wrapper->numberOfRows); */ } - if (wrapper->numberOfRows <= (unsigned long)offset) { - return Qnil; - /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */ + if (wrapper->numberOfRows <= (unsigned long)c_seek) { + if (!c_count) return Qnil; + else return rb_ary_new(); + /* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", c_seek, wrapper->numberOfRows); */ } - mysql_data_seek(wrapper->result, offset); + mysql_data_seek(wrapper->result, c_seek); // Backward compat args.symbolizeKeys = symbolizeKeys; @@ -835,7 +857,7 @@ static VALUE rb_mysql_result_element(VALUE self, VALUE seek) { args.cast = cast; args.db_timezone = db_timezone; args.app_timezone = app_timezone; - args.block_given = Qnil; + args.block_given = block; if (wrapper->stmt) { fetch_row_func = rb_mysql_result_fetch_row_stmt; @@ -843,7 +865,22 @@ static VALUE rb_mysql_result_element(VALUE self, VALUE seek) { fetch_row_func = rb_mysql_result_fetch_row; } - return fetch_row_func(self, fields, &args); + if (!c_count) { + return fetch_row_func(self, fields, &args); + } + + /* given ary = [1, 2, 3] then ary[1, 100] returns [2, 3] */ + if ((unsigned long)(c_seek + c_count) > wrapper->numberOfRows) { + c_count = wrapper->numberOfRows - c_seek; + } + + /* return an array! */ + rows = rb_ary_new2(c_count); + for (i = 0; i < c_count; i++) { + row = fetch_row_func(self, fields, &args); + rb_ary_store(rows, i, row); + } + return rows; } static VALUE rb_mysql_result_each_(VALUE self, @@ -1042,7 +1079,7 @@ void init_mysql2_result() { cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime")); cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject); - rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, 1); + rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, -1); rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1); rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0); rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0); diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index ed73da587..45fdd823a 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -100,31 +100,46 @@ context "#[]" do it "should return results when accessed by [offset]" do - result = @client.query "SELECT 1 UNION SELECT 2" - result[1][result.fields.first].should eql(2) - result[0][result.fields.first].should eql(1) + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" + result[1].should eql({"col" => 2}) + result[0].should eql({"col" => 1}) end it "should return results when accessed by negative [offset]" do - result = @client.query "SELECT 1 UNION SELECT 2" - result[-1][result.fields.first].should eql(2) - result[-2][result.fields.first].should eql(1) + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" + result[-1].should eql({"col" => 2}) + result[-2].should eql({"col" => 1}) + end + + it "should return array of results when accessed by [offset, count]" do + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" + result[1, 1].should eql([{"col" => 2}]) + result[-2, 10].should eql([{"col" => 1}, {"col" => 2}]) end it "should return nil if we use too large [offset]" do - result = @client.query "SELECT 1 UNION SELECT 2" + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" result[2].should be_nil result[200].should be_nil end it "should return nil if we use too negative [offset]" do - result = @client.query "SELECT 1 UNION SELECT 2" + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" result[-3].should be_nil result[-300].should be_nil end + it "should accept hash args in [offset, {:foo => bar}] and [offset, count, {:foo => bar}]" do + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" + result[1, {:symbolize_keys => true}].should eql({:col => 2}) + result[1, 1, {:symbolize_keys => true}].should eql([{:col => 2}]) + # This syntax does not work in Ruby 1.8: + # result[1, :symbolize_keys => true].should eql({:col => 2}) + # result[1, 1, :symbolize_keys => true].should eql([{:col => 2}]) + end + it "should throw an exception if we use an [offset] in streaming mode" do - result = @client.query "SELECT 1 UNION SELECT 2", :stream => true + result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col", :stream => true expect { result[0] }.to raise_exception(Mysql2::Error) end end From ac7728b08aa3a7cf0fb2304bded75ddab6149420 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 11 Jun 2015 16:33:11 -0700 Subject: [PATCH 4/6] Convert [] specs to expect syntax --- spec/mysql2/result_spec.rb | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index 45fdd823a..c195cd24c 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -101,41 +101,42 @@ context "#[]" do it "should return results when accessed by [offset]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[1].should eql({"col" => 2}) - result[0].should eql({"col" => 1}) + expect(result[1]).to eql({"col" => 2}) + expect(result[0]).to eql({"col" => 1}) end it "should return results when accessed by negative [offset]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[-1].should eql({"col" => 2}) - result[-2].should eql({"col" => 1}) + expect(result[-1]).to eql({"col" => 2}) + expect(result[-2]).to eql({"col" => 1}) end it "should return array of results when accessed by [offset, count]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[1, 1].should eql([{"col" => 2}]) - result[-2, 10].should eql([{"col" => 1}, {"col" => 2}]) + expect(result[1, 1]).to eql([{"col" => 2}]) + expect(result[-2, 10]).to eql([{"col" => 1}, {"col" => 2}]) end it "should return nil if we use too large [offset]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[2].should be_nil - result[200].should be_nil + expect(result[2]).to be_nil + expect(result[200]).to be_nil end it "should return nil if we use too negative [offset]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[-3].should be_nil - result[-300].should be_nil + expect(result[-3]).to be_nil + expect(result[-300]).to be_nil end it "should accept hash args in [offset, {:foo => bar}] and [offset, count, {:foo => bar}]" do result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col" - result[1, {:symbolize_keys => true}].should eql({:col => 2}) - result[1, 1, {:symbolize_keys => true}].should eql([{:col => 2}]) - # This syntax does not work in Ruby 1.8: - # result[1, :symbolize_keys => true].should eql({:col => 2}) - # result[1, 1, :symbolize_keys => true].should eql([{:col => 2}]) + expect(result[1, {:symbolize_keys => true}]).to eql({:col => 2}) + expect(result[1, 1, {:symbolize_keys => true}]).to eql([{:col => 2}]) + + # This syntax can't be parsed by Ruby 1.8: + # expect(result[1, :symbolize_keys => true]).to eql({:col => 2}) + # expect(result[1, 1, :symbolize_keys => true]).to eql([{:col => 2}]) end it "should throw an exception if we use an [offset] in streaming mode" do From 97c048a2723c2d2d34e3519ea08d8bc727f0b614 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 11 Jun 2015 16:43:35 -0700 Subject: [PATCH 5/6] Further move query options into a separate function --- ext/mysql2/result.c | 91 +++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 56 deletions(-) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index d2b3ea75d..8cd896f2a 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -755,50 +755,60 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) { return wrapper->fields; } -static void rb_mysql_row_query_options(VALUE opts, ID *db_timezone, ID *app_timezone, int *symbolizeKeys, int *asArray, int *castBool, int *cast, int *cacheRows) { +static result_each_args rb_mysql_row_query_options(VALUE self, VALUE opts) { ID dbTz, appTz; + VALUE defaults; + result_each_args args; + + defaults = rb_iv_get(self, "@query_options"); + Check_Type(defaults, T_HASH); + if (!NIL_P(opts)) { + opts = rb_funcall(defaults, intern_merge, 1, opts); + } else { + opts = defaults; + } - *symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys)); - *asArray = rb_hash_aref(opts, sym_as) == sym_array; - *castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans)); - *cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows)); - *cast = RTEST(rb_hash_aref(opts, sym_cast)); + args.symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys)); + args.asArray = rb_hash_aref(opts, sym_as) == sym_array; + args.castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans)); + args.cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows)); + args.cast = RTEST(rb_hash_aref(opts, sym_cast)); + args.block_given = Qnil; dbTz = rb_hash_aref(opts, sym_database_timezone); if (dbTz == sym_local) { - *db_timezone = intern_local; + args.db_timezone = intern_local; } else if (dbTz == sym_utc) { - *db_timezone = intern_utc; + args.db_timezone = intern_utc; } else { if (!NIL_P(dbTz)) { rb_warn(":database_timezone option must be :utc or :local - defaulting to :local"); } - *db_timezone = intern_local; + args.db_timezone = intern_local; } appTz = rb_hash_aref(opts, sym_application_timezone); if (appTz == sym_local) { - *app_timezone = intern_local; + args.app_timezone = intern_local; } else if (appTz == sym_utc) { - *app_timezone = intern_utc; + args.app_timezone = intern_utc; } else { - *app_timezone = Qnil; + args.app_timezone = Qnil; } + + return args; } static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { result_each_args args; MYSQL_FIELD *fields = NULL; - ID db_timezone, app_timezone; VALUE seek, count, row, rows; long i, c_seek, c_count = 0; - int symbolizeKeys, asArray, castBool, cacheRows, cast; - VALUE defaults, block, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + VALUE (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + VALUE block, opts; GET_RESULT(self); - defaults = rb_iv_get(self, "@query_options"); - Check_Type(defaults, T_HASH); rb_scan_args(argc, argv, "12&", &seek, &count, &opts, &block); /* If the second arg is a hash, it's the opts and there's no count */ @@ -814,13 +824,8 @@ static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { if (!c_count) return rb_ary_new(); } - if (!NIL_P(opts)) { - opts = rb_funcall(defaults, intern_merge, 1, opts); - } else { - opts = defaults; - } - - rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &cacheRows); + args = rb_mysql_row_query_options(self, opts); + args.block_given = block; if (wrapper->is_streaming) { rb_raise(cMysql2Error, "Element reference operator #[] cannot be used in streaming mode."); @@ -848,16 +853,7 @@ static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { } mysql_data_seek(wrapper->result, c_seek); - - // Backward compat - args.symbolizeKeys = symbolizeKeys; - args.asArray = asArray; - args.castBool = castBool; - args.cacheRows = cacheRows; - args.cast = cast; - args.db_timezone = db_timezone; - args.app_timezone = app_timezone; - args.block_given = block; + fields = mysql_fetch_fields(wrapper->result); if (wrapper->stmt) { fetch_row_func = rb_mysql_result_fetch_row_stmt; @@ -974,21 +970,14 @@ static VALUE rb_mysql_result_each_(VALUE self, static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { result_each_args args; - VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); - ID db_timezone, app_timezone; - int symbolizeKeys, asArray, castBool, cacheRows, cast; + VALUE (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + VALUE opts, block; GET_RESULT(self); - defaults = rb_iv_get(self, "@query_options"); - Check_Type(defaults, T_HASH); - if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) { - opts = rb_funcall(defaults, intern_merge, 1, opts); - } else { - opts = defaults; - } - - rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &cacheRows); + rb_scan_args(argc, argv, "01&", &opts, &block); + args = rb_mysql_row_query_options(self, opts); + args.block_given = block; if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) { wrapper->numberOfRows = wrapper->stmt ? mysql_stmt_num_rows(wrapper->stmt) : mysql_num_rows(wrapper->result); @@ -999,16 +988,6 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { wrapper->rows = rb_ary_new2(wrapper->numberOfRows); } - // Backward compat - args.symbolizeKeys = symbolizeKeys; - args.asArray = asArray; - args.castBool = castBool; - args.cacheRows = cacheRows; - args.cast = cast; - args.db_timezone = db_timezone; - args.app_timezone = app_timezone; - args.block_given = block; - if (wrapper->stmt) { fetch_row_func = rb_mysql_result_fetch_row_stmt; } else { From 6fce37cf71996340e2fc8e08b16253fb2b2c4582 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 11 Jun 2015 16:44:14 -0700 Subject: [PATCH 6/6] typedef fetch_row_func for clarity --- ext/mysql2/result.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 8cd896f2a..a054f9878 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -66,6 +66,8 @@ typedef struct { VALUE block_given; } result_each_args; +typedef VALUE (*fetch_row_func_t)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); + VALUE cBigDecimal, cDateTime, cDate; static VALUE cMysql2Result; static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset; @@ -804,8 +806,8 @@ static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { MYSQL_FIELD *fields = NULL; VALUE seek, count, row, rows; long i, c_seek, c_count = 0; - VALUE (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); VALUE block, opts; + fetch_row_func_t fetch_row_func; GET_RESULT(self); @@ -880,7 +882,7 @@ static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) { } static VALUE rb_mysql_result_each_(VALUE self, - VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args), + fetch_row_func_t fetch_row_func, const result_each_args *args) { unsigned long i; @@ -970,8 +972,8 @@ static VALUE rb_mysql_result_each_(VALUE self, static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { result_each_args args; - VALUE (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); VALUE opts, block; + fetch_row_func_t fetch_row_func; GET_RESULT(self);