Skip to content

Commit 288c3d7

Browse files
committed
Result #[] with Array-like [offset, count] plus options in [offset, {opts hash}] and [offset, count, {opts hash}].
1 parent 0ed17f8 commit 288c3d7

File tree

2 files changed

+74
-27
lines changed

2 files changed

+74
-27
lines changed

Diff for: ext/mysql2/result.c

+50-18
Original file line numberDiff line numberDiff line change
@@ -470,49 +470,81 @@ static void rb_mysql_row_query_options(VALUE opts, ID *db_timezone, ID *app_time
470470
}
471471
}
472472

473-
static VALUE rb_mysql_result_element(VALUE self, VALUE seek) {
474-
VALUE row, opts;
473+
static VALUE rb_mysql_result_element(int argc, VALUE * argv, VALUE self) {
474+
VALUE seek, count, defaults, opts;
475+
VALUE row, rows;
475476
ID db_timezone, app_timezone;
476-
long offset;
477+
long i, c_seek, c_count = 0;
477478
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
478479
mysql2_result_wrapper * wrapper;
479480

480481
GetMysql2Result(self, wrapper);
481482

482-
offset = NUM2LONG(seek);
483+
rb_scan_args(argc, argv, "12", &seek, &count, &opts);
484+
/* If the second arg is a hash, it's the opts and there's no count */
485+
if (TYPE(count) == T_HASH) {
486+
opts = count;
487+
count = Qnil;
488+
}
483489

484-
if (!wrapper->numberOfRows) {
485-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
490+
c_seek = NUM2LONG(seek);
491+
if (!NIL_P(count)) {
492+
c_count = NUM2LONG(count);
493+
/* Special case: ary[x, 0] returns []*/
494+
if (!c_count) return rb_ary_new();
486495
}
487496

488-
opts = rb_iv_get(self, "@query_options");
497+
defaults = rb_iv_get(self, "@query_options");
498+
if (!NIL_P(opts)) {
499+
opts = rb_funcall(defaults, intern_merge, 1, opts);
500+
} else {
501+
opts = defaults;
502+
}
489503
rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &streaming, &cacheRows);
490504

491505
if (streaming) {
492506
rb_raise(cMysql2Error, "Element reference operator #[] cannot be used in streaming mode.");
493507
}
494508

509+
if (!wrapper->numberOfRows) {
510+
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
511+
}
512+
495513
/* count back from the end if passed a negative number */
496-
if (offset < 0) {
497-
offset = wrapper->numberOfRows + offset;
514+
if (c_seek < 0) {
515+
c_seek = wrapper->numberOfRows + c_seek;
498516
}
499517

500518
/* negative offset was too big */
501-
if (offset < 0) {
519+
if (c_seek < 0) {
502520
return Qnil;
503-
/* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */
521+
/* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", c_seek, wrapper->numberOfRows); */
504522
}
505523

506-
if (wrapper->numberOfRows <= (unsigned long)offset) {
507-
return Qnil;
508-
/* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */
524+
if (wrapper->numberOfRows <= (unsigned long)c_seek) {
525+
if (!c_count) return Qnil;
526+
else return rb_ary_new();
527+
/* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", c_seek, wrapper->numberOfRows); */
509528
}
510529

511-
mysql_data_seek(wrapper->result, offset);
530+
mysql_data_seek(wrapper->result, c_seek);
512531

513-
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast);
532+
if (!c_count) {
533+
return rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast);
534+
}
535+
536+
/* given ary = [1, 2, 3] then ary[1, 100] returns [2, 3] */
537+
if ((unsigned long)(c_seek + c_count) > wrapper->numberOfRows) {
538+
c_count = wrapper->numberOfRows - c_seek;
539+
}
514540

515-
return row;
541+
/* return an array! */
542+
rows = rb_ary_new2(c_count);
543+
for (i = 0; i < c_count; i++) {
544+
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast);
545+
rb_ary_store(rows, i, row);
546+
}
547+
return rows;
516548
}
517549

518550
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
@@ -658,7 +690,7 @@ void init_mysql2_result() {
658690
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
659691

660692
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
661-
rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, 1);
693+
rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, -1);
662694
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
663695
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
664696
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);

Diff for: spec/mysql2/result_spec.rb

+24-9
Original file line numberDiff line numberDiff line change
@@ -125,31 +125,46 @@
125125

126126
context "#[]" do
127127
it "should return results when accessed by [offset]" do
128-
result = @client.query "SELECT 1 UNION SELECT 2"
129-
result[1][result.fields.first].should eql(2)
130-
result[0][result.fields.first].should eql(1)
128+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
129+
result[1].should eql({"col" => 2})
130+
result[0].should eql({"col" => 1})
131131
end
132132

133133
it "should return results when accessed by negative [offset]" do
134-
result = @client.query "SELECT 1 UNION SELECT 2"
135-
result[-1][result.fields.first].should eql(2)
136-
result[-2][result.fields.first].should eql(1)
134+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
135+
result[-1].should eql({"col" => 2})
136+
result[-2].should eql({"col" => 1})
137+
end
138+
139+
it "should return array of results when accessed by [offset, count]" do
140+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
141+
result[1, 1].should eql([{"col" => 2}])
142+
result[-2, 10].should eql([{"col" => 1}, {"col" => 2}])
137143
end
138144

139145
it "should return nil if we use too large [offset]" do
140-
result = @client.query "SELECT 1 UNION SELECT 2"
146+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
141147
result[2].should be_nil
142148
result[200].should be_nil
143149
end
144150

145151
it "should return nil if we use too negative [offset]" do
146-
result = @client.query "SELECT 1 UNION SELECT 2"
152+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
147153
result[-3].should be_nil
148154
result[-300].should be_nil
149155
end
150156

157+
it "should accept hash args in [offset, {:foo => bar}] and [offset, count, {:foo => bar}]" do
158+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col"
159+
result[1, {:symbolize_keys => true}].should eql({:col => 2})
160+
result[1, 1, {:symbolize_keys => true}].should eql([{:col => 2}])
161+
# This syntax does not work in Ruby 1.8:
162+
# result[1, :symbolize_keys => true].should eql({:col => 2})
163+
# result[1, 1, :symbolize_keys => true].should eql([{:col => 2}])
164+
end
165+
151166
it "should throw an exception if we use an [offset] in streaming mode" do
152-
result = @client.query "SELECT 1 UNION SELECT 2", :stream => true
167+
result = @client.query "SELECT 1 AS col UNION SELECT 2 AS col", :stream => true
153168
expect { result[0] }.to raise_exception(Mysql2::Error)
154169
end
155170
end

0 commit comments

Comments
 (0)