Skip to content

Commit 0ee61f1

Browse files
committed
Merge branch 'master' of github.com:ged/ruby-pg
2 parents 9afa148 + f9d05f5 commit 0ee61f1

File tree

16 files changed

+351
-14
lines changed

16 files changed

+351
-14
lines changed

.github/workflows/binary-gems.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,26 @@ jobs:
171171
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
172172
docker build --rm --platform ${{matrix.image_platform}} --build-arg from_image=${{matrix.from_image}} -t ruby-test -f spec/env/Dockerfile.${{matrix.dockerfile}} .
173173
docker run --rm -t --network=host -v `pwd`:/build ruby-test
174+
175+
job_binary_yugabyte:
176+
name: yugabyte (${{matrix.gem_platform}}
177+
needs: rcd_build
178+
strategy:
179+
fail-fast: false
180+
matrix:
181+
include:
182+
- gem_platform: x86_64-linux
183+
184+
runs-on: ubuntu-latest
185+
steps:
186+
- uses: actions/checkout@v4
187+
- name: Download gem-${{ matrix.gem_platform }}
188+
uses: actions/download-artifact@v4
189+
with:
190+
name: binary-gem-${{ matrix.gem_platform }}
191+
- name: Build image and Run tests
192+
run: |
193+
sudo apt-get install -y docker-compose
194+
cp -v pg-*.gem misc/yugabyte/
195+
cd misc/yugabyte
196+
docker-compose up --abort-on-container-exit --exit-code-from pg

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ end
1313
group :test do
1414
gem "bundler", ">= 1.16", "< 3.0"
1515
gem "rake-compiler", "~> 1.0"
16-
gem "rake-compiler-dock", "~> 1.5"
16+
gem "rake-compiler-dock", "~> 1.8.0"
1717
gem "rspec", "~> 3.5"
1818
# "bigdecimal" is a gem on ruby-3.4+ and it's optional for ruby-pg.
1919
# Specs should succeed without it, but 4 examples are then excluded.

Rakefile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ CrossLibraries = [
4747
['x64-mingw-ucrt', 'mingw64', 'x86_64-w64-mingw32'],
4848
['x86-mingw32', 'mingw', 'i686-w64-mingw32'],
4949
['x64-mingw32', 'mingw64', 'x86_64-w64-mingw32'],
50-
['x86_64-linux', 'linux-x86_64', 'x86_64-redhat-linux-gnu'],
50+
['x86_64-linux', 'linux-x86_64', 'x86_64-linux-gnu'],
5151
].map do |platform, openssl_config, toolchain|
5252
CrossLibrary.new platform, openssl_config, toolchain
5353
end
@@ -100,13 +100,12 @@ CrossLibraries.each do |xlib|
100100
desc "Build fat binary gem for platform #{platform}"
101101
task "gem:native:#{platform}" => ['gem:native:prepare'] do
102102
RakeCompilerDock.sh <<-EOT, platform: platform
103-
#{ "sudo yum install -y perl-IPC-Cmd bison flex &&" if platform =~ /linux/ }
104103
#{ # remove nm on Linux to suppress PostgreSQL's check for exit which raises thread_exit as a false positive:
105-
"sudo mv `which nm` `which nm`.bak && sudo mv `which nm` `which nm`.bak &&" if platform =~ /linux/ }
106-
#{ "sudo apt-get update && sudo apt-get install -y bison flex &&" if platform =~ /mingw/ }
104+
"sudo mv `which nm` `which nm`.bak &&" if platform =~ /linux/ }
105+
sudo apt-get update && sudo apt-get install -y bison flex &&
107106
(cp build/gem/gem-*.pem ~/.gem/ || true) &&
108107
bundle install --local &&
109-
rake native:#{platform} pkg/#{$gem_spec.full_name}-#{platform}.gem MAKEOPTS=-j`nproc` RUBY_CC_VERSION=3.3.0:3.2.0:3.1.0:3.0.0:2.7.0
108+
rake native:#{platform} pkg/#{$gem_spec.full_name}-#{platform}.gem MAKEOPTS=-j`nproc` RUBY_CC_VERSION=3.4.1:3.3.5:3.2.6:3.1.6:3.0.7:2.7.8
110109
EOT
111110
end
112111
desc "Build the native binary gems"

