pg 0.18.4 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +689 -5
  5. data/History.rdoc +56 -0
  6. data/Manifest.txt +1 -18
  7. data/README.rdoc +13 -9
  8. data/Rakefile +15 -17
  9. data/Rakefile.cross +8 -7
  10. data/ext/errorcodes.def +25 -0
  11. data/ext/errorcodes.txt +13 -1
  12. data/ext/extconf.rb +9 -1
  13. data/ext/gvl_wrappers.h +4 -0
  14. data/ext/pg.c +4 -3
  15. data/ext/pg.h +6 -3
  16. data/ext/pg_binary_encoder.c +8 -8
  17. data/ext/pg_coder.c +31 -10
  18. data/ext/pg_connection.c +252 -98
  19. data/ext/pg_copy_coder.c +34 -4
  20. data/ext/pg_result.c +20 -14
  21. data/ext/pg_text_encoder.c +62 -42
  22. data/ext/pg_type_map.c +14 -7
  23. data/lib/pg/basic_type_mapping.rb +35 -8
  24. data/lib/pg/connection.rb +46 -10
  25. data/lib/pg/deprecated_constants.rb +21 -0
  26. data/lib/pg/result.rb +10 -5
  27. data/lib/pg/text_decoder.rb +7 -0
  28. data/lib/pg/text_encoder.rb +8 -0
  29. data/lib/pg.rb +21 -9
  30. data/spec/helpers.rb +6 -9
  31. data/spec/pg/basic_type_mapping_spec.rb +54 -0
  32. data/spec/pg/connection_spec.rb +158 -26
  33. data/spec/pg/result_spec.rb +11 -4
  34. data/spec/pg/type_map_by_class_spec.rb +2 -2
  35. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  36. data/spec/pg/type_spec.rb +82 -2
  37. data.tar.gz.sig +0 -0
  38. metadata +50 -64
  39. metadata.gz.sig +0 -0
  40. data/sample/array_insert.rb +0 -20
  41. data/sample/async_api.rb +0 -106
  42. data/sample/async_copyto.rb +0 -39
  43. data/sample/async_mixed.rb +0 -56
  44. data/sample/check_conn.rb +0 -21
  45. data/sample/copyfrom.rb +0 -81
  46. data/sample/copyto.rb +0 -19
  47. data/sample/cursor.rb +0 -21
  48. data/sample/disk_usage_report.rb +0 -186
  49. data/sample/issue-119.rb +0 -94
  50. data/sample/losample.rb +0 -69
  51. data/sample/minimal-testcase.rb +0 -17
  52. data/sample/notify_wait.rb +0 -72
  53. data/sample/pg_statistics.rb +0 -294
  54. data/sample/replication_monitor.rb +0 -231
  55. data/sample/test_binary_values.rb +0 -33
  56. data/sample/wal_shipper.rb +0 -434
  57. data/sample/warehouse_partitions.rb +0 -320
data/ext/pg_copy_coder.c CHANGED
@@ -167,9 +167,15 @@ pg_copycoder_type_map_get(VALUE self)
167
167
  * conn.put_copy_data ["string2", 42, true]
168
168
  * end
169
169
  * This creates +my_table+ and inserts two rows.
170
+ *
171
+ * It is possible to manually assign a type encoder for each column per PG::TypeMapByColumn,
172
+ * or to make use of PG::BasicTypeMapBasedOnResult to assign them based on the table OIDs.
173
+ *
174
+ * See also PG::TextDecoder::CopyRow for the decoding direction with
175
+ * PG::Connection#get_copy_data .
170
176
  */
171
177
  static int
