Skip to content

Commit

Permalink
Add field_name_type setter and getter to PG::Connection
Browse files Browse the repository at this point in the history
It allows to set the field name type on a connection scope.

Added methods are:
    * PG::Connection#field_name_type=
    * PG::Connection#field_name_type
  • Loading branch information
larskanis committed Nov 19, 2019
1 parent 8fd166a commit fd66f35
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
6 changes: 6 additions & 0 deletions ext/pg.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ typedef struct {
VALUE decoder_for_get_copy_data;
/* Ruby encoding index of the client/internal encoding */
int enc_idx : PG_ENC_IDX_BITS;
/* flags controlling Symbol/String field names */
unsigned int flags : 2;

#if defined(_WIN32)
/* File descriptor to be used for rb_w32_unwrap_io_handle() */
Expand Down Expand Up @@ -168,6 +170,10 @@ typedef VALUE (* t_pg_typecast_result)(t_typemap *, VALUE, int, int);
typedef t_pg_coder *(* t_pg_typecast_query_param)(t_typemap *, VALUE, int);
typedef VALUE (* t_pg_typecast_copy_get)( t_typemap *, VALUE, int, int, int );

#define PG_RESULT_FIELD_NAMES_MASK 0x03
#define PG_RESULT_FIELD_NAMES_SYMBOL 0x01
#define PG_RESULT_FIELD_NAMES_STATIC_SYMBOL 0x02

#define PG_CODER_TIMESTAMP_DB_UTC 0x0
#define PG_CODER_TIMESTAMP_DB_LOCAL 0x1
#define PG_CODER_TIMESTAMP_APP_UTC 0x0
Expand Down
59 changes: 59 additions & 0 deletions ext/pg_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
VALUE rb_cPGconn;
static ID s_id_encode;
static VALUE sym_type, sym_format, sym_value;
static VALUE sym_symbol, sym_string, sym_static_symbol;

static PQnoticeReceiver default_notice_receiver = NULL;
static PQnoticeProcessor default_notice_processor = NULL;
Expand Down Expand Up @@ -4060,6 +4061,58 @@ pgconn_decoder_for_get_copy_data_get(VALUE self)
return this->decoder_for_get_copy_data;
}

/*
* call-seq:
* conn.field_name_type = Symbol
*
* Set default type of field names of results retrieved by this connection.
* It can be set to one of:
* * +:string+ to use String based field names
* * +:symbol+ to use Symbol based field names
*
* The default is +:string+ .
*
* Settings the type of field names affects only future results.
*
* See further description at PG::Result#field_name_type=
*
*/
static VALUE
pgconn_field_name_type_set(VALUE self, VALUE sym)
{
t_pg_connection *this = pg_get_connection( self );

this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
else if ( sym == sym_string );
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);

return sym;
}

/*
* call-seq:
* conn.field_name_type -> Symbol
*
* Get type of field names.
*
* See description at #field_name_type=
*/
static VALUE
pgconn_field_name_type_get(VALUE self)
{
t_pg_connection *this = pg_get_connection( self );

if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
return sym_symbol;
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
return sym_static_symbol;
} else {
return sym_string;
}
}


/*
* Document-class: PG::Connection
Expand All @@ -4071,6 +4124,9 @@ init_pg_connection()
sym_type = ID2SYM(rb_intern("type"));
sym_format = ID2SYM(rb_intern("format"));
sym_value = ID2SYM(rb_intern("value"));
sym_string = ID2SYM(rb_intern("string"));
sym_symbol = ID2SYM(rb_intern("symbol"));
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));

rb_cPGconn = rb_define_class_under( rb_mPG, "Connection", rb_cObject );
rb_include_module(rb_cPGconn, rb_mPGconstants);
Expand Down Expand Up @@ -4255,4 +4311,7 @@ init_pg_connection()
rb_define_method(rb_cPGconn, "encoder_for_put_copy_data", pgconn_encoder_for_put_copy_data_get, 0);
rb_define_method(rb_cPGconn, "decoder_for_get_copy_data=", pgconn_decoder_for_get_copy_data_set, 1);
rb_define_method(rb_cPGconn, "decoder_for_get_copy_data", pgconn_decoder_for_get_copy_data_get, 0);

rb_define_method(rb_cPGconn, "field_name_type=", pgconn_field_name_type_set, 1 );
rb_define_method(rb_cPGconn, "field_name_type", pgconn_field_name_type_get, 0 );
}
7 changes: 2 additions & 5 deletions ext/pg_result.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@

#include "pg.h"

#define PG_RESULT_FIELD_NAMES_MASK 0x03
#define PG_RESULT_FIELD_NAMES_SYMBOL 0x01
#define PG_RESULT_FIELD_NAMES_STATIC_SYMBOL 0x02

VALUE rb_cPGresult;
static VALUE sym_symbol, sym_string, sym_static_symbol;

Expand Down Expand Up @@ -206,6 +202,7 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
this->enc_idx = p_conn->enc_idx;
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
this->p_typemap = DATA_PTR( this->typemap );
this->flags = p_conn->flags;
} else {
this->enc_idx = rb_locale_encindex();
}
Expand Down Expand Up @@ -1504,7 +1501,7 @@ pgresult_stream_each_tuple(VALUE self)
* * +:symbol+ to use Symbol based field names
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably removed in future.
*
* The default is +:string+ .
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
*
* This setting affects several result methods:
* * keys of Hash returned by #[] , #each and #stream_each
Expand Down
29 changes: 29 additions & 0 deletions spec/pg/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,35 @@
end
end

describe :field_name_type do
it "uses string field names per default" do
conn = PG.connect( @conninfo )
expect(conn.field_name_type).to eq(:string)
conn.finish
end

it "can set string field names" do
@conn.field_name_type = :string
expect(@conn.field_name_type).to eq(:string)
res = @conn.exec("SELECT 1 as az")
expect(res.field_name_type).to eq(:string)
expect(res.fields).to eq(["az"])
end

it "can set symbol field names" do
@conn.field_name_type = :symbol
expect(@conn.field_name_type).to eq(:symbol)
res = @conn.exec("SELECT 1 as az")
expect(res.field_name_type).to eq(:symbol)
expect(res.fields).to eq([:az])
end

it "can't set invalid values" do
expect{ @conn.field_name_type = :sym }.to raise_error(ArgumentError, /invalid argument :sym/)
expect{ @conn.field_name_type = "symbol" }.to raise_error(ArgumentError, /invalid argument "symbol"/)
end
end

describe "deprecated forms of methods" do
it "should forward exec to exec_params" do
res = @conn.exec("VALUES($1::INT)", [7]).values
Expand Down

0 comments on commit fd66f35

Please sign in to comment.