pg 1.4.3 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +15 -9
  4. data/.github/workflows/binary-gems.yml +43 -12
  5. data/.github/workflows/source-gem.yml +28 -20
  6. data/.gitignore +11 -2
  7. data/.travis.yml +2 -2
  8. data/{History.rdoc → History.md} +251 -145
  9. data/README.ja.md +276 -0
  10. data/README.md +286 -0
  11. data/Rakefile +15 -6
  12. data/Rakefile.cross +7 -11
  13. data/certs/larskanis-2023.pem +24 -0
  14. data/ext/errorcodes.def +4 -0
  15. data/ext/errorcodes.txt +2 -1
  16. data/ext/pg.c +14 -30
  17. data/ext/pg.h +11 -5
  18. data/ext/pg_binary_decoder.c +80 -1
  19. data/ext/pg_binary_encoder.c +225 -1
  20. data/ext/pg_coder.c +17 -8
  21. data/ext/pg_connection.c +162 -64
  22. data/ext/pg_copy_coder.c +307 -18
  23. data/ext/pg_errors.c +1 -1
  24. data/ext/pg_record_coder.c +6 -5
  25. data/ext/pg_result.c +102 -26
  26. data/ext/pg_text_decoder.c +28 -10
  27. data/ext/pg_text_encoder.c +23 -10
  28. data/ext/pg_tuple.c +35 -32
  29. data/ext/pg_type_map.c +4 -3
  30. data/ext/pg_type_map_all_strings.c +3 -3
  31. data/ext/pg_type_map_by_class.c +6 -4
  32. data/ext/pg_type_map_by_column.c +9 -5
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +8 -5
  35. data/ext/pg_type_map_in_ruby.c +6 -3
  36. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  37. data/lib/pg/basic_type_map_for_queries.rb +13 -8
  38. data/lib/pg/basic_type_map_for_results.rb +26 -3
  39. data/lib/pg/basic_type_registry.rb +30 -32
  40. data/lib/pg/binary_decoder/date.rb +9 -0
  41. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  42. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  43. data/lib/pg/coder.rb +15 -13
  44. data/lib/pg/connection.rb +148 -86
  45. data/lib/pg/exceptions.rb +7 -0
  46. data/lib/pg/text_decoder/date.rb +18 -0
  47. data/lib/pg/text_decoder/inet.rb +9 -0
  48. data/lib/pg/text_decoder/json.rb +14 -0
  49. data/lib/pg/text_decoder/numeric.rb +9 -0
  50. data/lib/pg/text_decoder/timestamp.rb +30 -0
  51. data/lib/pg/text_encoder/date.rb +12 -0
  52. data/lib/pg/text_encoder/inet.rb +28 -0
  53. data/lib/pg/text_encoder/json.rb +14 -0
  54. data/lib/pg/text_encoder/numeric.rb +9 -0
  55. data/lib/pg/text_encoder/timestamp.rb +24 -0
  56. data/lib/pg/version.rb +1 -1
  57. data/lib/pg.rb +55 -15
  58. data/pg.gemspec +4 -2
  59. data/rakelib/task_extension.rb +1 -1
  60. data/translation/.po4a-version +7 -0
  61. data/translation/po/all.pot +910 -0
  62. data/translation/po/ja.po +1047 -0
  63. data/translation/po4a.cfg +12 -0
  64. data.tar.gz.sig +0 -0
  65. metadata +101 -32
  66. metadata.gz.sig +0 -0
  67. data/README.ja.rdoc +0 -13
  68. data/README.rdoc +0 -214
  69. data/lib/pg/binary_decoder.rb +0 -23
  70. data/lib/pg/constants.rb +0 -12
  71. data/lib/pg/text_decoder.rb +0 -46
  72. data/lib/pg/text_encoder.rb +0 -59
