Skip to content

Commit

Permalink
Merge from git branch.
Browse files Browse the repository at this point in the history
  • Loading branch information
larskanis committed Feb 11, 2015
2 parents 7532c55 + 188b44f commit d6cedfb
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 102 deletions.
4 changes: 4 additions & 0 deletions ext/extconf.rb
Expand Up @@ -94,6 +94,10 @@
have_header 'unistd.h'
have_header 'ruby/st.h' or have_header 'st.h' or abort "pg currently requires the ruby/st.h header"

checking_for "C99 variable length arrays" do
$defs.push( "-DHAVE_VARIABLE_LENGTH_ARRAYS" ) if try_compile('void test_vla(int l){ int vla[l]; }')
end

create_header()
create_makefile( "pg_ext" )

2 changes: 1 addition & 1 deletion ext/pg.c
Expand Up @@ -252,7 +252,7 @@ pg_get_rb_encoding_as_pg_encoding( rb_encoding *enc )
* char *current_out, *end_capa;
* PG_RB_STR_NEW( string, current_out, end_capa );
* while( data_is_going_to_be_processed ){
* PG_RB_STR_ENSURE_CAPA( string, 2 current_out, end_capa );
* PG_RB_STR_ENSURE_CAPA( string, 2, current_out, end_capa );
* *current_out++ = databyte1;
* *current_out++ = databyte2;
* }
Expand Down
10 changes: 10 additions & 0 deletions ext/pg.h
Expand Up @@ -133,6 +133,15 @@
typedef long suseconds_t;
#endif

