123456789_123456789_123456789_123456789_123456789_

Class: PG::Tuple

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, Enumerable
Inherits: Object
Defined in: ext/pg_tuple.c,
ext/pg_tuple.c,
lib/pg/tuple.rb

Overview

The class to represent one query result tuple (row). An instance of this class can be created by 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 Tuple holds a reference to the related 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"

Instance Method Summary

Instance Method Details

#[](key ) ⇒ value

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.

Depending on Result#field_name_type= a string or symbol key is interpreted as column name.

If the key can’t be found, it returns nil .

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 309

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(self, field_num);
}

#each {|key, value| ... }

Invokes block for each field name and value in the tuple.

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 355

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, self);
	} else {
		int i;
		for( i = 0; i < this->num_fields; i++ ){
			VALUE value = pg_tuple_materialize_field(self, i);
			rb_yield_values(2, RARRAY_AREF(field_names, i), value);
		}
	}

	pg_tuple_detach(self);
	return self;
}

#each_key(&block)

[ GitHub ]

  
# File 'lib/pg/tuple.rb', line 23

def each_key(&block)
	if fn=field_names
		fn.each(&block)
	else
		field_map.each_key(&block)
	end
end

#each_value {|value| ... }

Invokes block for each field value in the tuple.

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 385

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(self, field_num);
		rb_yield(value);
	}

	pg_tuple_detach(self);
	return self;
}

#fetch(key) → value) #fetch(key, default) → value) #fetch(key) { |key| block } → value)

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.

Depending on Result#field_name_type= a string or symbol 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.

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 252

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(self, field_num);
}

#field_map (private)

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 419

static VALUE
pg_tuple_field_map(VALUE self)
{
	t_pg_tuple *this = pg_tuple_get_this(self);
	return this->field_map;
}

#field_names (private)

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 426

static VALUE
pg_tuple_field_names(VALUE self)
{
	t_pg_tuple *this = pg_tuple_get_this(self);
	return pg_tuple_get_field_names(this);
}

#has_key?(key) ⇒ Boolean Also known as: #key?

[ GitHub ]

  
# File 'lib/pg/tuple.rb', line 14

def has_key?(key)
	field_map.has_key?(key)
end

#index(key) → integer)

Returns the field number which matches the given column name.

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 452

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);
}

#inspect

Return a String representation of the object suitable for debugging.

[ GitHub ]

  
# File 'lib/pg/tuple.rb', line 10

def inspect
	"#<#{self.class} #{self.map{|k,v| "#{k}: #{v.inspect}" }.join(", ") }>"
end

#key?(key)

Alias for #has_key?.

[ GitHub ]

  
# File 'lib/pg/tuple.rb', line 17

alias key? has_key?

#keys

[ GitHub ]

  
# File 'lib/pg/tuple.rb', line 19

def keys
	field_names || field_map.keys.freeze
end

#length(→ integer) Also known as: #size

Returns number of fields of this tuple.

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 439

static VALUE
pg_tuple_length(VALUE self)
{
	t_pg_tuple *this = pg_tuple_get_this(self);
	return INT2NUM(this->num_fields);
}

#marshal_dump (private)

methods for marshaling

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 460

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(self);

	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);

        rb_copy_generic_ivar(a, self);

	return a;
}

#marshal_load(a) (private)

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 482

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);

	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_LENINT(values);

	if (RARRAY_LENINT(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));

	RB_OBJ_WRITE(self, &this->result, Qnil);
	RB_OBJ_WRITE(self, &this->typemap, Qnil);
	this->row_num = -1;
	this->num_fields = num_fields;
	RB_OBJ_WRITE(self, &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);
		RB_OBJ_WRITE(self, &this->values[i], v);
	}

	if( dup_names ){
		RB_OBJ_WRITE(self, &this->values[num_fields], field_names);
	}

	RTYPEDDATA_DATA(self) = this;

	rb_copy_generic_ivar(self, a);

	return self;
}

#length(→ integer) #size(→ integer)

Alias for #length.

#valuesArray

Returns the values of this tuple as Array. res.tuple(i).values is equal to res.tuple_values(i) .

[ GitHub ]

  
# File 'ext/pg_tuple.c', line 410

static VALUE
pg_tuple_values(VALUE self)
{
	t_pg_tuple *this = pg_tuple_get_this(self);

	pg_tuple_materialize(self);
	return rb_ary_new4(this->num_fields, &this->values[0]);
}