@@ -0,0 +1,24 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEBDCCAmygAwIBAgIBAjANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1sYXJz
3
+ L0RDPWdyZWl6LXJlaW5zZG9yZi9EQz1kZTAeFw0yMzAyMTUxNzQxMTVaFw0yNDAy
4
+ MTUxNzQxMTVaMCgxJjAkBgNVBAMMHWxhcnMvREM9Z3JlaXotcmVpbnNkb3JmL0RD
5
+ PWRlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwum6Y1KznfpzXOT/
6
+ mZgJTBbxZuuZF49Fq3K0WA67YBzNlDv95qzSp7V/7Ek3NCcnT7G+2kSuhNo1FhdN
7
+ eSDO/moYebZNAcu3iqLsuzuULXPLuoU0GsMnVMqV9DZPh7cQHE5EBZ7hlzDBK7k/
8
+ 8nBMvR0mHo77kIkapHc26UzVq/G0nKLfDsIHXVylto3PjzOumjG6GhmFN4r3cP6e
9
+ SDfl1FSeRYVpt4kmQULz/zdSaOH3AjAq7PM2Z91iGwQvoUXMANH2v89OWjQO/NHe
10
+ JMNDFsmHK/6Ji4Kk48Z3TyscHQnipAID5GhS1oD21/WePdj7GhmbF5gBzkV5uepd
11
+ eJQPgWGwrQW/Z2oPjRuJrRofzWfrMWqbOahj9uth6WSxhNexUtbjk6P8emmXOJi5
12
+ chQPnWX+N3Gj+jjYxqTFdwT7Mj3pv1VHa+aNUbqSPpvJeDyxRIuo9hvzDaBHb/Cg
13
+ 9qRVcm8a96n4t7y2lrX1oookY6bkBaxWOMtWlqIprq8JZXM9AgMBAAGjOTA3MAkG
14
+ A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQ4h1tIyvdUWtMI739xMzTR
15
+ 7EfMFzANBgkqhkiG9w0BAQsFAAOCAYEAQAcuTARfiiVUVx5KURICfdTM2Kd7LhOn
16
+ qt3Vs4ANGvT226LEp3RnQ+kWGQYMRb3cw3LY2TNQRPlnZxE994mgjBscN4fbjXqO
17
+ T0JbVpeszRZa5k1goggbnWT7CO7yU7WcHh13DaSubY7HUpAJn2xz9w2stxQfN/EE
18
+ VMlnDJ1P7mUHAvpK8X9j9h7Xlc1niViT18MYwux8mboVTryrLr+clATUkkM3yBF0
19
+ RV+c34ReW5eXO9Tr6aKTxh/pFC9ggDT6jOxuJgSvG8HWJzVf4NDvMavIas4KYjiI
20
+ BU6CpWaG5NxicqL3BERi52U43HV08br+LNVpb7Rekgve/PJuSFnAR015bhSRXe5U
21
+ vBioD1qW2ZW9tXg8Ww2IfDaO5a1So5Xby51rhNlyo6ATj2NkuLWZUKPKHhAz0TKm
22
+ Dzx/gFSOrRoCt2mXNgrmcAfr386AfaMvCh7cXqdxZwmVo7ILZCYXck0pajvubsDd
23
+ NUIIFkVXvd1odFyK9LF1RFAtxn/iAmpx
24
+ -----END CERTIFICATE-----
data/ext/errorcodes.def CHANGED
@@ -366,6 +366,10 @@
366
366
  VALUE klass = define_error_class( "SqlJsonScalarRequired", "22" );
367
367
  register_error_class( "2203F", klass );
368
368
  }