172
- pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
178
+ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
173
179
  {
174
180
  t_pg_copycoder *this = (t_pg_copycoder *)conv;
175
181
  t_pg_coder_enc_func enc_func;
@@ -184,6 +190,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
184
190
 
185
191
  /* Allocate a new string with embedded capacity and realloc exponential when needed. */
186
192
  PG_RB_STR_NEW( *intermediate, current_out, end_capa_ptr );
193
+ PG_ENCODING_SET_NOCHECK(*intermediate, enc_idx);
187
194
 
188
195
  for( i=0; i<RARRAY_LEN(value); i++){
189
196
  char *ptr1;
@@ -211,7 +218,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
211
218
  enc_func = pg_coder_enc_func(p_elem_coder);
212
219
 
213
220
  /* 1st pass for retiving the required memory space */
214
- strlen = enc_func(p_elem_coder, entry, NULL, &subint);
221
+ strlen = enc_func(p_elem_coder, entry, NULL, &subint, enc_idx);
215
222
 
216
223
  if( strlen == -1 ){
217
224
  /* we can directly use String value in subint */
@@ -234,7 +241,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
234
241
  PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2, current_out, end_capa_ptr );
235
242
 
236
243
  /* Place the unescaped string at current output position. */
237
- strlen = enc_func(p_elem_coder, entry, current_out, &subint);
244
+ strlen = enc_func(p_elem_coder, entry, current_out, &subint, enc_idx);
238
245
 
239
246
  ptr1 = current_out;
240
247
  ptr2 = current_out + strlen;
@@ -301,15 +308,38 @@ GetDecimalFromHex(char hex)
301
308
  * strings by PG::TextDecoder::String.
302
309
  *
303
310
  * Example with default type map ( TypeMapAllStrings ):
311
+ * conn.exec("CREATE TABLE my_table AS VALUES('astring', 7, FALSE), ('string2', 42, TRUE) ")
312
+ *
304
313
  * deco = PG::TextDecoder::CopyRow.new
305
314
  * conn.copy_data "COPY my_table TO STDOUT", deco do
306
315
  * while row=conn.get_copy_data
307
316
  * p row
308
317
  * end
309
318
  * end
310
- * This prints all rows of +my_table+ to stdout:
319
+ * This prints all rows of +my_table+ :
311
320
  * ["astring", "7", "f"]
312
321
  * ["string2", "42", "t"]
322
+ *
323
+ * Example with column based type map:
324
+ * tm = PG::TypeMapByColumn.new( [
325
+ * PG::TextDecoder::String.new,
326
+ * PG::TextDecoder::Integer.new,
327
+ * PG::TextDecoder::Boolean.new] )
328
+ * deco = PG::TextDecoder::CopyRow.new( type_map: tm )
329
+ * conn.copy_data "COPY my_table TO STDOUT", deco do
330
+ * while row=conn.get_copy_data
331
+ * p row
332
+ * end
333
+ * end
334
+ * This prints the rows with type casted columns:
335
+ * ["astring", 7, false]
336
+ * ["string2", 42, true]
337
+ *
338
+ * Instead of manually assigning a type decoder for each column, PG::BasicTypeMapForResults
339
+ * can be used to assign them based on the table OIDs.
340
+ *
341
+ * See also PG::TextEncoder::CopyRow for the encoding direction with
342
+ * PG::Connection#put_copy_data .
313
343
  */
314
344
  /*
315
345
  * Parse the current line into separate attributes (fields),
data/ext/pg_result.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
3
+ * $Id: pg_result.c,v c4a1abc36c47 2017/06/02 01:00:09 ged $
4
4
  *
5
5
  */
6
6
 
@@ -288,7 +288,7 @@ static void pgresult_init_fnames(VALUE self)
288
288
  *
289
289
  * Example:
290
290
  * require 'pg'
291
- * conn = PGconn.open(:dbname => 'test')
291
+ * conn = PG.connect(:dbname => 'test')
292
292
  * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
293
293
  * res.getvalue(0,0) # '1'
294
294
  * res[0]['b'] # '2'
@@ -302,7 +302,7 @@ static void pgresult_init_fnames(VALUE self)
302
302
 
303
303
  /*
304
304
  * call-seq:
305
- * res.result_status() -> Fixnum
305
+ * res.result_status() -> Integer
306
306
  *
307
307
  * Returns the status of the query. The status value is one of:
308
308
  * * +PGRES_EMPTY_QUERY+
@@ -414,7 +414,7 @@ pgresult_error_field(VALUE self, VALUE field)
414
414
 
415
415
  /*
416
416
  * call-seq:
417
- * res.ntuples() -> Fixnum
417
+ * res.ntuples() -> Integer
418
418
  *
419
419
  * Returns the number of tuples in the query result.
420
420
  */
@@ -466,7 +466,7 @@ pgresult_fname(VALUE self, VALUE index)
466
466
 
467
467
  /*
468
468
  * call-seq:
469
- * res.fnumber( name ) -> Fixnum
469
+ * res.fnumber( name ) -> Integer
470
470
  *
471
471
  * Returns the index of the field specified by the string +name+.
472
472
  * The given +name+ is treated like an identifier in an SQL command, that is,
@@ -527,7 +527,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
527
527
 
528
528
  /*
529
529
  * call-seq:
530
- * res.ftablecol( column_number ) -> Fixnum
530
+ * res.ftablecol( column_number ) -> Integer
531
531
  *
532
532
  * Returns the column number (within its table) of the table from
533
533
  * which the column _column_number_ is made up.
@@ -552,7 +552,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
552
552
 
553
553
  /*
554
554
  * call-seq:
555
- * res.fformat( column_number ) -> Fixnum
555
+ * res.fformat( column_number ) -> Integer
556
556
  *
557
557
  * Returns the format (0 for text, 1 for binary) of column
558
558
  * _column_number_.
@@ -696,7 +696,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
696
696
 
697
697
  /*
698
698
  * call-seq:
699
- * res.getlength( tup_num, field_num ) -> Fixnum
699
+ * res.getlength( tup_num, field_num ) -> Integer
700
700
  *
701
701
  * Returns the (String) length of the field in bytes.
702
702
  *
@@ -721,7 +721,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
721
721
 
722
722
  /*
723
723
  * call-seq:
724
- * res.nparams() -> Fixnum
724
+ * res.nparams() -> Integer
725
725
  *
726
726
  * Returns the number of parameters of a prepared statement.
727
727
  * Only useful for the result returned by conn.describePrepared
@@ -772,11 +772,17 @@ pgresult_cmd_status(VALUE self)
772
772
  * Returns the number of tuples (rows) affected by the SQL command.
773
773
  *
774
774
  * If the SQL command that generated the PG::Result was not one of:
775
- * * +INSERT+
776
- * * +UPDATE+
777
- * * +DELETE+
778
- * * +MOVE+
779
- * * +FETCH+
775
+ *
776
+ * * <tt>SELECT</tt>
777
+ * * <tt>CREATE TABLE AS</tt>
778
+ * * <tt>INSERT</tt>
779
+ * * <tt>UPDATE</tt>
780
+ * * <tt>DELETE</tt>
781
+ * * <tt>MOVE</tt>
782
+ * * <tt>FETCH</tt>
783
+ * * <tt>COPY</tt>
784
+ * * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
785
+ *
780
786
  * or if no tuples were affected, <tt>0</tt> is returned.
781
787
  */
782
788
  static VALUE
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_encoder.c - PG::TextEncoder module
3
- * $Id: pg_text_encoder.c,v 4f0bd19e9111 2015/09/24 18:55:11 lars $
3
+ * $Id: pg_text_encoder.c,v e61a06f1f5ed 2015/12/25 21:14:21 lars $
4
4
  *
5
5
  */
6
6
 
@@ -28,6 +28,7 @@
28
28
  * intermediate - Pointer to a VALUE that might be set by the encoding function to some
29
29
  * value in the first call that can be retrieved later in the second call.
30
30
  * This VALUE is not yet initialized by the caller.
31
+ * enc_idx - Index of the output Encoding that strings should be converted to.
31
32
  *
32
33
  * Returns:
33
34
  * >= 0 - If out==NULL the encoder function must return the expected output buffer size.
@@ -50,7 +51,7 @@ VALUE rb_mPG_TextEncoder;
50
51
  static ID s_id_encode;
51
52
  static ID s_id_to_i;
52
53
 
53
- static int pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate);
54
+ static int pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx);
54
55
 