#if defined(HAVE_VARIABLE_LENGTH_ARRAYS)
#define PG_VARIABLE_LENGTH_ARRAY(type, name, len, maxlen) type name[(len)];
#else
#define PG_VARIABLE_LENGTH_ARRAY(type, name, len, maxlen) \
type name[(maxlen)] = {(len)>(maxlen) ? (rb_raise(rb_eArgError, "Number of " #name " (%d) exceeds allowed maximum of " #maxlen, (len) ), (type)1) : (type)0};

#define PG_MAX_COLUMNS 4000
#endif

/* The data behind each PG::Connection object */
typedef struct {
PGconn *pgconn;
Expand Down Expand Up @@ -313,6 +322,7 @@ VALUE lookup_error_class _(( const char * ));
VALUE pg_bin_dec_bytea _(( t_pg_coder*, char *, int, int, int, int ));
VALUE pg_text_dec_string _(( t_pg_coder*, char *, int, int, int, int ));
int pg_coder_enc_to_s _(( t_pg_coder*, VALUE, char *, VALUE *));
int pg_text_enc_identifier _(( t_pg_coder*, VALUE, char *, VALUE *));
t_pg_coder_enc_func pg_coder_enc_func _(( t_pg_coder* ));
t_pg_coder_dec_func pg_coder_dec_func _(( t_pg_coder*, int ));
void pg_define_coder _(( const char *, void *, VALUE, VALUE ));
Expand Down
31 changes: 11 additions & 20 deletions ext/pg_connection.c
Expand Up @@ -2997,7 +2997,9 @@ pgconn_transaction(VALUE self)
/*
* call-seq:
* PG::Connection.quote_ident( str ) -> String
* PG::Connection.quote_ident( array ) -> String
* conn.quote_ident( str ) -> String
* conn.quote_ident( array ) -> String
*
* Returns a string that is safe for inclusion in a SQL query as an
* identifier. Note: this is not a quote function for values, but for
Expand All @@ -3014,31 +3016,20 @@ pgconn_transaction(VALUE self)
* Similarly, this function also protects against special characters,
* and other things that might allow SQL injection if the identifier
* comes from an untrusted source.
*
* If the parameter is an Array, then all it's values are separately quoted
* and then joined by a "." character. This can be used for identifiers in
* the form "schema"."table"."column" .
*
* This method is functional identical to the encoder PG::TextEncoder::Identifier .
*
*/
static VALUE
pgconn_s_quote_ident(VALUE self, VALUE in_str)
{
VALUE ret;
char *str = StringValuePtr(in_str);
/* result size at most NAMEDATALEN*2 plus surrounding
* double-quotes. */
char buffer[NAMEDATALEN*2+2];
unsigned int i=0,j=0;
unsigned int str_len = RSTRING_LENINT(in_str);

if(str_len >= NAMEDATALEN) {
rb_raise(rb_eArgError,
"Input string is longer than NAMEDATALEN-1 (%d)",
NAMEDATALEN-1);
}
buffer[j++] = '"';
for(i = 0; i < str_len && str[i]; i++) {
if(str[i] == '"')
buffer[j++] = '"';
buffer[j++] = str[i];
}
buffer[j++] = '"';
ret = rb_str_new(buffer,j);
pg_text_enc_identifier(NULL, in_str, NULL, &ret);

OBJ_INFECT(ret, in_str);
PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET( rb_obj_class(self) == rb_cPGconn ? self : in_str ));

Expand Down
6 changes: 3 additions & 3 deletions ext/pg_result.c
Expand Up @@ -863,7 +863,7 @@ pgresult_each_row(VALUE self)
num_fields = PQnfields(this->pgresult);

for ( row = 0; row < num_rows; row++ ) {
VALUE row_values[num_fields];
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)

/* populate the row */
for ( field = 0; field < num_fields; field++ ) {
Expand Down Expand Up @@ -892,7 +892,7 @@ pgresult_values(VALUE self)
VALUE results = rb_ary_new2( num_rows );

for ( row = 0; row < num_rows; row++ ) {
VALUE row_values[num_fields];
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)

/* populate the row */
for ( field = 0; field < num_fields; field++ ) {
Expand Down Expand Up @@ -1176,7 +1176,7 @@ pgresult_stream_each_row(VALUE self)
}

for ( row = 0; row < ntuples; row++ ) {
VALUE row_values[nfields];
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
int field;

/* populate the row */
Expand Down
15 changes: 6 additions & 9 deletions ext/pg_text_decoder.c
Expand Up @@ -293,7 +293,7 @@ pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, in
}

/*
* Document-class: PG::TextDecoder::Identifier < PG::CompositeDecoder
* Document-class: PG::TextDecoder::Identifier < PG::SimpleDecoder
*
* This is the decoder class for PostgreSQL identifiers.
*
Expand All @@ -305,16 +305,13 @@ pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, in
static VALUE
pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
{
t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);

/* Return value: array */
VALUE array;
VALUE elem;
int word_index = 0;
int index;
/* Use a buffer of the same length, as that will be the worst case */
char word[len + 1];
PG_VARIABLE_LENGTH_ARRAY(char, word, len + 1, NAMEDATALEN)

/* The current character in the input string. */
char c;
Expand All @@ -331,7 +328,7 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
if(c == '.' && openQuote < 2 ) {
word[word_index] = 0;

elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
rb_ary_push(array, elem);

openQuote = 0;
Expand All @@ -353,7 +350,7 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
}

word[word_index] = 0;
elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
rb_ary_push(array, elem);

return array;
Expand Down Expand Up @@ -412,11 +409,11 @@ init_pg_text_decoder()
pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
pg_define_coder( "Bytea", pg_text_dec_bytea, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_SimpleDecoder ); */
pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );

/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_CompositeDecoder ); */
pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "FromBase64", rb_cPG_CompositeDecoder ); */
pg_define_coder( "FromBase64", pg_text_dec_from_base64, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
}
73 changes: 35 additions & 38 deletions ext/pg_text_encoder.c
Expand Up @@ -455,39 +455,34 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
}
}