369
+ {
370
+ VALUE klass = define_error_class( "SqlJsonItemCannotBeCastToTargetType", "22" );
371
+ register_error_class( "2203G", klass );
372
+ }
369
373
  {
370
374
  VALUE klass = define_error_class( "IntegrityConstraintViolation", NULL );
371
375
  register_error_class( "23000", klass );
data/ext/errorcodes.txt CHANGED
@@ -2,7 +2,7 @@
2
2
  # errcodes.txt
3
3
  # PostgreSQL error codes
4
4
  #
5
- # Copyright (c) 2003-2021, PostgreSQL Global Development Group
5
+ # Copyright (c) 2003-2022, PostgreSQL Global Development Group
6
6
  #
7
7
  # This list serves as the basis for generating source files containing error
8
8
  # codes. It is kept in a common format to make sure all these source files have
@@ -222,6 +222,7 @@ Section: Class 22 - Data Exception
222
222
  2203D E ERRCODE_TOO_MANY_JSON_ARRAY_ELEMENTS too_many_json_array_elements
223
223
  2203E E ERRCODE_TOO_MANY_JSON_OBJECT_MEMBERS too_many_json_object_members
224
224
  2203F E ERRCODE_SQL_JSON_SCALAR_REQUIRED sql_json_scalar_required
225
+ 2203G E ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE sql_json_item_cannot_be_cast_to_target_type
225
226
 
226
227
  Section: Class 23 - Integrity Constraint Violation
227
228
 
data/ext/pg.c CHANGED
@@ -33,7 +33,6 @@
33
33
  *
34
34
  * - PQfreemem -- unnecessary: copied to ruby object, then freed. Ruby object's
35
35
  * memory is freed when it is garbage collected.
36
- * - PQbinaryTuples -- better to use PQfformat
37
36
  * - PQprint -- not very useful
38
37
  * - PQsetdb -- not very useful
39
38
  * - PQoidStatus -- deprecated, use PQoidValue
@@ -47,7 +46,6 @@
47
46
  */
48
47
 
49
48
  #include "pg.h"
50
- #include "pg_config.h"
51
49
 
52
50
  int pg_skip_deprecation_warning;
53
51
  VALUE rb_mPG;
@@ -75,6 +73,7 @@ VALUE rb_mPGconstants;
75
73
  * The mapping from canonical encoding names in PostgreSQL to ones in Ruby.
76
74
  */
77
75
  const char * const (pg_enc_pg2ruby_mapping[][2]) = {
76
+ {"UTF8", "UTF-8" },
78
77
  {"BIG5", "Big5" },
79
78
  {"EUC_CN", "GB2312" },
80
79
  {"EUC_JP", "EUC-JP" },
@@ -106,7 +105,6 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
106
105
  {"SHIFT_JIS_2004","Windows-31J" },
107
106
  /* {"SQL_ASCII", NULL }, special case*/
108
107
  {"UHC", "CP949" },
109
- {"UTF8", "UTF-8" },
110
108
  {"WIN866", "IBM866" },
111
109
  {"WIN874", "Windows-874" },
112
110
  {"WIN1250", "Windows-1250"},
@@ -121,12 +119,6 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
121
119
  };
122
120
 
123
121
 
124
- /*
125
- * A cache of mapping from PostgreSQL's encoding indices to Ruby's rb_encoding*s.
126
- */
127
- static struct st_table *enc_pg2ruby;
128
-
129
-
130
122
  /*
131
123
  * Return the given PostgreSQL encoding ID as an rb_encoding.
132
124
  *
@@ -136,21 +128,8 @@ static struct st_table *enc_pg2ruby;
136
128
  rb_encoding *
137
129
  pg_get_pg_encoding_as_rb_encoding( int enc_id )
138
130
  {
139
- rb_encoding *enc;
140
-
141
- /* Use the cached value if it exists */
142
- if ( st_lookup(enc_pg2ruby, (st_data_t)enc_id, (st_data_t*)&enc) ) {
143
- return enc;
144
- }
145
- else {
146
- const char *name = pg_encoding_to_char( enc_id );
147
-
148
- enc = pg_get_pg_encname_as_rb_encoding( name );
149
- st_insert( enc_pg2ruby, (st_data_t)enc_id, (st_data_t)enc );
150
-
151
- return enc;
152
- }
153
-
131
+ const char *name = pg_encoding_to_char( enc_id );
132
+ return pg_get_pg_encname_as_rb_encoding( name );
154
133
  }
155
134
 
156
135
  /*
@@ -353,8 +332,13 @@ pg_s_init_ssl(VALUE self, VALUE do_ssl)
353
332
  **************************************************************************/
354
333
 
355
334
  void
