class PG::Tuple
The class to represent one query result tuple (row). An instance of this class can be created by PG::Result#tuple .
All field values of the tuple are retrieved on demand from the underlying PGresult object and converted to a Ruby object. Subsequent access to the same field returns the same object, since they are cached when materialized. Each PG::Tuple holds a reference to the related PG::Result object, but gets detached, when all fields are materialized.
Example:
require 'pg' conn = PG.connect(:dbname => 'test') res = conn.exec('VALUES(1,2), (3,4)') t0 = res.tuple(0) # => #<PG::Tuple column1: "1", column2: "2"> t1 = res.tuple(1) # => #<PG::Tuple column1: "3", column2: "4"> t1[0] # => "3" t1["column2"] # => "4"
Public Instance Methods
Returns field name.
static VALUE
pg_tuple_aref(VALUE self, VALUE key)
{
VALUE index;
int field_num;
t_pg_tuple *this = pg_tuple_get_this(self);
switch(rb_type(key)){
case T_FIXNUM:
case T_BIGNUM:
field_num = NUM2INT(key);
if ( field_num < 0 )
field_num = this->num_fields + field_num;
if ( field_num < 0 || field_num >= this->num_fields )
return Qnil;
break;
default:
index = rb_hash_aref(this->field_map, key);
if( index == Qnil ) return Qnil;
field_num = NUM2INT(index);
}
return pg_tuple_materialize_field(this, field_num);
}
Invokes block for each field name and value in the tuple.
static VALUE
pg_tuple_each(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
VALUE field_names;
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
field_names = pg_tuple_get_field_names(this);
if( field_names == Qfalse ){
rb_hash_foreach(this->field_map, pg_tuple_yield_key_value, (VALUE)this);
} else {
int i;
for( i = 0; i < this->num_fields; i++ ){
VALUE value = pg_tuple_materialize_field(this, i);
rb_yield_values(2, RARRAY_AREF(field_names, i), value);
}
}
pg_tuple_detach(this);
return self;
}
# File lib/pg/tuple.rb, line 22 def each_key(&block) if fn=field_names fn.each(&block) else field_map.each_key(&block) end end
Invokes block for each field value in the tuple.
static VALUE
pg_tuple_each_value(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
int field_num;
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
for(field_num = 0; field_num < this->num_fields; field_num++) {
VALUE value = pg_tuple_materialize_field(this, field_num);
rb_yield(value);
}
pg_tuple_detach(this);
return self;
}
Returns a field value by either column index or column name.
An integer key is interpreted as column index. Negative values
of index count from the end of the array.
A string key is interpreted as column name.
If the key can't be found, there are several options: With no other arguments, it will raise a IndexError exception; if default is given, then that will be returned; if the optional code block is specified, then that will be run and its result returned.
static VALUE
pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
{
VALUE key;
long block_given;
VALUE index;
int field_num;
t_pg_tuple *this = pg_tuple_get_this(self);
rb_check_arity(argc, 1, 2);
key = argv[0];
block_given = rb_block_given_p();
if (block_given && argc == 2) {
rb_warn("block supersedes default value argument");
}
switch(rb_type(key)){
case T_FIXNUM:
case T_BIGNUM:
field_num = NUM2INT(key);
if ( field_num < 0 )
field_num = this->num_fields + field_num;
if ( field_num < 0 || field_num >= this->num_fields ){
if (block_given) return rb_yield(key);
if (argc == 1) rb_raise( rb_eIndexError, "Index %d is out of range", field_num );
return argv[1];
}
break;
default:
index = rb_hash_aref(this->field_map, key);
if (index == Qnil) {
if (block_given) return rb_yield(key);
if (argc == 1) rb_raise( rb_eKeyError, "column not found" );
return argv[1];
}
field_num = NUM2INT(index);
}
return pg_tuple_materialize_field(this, field_num);
}
# File lib/pg/tuple.rb, line 13 def has_key?(key) field_map.has_key?(key) end
Returns the field number which matches the given column name.
static VALUE
pg_tuple_index(VALUE self, VALUE key)
{
t_pg_tuple *this = pg_tuple_get_this(self);
return rb_hash_aref(this->field_map, key);
}
Return a String representation of the object suitable for debugging.
# File lib/pg/tuple.rb, line 9 def inspect "#<#{self.class} #{self.map{|k,v| "#{k}: #{v.inspect}" }.join(", ") }>" end
# File lib/pg/tuple.rb, line 18 def keys field_names || field_map.keys.freeze end
Returns number of fields of this tuple.
static VALUE
pg_tuple_length(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
return INT2NUM(this->num_fields);
}
Returns the values of this tuple as Array. +res.tuple(i).values+ is equal to +res.tuple_values(i)+ .
static VALUE
pg_tuple_values(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
pg_tuple_materialize(this);
return rb_ary_new4(this->num_fields, &this->values[0]);
}
Private Instance Methods
static VALUE
pg_tuple_field_map(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
return this->field_map;
}
static VALUE
pg_tuple_field_names(VALUE self)
{
t_pg_tuple *this = pg_tuple_get_this(self);
return pg_tuple_get_field_names(this);
}
static VALUE
pg_tuple_dump(VALUE self)
{
VALUE field_names;
VALUE values;
VALUE a;
t_pg_tuple *this = pg_tuple_get_this(self);
pg_tuple_materialize(this);
field_names = pg_tuple_get_field_names(this);
if( field_names == Qfalse )
field_names = rb_funcall(this->field_map, rb_intern("keys"), 0);
values = rb_ary_new4(this->num_fields, &this->values[0]);
a = rb_ary_new3(2, field_names, values);
if (FL_TEST(self, FL_EXIVAR)) {
rb_copy_generic_ivar(a, self);
FL_SET(a, FL_EXIVAR);
}
return a;
}
static VALUE
pg_tuple_load(VALUE self, VALUE a)
{
int num_fields;
int i;
t_pg_tuple *this;
VALUE values;
VALUE field_names;
VALUE field_map;
int dup_names;
rb_check_frozen(self);
rb_check_trusted(self);
TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
if (this)
rb_raise(rb_eTypeError, "tuple is not empty");
Check_Type(a, T_ARRAY);
if (RARRAY_LEN(a) != 2)
rb_raise(rb_eTypeError, "expected an array of 2 elements");
field_names = RARRAY_AREF(a, 0);
Check_Type(field_names, T_ARRAY);
rb_obj_freeze(field_names);
values = RARRAY_AREF(a, 1);
Check_Type(values, T_ARRAY);
num_fields = RARRAY_LEN(values);
if (RARRAY_LEN(field_names) != num_fields)
rb_raise(rb_eTypeError, "different number of fields and values");
field_map = rb_hash_new();
for( i = 0; i < num_fields; i++ ){
rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
}
rb_obj_freeze(field_map);
dup_names = num_fields != (int)RHASH_SIZE(field_map);
this = (t_pg_tuple *)xmalloc(
sizeof(*this) +
sizeof(*this->values) * num_fields +
sizeof(*this->values) * (dup_names ? 1 : 0));
RTYPEDDATA_DATA(self) = this;
this->result = Qnil;
this->typemap = Qnil;
this->row_num = -1;
this->num_fields = num_fields;
this->field_map = field_map;
for( i = 0; i < num_fields; i++ ){
VALUE v = RARRAY_AREF(values, i);
if( v == Qundef )
rb_raise(rb_eTypeError, "field %d is not materialized", i);
this->values[i] = v;
}
if( dup_names ){
this->values[num_fields] = field_names;
}
if (FL_TEST(a, FL_EXIVAR)) {
rb_copy_generic_ivar(self, a);
FL_SET(self, FL_EXIVAR);
}
return self;
}