static int
quote_identifier_buffer( void *_this, char *p_in, int strlen, char *p_out ){
static char *
quote_identifier( VALUE value, VALUE out_string, char *current_out ){
char *p_in = RSTRING_PTR(value);
char *ptr1;
char *ptr2;
int backslashs = 0;
size_t strlen = RSTRING_LEN(value);
char *end_capa = current_out;

/* count required backlashs */
PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
*current_out++ = '"';
for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
if (*ptr1 == '"'){
backslashs++;
char c = *ptr1;
if (c == '"'){
strlen++;
PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
*current_out++ = '"';
} else if (c == 0){
break;
}
*current_out++ = c;
}
PG_RB_STR_ENSURE_CAPA( out_string, 1, current_out, end_capa );
*current_out++ = '"';

ptr1 = p_in + strlen;
ptr2 = p_out + strlen + backslashs + 2;
/* Write end quote */
*--ptr2 = '"';

/* Then store the escaped string on the final position, walking
* right to left, until all backslashs are placed. */
while( ptr1 != p_in ) {
*--ptr2 = *--ptr1;
if(*ptr2 == '"'){
*--ptr2 = '"';
}
}
/* Write start quote */
*p_out = '"';
return strlen + backslashs + 2;
return current_out;
}

static char *
pg_text_enc_array_identifier(t_pg_composite_coder *this, VALUE value, VALUE string, char *out)
pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
{
int i;
int nr_elems;
Expand All @@ -498,7 +493,7 @@ pg_text_enc_array_identifier(t_pg_composite_coder *this, VALUE value, VALUE stri
for( i=0; i<nr_elems; i++){
VALUE entry = rb_ary_entry(value, i);

out = quote_string(this->elem, entry, string, out, this->needs_quotation, quote_identifier_buffer, this);
out = quote_identifier(entry, string, out);
if( i < nr_elems-1 ){
out = pg_rb_str_ensure_capa( string, 1, out, NULL );
*out++ = '.';
Expand All @@ -508,27 +503,29 @@ pg_text_enc_array_identifier(t_pg_composite_coder *this, VALUE value, VALUE stri
}

/*
* Document-class: PG::TextEncoder::Identifier < PG::CompositeEncoder
* Document-class: PG::TextEncoder::Identifier < PG::SimpleEncoder
*
* This is the encoder class for PostgreSQL identifiers.
*
* An Array value can be used for "schema.table.column" type identifiers:
* PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
* => "schema"."table"."column"
* => '"schema"."table"."column"'
*
* This encoder can also be used per PG::Connection#quote_ident .
*/
static int
pg_text_enc_identifier(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
int
pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
{
t_pg_composite_coder *this = (t_pg_composite_coder *)conv;

*intermediate = rb_str_new(NULL, 0);
out = RSTRING_PTR(*intermediate);

UNUSED( this );
if( TYPE(value) == T_ARRAY){
out = pg_text_enc_array_identifier(this, value, *intermediate, out);
*intermediate = rb_str_new(NULL, 0);
out = RSTRING_PTR(*intermediate);
out = pg_text_enc_array_identifier(value, *intermediate, out);
} else {
out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_identifier_buffer, this);
StringValue(value);
*intermediate = rb_str_new(NULL, RSTRING_LEN(value) + 2);
out = RSTRING_PTR(*intermediate);
out = quote_identifier(value, *intermediate, out);
}
rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
return -1;
Expand Down Expand Up @@ -651,11 +648,11 @@ init_pg_text_encoder()
pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
pg_define_coder( "Bytea", pg_text_enc_bytea, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Identifier", rb_cPG_SimpleEncoder ); */
pg_define_coder( "Identifier", pg_text_enc_identifier, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );

/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Array", rb_cPG_CompositeEncoder ); */
pg_define_coder( "Array", pg_text_enc_array, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Identifier", rb_cPG_CompositeEncoder ); */
pg_define_coder( "Identifier", pg_text_enc_identifier, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "QuotedLiteral", rb_cPG_CompositeEncoder ); */
pg_define_coder( "QuotedLiteral", pg_text_enc_quoted_literal, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "ToBase64", rb_cPG_CompositeEncoder ); */
Expand Down
2 changes: 1 addition & 1 deletion ext/pg_type_map_by_mri_type.c
Expand Up @@ -39,7 +39,7 @@ static VALUE rb_cTypeMapByMriType;
typedef struct {
t_typemap typemap;
struct pg_tmbmt_converter {
FOR_EACH_MRI_TYPE( DECLARE_CODER );
FOR_EACH_MRI_TYPE( DECLARE_CODER )
} coders;
} t_tmbmt;

Expand Down
11 changes: 11 additions & 0 deletions spec/pg/connection_spec.rb
Expand Up @@ -1193,9 +1193,20 @@
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
expect( escaped ).to eq( "\"string to\"" )
end
end

it "can quote bigger strings with quote_ident" do
original = "'01234567\"" * 100
escaped = described_class.quote_ident( original + "\0afterzero" )
expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
end

it "can quote Arrays with quote_ident" do
original = "'01234567\""
escaped = described_class.quote_ident( [original]*3 )
expected = ["\"" + original.gsub("\"", "\"\"") + "\""] * 3
expect( escaped ).to eq( expected.join(".") )
end

describe "Ruby 1.9.x default_internal encoding" do

Expand Down

0 comments on commit d6cedfb

Please sign in to comment.