55
56
  VALUE
56
57
  pg_obj_to_i( VALUE value )
@@ -76,7 +77,7 @@ pg_obj_to_i( VALUE value )
76
77
  *
77
78
  */
78
79
  static int
79
- pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
80
+ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
80
81
  {
81
82
  switch( TYPE(value) ){
82
83
  case T_FALSE:
@@ -94,10 +95,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
94
95
  if(out) *out = '1';
95
96
  return 1;
96
97
  } else {
97
- return pg_text_enc_integer(this, value, out, intermediate);
98
+ return pg_text_enc_integer(this, value, out, intermediate, enc_idx);
98
99
  }
99
100
  default:
100
- return pg_coder_enc_to_s(this, value, out, intermediate);
101
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
101
102
  }
102
103
  /* never reached */
103
104
  return 0;
@@ -113,9 +114,14 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
113
114
  *
114
115
  */
115
116
  int
116
- pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
117
+ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
117
118
  {
118
- *intermediate = rb_obj_as_string(value);
119
+ VALUE str = rb_obj_as_string(value);
120
+ if( ENCODING_GET(str) == enc_idx ){
121
+ *intermediate = str;
122
+ }else{
123
+ *intermediate = rb_str_export_to_enc(str, rb_enc_from_index(enc_idx));
124
+ }
119
125
  return -1;
120
126
  }
