pg 1.4.4 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) 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 +42 -11
  5. data/.github/workflows/source-gem.yml +24 -18
  6. data/.gitignore +11 -2
  7. data/.travis.yml +2 -2
  8. data/{History.rdoc → History.md} +246 -150
  9. data/README.ja.md +276 -0
  10. data/README.md +286 -0
  11. data/Rakefile +13 -4
  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 +13 -28
  17. data/ext/pg.h +10 -5
  18. data/ext/pg_binary_decoder.c +79 -0
  19. data/ext/pg_binary_encoder.c +224 -0
  20. data/ext/pg_coder.c +16 -7
  21. data/ext/pg_connection.c +160 -61
  22. data/ext/pg_copy_coder.c +306 -17
  23. data/ext/pg_record_coder.c +5 -4
  24. data/ext/pg_result.c +91 -17
  25. data/ext/pg_text_decoder.c +28 -10
  26. data/ext/pg_text_encoder.c +22 -9
  27. data/ext/pg_tuple.c +34 -31
  28. data/ext/pg_type_map.c +3 -2
  29. data/ext/pg_type_map_all_strings.c +2 -2
  30. data/ext/pg_type_map_by_class.c +5 -3
  31. data/ext/pg_type_map_by_column.c +7 -3
  32. data/ext/pg_type_map_by_oid.c +7 -4
  33. data/ext/pg_type_map_in_ruby.c +5 -2
  34. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  35. data/lib/pg/basic_type_map_for_queries.rb +13 -8
  36. data/lib/pg/basic_type_map_for_results.rb +26 -3
  37. data/lib/pg/basic_type_registry.rb +30 -32
  38. data/lib/pg/binary_decoder/date.rb +9 -0
  39. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  40. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  41. data/lib/pg/coder.rb +15 -13
  42. data/lib/pg/connection.rb +104 -30
  43. data/lib/pg/exceptions.rb +7 -0
  44. data/lib/pg/text_decoder/date.rb +18 -0
  45. data/lib/pg/text_decoder/inet.rb +9 -0
  46. data/lib/pg/text_decoder/json.rb +14 -0
  47. data/lib/pg/text_decoder/numeric.rb +9 -0
  48. data/lib/pg/text_decoder/timestamp.rb +30 -0
  49. data/lib/pg/text_encoder/date.rb +12 -0
  50. data/lib/pg/text_encoder/inet.rb +28 -0
  51. data/lib/pg/text_encoder/json.rb +14 -0
  52. data/lib/pg/text_encoder/numeric.rb +9 -0
  53. data/lib/pg/text_encoder/timestamp.rb +24 -0
  54. data/lib/pg/version.rb +1 -1
  55. data/lib/pg.rb +55 -15
  56. data/pg.gemspec +4 -2
  57. data/rakelib/task_extension.rb +1 -1
  58. data/translation/.po4a-version +7 -0
  59. data/translation/po/all.pot +910 -0
  60. data/translation/po/ja.po +1047 -0
  61. data/translation/po4a.cfg +12 -0
  62. data.tar.gz.sig +0 -0
  63. metadata +109 -34
  64. metadata.gz.sig +0 -0
  65. data/README.ja.rdoc +0 -13
  66. data/README.rdoc +0 -233
  67. data/lib/pg/binary_decoder.rb +0 -23
  68. data/lib/pg/constants.rb +0 -12
  69. data/lib/pg/text_decoder.rb +0 -46
  70. 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
@@ -74,6 +73,7 @@ VALUE rb_mPGconstants;
74
73
  * The mapping from canonical encoding names in PostgreSQL to ones in Ruby.
75
74
  */
76
75
  const char * const (pg_enc_pg2ruby_mapping[][2]) = {
76
+ {"UTF8", "UTF-8" },
77
77
  {"BIG5", "Big5" },
78
78
  {"EUC_CN", "GB2312" },
79
79
  {"EUC_JP", "EUC-JP" },
@@ -105,7 +105,6 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
105
105
  {"SHIFT_JIS_2004","Windows-31J" },
106
106
  /* {"SQL_ASCII", NULL }, special case*/
107
107
  {"UHC", "CP949" },
108
- {"UTF8", "UTF-8" },
109
108
  {"WIN866", "IBM866" },
110
109
  {"WIN874", "Windows-874" },
111
110
  {"WIN1250", "Windows-1250"},
@@ -120,12 +119,6 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
120
119
  };
121
120
 
122
121
 
