pg 1.5.8 → 1.6.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Gemfile +7 -4
- data/History.md +37 -0
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +3 -3
- data/README.md +4 -4
- data/Rakefile +56 -13
- data/ext/errorcodes.def +4 -5
- data/ext/errorcodes.txt +2 -5
- data/ext/extconf.rb +119 -13
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +16 -5
- data/ext/pg.h +8 -9
- data/ext/pg_binary_decoder.c +150 -0
- data/ext/pg_binary_encoder.c +203 -7
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +3 -5
- data/ext/pg_connection.c +337 -148
- data/ext/pg_copy_coder.c +2 -2
- data/ext/pg_record_coder.c +1 -1
- data/ext/pg_result.c +9 -11
- data/ext/pg_text_encoder.c +2 -2
- data/ext/pg_tuple.c +2 -2
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +1 -1
- data/ext/pg_type_map_by_class.c +1 -1
- data/ext/pg_type_map_by_column.c +1 -1
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +1 -1
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/lib/pg/basic_type_registry.rb +2 -2
- data/lib/pg/cancel_connection.rb +30 -0
- data/lib/pg/connection.rb +187 -133
- data/lib/pg/version.rb +1 -1
- data/lib/pg.rb +13 -8
- data/pg.gemspec +5 -3
- data.tar.gz.sig +0 -0
- metadata +10 -21
- metadata.gz.sig +0 -0
- data/.appveyor.yml +0 -42
- data/.gems +0 -6
- data/.gemtest +0 -0
- data/.github/workflows/binary-gems.yml +0 -117
- data/.github/workflows/source-gem.yml +0 -152
- data/.gitignore +0 -22
- data/.hgsigs +0 -34
- data/.hgtags +0 -41
- data/.irbrc +0 -23
- data/.pryrc +0 -23
- data/.tm_properties +0 -21
- data/.travis.yml +0 -49
- data/Rakefile.cross +0 -298
    
        data/ext/pg.h
    CHANGED
    
    | @@ -76,14 +76,7 @@ typedef long suseconds_t; | |
| 76 76 | 
             
            	#define PG_MAX_COLUMNS 4000
         | 
| 77 77 | 
             
            #endif
         | 
| 78 78 |  | 
| 79 | 
            -
            #ifdef HAVE_RB_GC_MARK_MOVABLE
         | 
| 80 | 
            -
            #define pg_compact_callback(x) (x)
         | 
| 81 79 | 
             
            #define pg_gc_location(x) x = rb_gc_location(x)
         | 
| 82 | 
            -
            #else
         | 
| 83 | 
            -
            #define rb_gc_mark_movable(x) rb_gc_mark(x)
         | 
| 84 | 
            -
            #define pg_compact_callback(x) {(x)}
         | 
| 85 | 
            -
            #define pg_gc_location(x) UNUSED(x)
         | 
| 86 | 
            -
            #endif
         | 
| 87 80 |  | 
| 88 81 | 
             
            /* For compatibility with ruby < 3.0 */
         | 
| 89 82 | 
             
            #ifndef RUBY_TYPED_FROZEN_SHAREABLE
         | 
| @@ -123,10 +116,8 @@ typedef struct { | |
| 123 116 | 
             
            	/* enable automatic flushing of send data at the end of send_query calls */
         | 
| 124 117 | 
             
            	unsigned int flush_data : 1;
         | 
| 125 118 |  | 
| 126 | 
            -
            #if defined(_WIN32)
         | 
| 127 119 | 
             
            	/* File descriptor to be used for rb_w32_unwrap_io_handle() */
         | 
| 128 120 | 
             
            	int ruby_sd;
         | 
| 129 | 
            -
            #endif
         | 
| 130 121 | 
             
            } t_pg_connection;
         | 
| 131 122 |  | 
| 132 123 | 
             
            typedef struct pg_coder t_pg_coder;
         | 
| @@ -312,6 +303,7 @@ void init_pg_text_decoder                              _(( void )); | |
| 312 303 | 
             
            void init_pg_binary_encoder                            _(( void ));
         | 
| 313 304 | 
             
            void init_pg_binary_decoder                            _(( void ));
         | 