121
127
 
@@ -129,11 +135,11 @@ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
129
135
  *
130
136
  */
131
137
  static int
132
- pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
138
+ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
133
139
  {
134
140
  if(out){
135
141
  if(TYPE(*intermediate) == T_STRING){
136
- return pg_coder_enc_to_s(this, value, out, intermediate);
142
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
137
143
  }else{
138
144
  char *start = out;
139
145
  int len;
@@ -206,13 +212,13 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
206
212
  if( ll < 100000000000000LL ){
207
213
  len = ll < 10000000000000LL ? 13 : 14;
208
214
  }else{
209
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
215
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
210
216
  }
211
217
  }
212
218
  }
213
219
  return sll < 0 ? len+1 : len;
214
220
  }else{
215
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
221
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
216
222
  }
217
223
  }
218
224
  }
@@ -225,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
225
231
  *
226
232
  */
227
233
  static int
228
- pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
234
+ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
229
235
  {
230
236
  if(out){
231
237
  double dvalue = NUM2DBL(value);
@@ -265,7 +271,7 @@ static const char hextab[] = {
265
271
  *
266
272
  */
267
273
  static int
268
- pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
274
+ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
269
275
  {
270
276
  if(out){
271
277
  size_t strlen = RSTRING_LEN(*intermediate);
@@ -344,13 +350,13 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
344
350
  }
345
351
 
346
352
  static char *
347
- quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data)
353
+ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data, int enc_idx)
348
354
  {
349
355
  int strlen;
350
356
  VALUE subint;
351
357
  t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
352
358
 
353
- strlen = enc_func(this, value, NULL, &subint);
359
+ strlen = enc_func(this, value, NULL, &subint, enc_idx);
354
360
 
355
361
  if( strlen == -1 ){
356
362
  /* we can directly use String value in subint */
@@ -376,20 +382,20 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
376
382
  current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
377
383
 
378
384
  /* Place the unescaped string at current output position. */
379
- strlen = enc_func(this, value, current_out, &subint);
385
+ strlen = enc_func(this, value, current_out, &subint, enc_idx);
380
386
 
381
387
  current_out += quote_buffer( func_data, current_out, strlen, current_out );
382
388
  }else{
383
389
  /* size of the unquoted string */
384
390
  current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
385
- current_out += enc_func(this, value, current_out, &subint);
391
+ current_out += enc_func(this, value, current_out, &subint, enc_idx);
386
392
  }
387
393
  }
388
394
  return current_out;
389
395
  }
390
396
 
391
397
  static char *
392
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote)
398
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
393
399
  {
394
400
  int i;
395
401
 
@@ -407,7 +413,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
407
413
 
408
414
  switch(TYPE(entry)){
409
415
  case T_ARRAY:
410
- current_out = write_array(this, entry, current_out, string, quote);
416
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx);
411
417
  break;
412
418
  case T_NIL:
413
419
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
@@ -417,7 +423,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
417
423
  *current_out++ = 'L';
418
424
  break;
419
425
  default:
420
- current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this );
426
+ current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
421
427
  }