ext/extconf.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def work_path
9696
# See https://github.com/cockroachdb/cockroach/issues/49734
9797
recipe.configure_options << "CFLAGS=-fcommon#{" -fPIC" if RUBY_PLATFORM =~ /linux/}"
9898
recipe.configure_options << "--without-keyutils"
99+
recipe.configure_options << "krb5_cv_attr_constructor_destructor=yes"
100+
recipe.configure_options << "ac_cv_func_regcomp=yes"
101+
recipe.configure_options << "ac_cv_printf_positional=yes"
99102
recipe.host = toolchain
100103
recipe.cook_and_activate
101104
end
@@ -111,6 +114,8 @@ def configure_defaults
111114
*(RUBY_PLATFORM=~/linux/ ? ['--with-gssapi'] : []),
112115
'--without-zlib',
113116
'--without-icu',
117+
'--without-readline',
118+
'ac_cv_search_gss_store_cred_into=',
114119
]
115120
end
116121
def compile
@@ -135,6 +140,8 @@ def install
135140
File.join(postgresql_recipe.port_path, "lib/libpq-ruby-pg.so.1")
136141
# Avoid dependency to external libgcc.dll on x86-mingw32
137142
$LDFLAGS << " -static-libgcc"
143+
# Avoid: "libpq.so: undefined reference to `dlopen'" in cross-ruby-2.7.8
144+
$LDFLAGS << " -Wl,--no-as-needed"
138145
# Find libpq in the ports directory coming from lib/3.3
139146
# It is shared between all compiled ruby versions.
140147
$LDFLAGS << " '-Wl,-rpath=$$ORIGIN/../../ports/#{gem_platform}/lib'"

ext/pg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ typedef struct {
206206
t_pg_coder comp;
207207
t_pg_coder *elem;
208208
int needs_quotation;
209+
int dimensions;
209210
char delimiter;
210211
} t_pg_composite_coder;
211212

ext/pg_binary_encoder.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i
320320
* This encoder expects an Array of values or sub-arrays as input.
321321
* Other values are passed through as byte string without interpretation.
322322
*
323+
* It is possible to enforce a number of dimensions to be encoded by #dimensions= .
324+
* Deeper nested arrays are then passed to the elements encoder and less nested arrays raise an ArgumentError.
325+
*
323326
* The accessors needs_quotation and delimiter are ignored for binary encoding.
324327
*
325328
*/
@@ -346,7 +349,8 @@ pg_bin_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
346349
dim_sizes[ndim-1] = RARRAY_LENINT(el1);
347350
nitems *= dim_sizes[ndim-1];
348351
el2 = rb_ary_entry(el1, 0);
349-
if (TYPE(el2) == T_ARRAY) {
352+
if ( (this->dimensions < 0 || ndim < this->dimensions) &&
353+
TYPE(el2) == T_ARRAY) {
350354
ndim++;
351355
if (ndim > MAXDIM)
352356
rb_raise( rb_eArgError, "unsupported number of array dimensions: >%d", ndim );
@@ -356,6 +360,9 @@ pg_bin_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
356360
el1 = el2;
357361
}
358362
}
363+
if( this->dimensions >= 0 && (ndim==0 ? 1 : ndim) != this->dimensions ){
364+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", ndim, this->dimensions);
365+
}
359366

360367
if(out){
361368
/* Second encoder pass -> write data to `out` */

ext/pg_coder.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pg_composite_encoder_allocate( VALUE klass )
135135
this->elem = NULL;
136136
this->needs_quotation = 1;
137137
this->delimiter = ',';
138+
this->dimensions = -1;
138139
rb_iv_set( self, "@elements_type", Qnil );
139140
return self;
140141
}
@@ -157,6 +158,7 @@ pg_composite_decoder_allocate( VALUE klass )
157158
this->elem = NULL;
158159
this->needs_quotation = 1;
159160
this->delimiter = ',';
161+
this->dimensions = -1;
160162
rb_iv_set( self, "@elements_type", Qnil );
161163
return self;
162164
}
@@ -421,6 +423,49 @@ pg_coder_delimiter_get(VALUE self)
421423
return rb_str_new(&this->delimiter, 1);
422424
}
423425