| 314 305 | 
             
            void init_pg_tuple                                     _(( void ));
         | 
| 306 | 
            +
            void init_pg_cancon                                    _(( void ));
         | 
| 315 307 | 
             
            VALUE lookup_error_class                               _(( const char * ));
         | 
| 316 308 | 
             
            VALUE pg_bin_dec_bytea                                 _(( t_pg_coder*, const char *, int, int, int, int ));
         | 
| 317 309 | 
             
            VALUE pg_text_dec_string                               _(( t_pg_coder*, const char *, int, int, int, int ));
         | 
| @@ -351,6 +343,13 @@ void pg_typemap_compact                                _(( void * )); | |
| 351 343 | 
             
            PGconn *pg_get_pgconn                                  _(( VALUE ));
         | 
| 352 344 | 
             
            t_pg_connection *pg_get_connection                     _(( VALUE ));
         | 
| 353 345 | 
             
            VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
         | 
| 346 | 
            +
            #ifdef __GNUC__
         | 
| 347 | 
            +
            __attribute__((format(printf, 3, 4)))
         | 
| 348 | 
            +
            #endif
         | 
| 349 | 
            +
            NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
         | 
| 350 | 
            +
            VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
         | 
| 351 | 
            +
            void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
         | 
| 352 | 
            +
             | 
| 354 353 |  | 
| 355 354 | 
             
            VALUE pg_new_result                                    _(( PGresult *, VALUE ));
         | 
| 356 355 | 
             
            VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
         | 
    
        data/ext/pg_binary_decoder.c
    CHANGED
    
    | @@ -133,6 +133,154 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int | |
| 133 133 | 
             
            	return out_value;
         | 
| 134 134 | 
             
            }
         | 
| 135 135 |  | 
| 136 | 
            +
            /*
         | 
| 137 | 
            +
             * Maximum number of array subscripts (arbitrary limit)
         | 
| 138 | 
            +
             */
         | 
| 139 | 
            +
            #define MAXDIM 6
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            /*
         | 
| 142 | 
            +
             * Document-class: PG::BinaryDecoder::Array < PG::CompositeDecoder
         | 
| 143 | 
            +
             *
         | 
| 144 | 
            +
             * This is a decoder class for conversion of binary array types.
         | 
| 145 | 
            +
             *
         | 
| 146 | 
            +
             * It returns an Array with possibly an arbitrary number of sub-Arrays.
         | 
| 147 | 
            +
             * All values are decoded according to the #elements_type accessor.
         | 
| 148 | 
            +
             * Sub-arrays are decoded recursively.
         | 
| 149 | 
            +
             *
         | 
| 150 | 
            +
             * This decoder simply ignores any dimension decorations preceding the array values.
         | 
| 151 | 
            +
             * It returns all array values as regular ruby Array with a zero based index, regardless of the index given in the dimension decoration.
         | 
| 152 | 
            +
             *
         | 
| 153 | 
            +
             * An array decoder which respects dimension decorations is waiting to be implemented.
         | 
| 154 | 
            +
             *
         | 
| 155 | 
            +
             */
         | 
| 156 | 
            +
            static VALUE
         | 
| 157 | 
            +
            pg_bin_dec_array(t_pg_coder *conv, const char *input_line, int len, int tuple, int field, int enc_idx)
         | 
