Skip to content

Commit 0ed17f8

Browse files
committed
Feature request #130: add element reference operator #[] to Mysql2::Result using mysql_data_seek.
1 parent 2eb4a8f commit 0ed17f8

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

ext/mysql2/result.c

+46
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,51 @@ 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;
475+
ID db_timezone, app_timezone;
476+
long offset;
477+
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
478+
mysql2_result_wrapper * wrapper;
479+
480+
GetMysql2Result(self, wrapper);
481+
482+
offset = NUM2LONG(seek);
483+
484+
if (!wrapper->numberOfRows) {
485+
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
486+
}
487+
488+
opts = rb_iv_get(self, "@query_options");
489+
rb_mysql_row_query_options(opts, &db_timezone, &app_timezone, &symbolizeKeys, &asArray, &castBool, &cast, &streaming, &cacheRows);
490+
491+
if (streaming) {
492+
rb_raise(cMysql2Error, "Element reference operator #[] cannot be used in streaming mode.");
493+
}
494+
495+
/* count back from the end if passed a negative number */
496+
if (offset < 0) {
497+
offset = wrapper->numberOfRows + offset;
498+
}
499+
500+
/* negative offset was too big */
501+
if (offset < 0) {
502+
return Qnil;
503+
/* rb_raise(cMysql2Error, "Out of range: offset %ld is beyond %lu rows (offset begins at 0).", offset, wrapper->numberOfRows); */
504+
}
505+
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); */
509+
}
510+
511+
mysql_data_seek(wrapper->result, offset);
512+
513+
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast);
514+
515+
return row;
516+
}
517+
473518
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
474519
VALUE defaults, opts, block;
475520
ID db_timezone, app_timezone;
@@ -613,6 +658,7 @@ void init_mysql2_result() {
613658
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
614659

615660
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
661+
rb_define_method(cMysql2Result, "[]", rb_mysql_result_element, 1);
616662
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
617663
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
618664
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);

spec/mysql2/result_spec.rb

+31
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,37 @@
123123
end
124124
end
125125

126+
context "#[]" do
127+
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)
131+
end
132+
133+
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)
137+
end
138+
139+
it "should return nil if we use too large [offset]" do
140+
result = @client.query "SELECT 1 UNION SELECT 2"
141+
result[2].should be_nil
142+
result[200].should be_nil
143+
end
144+
145+
it "should return nil if we use too negative [offset]" do
146+
result = @client.query "SELECT 1 UNION SELECT 2"
147+
result[-3].should be_nil
148+
result[-300].should be_nil
149+
end
150+
151+
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
153+
expect { result[0] }.to raise_exception(Mysql2::Error)
154+
end
155+
end
156+
126157
context "#fields" do
127158
before(:each) do
128159
@client.query "USE test"

0 commit comments

Comments
 (0)