422
428
  }
423
429
  current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
@@ -439,21 +445,23 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
439
445
  *
440
446
  */
441
447
  static int
442
- pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
448
+ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
443
449
  {
444
450
  char *end_ptr;
445
451
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
446
452
 
447
453
  if( TYPE(value) == T_ARRAY){
448
- *intermediate = rb_str_new(NULL, 0);
454
+ VALUE out_str = rb_str_new(NULL, 0);
455
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
449
456
 
450
- end_ptr = write_array(this, value, RSTRING_PTR(*intermediate), *intermediate, this->needs_quotation);
457
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
451
458
 
452
- rb_str_set_len( *intermediate, end_ptr - RSTRING_PTR(*intermediate) );
459
+ rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
460
+ *intermediate = out_str;
453
461
 
454
462
  return -1;
455
463
  } else {
456
- return pg_coder_enc_to_s( conv, value, out, intermediate );
464
+ return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
457
465
  }
458
466
  }
459
467
 
@@ -484,7 +492,7 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
484
492
  }
485
493
 
486
494
  static char *
487
- pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
495
+ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
488
496
  {
489
497
  int i;
490
498
  int nr_elems;
@@ -496,6 +504,9 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
496
504
  VALUE entry = rb_ary_entry(value, i);
497
505
 
498
506
  StringValue(entry);
507
+ if( ENCODING_GET(entry) != enc_idx ){
508
+ entry = rb_str_export_to_enc(entry, rb_enc_from_index(enc_idx));
509
+ }
499
510
  out = quote_identifier(entry, string, out);
500
511
  if( i < nr_elems-1 ){
501
512
  out = pg_rb_str_ensure_capa( string, 1, out, NULL );
@@ -517,20 +528,26 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
517
528
  * This encoder can also be used per PG::Connection#quote_ident .
518
529
  */
519
530
  int
520
- pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
531
+ pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
521
532
  {
533
+ VALUE out_str;
522
534
  UNUSED( this );
523
535
  if( TYPE(value) == T_ARRAY){
524
- *intermediate = rb_str_new(NULL, 0);
525
- out = RSTRING_PTR(*intermediate);
526
- out = pg_text_enc_array_identifier(value, *intermediate, out);
536
+ out_str = rb_str_new(NULL, 0);
537
+ out = RSTRING_PTR(out_str);
538
+ out = pg_text_enc_array_identifier(value, out_str, out, enc_idx);
527
539
  } else {
528
540
  StringValue(value);
529
- *intermediate = rb_str_new(NULL, RSTRING_LEN(value) + 2);
530
- out = RSTRING_PTR(*intermediate);
531
- out = quote_identifier(value, *intermediate, out);
541
+ if( ENCODING_GET(value) != enc_idx ){
542
+ value = rb_str_export_to_enc(value, rb_enc_from_index(enc_idx));
543
+ }
544
+ out_str = rb_str_new(NULL, RSTRING_LEN(value) + 2);
545
+ out = RSTRING_PTR(out_str);
546
+ out = quote_identifier(value, out_str, out);
532
547
  }
533
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
548
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
549
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
550
+ *intermediate = out_str;
534
551
  return -1;
535
552
  }
536
553
 
@@ -576,14 +593,16 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
576
593
  *
577
594
  */
578
595
  static int
579
- pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
596
+ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
580
597
  {
581
598
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
599
+ VALUE out_str = rb_str_new(NULL, 0);
600
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
582
601
 
583
- *intermediate = rb_str_new(NULL, 0);
584
- out = RSTRING_PTR(*intermediate);
585
- out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_literal_buffer, this);
586
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
602
+ out = RSTRING_PTR(out_str);
603
+ out = quote_string(this->elem, value, out_str, out, this->needs_quotation, quote_literal_buffer, this, enc_idx);
604
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
605
+ *intermediate = out_str;
587
606
  return -1;
588
607
  }
589
608
 
@@ -594,7 +613,7 @@ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *inte
594
613
  *
595
614
  */
596
615
  static int
597
- pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
616
+ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
598
617
  {
599
618
  int strlen;
600
619
  VALUE subint;
@@ -603,13 +622,13 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
603
622
 
604
623
  if(out){
605
624
  /* Second encoder pass, if required */
606
- strlen = enc_func(this->elem, value, out, intermediate);
625
+ strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
607
626
  base64_encode( out, out, strlen );
608
627
 
609
628
  return BASE64_ENCODED_SIZE(strlen);
610
629
  } else {
611
630
  /* First encoder pass */
612
- strlen = enc_func(this->elem, value, NULL, &subint);
631
+ strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
613
632
 
614
633
  if( strlen == -1 ){
615
634
  /* Encoded string is returned in subint */
@@ -617,6 +636,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
617
636
 
618
637
  strlen = RSTRING_LENINT(subint);
619
638
  out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
639
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
620
640
 
621
641
  base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
622
642
  *intermediate = out_str;
data/ext/pg_type_map.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_type_map.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
3
+ * $Id: pg_type_map.c,v 2af122820861 2017/01/14 19:56:36 lars $
4
4
  *
5
5
  */
6
6
 
@@ -11,46 +11,53 @@ VALUE rb_mDefaultTypeMappable;
11
11
  static ID s_id_fit_to_query;
12
12
  static ID s_id_fit_to_result;
13
13
 
14
+ NORETURN( VALUE
15
+ pg_typemap_fit_to_result( VALUE self, VALUE result ));
16
+ NORETURN( VALUE
17
+ pg_typemap_fit_to_query( VALUE self, VALUE params ));
18
+ NORETURN( int
19
+ pg_typemap_fit_to_copy_get( VALUE self ));
20
+ NORETURN( VALUE
21
+ pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field ));
22
+ NORETURN( t_pg_coder *
23
+ pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field ));
24
+ NORETURN( VALUE
25
+ pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx ));
26
+
14
27
  VALUE
15
28
  pg_typemap_fit_to_result( VALUE self, VALUE result )
16
29
  {
17
30
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map result values", rb_obj_classname(self) );
18
- return Qnil;
19
31
  }
20
32
 
21
33
  VALUE
22
34
  pg_typemap_fit_to_query( VALUE self, VALUE params )
23
35
  {
24
36
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
25
- return Qnil;
26
37
  }
27
38
 
28
39
  int
29
40
  pg_typemap_fit_to_copy_get( VALUE self )
30
41
  {
31
42
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map get_copy_data results", rb_obj_classname(self) );
32
- return Qnil;
33
43
  }
34
44
 
35
45
  VALUE
36
46
  pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
37
47
  {
38
48
  rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
39
- return Qnil;
40
49
  }
41
50
 
42
51
  t_pg_coder *
43
52
  pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
44
53
  {
45
54
  rb_raise( rb_eNotImpError, "type map is not suitable to map query params" );
46
- return NULL;
47
55
  }
48
56
 
49
57
  VALUE
50
58
  pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
51
59
  {
52
60
  rb_raise( rb_eNotImpError, "type map is not suitable to map get_copy_data results" );
53
- return Qnil;
54
61
  }
55
62
 
56
63
  const struct pg_typemap_funcs pg_typemap_funcs = {
@@ -188,7 +188,8 @@ module PG::BasicTypeRegistry
188
188
  # register_type 'polygon', OID::Text.new
189
189
  # register_type 'circle', OID::Text.new
190
190
  # register_type 'hstore', OID::Hstore.new
191
- # register_type 'json', OID::Json.new
191
+ register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
192
+ alias_type 0, 'jsonb', 'json'
192
193
  # register_type 'citext', OID::Text.new
193
194
  # register_type 'ltree', OID::Text.new
194
195
  #
@@ -226,8 +227,8 @@ end
226
227
  #
227
228
  # Example:
228
229
  # conn = PG::Connection.new
229
- # # Assign a default ruleset for type casts of input and output values.
230
- # conn.type_mapping = PG::BasicTypeMapping.new(conn)
230
+ # # Assign a default ruleset for type casts of output values.
231
+ # conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
231
232
  # # Execute a query.
232
233
  # res = conn.exec_params( "SELECT $1::INT", ['5'] )
233
234
  # # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
@@ -236,8 +237,28 @@ end
236
237
  #
237
238
  # PG::TypeMapByOid#fit_to_result(result, false) can be used to generate
238
239
  # a result independent PG::TypeMapByColumn type map, which can subsequently be used
239
- # to cast #get_copy_data fields. See also PG::BasicTypeMapBasedOnResult .
240
+ # to cast #get_copy_data fields:
241
+ #
242
+ # For the following table:
243
+ # conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
244
+ #
245
+ # # Retrieve table OIDs per empty result set.
246
+ # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
247
+ # # Build a type map for common database to ruby type decoders.
248
+ # btm = PG::BasicTypeMapForResults.new(conn)
249
+ # # Build a PG::TypeMapByColumn with decoders suitable for copytable.
250
+ # tm = btm.build_column_map( res )
251
+ # row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
252
+ #
253
+ # conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
254
+ # while row=conn.get_copy_data
255
+ # p row
256
+ # end
257
+ # end
258
+ # This prints the rows with type casted columns:
259
+ # ["a", 123, [5, 4, 3]]
240
260
  #
261
+ # See also PG::BasicTypeMapBasedOnResult for the encoder direction.
241
262
  class PG::BasicTypeMapForResults < PG::TypeMapByOid
242
263
  include PG::BasicTypeRegistry
243
264
 
@@ -290,12 +311,17 @@ end
290
311
  #
291
312
  # # Retrieve table OIDs per empty result set.
292
313
  # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
293
- # tm = basic_type_mapping.build_column_map( res )
314
+ # # Build a type map for common ruby to database type encoders.
315
+ # btm = PG::BasicTypeMapBasedOnResult.new(conn)
316
+ # # Build a PG::TypeMapByColumn with encoders suitable for copytable.
317
+ # tm = btm.build_column_map( res )
294
318
  # row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
295
319
  #
296
320
  # conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
297
321
  # conn.put_copy_data ['a', 123, [5,4,3]]
298
322
  # end
323
+ # This inserts a single row into copytable with type casts from ruby to
324
+ # database types.
299
325
  class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
300
326
  include PG::BasicTypeRegistry
301
327
 
@@ -314,15 +340,16 @@ end
314
340
  # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
315
341
  # PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
316
342
  #
317
- # Query params are type casted based on the MRI internal type of the given value.
343
+ # Query params are type casted based on the class of the given value.
318
344
  #
319
345
  # Higher level libraries will most likely not make use of this class, but use their
320
- # own set of rules to choose suitable encoders and decoders.
346
+ # own derivation of PG::TypeMapByClass or another set of rules to choose suitable
347
+ # encoders and decoders for the values to be sent.
321
348
  #
322
349
  # Example:
323
350
  # conn = PG::Connection.new
324
351
  # # Assign a default ruleset for type casts of input and output values.
325
- # conn.type_mapping_for_queries = PG::BasicTypeMapForQueries.new(conn)
352
+ # conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
326
353
  # # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
327
354
  # # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
328
355
  # res = conn.exec_params( "SELECT $1", [5] )