123
- /*
124
- * A cache of mapping from PostgreSQL's encoding indices to Ruby's rb_encoding*s.
125
- */
126
- static struct st_table *enc_pg2ruby;
127
-
128
-
129
122
  /*
130
123
  * Return the given PostgreSQL encoding ID as an rb_encoding.
131
124
  *
@@ -135,21 +128,8 @@ static struct st_table *enc_pg2ruby;
135
128
  rb_encoding *
136
129
  pg_get_pg_encoding_as_rb_encoding( int enc_id )
137
130
  {
138
- rb_encoding *enc;
139
-
140
- /* Use the cached value if it exists */
141
- if ( st_lookup(enc_pg2ruby, (st_data_t)enc_id, (st_data_t*)&enc) ) {
142
- return enc;
143
- }
144
- else {
145
- const char *name = pg_encoding_to_char( enc_id );
146
-
147
- enc = pg_get_pg_encname_as_rb_encoding( name );
148
- st_insert( enc_pg2ruby, (st_data_t)enc_id, (st_data_t)enc );
149
-
150
- return enc;
151
- }
152
-
131
+ const char *name = pg_encoding_to_char( enc_id );
132
+ return pg_get_pg_encname_as_rb_encoding( name );
153
133
  }
154
134
 
155
135
  /*
@@ -354,6 +334,11 @@ pg_s_init_ssl(VALUE self, VALUE do_ssl)
354
334
  void
355
335
  Init_pg_ext(void)
356
336
  {
337
+
338
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
339
+ rb_ext_ractor_safe(PQisthreadsafe());
340
+ #endif
341
+
357
342
  if( RTEST(rb_eval_string("ENV['PG_SKIP_DEPRECATION_WARNING']")) ){
358
343
  /* Set all bits to disable all deprecation warnings. */
359
344
  pg_skip_deprecation_warning = 0xFFFF;
@@ -372,8 +357,8 @@ Init_pg_ext(void)
372
357
  SINGLETON_ALIAS( rb_mPG, "is_threadsafe?", "isthreadsafe" );
373
358
  SINGLETON_ALIAS( rb_mPG, "threadsafe?", "isthreadsafe" );
374
359
 
375
- rb_define_singleton_method( rb_mPG, "init_openssl", pg_s_init_openssl, 2 );
376
- 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 );
377
362
 
378
363
 
379
364
  /****** PG::Connection CLASS CONSTANTS: Connection Status ******/
@@ -679,11 +664,12 @@ Init_pg_ext(void)
679
664
  rb_define_const(rb_mPGconstants, "INVALID_OID", INT2FIX(InvalidOid));
680
665
  rb_define_const(rb_mPGconstants, "InvalidOid", INT2FIX(InvalidOid));
681
666
 
667
+ /* PostgreSQL compiled in default port */
668
+ rb_define_const(rb_mPGconstants, "DEF_PGPORT", INT2FIX(DEF_PGPORT));
669
+
682
670
  /* Add the constants to the toplevel namespace */
683
671
  rb_include_module( rb_mPG, rb_mPGconstants );
684
672
 
685
- enc_pg2ruby = st_init_numtable();
686
-
687
673
  /* Initialize the main extension classes */
688
674
  init_pg_connection();
689
675
  init_pg_result();
@@ -704,4 +690,3 @@ Init_pg_ext(void)
704
690
  init_pg_recordcoder();
705
691
  init_pg_tuple();
706
692
  }
707
-
data/ext/pg.h CHANGED
@@ -76,10 +76,6 @@ typedef long suseconds_t;
76
76
  #define PG_MAX_COLUMNS 4000
77
77
  #endif
78
78
 
79
- #ifndef RARRAY_AREF
80
- #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
81
- #endif
82
-
83
79
  #ifdef HAVE_RB_GC_MARK_MOVABLE
84
80
  #define pg_compact_callback(x) (x)
85
81
  #define pg_gc_location(x) x = rb_gc_location(x)
@@ -89,6 +85,12 @@ typedef long suseconds_t;
89
85
  #define pg_gc_location(x) UNUSED(x)
90
86
  #endif
91
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
92
94
  #define PG_ENC_IDX_BITS 28
93
95
 
94
96
  /* The data behind each PG::Connection object */
@@ -97,6 +99,9 @@ typedef struct {
97
99
 
98
100
  /* Cached IO object for the socket descriptor */
99
101
  VALUE socket_io;
102
+ /* function pointers of the original libpq notice receivers */
103
+ PQnoticeReceiver default_notice_receiver;
104
+ PQnoticeProcessor default_notice_processor;
100
105
  /* Proc object that receives notices as PG::Result objects */
101
106
  VALUE notice_receiver;
102
107
  /* Proc object that receives notices as String objects */
@@ -314,7 +319,7 @@ int pg_coder_enc_to_s _(( t_pg_coder*, VALUE, c
314
319
  int pg_text_enc_identifier _(( t_pg_coder*, VALUE, char *, VALUE *, int));
315
320
  t_pg_coder_enc_func pg_coder_enc_func _(( t_pg_coder* ));
316
321
  t_pg_coder_dec_func pg_coder_dec_func _(( t_pg_coder*, int ));
317
- void pg_define_coder _(( const char *, void *, VALUE, VALUE ));
322
+ VALUE pg_define_coder _(( const char *, void *, VALUE, VALUE ));
318
323
  VALUE pg_obj_to_i _(( VALUE ));
319
324
  VALUE pg_tmbc_allocate _(( void ));
320
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
  *
@@ -209,6 +287,7 @@ 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
  *
@@ -141,6 +353,10 @@ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermed
141
353
  void
142
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(void)
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