356
- Init_pg_ext()
335
+ Init_pg_ext(void)
357
336
  {
337
+
338
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
339
+ rb_ext_ractor_safe(PQisthreadsafe());
340
+ #endif
341
+
358
342
  if( RTEST(rb_eval_string("ENV['PG_SKIP_DEPRECATION_WARNING']")) ){
359
343
  /* Set all bits to disable all deprecation warnings. */
360
344
  pg_skip_deprecation_warning = 0xFFFF;
@@ -373,8 +357,8 @@ Init_pg_ext()
373
357
  SINGLETON_ALIAS( rb_mPG, "is_threadsafe?", "isthreadsafe" );
374
358
  SINGLETON_ALIAS( rb_mPG, "threadsafe?", "isthreadsafe" );
375
359
 
376
- rb_define_singleton_method( rb_mPG, "init_openssl", pg_s_init_openssl, 2 );
377
- rb_define_singleton_method( rb_mPG, "init_ssl", pg_s_init_ssl, 1 );
360
+ rb_define_singleton_method( rb_mPG, "init_openssl", pg_s_init_openssl, 2 );
361
+ rb_define_singleton_method( rb_mPG, "init_ssl", pg_s_init_ssl, 1 );
378
362
 
379
363
 
380
364
  /****** PG::Connection CLASS CONSTANTS: Connection Status ******/
@@ -680,11 +664,12 @@ Init_pg_ext()
680
664
  rb_define_const(rb_mPGconstants, "INVALID_OID", INT2FIX(InvalidOid));
681
665
  rb_define_const(rb_mPGconstants, "InvalidOid", INT2FIX(InvalidOid));
682
666
 
667
+ /* PostgreSQL compiled in default port */
668
+ rb_define_const(rb_mPGconstants, "DEF_PGPORT", INT2FIX(DEF_PGPORT));
669
+
683
670
  /* Add the constants to the toplevel namespace */
684
671
  rb_include_module( rb_mPG, rb_mPGconstants );
685
672
 
686
- enc_pg2ruby = st_init_numtable();
687
-
688
673
  /* Initialize the main extension classes */
689
674
  init_pg_connection();
690
675
  init_pg_result();
@@ -705,4 +690,3 @@ Init_pg_ext()
705
690
  init_pg_recordcoder();
706
691
  init_pg_tuple();
707
692
  }
708
-
data/ext/pg.h CHANGED
@@ -57,6 +57,7 @@
57
57
  #endif
58
58
 
59
59
  /* PostgreSQL headers */
60
+ #include "pg_config.h"
60
61
  #include "libpq-fe.h"
61
62
  #include "libpq/libpq-fs.h" /* large-object interface */
62
63
  #include "pg_config_manual.h"
@@ -75,10 +76,6 @@ typedef long suseconds_t;
75
76
  #define PG_MAX_COLUMNS 4000
76
77
  #endif
77
78
 
78
- #ifndef RARRAY_AREF
79
- #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
80
- #endif
81
-
82
79
  #ifdef HAVE_RB_GC_MARK_MOVABLE
83
80
  #define pg_compact_callback(x) (x)
84
81
  #define pg_gc_location(x) x = rb_gc_location(x)
@@ -88,6 +85,12 @@ typedef long suseconds_t;
88
85
  #define pg_gc_location(x) UNUSED(x)
89
86
  #endif
90
87
 
88
+ /* For compatibility with ruby < 3.0 */
89
+ #ifndef RUBY_TYPED_FROZEN_SHAREABLE
90
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE 0
91
+ #else
92
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
93
+ #endif
91
94
  #define PG_ENC_IDX_BITS 28
92
95
 
93
96
  /* The data behind each PG::Connection object */
@@ -96,6 +99,9 @@ typedef struct {
96
99
 
97
100
  /* Cached IO object for the socket descriptor */
98
101
  VALUE socket_io;
102
+ /* function pointers of the original libpq notice receivers */
103
+ PQnoticeReceiver default_notice_receiver;
104
+ PQnoticeProcessor default_notice_processor;
99
105
  /* Proc object that receives notices as PG::Result objects */
100
106
  VALUE notice_receiver;
101
107
  /* Proc object that receives notices as String objects */
@@ -313,7 +319,7 @@ int pg_coder_enc_to_s _(( t_pg_coder*, VALUE, c
313
319
  int pg_text_enc_identifier _(( t_pg_coder*, VALUE, char *, VALUE *, int));
314
320
  t_pg_coder_enc_func pg_coder_enc_func _(( t_pg_coder* ));
315
321
  t_pg_coder_dec_func pg_coder_dec_func _(( t_pg_coder*, int ));
316
- void pg_define_coder _(( const char *, void *, VALUE, VALUE ));
322
+ VALUE pg_define_coder _(( const char *, void *, VALUE, VALUE ));
317
323
  VALUE pg_obj_to_i _(( VALUE ));
318
324
  VALUE pg_tmbc_allocate _(( void ));
319
325
  void pg_coder_init_encoder _(( VALUE ));
@@ -12,6 +12,8 @@
12
12
  #endif
13
13
 
14
14
  VALUE rb_mPG_BinaryDecoder;
15
+ static VALUE s_Date;
16
+ static ID s_id_new;
15
17
 
16
18
 
17
19
  /*
@@ -195,6 +197,82 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
195
197
  }
196
198
  }
197
199
 
200
+ #define PG_INT32_MIN (-0x7FFFFFFF-1)
201
+ #define PG_INT32_MAX (0x7FFFFFFF)
202
+ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
203
+ #define MONTHS_PER_YEAR 12
204
+
205
+ /* taken from PostgreSQL sources at src/backend/utils/adt/datetime.c */
206
+ void
207
+ j2date(int jd, int *year, int *month, int *day)
208
+ {
209
+ unsigned int julian;
210
+ unsigned int quad;
211
+ unsigned int extra;
212
+ int y;
213
+
214
+ julian = jd;
215
+ julian += 32044;
216
+ quad = julian / 146097;
217
+ extra = (julian - quad * 146097) * 4 + 3;
218
+ julian += 60 + quad * 3 + extra / 146097;
219
+ quad = julian / 1461;
220
+ julian -= quad * 1461;
221
+ y = julian * 4 / 1461;
222
+ julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
223
+ + 123;
224
+ y += quad * 4;
225
+ *year = y - 4800;
226
+ quad = julian * 2141 / 65536;
227
+ *day = julian - 7834 * quad / 256;
228
+ *month = (quad + 10) % MONTHS_PER_YEAR + 1;
229
+ } /* j2date() */
230
+
231
+ /*
232
+ * Document-class: PG::BinaryDecoder::Date < PG::SimpleDecoder
233
+ *
234
+ * This is a decoder class for conversion of PostgreSQL binary date
235
+ * to Ruby Date objects.
236
+ */
237
+ static VALUE
238
+ pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
239
+ {
240
+ int year, month, day;
241
+ int date;
242
+
243
+ if (len != 4) {
244
+ rb_raise(rb_eTypeError, "unexpected date format != 4 bytes");
245
+ }
246
+
247
+ date = read_nbo32(val);
248
+ switch(date){
249
+ case PG_INT32_MAX:
250
+ return rb_str_new2("infinity");
251
+ case PG_INT32_MIN:
252
+ return rb_str_new2("-infinity");
253
+ default:
254
+ j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
255
+
256
+ return rb_funcall(s_Date, s_id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
257
+ }
258
+ }
259
+
260
+ /* called per autoload when BinaryDecoder::Date is used */
261
+ static VALUE
262
+ init_pg_bin_decoder_date(VALUE rb_mPG_BinaryDecoder)
263
+ {
264
+ rb_require("date");
265
+ s_Date = rb_const_get(rb_cObject, rb_intern("Date"));
266
+ rb_gc_register_mark_object(s_Date);
267
+ s_id_new = rb_intern("new");
268
+
269
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Date", rb_cPG_SimpleDecoder ); */
270
+ pg_define_coder( "Date", pg_bin_dec_date, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
271
+
272
+ return Qnil;
273
+ }
274
+
275
+
198
276
  /*
199
277
  * Document-class: PG::BinaryDecoder::String < PG::SimpleDecoder
200
278
  *
@@ -205,10 +283,11 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
205
283
  */
206
284
 
207
285
  void
208
- init_pg_binary_decoder()
286
+ init_pg_binary_decoder(void)
209
287
  {
210
288
  /* This module encapsulates all decoder classes with binary input format */
211
289
  rb_mPG_BinaryDecoder = rb_define_module_under( rb_mPG, "BinaryDecoder" );
290
+ rb_define_private_method(rb_singleton_class(rb_mPG_BinaryDecoder), "init_date", init_pg_bin_decoder_date, 0);
212
291
 
213
292
  /* Make RDoc aware of the decoder classes... */
214
293
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -11,6 +11,9 @@
11
11
  #endif
12
12
 
13
13
  VALUE rb_mPG_BinaryEncoder;
14
+ static ID s_id_year;
15
+ static ID s_id_month;
16
+ static ID s_id_day;
14
17
 
15
18
 
16
19
  /*
@@ -93,6 +96,215 @@ pg_bin_enc_int8(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, i
93
96
  return 8;
94
97
  }
95
98
 
99
+ /*
100
+ * Document-class: PG::BinaryEncoder::Float4 < PG::SimpleEncoder
101
+ *
102
+ * This is the binary encoder class for the PostgreSQL +float4+ type.
103
+ *
104
+ */
105
+ static int
106
+ pg_bin_enc_float4(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
107
+ {
108
+ union {
109
+ float f;
110
+ int32_t i;
111
+ } swap4;
112
+
113
+ if(out){
114
+ swap4.f = NUM2DBL(*intermediate);
115
+ write_nbo32(swap4.i, out);
116
+ }else{
117
+ *intermediate = value;
118
+ }
119
+ return 4;
120
+ }
121
+
122
+ /*
123
+ * Document-class: PG::BinaryEncoder::Float8 < PG::SimpleEncoder
124
+ *
125
+ * This is the binary encoder class for the PostgreSQL +float8+ type.
126
+ *
127
+ */
128
+ static int
129
+ pg_bin_enc_float8(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
130
+ {
131
+ union {
132
+ double f;
133
+ int64_t i;
134
+ } swap8;
135
+
136
+ if(out){
137
+ swap8.f = NUM2DBL(*intermediate);
138
+ write_nbo64(swap8.i, out);
139
+ }else{
140
+ *intermediate = value;
141
+ }
142
+ return 8;
143
+ }
144
+
145
+ #define PG_INT32_MIN (-0x7FFFFFFF-1)
146
+ #define PG_INT32_MAX (0x7FFFFFFF)
147
+ #define PG_INT64_MIN (-0x7FFFFFFFFFFFFFFFL - 1)
148
+ #define PG_INT64_MAX 0x7FFFFFFFFFFFFFFFL
149
+
150
+ /*
151
+ * Document-class: PG::BinaryEncoder::Timestamp < PG::SimpleEncoder
152
+ *
153
+ * This is a encoder class for conversion of Ruby Time objects to PostgreSQL binary timestamps.
154
+ *
155
+ * The following flags can be used to specify timezone interpretation:
156
+ * * +PG::Coder::TIMESTAMP_DB_UTC+ : Send timestamp as UTC time (default)
157
+ * * +PG::Coder::TIMESTAMP_DB_LOCAL+ : Send timestamp as local time (slower)
158
+ *
159
+ * Example:
160
+ * enco = PG::BinaryEncoder::Timestamp.new(flags: PG::Coder::TIMESTAMP_DB_UTC)
161
+ * enco.encode(Time.utc(2000, 1, 1)) # => "\x00\x00\x00\x00\x00\x00\x00\x00"
162
+ *
163
+ * String values are expected to contain a binary data with a length of 8 byte.
164
+ *
165
+ */
166
+ static int
167
+ pg_bin_enc_timestamp(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
168
+ {
169
+ if(out){
170
+ int64_t timestamp;
171
+ struct timespec ts;
172
+
173
+ /* second call -> write data to *out */
174
+ switch(TYPE(*intermediate)){
175
+ case T_STRING:
176
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
177
+ case T_TRUE:
178
+ write_nbo64(PG_INT64_MAX, out);
179
+ return 8;
180
+ case T_FALSE:
181
+ write_nbo64(PG_INT64_MIN, out);
182
+ return 8;
183
+ }
184
+
185
+ ts = rb_time_timespec(*intermediate);
186
+ /* PostgreSQL's timestamp is based on year 2000 and Ruby's time is based on 1970.
187
+ * Adjust the 30 years difference. */
188
+ timestamp = (ts.tv_sec - 10957L * 24L * 3600L) * 1000000 + (ts.tv_nsec / 1000);
189
+
190
+ if( this->flags & PG_CODER_TIMESTAMP_DB_LOCAL ) {
191
+ /* send as local time */
192
+ timestamp += NUM2LL(rb_funcall(*intermediate, rb_intern("utc_offset"), 0)) * 1000000;
193
+ }
194
+
195
+ write_nbo64(timestamp, out);
196
+ }else{
197
+ /* first call -> determine the required length */
198
+ if(TYPE(value) == T_STRING){
199
+ char *pstr = RSTRING_PTR(value);
200
+ if(RSTRING_LEN(value) >= 1){
201
+ switch(pstr[0]) {
202
+ case 'I':
203
+ case 'i':
204
+ *intermediate = Qtrue;
205
+ return 8;
206
+ case '-':
207
+ if (RSTRING_LEN(value) >= 2 && (pstr[1] == 'I' || pstr[1] == 'i')) {
208
+ *intermediate = Qfalse;
209
+ return 8;
210
+ }
211
+ }
212
+ }
213
+
214
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
215
+ }
216
+
217
+ if( this->flags & PG_CODER_TIMESTAMP_DB_LOCAL ) {
218
+ /* make a local time, so that utc_offset is set */
219
+ value = rb_funcall(value, rb_intern("getlocal"), 0);
220
+ }
221
+ *intermediate = value;
222
+ }
223
+ return 8;
224
+ }
225
+
226
+ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
227
+ int
228
+ date2j(int year, int month, int day)
229
+ {
230
+ int julian;
231
+ int century;
232
+
233
+ if (month > 2)
234
+ {
235
+ month += 1;
236
+ year += 4800;
237
+ }
238
+ else
239
+ {
240
+ month += 13;
241
+ year += 4799;
242
+ }
243
+
244
+ century = year / 100;
245
+ julian = year * 365 - 32167;
246
+ julian += year / 4 - century + century / 4;
247
+ julian += 7834 * month / 256 + day;
248
+
249
+ return julian;
250
+ } /* date2j() */
251
+
252
+ /*
253
+ * Document-class: PG::BinaryEncoder::Date < PG::SimpleEncoder
254
+ *
255
+ * This is a encoder class for conversion of Ruby Date objects to PostgreSQL binary date.
256
+ *
257
+ * String values are expected to contain a binary data with a length of 4 byte.
258
+ *
259
+ */
260
+ static int
261
+ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
262
+ {
263
+ if(out){
264
+ /* second call -> write data to *out */
265
+ switch(TYPE(*intermediate)){
266
+ case T_STRING:
267
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
268
+ case T_TRUE:
269
+ write_nbo32(PG_INT32_MAX, out);
270
+ return 4;
271
+ case T_FALSE:
272
+ write_nbo32(PG_INT32_MIN, out);
273
+ return 4;
274
+ }
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
+ }else{
283
+ /* first call -> determine the required length */
284
+ if(TYPE(value) == T_STRING){
285
+ char *pstr = RSTRING_PTR(value);
286
+ if(RSTRING_LEN(value) >= 1){
287
+ switch(pstr[0]) {
288
+ case 'I':
289
+ case 'i':
290
+ *intermediate = Qtrue;
291
+ return 4;
292
+ case '-':
293
+ if (RSTRING_LEN(value) >= 2 && (pstr[1] == 'I' || pstr[1] == 'i')) {
294
+ *intermediate = Qfalse;
295
+ return 4;
296
+ }
297
+ }
298
+ }
299
+
300
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
301
+ }
302
+
303
+ *intermediate = value;
304
+ }
305
+ return 4;
306
+ }
307
+
96
308
  /*
97
309
  * Document-class: PG::BinaryEncoder::FromBase64 < PG::CompositeEncoder
98
310
  *
@@ -139,8 +351,12 @@ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermed
139
351
  }
140
352
 
141
353
  void
142
- init_pg_binary_encoder()
354
+ init_pg_binary_encoder(void)
143
355
  {
356
+ s_id_year = rb_intern("year");
357
+ s_id_month = rb_intern("month");
358
+ s_id_day = rb_intern("day");
359
+
144
360
  /* This module encapsulates all encoder classes with binary output format */
145
361
  rb_mPG_BinaryEncoder = rb_define_module_under( rb_mPG, "BinaryEncoder" );
146
362
 
@@ -153,10 +369,18 @@ init_pg_binary_encoder()
153
369
  pg_define_coder( "Int4", pg_bin_enc_int4, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
154
370
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Int8", rb_cPG_SimpleEncoder ); */
155
371
  pg_define_coder( "Int8", pg_bin_enc_int8, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
372
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Float4", rb_cPG_SimpleEncoder ); */
373
+ pg_define_coder( "Float4", pg_bin_enc_float4, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
374
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Float8", rb_cPG_SimpleEncoder ); */
375
+ pg_define_coder( "Float8", pg_bin_enc_float8, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
156
376
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "String", rb_cPG_SimpleEncoder ); */
157
377
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
158
378
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
159
379
  pg_define_coder( "Bytea", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
380
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Timestamp", rb_cPG_SimpleEncoder ); */
381
+ pg_define_coder( "Timestamp", pg_bin_enc_timestamp, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
382
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Date", rb_cPG_SimpleEncoder ); */
383
+ pg_define_coder( "Date", pg_bin_enc_date, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
160
384
 
161
385
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "FromBase64", rb_cPG_CompositeEncoder ); */
162
386
  pg_define_coder( "FromBase64", pg_bin_enc_from_base64, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
data/ext/pg_coder.c CHANGED
@@ -35,7 +35,7 @@ pg_coder_init_encoder( VALUE self )
35
35
  this->enc_func = NULL;
36
36
  }
37
37
  this->dec_func = NULL;
38
- this->coder_obj = self;
38
+ RB_OBJ_WRITE(self, &this->coder_obj, self);
39
39
  this->oid = 0;
40
40
  this->format = 0;
41
41
  this->flags = 0;
@@ -54,7 +54,7 @@ pg_coder_init_decoder( VALUE self )
54
54
  } else {
55
55
  this->dec_func = NULL;
56
56
  }
57
- this->coder_obj = self;
57
+ RB_OBJ_WRITE(self, &this->coder_obj, self);
58
58
  this->oid = 0;
59
59
  this->format = 0;
60
60
  this->flags = 0;
@@ -99,7 +99,9 @@ const rb_data_type_t pg_coder_type = {
99
99
  },
100
100
  0,
101
101
  0,
102
- RUBY_TYPED_FREE_IMMEDIATELY,
102
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
103
+ // macro to update VALUE references, as to trigger write barriers.
104
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
103
105
  };
104
106
 
105
107
  static VALUE
@@ -121,7 +123,7 @@ static const rb_data_type_t pg_composite_coder_type = {
121
123
  },
122
124
  &pg_coder_type,
123
125
  0,
124
- RUBY_TYPED_FREE_IMMEDIATELY,
126
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
125
127
  };
126
128
 
127
129
  static VALUE
@@ -273,6 +275,7 @@ static VALUE
273
275
  pg_coder_oid_set(VALUE self, VALUE oid)
274
276
  {
275
277
  t_pg_coder *this = RTYPEDDATA_DATA(self);
278
+ rb_check_frozen(self);
276
279
  this->oid = NUM2UINT(oid);
277
280
  return oid;
278
281
  }
@@ -304,6 +307,7 @@ static VALUE
304
307
  pg_coder_format_set(VALUE self, VALUE format)
305
308
  {
306
309
  t_pg_coder *this = RTYPEDDATA_DATA(self);
310
+ rb_check_frozen(self);
307
311
  this->format = NUM2INT(format);
308
312
  return format;
309
313
  }
@@ -335,6 +339,7 @@ static VALUE
335
339
  pg_coder_flags_set(VALUE self, VALUE flags)
336
340
  {
337
341
  t_pg_coder *this = RTYPEDDATA_DATA(self);
342
+ rb_check_frozen(self);
338
343
  this->flags = NUM2INT(flags);
339
344
  return flags;
340
345
  }
@@ -366,6 +371,7 @@ static VALUE
366
371
  pg_coder_needs_quotation_set(VALUE self, VALUE needs_quotation)
367
372
  {
368
373
  t_pg_composite_coder *this = RTYPEDDATA_DATA(self);
374
+ rb_check_frozen(self);
369
375
  this->needs_quotation = RTEST(needs_quotation);
370
376
  return needs_quotation;
371
377
  }
@@ -396,6 +402,7 @@ static VALUE
396
402
  pg_coder_delimiter_set(VALUE self, VALUE delimiter)
397
403
  {
398
404
  t_pg_composite_coder *this = RTYPEDDATA_DATA(self);
405
+ rb_check_frozen(self);
399
406
  StringValue(delimiter);
400
407
  if(RSTRING_LEN(delimiter) != 1)
401
408
  rb_raise( rb_eArgError, "delimiter size must be one byte");
@@ -430,6 +437,7 @@ pg_coder_elements_type_set(VALUE self, VALUE elem_type)
430
437
  {
431
438
  t_pg_composite_coder *this = RTYPEDDATA_DATA( self );
432
439
 
440
+ rb_check_frozen(self);
433
441
  if ( NIL_P(elem_type) ){
434
442
  this->elem = NULL;
435
443
  } else if ( rb_obj_is_kind_of(elem_type, rb_cPG_Coder) ){
@@ -452,10 +460,10 @@ static const rb_data_type_t pg_coder_cfunc_type = {
452
460
  },
453
461
  0,
454
462
  0,
455
- RUBY_TYPED_FREE_IMMEDIATELY,
463
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
456
464
  };
457
465
 
458
- void
466
+ VALUE
459
467
  pg_define_coder( const char *name, void *func, VALUE base_klass, VALUE nsp )
460
468
  {
461
469
  VALUE cfunc_obj = TypedData_Wrap_Struct( rb_cObject, &pg_coder_cfunc_type, func );
@@ -468,9 +476,10 @@ pg_define_coder( const char *name, void *func, VALUE base_klass, VALUE nsp )
468
476
  if( nsp==rb_mPG_BinaryDecoder || nsp==rb_mPG_TextDecoder )
469
477
  rb_define_method( coder_klass, "decode", pg_coder_decode, -1 );
470
478
 
471
- rb_define_const( coder_klass, "CFUNC", cfunc_obj );
479
+ rb_define_const( coder_klass, "CFUNC", rb_obj_freeze(cfunc_obj) );
472
480
 
473
481
  RB_GC_GUARD(cfunc_obj);
482
+ return coder_klass;
474
483
  }
475
484
 
476
485
 
@@ -537,7 +546,7 @@ pg_coder_dec_func(t_pg_coder *this, int binary)
537
546
 
538
547
 
539
548
  void
540
- init_pg_coder()
549
+ init_pg_coder(void)
541
550
  {
542
551
  s_id_encode = rb_intern("encode");
543
552
  s_id_decode = rb_intern("decode");