| 158 | 
            +
            {
         | 
| 159 | 
            +
            	t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
         | 
| 160 | 
            +
            	t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            	/* Current field */
         | 
| 163 | 
            +
            	VALUE field_str;
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            	int32_t nitems32;
         | 
| 166 | 
            +
            	int i;
         | 
| 167 | 
            +
            	int ndim;
         | 
| 168 | 
            +
            	int nitems;
         | 
| 169 | 
            +
            	int flags;
         | 
| 170 | 
            +
            	int dim;
         | 
| 171 | 
            +
            	int dim_sizes[MAXDIM];
         | 
| 172 | 
            +
            	VALUE arrays[MAXDIM];
         | 
| 173 | 
            +
            	char *output_ptr;
         | 
| 174 | 
            +
            	const char *cur_ptr;
         | 
| 175 | 
            +
            	const char *line_end_ptr;
         | 
| 176 | 
            +
            	char *end_capa_ptr;
         | 
| 177 | 
            +
             | 
| 178 | 
            +
            	/* Allocate a new string with embedded capacity and realloc later with
         | 
| 179 | 
            +
            	 * exponential growing size when needed. */
         | 
| 180 | 
            +
            	PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            	/* set pointer variables for loop */
         | 
| 183 | 
            +
            	cur_ptr = input_line;
         | 
| 184 | 
            +
            	line_end_ptr = input_line + len;
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            	/* read number of dimensions */
         | 
| 187 | 
            +
            	if (line_end_ptr - cur_ptr < 4 ) goto length_error;
         | 
| 188 | 
            +
            	ndim = read_nbo32(cur_ptr);
         | 
| 189 | 
            +
            	if (ndim < 0 || ndim > MAXDIM) {
         | 
| 190 | 
            +
            		rb_raise( rb_eArgError, "unsupported number of array dimensions: %d", ndim );
         | 
| 191 | 
            +
            	}
         | 
| 192 | 
            +
            	cur_ptr += 4;
         | 
| 193 | 
            +
             | 
| 194 | 
            +
            	/* read flags */
         | 
| 195 | 
            +
            	if (line_end_ptr - cur_ptr < 4 ) goto length_error;
         | 
| 196 | 
            +
            	flags = read_nbo32(cur_ptr);
         | 
| 197 | 
            +
            	if (flags != 0 && flags != 1) {
         | 
| 198 | 
            +
            		rb_raise( rb_eArgError, "unsupported binary array flags: %d", flags );
         | 
| 199 | 
            +
            	}
         | 
| 200 | 
            +
            	cur_ptr += 4;
         | 
| 201 | 
            +
             | 
| 202 | 
            +
            	/* ignore element OID */
         | 
| 203 | 
            +
            	if (line_end_ptr - cur_ptr < 4 ) goto length_error;
         | 
| 204 | 
            +
            	cur_ptr += 4;
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            	nitems32 = ndim == 0 ? 0 : 1;
         | 
| 207 | 
            +
            	for (i = 0; i < ndim; i++) {
         | 
| 208 | 
            +
            		int64_t prod;
         | 
| 209 | 
            +
             | 
| 210 | 
            +
            		/* read size of dimensions and ignore lower bound */
         | 
| 211 | 
            +
            		if (line_end_ptr - cur_ptr < 8 ) goto length_error;
         | 
| 212 | 
            +
            		dim_sizes[i] = read_nbo32(cur_ptr);
         | 
| 213 | 
            +
            		prod = (int64_t) nitems32 * (int64_t) dim_sizes[i];
         | 
| 214 | 
            +
            		nitems32 = (int32_t) prod;
         | 
| 215 | 
            +
            		if (dim_sizes[i] < 0 || (int64_t) nitems32 != prod) {
         | 
| 216 | 
            +
            			rb_raise( rb_eArgError, "unsupported array size: %" PRId64, prod );
         | 
| 217 | 
            +
            		}
         | 
| 218 | 
            +
            		cur_ptr += 8;
         | 
| 219 | 
            +
            	}
         | 
| 220 | 
            +
            	nitems = (int)nitems32;
         | 
| 221 | 
            +
             | 
| 222 | 
            +
            	dim = 0;
         | 
| 223 | 
            +
            	arrays[dim] = rb_ary_new2(ndim == 0 ? 0 : dim_sizes[dim]);
         | 
| 224 | 
            +
            	for (i = 0; i < nitems; i++) {
         | 
| 225 | 
            +
            		int input_len;
         | 
| 226 | 
            +
             | 
| 227 | 
            +
            		/* traverse dimensions down */
         | 
| 228 | 
            +
            		while (dim < ndim - 1) {
         | 
| 229 | 
            +
            			dim++;
         | 
| 230 | 
            +
            			arrays[dim] = rb_ary_new2(dim_sizes[dim]);
         | 
| 231 | 
            +
            			rb_ary_push(arrays[dim - 1], arrays[dim]);
         | 
| 232 | 
            +
            		}
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            		/* read element length */
         | 
| 235 | 
            +
            		if (line_end_ptr - cur_ptr < 4 ) goto length_error;
         | 
| 236 | 
            +
            		input_len = read_nbo32(cur_ptr);
         | 
| 237 | 
            +
            		cur_ptr += 4;
         | 
| 238 | 
            +
             | 
| 239 | 
            +
            		/* convert and put element into array */
         | 
| 240 | 
            +
            		if (input_len < 0) {
         | 
| 241 | 
            +
            			if (input_len != -1) goto length_error;
         | 
| 242 | 
            +
            			/* NULL indicator */
         | 
| 243 | 
            +
            			rb_ary_push(arrays[dim], Qnil);
         | 
| 244 | 
            +
            		} else {
         | 
| 245 | 
            +
            			VALUE field_value;
         | 
| 246 | 
            +
            			if (line_end_ptr - cur_ptr < input_len ) goto length_error;
         | 
| 247 | 
            +
             | 
| 248 | 
            +
            			/* copy input data to field_str */
         | 
| 249 | 
            +
            			PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
         | 
| 250 | 
            +
            			memcpy(output_ptr, cur_ptr, input_len);
         | 
| 251 | 
            +
            			cur_ptr += input_len;
         | 
| 252 | 
            +
            			output_ptr += input_len;
         | 
| 253 | 
            +
            			/* convert field_str through the type map */
         | 
| 254 | 
            +
            			rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
         | 
| 255 | 
            +
            			field_value = dec_func(this->elem, RSTRING_PTR(field_str), input_len, tuple, field, enc_idx);
         | 
| 256 | 
            +
             | 
| 257 | 
            +
            			rb_ary_push(arrays[dim], field_value);
         | 
| 258 | 
            +
             | 
| 259 | 
            +
            			if( field_value == field_str ){
         | 
| 260 | 
            +
            				/* Our output string will be send to the user, so we can not reuse
         | 
| 261 | 
            +
            				* it for the next field. */
         | 
| 262 | 
            +
            				PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
         | 
| 263 | 
            +
            			}
         | 
| 264 | 
            +
            		}
         | 
| 265 | 
            +
             | 
| 266 | 
            +
            		/* Reset the pointer to the start of the output/buffer string. */
         | 
| 267 | 
            +
            		output_ptr = RSTRING_PTR(field_str);
         | 
| 268 | 
            +
             | 
| 269 | 
            +
            		/* traverse dimensions up */
         | 
| 270 | 
            +
            		while (RARRAY_LEN(arrays[dim]) >= dim_sizes[dim] && dim > 0) {
         | 
| 271 | 
            +
            			dim--;
         | 
| 272 | 
            +
            		}
         | 
| 273 | 
            +
            	}
         | 
| 274 | 
            +
             | 
| 275 | 
            +
            	if (cur_ptr < line_end_ptr)
         | 
| 276 | 
            +
            		rb_raise( rb_eArgError, "trailing data after binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
         | 
| 277 | 
            +
             | 
| 278 | 
            +
            	return arrays[0];
         | 
| 279 | 
            +
             | 
| 280 | 
            +
            length_error:
         | 
| 281 | 
            +
            	rb_raise( rb_eArgError, "premature end of binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
         | 
| 282 | 
            +
            }
         | 
| 283 | 
            +
             | 
| 136 284 | 
             
            #define PG_INT64_MIN	(-0x7FFFFFFFFFFFFFFFL - 1)
         | 
| 137 285 | 
             
            #define PG_INT64_MAX	0x7FFFFFFFFFFFFFFFL
         | 
| 138 286 |  | 
| @@ -305,6 +453,8 @@ init_pg_binary_decoder(void) | |
| 305 453 | 
             
            	/* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
         | 
| 306 454 | 
             
            	pg_define_coder( "Timestamp", pg_bin_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
         | 
| 307 455 |  | 
| 456 | 
            +
            	/* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Array", rb_cPG_CompositeDecoder ); */
         | 
| 457 | 
            +
            	pg_define_coder( "Array", pg_bin_dec_array, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
         | 
| 308 458 | 
             
            	/* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "ToBase64", rb_cPG_CompositeDecoder ); */
         | 
| 309 459 | 
             
            	pg_define_coder( "ToBase64", pg_bin_dec_to_base64, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
         | 
| 310 460 | 
             
            }
         | 
    
        data/ext/pg_binary_encoder.c
    CHANGED
    
    | @@ -271,14 +271,13 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i | |
| 271 271 | 
             
            			case T_FALSE:
         | 
| 272 272 | 
             
            				write_nbo32(PG_INT32_MIN, out);
         | 
| 273 273 | 
             
            				return 4;
         | 
| 274 | 
            +
            		} {
         | 
| 275 | 
            +
            			VALUE year = rb_funcall(value, s_id_year, 0);
         | 
| 276 | 
            +
            			VALUE month = rb_funcall(value, s_id_month, 0);
         | 
| 277 | 
            +
            			VALUE day = rb_funcall(value, s_id_day, 0);
         | 
| 278 | 
            +
            			int jday = date2j(NUM2INT(year), NUM2INT(month), NUM2INT(day)) - POSTGRES_EPOCH_JDATE;
         | 
| 279 | 
            +
            			write_nbo32(jday, out);
         | 
| 274 280 | 
             
            		}
         | 
| 275 | 
            -
             | 
| 276 | 
            -
            		VALUE year = rb_funcall(value, s_id_year, 0);
         | 
| 277 | 
            -
            		VALUE month = rb_funcall(value, s_id_month, 0);
         | 
| 278 | 
            -
            		VALUE day = rb_funcall(value, s_id_day, 0);
         | 
| 279 | 
            -
            		int jday = date2j(NUM2INT(year), NUM2INT(month), NUM2INT(day)) - POSTGRES_EPOCH_JDATE;
         | 
| 280 | 
            -
            		write_nbo32(jday, out);
         | 
| 281 | 
            -
             | 
| 282 281 | 
             
            	}else{
         | 
| 283 282 | 
             
            		/* first call -> determine the required length */
         | 
| 284 283 | 
             
            		if(TYPE(value) == T_STRING){
         | 
| @@ -305,6 +304,201 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i | |
| 305 304 | 
             
            	return 4;
         | 
| 306 305 | 
             
            }
         | 
| 307 306 |  | 
| 307 | 
            +
            /*
         | 
| 308 | 
            +
             * Maximum number of array subscripts (arbitrary limit)
         | 
| 309 | 
            +
             */
         | 
| 310 | 
            +
            #define MAXDIM 6
         | 
| 311 | 
            +
             | 
| 312 | 
            +
            /*
         | 
| 313 | 
            +
             * Document-class: PG::BinaryEncoder::Array < PG::CompositeEncoder
         | 
| 314 | 
            +
             *
         | 
| 315 | 
            +
             * This is the encoder class for PostgreSQL array types in binary format.
         | 
| 316 | 
            +
             *
         | 
| 317 | 
            +
             * All values are encoded according to the #elements_type
         | 
| 318 | 
            +
             * accessor. Sub-arrays are encoded recursively.
         | 
| 319 | 
            +
             *
         | 
| 320 | 
            +
             * This encoder expects an Array of values or sub-arrays as input.
         | 
| 321 | 
            +
             * Other values are passed through as byte string without interpretation.
         | 
| 322 | 
            +
             *
         | 
| 323 | 
            +
             * The accessors needs_quotation and delimiter are ignored for binary encoding.
         | 
| 324 | 
            +
             *
         | 
| 325 | 
            +
             */
         | 
| 326 | 
            +
            static int
         | 
| 327 | 
            +
            pg_bin_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
         | 
| 328 | 
            +
            {
         | 
| 329 | 
            +
            	if (TYPE(value) == T_ARRAY) {
         | 
| 330 | 
            +
            		t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
         | 
| 331 | 
            +
            		t_pg_coder_enc_func enc_func = pg_coder_enc_func(this->elem);
         | 
| 332 | 
            +
            		int dim_sizes[MAXDIM];
         | 
| 333 | 
            +
            		int ndim = 1;
         | 
| 334 | 
            +
            		int nitems = 1;
         | 
| 335 | 
            +
            		VALUE el1 = value;
         | 
| 336 | 
            +
             | 
| 337 | 
            +
            		if (RARRAY_LEN(value) == 0) {
         | 
| 338 | 
            +
            			nitems = 0;
         | 
| 339 | 
            +
            			ndim = 0;
         | 
| 340 | 
            +
            			dim_sizes[0] = 0;
         | 
| 341 | 
            +
            		} else {
         | 
| 342 | 
            +
            			/* Determine number of dimensions, sizes of dimensions and number of items */
         | 
| 343 | 
            +
            			while(1) {
         | 
| 344 | 
            +
            				VALUE el2;
         | 
| 345 | 
            +
             | 
| 346 | 
            +
            				dim_sizes[ndim-1] = RARRAY_LENINT(el1);
         | 
| 347 | 
            +
            				nitems *= dim_sizes[ndim-1];
         | 
| 348 | 
            +
            				el2 = rb_ary_entry(el1, 0);
         | 
| 349 | 
            +
            				if (TYPE(el2) == T_ARRAY) {
         | 
| 350 | 
            +
            					ndim++;
         | 
| 351 | 
            +
            					if (ndim > MAXDIM)
         | 
| 352 | 
            +
            						rb_raise( rb_eArgError, "unsupported number of array dimensions: >%d", ndim );
         | 
| 353 | 
            +
            				} else {
         | 
| 354 | 
            +
            					break;
         | 
| 355 | 
            +
            				}
         | 
| 356 | 
            +
            				el1 = el2;
         | 
| 357 | 
            +
            			}
         | 
| 358 | 
            +
            		}
         | 
| 359 | 
            +
             | 
| 360 | 
            +
            		if(out){
         | 
| 361 | 
            +
            			/* Second encoder pass -> write data to `out` */
         | 
| 362 | 
            +
            			int dimpos[MAXDIM];
         | 
| 363 | 
            +
            			VALUE arrays[MAXDIM];
         | 
| 364 | 
            +
            			int dim = 0;
         | 
| 365 | 
            +
            			int item_idx = 0;
         | 
| 366 | 
            +
            			int i;
         | 
| 367 | 
            +
            			char *orig_out = out;
         | 
| 368 | 
            +
            			Oid elem_oid = this->elem ? this->elem->oid : 0;
         | 
| 369 | 
            +
             | 
| 370 | 
            +
            			write_nbo32(ndim, out); out += 4;
         | 
| 371 | 
            +
            			write_nbo32(1 /* flags */, out); out += 4;
         | 
| 372 | 
            +
            			write_nbo32(elem_oid, out); out += 4;
         | 
| 373 | 
            +
            			for (i = 0; i < ndim; i++) {
         | 
| 374 | 
            +
            				dimpos[i] = 0;
         | 
| 375 | 
            +
            				write_nbo32(dim_sizes[i], out); out += 4;
         | 
| 376 | 
            +
            				write_nbo32(1 /* offset */, out); out += 4;
         | 
| 377 | 
            +
            			}
         | 
| 378 | 
            +
            			arrays[0] = value;
         | 
| 379 | 
            +
             | 
| 380 | 
            +
            			while(1) {
         | 
| 381 | 
            +
            				/* traverse tree down */
         | 
| 382 | 
            +
            				while (dim < ndim - 1) {
         | 
| 383 | 
            +
            					arrays[dim + 1] = rb_ary_entry(arrays[dim], dimpos[dim]);
         | 
| 384 | 
            +
            					dim++;
         | 
| 385 | 
            +
            				}
         | 
| 386 | 
            +
             | 
| 387 | 
            +
            				for (i = 0; i < dim_sizes[dim]; i++) {
         | 
| 388 | 
            +
            					VALUE item = rb_ary_entry(arrays[dim], i);
         | 
| 389 | 
            +
             | 
| 390 | 
            +
            					if (NIL_P(item)) {
         | 
| 391 | 
            +
            						write_nbo32(-1, out); out += 4;
         | 
| 392 | 
            +
            					} else {
         | 
| 393 | 
            +
            						/* Encoded string is returned in subint */
         | 
| 394 | 
            +
            						int strlen;
         | 
| 395 | 
            +
            						VALUE is_one_pass = rb_ary_entry(*intermediate, item_idx++);
         | 
| 396 | 
            +
            						VALUE subint = rb_ary_entry(*intermediate, item_idx++);
         | 
| 397 | 
            +
             | 
| 398 | 
            +
            						if (is_one_pass == Qtrue) {
         | 
| 399 | 
            +
            							strlen = RSTRING_LENINT(subint);
         | 
| 400 | 
            +
            							memcpy( out + 4, RSTRING_PTR(subint), strlen);
         | 
| 401 | 
            +
            						} else {
         | 
| 402 | 
            +
            							strlen = enc_func(this->elem, item, out + 4, &subint, enc_idx);
         | 
| 403 | 
            +
            						}
         | 
| 404 | 
            +
            						write_nbo32(strlen, out);
         | 
| 405 | 
            +
            						out += 4 /* length */ + strlen;
         | 
| 406 | 
            +
            					}
         | 
| 407 | 
            +
            				}
         | 
| 408 | 
            +
             | 
| 409 | 
            +
            				/* traverse tree up and go to next sibling array */
         | 
| 410 | 
            +
            				do {
         | 
| 411 | 
            +
            					if (dim > 0) {
         | 
| 412 | 
            +
            						dimpos[dim] = 0;
         | 
| 413 | 
            +
            						dim--;
         | 
| 414 | 
            +
            						dimpos[dim]++;
         | 
| 415 | 
            +
            					} else {
         | 
| 416 | 
            +
            						goto finished2;
         | 
| 417 | 
            +
            					}
         | 
| 418 | 
            +
            				} while (dimpos[dim] >= dim_sizes[dim]);
         | 
| 419 | 
            +
            			}
         | 
| 420 | 
            +
            			finished2:
         | 
| 421 | 
            +
            			return (int)(out - orig_out);
         | 
| 422 | 
            +
             | 
| 423 | 
            +
            		} else {
         | 
| 424 | 
            +
            			/* First encoder pass -> determine required buffer space for `out` */
         | 
| 425 | 
            +
             | 
| 426 | 
            +
            			int dimpos[MAXDIM];
         | 
| 427 | 
            +
            			VALUE arrays[MAXDIM];
         | 
| 428 | 
            +
            			int dim = 0;
         | 
| 429 | 
            +
            			int item_idx = 0;
         | 
| 430 | 
            +
            			int i;
         | 
| 431 | 
            +
            			int size_sum = 0;
         | 
| 432 | 
            +
             | 
| 433 | 
            +
            			*intermediate = rb_ary_new2(nitems);
         | 
| 434 | 
            +
             | 
| 435 | 
            +
            			for (i = 0; i < MAXDIM; i++) {
         | 
| 436 | 
            +
            				dimpos[i] = 0;
         | 
| 437 | 
            +
            			}
         | 
| 438 | 
            +
            			arrays[0] = value;
         | 
| 439 | 
            +
             | 
| 440 | 
            +
            			while(1) {
         | 
| 441 | 
            +
             | 
| 442 | 
            +
            				/* traverse tree down */
         | 
| 443 | 
            +
            				while (dim < ndim - 1) {
         | 
| 444 | 
            +
            					VALUE array = rb_ary_entry(arrays[dim], dimpos[dim]);
         | 
| 445 | 
            +
            					if (TYPE(array) != T_ARRAY) {
         | 
| 446 | 
            +
            						rb_raise( rb_eArgError, "expected Array instead of %+"PRIsVALUE" in dimension %d", array, dim + 1 );
         | 
| 447 | 
            +
            					}
         | 
| 448 | 
            +
            					if (dim_sizes[dim + 1] != RARRAY_LEN(array)) {
         | 
| 449 | 
            +
            						rb_raise( rb_eArgError, "varying number of array elements (%d and %d) in dimension %d", dim_sizes[dim + 1], RARRAY_LENINT(array), dim + 1 );
         | 
| 450 | 
            +
            					}
         | 
| 451 | 
            +
            					arrays[dim + 1] = array;
         | 
| 452 | 
            +
            					dim++;
         | 
| 453 | 
            +
            				}
         | 
| 454 | 
            +
             | 
| 455 | 
            +
            				for (i = 0; i < dim_sizes[dim]; i++) {
         | 
| 456 | 
            +
            					VALUE item = rb_ary_entry(arrays[dim], i);
         | 
| 457 | 
            +
             | 
| 458 | 
            +
            					if (NIL_P(item)) {
         | 
| 459 | 
            +
            						size_sum += 4 /* length bytes = -1 */;
         | 
| 460 | 
            +
            					} else {
         | 
| 461 | 
            +
            						VALUE subint;
         | 
| 462 | 
            +
            						int strlen = enc_func(this->elem, item, NULL, &subint, enc_idx);
         | 
| 463 | 
            +
             | 
| 464 | 
            +
            						/* Gather all intermediate values of elements into an array, which is returned as intermediate for the array encoder */
         | 
| 465 | 
            +
            						if( strlen == -1 ){
         | 
| 466 | 
            +
            							/* Encoded string is returned in subint */
         | 
| 467 | 
            +
            							rb_ary_store(*intermediate, item_idx++, Qtrue);
         | 
| 468 | 
            +
            							rb_ary_store(*intermediate, item_idx++, subint);
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            							strlen = RSTRING_LENINT(subint);
         | 
| 471 | 
            +
            						} else {
         | 
| 472 | 
            +
            							/* Two passes necessary */
         | 
| 473 | 
            +
            							rb_ary_store(*intermediate, item_idx++, Qfalse);
         | 
| 474 | 
            +
            							rb_ary_store(*intermediate, item_idx++, subint);
         | 
| 475 | 
            +
            						}
         | 
| 476 | 
            +
            						size_sum += 4 /* length bytes */ + strlen;
         | 
| 477 | 
            +
            					}
         | 
| 478 | 
            +
            				}
         | 
| 479 | 
            +
             | 
| 480 | 
            +
            				/* traverse tree up and go to next sibling array */
         | 
| 481 | 
            +
            				do {
         | 
| 482 | 
            +
            					if (dim > 0) {
         | 
| 483 | 
            +
            						dimpos[dim] = 0;
         | 
| 484 | 
            +
            						dim--;
         | 
| 485 | 
            +
            						dimpos[dim]++;
         | 
| 486 | 
            +
            					} else {
         | 
| 487 | 
            +
            						goto finished1;
         | 
| 488 | 
            +
            					}
         | 
| 489 | 
            +
            				} while (dimpos[dim] >= dim_sizes[dim]);
         | 
| 490 | 
            +
            			}
         | 
| 491 | 
            +
            			finished1:;
         | 
| 492 | 
            +
             | 
| 493 | 
            +
            			return 4 /* ndim */ + 4 /* flags */ + 4 /* oid */ +
         | 
| 494 | 
            +
            				ndim * (4 /* dim size */ + 4 /* dim offset */) +
         | 
| 495 | 
            +
            				size_sum;
         | 
| 496 | 
            +
            		}
         | 
| 497 | 
            +
            	} else {
         | 
| 498 | 
            +
            		return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
         | 
| 499 | 
            +
            	}
         | 
| 500 | 
            +
            }
         | 
| 501 | 
            +
             | 
| 308 502 | 
             
            /*
         | 
| 309 503 | 
             
             * Document-class: PG::BinaryEncoder::FromBase64 < PG::CompositeEncoder
         | 
| 310 504 | 
             
             *
         | 
| @@ -382,6 +576,8 @@ init_pg_binary_encoder(void) | |
| 382 576 | 
             
            	/* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Date", rb_cPG_SimpleEncoder ); */
         | 
| 383 577 | 
             
            	pg_define_coder( "Date", pg_bin_enc_date, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
         | 
| 384 578 |  | 
| 579 | 
            +
            	/* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Array", rb_cPG_CompositeEncoder ); */
         | 
| 580 | 
            +
            	pg_define_coder( "Array", pg_bin_enc_array, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
         | 
| 385 581 | 
             
            	/* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "FromBase64", rb_cPG_CompositeEncoder ); */
         | 
| 386 582 | 
             
            	pg_define_coder( "FromBase64", pg_bin_enc_from_base64, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
         | 
| 387 583 | 
             
            }
         |