426+
/*
427+
* call-seq:
428+
* coder.dimensions = Integer
429+
* coder.dimensions = nil
430+
*
431+
* Set number of array dimensions to be encoded.
432+
*
433+
* This property ensures, that this number of dimensions is always encoded.
434+
* If less dimensions than this number are in the given value, an ArgumentError is raised.
435+
* If more dimensions than this number are in the value, the Array value is passed to the next encoder.
436+
*
437+
* Setting dimensions is especially useful, when a Record shall be encoded into an Array, since the Array encoder can not distinguish if the array shall be encoded as a higher dimension or as a record otherwise.
438+
*
439+
* The default is +nil+.
440+
*
441+
* See #dimensions
442+
*/
443+
static VALUE
444+
pg_coder_dimensions_set(VALUE self, VALUE dimensions)
445+
{
446+
t_pg_composite_coder *this = RTYPEDDATA_DATA(self);
447+
rb_check_frozen(self);
448+
if(!NIL_P(dimensions) && NUM2INT(dimensions) < 0)
449+
rb_raise( rb_eArgError, "dimensions must be nil or >= 0");
450+
this->dimensions = NIL_P(dimensions) ? -1 : NUM2INT(dimensions);
451+
return dimensions;
452+
}
453+
454+
/*
455+
* call-seq:
456+
* coder.dimensions -> Integer | nil
457+
*
458+
* Get number of enforced array dimensions or +nil+ if not set.
459+
*
460+
* See #dimensions=
461+
*/
462+
static VALUE
463+
pg_coder_dimensions_get(VALUE self)
464+
{
465+
t_pg_composite_coder *this = RTYPEDDATA_DATA(self);
466+
return this->dimensions < 0 ? Qnil : INT2NUM(this->dimensions);
467+
}
468+
424469
/*
425470
* call-seq:
426471
* coder.elements_type = coder
@@ -602,6 +647,8 @@ init_pg_coder(void)
602647
*
603648
* This is the base class for all type cast classes of PostgreSQL types,
604649
* that are made up of some sub type.
650+
*
651+
* See PG::TextEncoder::Array, PG::TextDecoder::Array, PG::BinaryEncoder::Array, PG::BinaryDecoder::Array, etc.
605652
*/
606653
rb_cPG_CompositeCoder = rb_define_class_under( rb_mPG, "CompositeCoder", rb_cPG_Coder );
607654
rb_define_method( rb_cPG_CompositeCoder, "elements_type=", pg_coder_elements_type_set, 1 );
@@ -610,6 +657,8 @@ init_pg_coder(void)
610657
rb_define_method( rb_cPG_CompositeCoder, "needs_quotation?", pg_coder_needs_quotation_get, 0 );
611658
rb_define_method( rb_cPG_CompositeCoder, "delimiter=", pg_coder_delimiter_set, 1 );
612659
rb_define_method( rb_cPG_CompositeCoder, "delimiter", pg_coder_delimiter_get, 0 );
660+
rb_define_method( rb_cPG_CompositeCoder, "dimensions=", pg_coder_dimensions_set, 1 );
661+
rb_define_method( rb_cPG_CompositeCoder, "dimensions", pg_coder_dimensions_get, 0 );
613662

614663
/* Document-class: PG::CompositeEncoder < PG::CompositeCoder */
615664
rb_cPG_CompositeEncoder = rb_define_class_under( rb_mPG, "CompositeEncoder", rb_cPG_CompositeCoder );

ext/pg_text_encoder.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,14 +537,18 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
537537
}
538538

539539
static char *
540-
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
540+
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx, int dimension)
541541
{
542542
int i;
543543

544544
/* size of "{}" */
545545
current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
546546
*current_out++ = '{';
547547

548+
if( RARRAY_LEN(value) == 0 && this->dimensions >= 0 && dimension != this->dimensions ){
549+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
550+
}
551+
548552
for( i=0; i<RARRAY_LEN(value); i++){
549553
VALUE entry = rb_ary_entry(value, i);
550554

@@ -554,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
554558
}
555559

556560
switch(TYPE(entry)){
557-
case T_ARRAY:
558-
current_out = write_array(this, entry, current_out, string, quote, enc_idx);
559-
break;
560561
case T_NIL:
562+
if( this->dimensions >= 0 && dimension != this->dimensions ){
563+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
564+
}
561565
current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
562566
*current_out++ = 'N';
563567
*current_out++ = 'U';
564568
*current_out++ = 'L';
565569
*current_out++ = 'L';
566570
break;
571+
case T_ARRAY:
572+
if( this->dimensions < 0 || dimension < this->dimensions ){
573+
current_out = write_array(this, entry, current_out, string, quote, enc_idx, dimension+1);
574+
break;
575+
}
576+
/* Number of dimensions reached -> handle array as normal value */
567577
default:
578+
if( this->dimensions >= 0 && dimension != this->dimensions ){
579+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
580+
}
568581
current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
569582
}
570583
}
@@ -596,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
596609
VALUE out_str = rb_str_new(NULL, 0);
597610
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
598611

599-
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
612+
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx, 1);
600613

601614
rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
602615
*intermediate = out_str;

ext/pg_type_map_by_column.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
5454
t_tmbc *this = RTYPEDDATA_DATA( self );
5555
t_typemap *default_tm;
5656

57+
Check_Type(params, T_ARRAY);
5758
nfields = (int)RARRAY_LEN( params );
5859
if ( this->nfields != nfields ) {
5960
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",

lib/pg/coder.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ def to_h
7676
elements_type: elements_type,
7777
needs_quotation: needs_quotation?,
7878
delimiter: delimiter,
79+
dimensions: dimensions,
7980
}
8081
end
8182

8283
def inspect
8384
str = super
84-
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
85+
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation#{dimensions && " #{dimensions} dimensions"}"
8586
str
8687
end
8788
end

0 commit comments

Comments
 (0)