yugabytedb-ysql 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +42 -0
  3. data/.gems +6 -0
  4. data/.gemtest +0 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +143 -0
  7. data/.gitignore +24 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/BSDL +22 -0
  15. data/Contributors.rdoc +46 -0
  16. data/Gemfile +18 -0
  17. data/History.md +901 -0
  18. data/LICENSE +56 -0
  19. data/Manifest.txt +73 -0
  20. data/POSTGRES +23 -0
  21. data/README-OS_X.rdoc +68 -0
  22. data/README-Windows.rdoc +56 -0
  23. data/README.ja.md +302 -0
  24. data/README.md +381 -0
  25. data/Rakefile +118 -0
  26. data/Rakefile.cross +299 -0
  27. data/certs/ged.pem +24 -0
  28. data/certs/kanis@comcard.de.pem +20 -0
  29. data/certs/larskanis-2022.pem +26 -0
  30. data/certs/larskanis-2023.pem +24 -0
  31. data/certs/larskanis-2024.pem +24 -0
  32. data/ext/errorcodes.def +1044 -0
  33. data/ext/errorcodes.rb +45 -0
  34. data/ext/errorcodes.txt +497 -0
  35. data/ext/extconf.rb +174 -0
  36. data/ext/gvl_wrappers.c +21 -0
  37. data/ext/gvl_wrappers.h +264 -0
  38. data/ext/pg.c +692 -0
  39. data/ext/pg.h +392 -0
  40. data/ext/pg_binary_decoder.c +308 -0
  41. data/ext/pg_binary_encoder.c +387 -0
  42. data/ext/pg_coder.c +624 -0
  43. data/ext/pg_connection.c +4681 -0
  44. data/ext/pg_copy_coder.c +917 -0
  45. data/ext/pg_errors.c +95 -0
  46. data/ext/pg_record_coder.c +522 -0
  47. data/ext/pg_result.c +1766 -0
  48. data/ext/pg_text_decoder.c +1005 -0
  49. data/ext/pg_text_encoder.c +827 -0
  50. data/ext/pg_tuple.c +572 -0
  51. data/ext/pg_type_map.c +200 -0
  52. data/ext/pg_type_map_all_strings.c +130 -0
  53. data/ext/pg_type_map_by_class.c +271 -0
  54. data/ext/pg_type_map_by_column.c +355 -0
  55. data/ext/pg_type_map_by_mri_type.c +313 -0
  56. data/ext/pg_type_map_by_oid.c +388 -0
  57. data/ext/pg_type_map_in_ruby.c +333 -0
  58. data/ext/pg_util.c +149 -0
  59. data/ext/pg_util.h +65 -0
  60. data/ext/vc/pg.sln +26 -0
  61. data/ext/vc/pg_18/pg.vcproj +216 -0
  62. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  63. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  64. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  65. data/lib/pg/basic_type_map_for_results.rb +104 -0
  66. data/lib/pg/basic_type_registry.rb +303 -0
  67. data/lib/pg/binary_decoder/date.rb +9 -0
  68. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  69. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  70. data/lib/pg/coder.rb +106 -0
  71. data/lib/pg/connection.rb +997 -0
  72. data/lib/pg/exceptions.rb +25 -0
  73. data/lib/pg/load_balance_service.rb +406 -0
  74. data/lib/pg/result.rb +43 -0
  75. data/lib/pg/text_decoder/date.rb +18 -0
  76. data/lib/pg/text_decoder/inet.rb +9 -0
  77. data/lib/pg/text_decoder/json.rb +14 -0
  78. data/lib/pg/text_decoder/numeric.rb +9 -0
  79. data/lib/pg/text_decoder/timestamp.rb +30 -0
  80. data/lib/pg/text_encoder/date.rb +12 -0
  81. data/lib/pg/text_encoder/inet.rb +28 -0
  82. data/lib/pg/text_encoder/json.rb +14 -0
  83. data/lib/pg/text_encoder/numeric.rb +9 -0
  84. data/lib/pg/text_encoder/timestamp.rb +24 -0
  85. data/lib/pg/tuple.rb +30 -0
  86. data/lib/pg/type_map_by_column.rb +16 -0
  87. data/lib/pg/version.rb +5 -0
  88. data/lib/ysql.rb +130 -0
  89. data/misc/openssl-pg-segfault.rb +31 -0
  90. data/misc/postgres/History.txt +9 -0
  91. data/misc/postgres/Manifest.txt +5 -0
  92. data/misc/postgres/README.txt +21 -0
  93. data/misc/postgres/Rakefile +21 -0
  94. data/misc/postgres/lib/postgres.rb +16 -0
  95. data/misc/ruby-pg/History.txt +9 -0
  96. data/misc/ruby-pg/Manifest.txt +5 -0
  97. data/misc/ruby-pg/README.txt +21 -0
  98. data/misc/ruby-pg/Rakefile +21 -0
  99. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  100. data/rakelib/task_extension.rb +46 -0
  101. data/sample/array_insert.rb +20 -0
  102. data/sample/async_api.rb +102 -0
  103. data/sample/async_copyto.rb +39 -0
  104. data/sample/async_mixed.rb +56 -0
  105. data/sample/check_conn.rb +21 -0
  106. data/sample/copydata.rb +71 -0
  107. data/sample/copyfrom.rb +81 -0
  108. data/sample/copyto.rb +19 -0
  109. data/sample/cursor.rb +21 -0
  110. data/sample/disk_usage_report.rb +177 -0
  111. data/sample/issue-119.rb +94 -0
  112. data/sample/losample.rb +69 -0
  113. data/sample/minimal-testcase.rb +17 -0
  114. data/sample/notify_wait.rb +72 -0
  115. data/sample/pg_statistics.rb +285 -0
  116. data/sample/replication_monitor.rb +222 -0
  117. data/sample/test_binary_values.rb +33 -0
  118. data/sample/wal_shipper.rb +434 -0
  119. data/sample/warehouse_partitions.rb +311 -0
  120. data/yugabytedb-ysql.gemspec +33 -0
  121. metadata +232 -0
@@ -0,0 +1,355 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ static VALUE rb_cTypeMapByColumn;
10
+ static ID s_id_decode;
11
+ static ID s_id_encode;
12
+
13
+ static VALUE pg_tmbc_s_allocate( VALUE klass );
14
+
15
+ static VALUE
16
+ pg_tmbc_fit_to_result( VALUE self, VALUE result )
17
+ {
18
+ int nfields;
19
+ t_tmbc *this = RTYPEDDATA_DATA( self );
20
+ t_typemap *default_tm;
21
+ VALUE sub_typemap;
22
+
23
+ nfields = PQnfields( pgresult_get(result) );
24
+ if ( this->nfields != nfields ) {
25
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
26
+ nfields, this->nfields );
27
+ }
28
+
29
+ /* Ensure that the default type map fits equally. */
30
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
31
+ sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
32
+
33
+ /* Did the default type return the same object ? */
34
+ if( sub_typemap == this->typemap.default_typemap ){
35
+ return self;
36
+ } else {
37
+ /* Our default type map built a new object, so we need to propagate it
38
+ * and build a copy of this type map and set it as default there.. */
39
+ VALUE new_typemap = pg_tmbc_s_allocate( rb_cTypeMapByColumn );
40
+ size_t struct_size = sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields;
41
+ t_tmbc *p_new_typemap = (t_tmbc *)xmalloc(struct_size);
42
+
43
+ memcpy( p_new_typemap, this, struct_size );
44
+ p_new_typemap->typemap.default_typemap = sub_typemap;
45
+ RTYPEDDATA_DATA(new_typemap) = p_new_typemap;
46
+ return new_typemap;
47
+ }
48
+ }
49
+
50
+ static VALUE
51
+ pg_tmbc_fit_to_query( VALUE self, VALUE params )
52
+ {
53
+ int nfields;
54
+ t_tmbc *this = RTYPEDDATA_DATA( self );
55
+ t_typemap *default_tm;
56
+
57
+ nfields = (int)RARRAY_LEN( params );
58
+ if ( this->nfields != nfields ) {
59
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
60
+ nfields, this->nfields );
61
+ }
62
+
63
+ /* Ensure that the default type map fits equally. */
64
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
65
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
66
+
67
+ return self;
68
+ }
69
+
70
+ static int
71
+ pg_tmbc_fit_to_copy_get( VALUE self )
72
+ {
73
+ t_tmbc *this = RTYPEDDATA_DATA( self );
74
+
75
+ /* Ensure that the default type map fits equally. */
76
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
77
+ default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
78
+
79
+ return this->nfields;
80
+ }
81
+
82
+
83
+ VALUE
84
+ pg_tmbc_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
85
+ {
86
+ t_pg_coder *p_coder = NULL;
87
+ t_pg_result *p_result = pgresult_get_this(result);
88
+ t_tmbc *this = (t_tmbc *) p_typemap;
89
+ t_typemap *default_tm;
90
+
91
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
92
+ return Qnil;
93
+ }
94
+
95
+ p_coder = this->convs[field].cconv;
96
+
97
+ if( p_coder ){
98
+ char * val = PQgetvalue( p_result->pgresult, tuple, field );
99
+ int len = PQgetlength( p_result->pgresult, tuple, field );
100
+
101
+ if( p_coder->dec_func ){
102
+ return p_coder->dec_func(p_coder, val, len, tuple, field, p_result->enc_idx);
103
+ } else {
104
+ t_pg_coder_dec_func dec_func;
105
+ dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
106
+ return dec_func(p_coder, val, len, tuple, field, p_result->enc_idx);
107
+ }
108
+ }
109
+
110
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
111
+ return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
112
+ }
113
+
114
+ static t_pg_coder *
115
+ pg_tmbc_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
116
+ {
117
+ t_tmbc *this = (t_tmbc *) p_typemap;
118
+
119
+ /* Number of fields were already checked in pg_tmbc_fit_to_query() */
120
+ t_pg_coder *p_coder = this->convs[field].cconv;
121
+
122
+ if( !p_coder ){
123
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
124
+ return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
125
+ }
126
+
127
+ return p_coder;
128
+ }
129
+
130
+ static VALUE
131
+ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
132
+ {
133
+ t_tmbc *this = (t_tmbc *) p_typemap;
134
+ t_pg_coder *p_coder;
135
+ t_pg_coder_dec_func dec_func;
136
+
137
+ if ( fieldno >= this->nfields || fieldno < 0 ) {
138
+ rb_raise( rb_eArgError, "number of copy fields (%d) exceeds number of mapped columns (%d)",
139
+ fieldno, this->nfields );
140
+ }
141
+
142
+ p_coder = this->convs[fieldno].cconv;
143
+
144
+ if( !p_coder ){
145
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
146
+ return default_tm->funcs.typecast_copy_get( default_tm, field_str, fieldno, format, enc_idx );
147
+ }
148
+
149
+ dec_func = pg_coder_dec_func( p_coder, format );
150
+
151
+ /* Is it a pure String conversion? Then we can directly send field_str to the user. */
152
+ if( dec_func == pg_text_dec_string ){
153
+ rb_str_modify(field_str);
154
+ PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
155
+ return field_str;
156
+ }
157
+ if( dec_func == pg_bin_dec_bytea ){
158
+ rb_str_modify(field_str);
159
+ PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
160
+ return field_str;
161
+ }
162
+
163
+ return dec_func( p_coder, RSTRING_PTR(field_str), RSTRING_LENINT(field_str), 0, fieldno, enc_idx );
164
+ }
165
+
166
+ const struct pg_typemap_funcs pg_tmbc_funcs = {
167
+ pg_tmbc_fit_to_result,
168
+ pg_tmbc_fit_to_query,
169
+ pg_tmbc_fit_to_copy_get,
170
+ pg_tmbc_result_value,
171
+ pg_tmbc_typecast_query_param,
172
+ pg_tmbc_typecast_copy_get
173
+ };
174
+
175
+ static void
176
+ pg_tmbc_mark( void *_this )
177
+ {
178
+ t_tmbc *this = (t_tmbc *)_this;
179
+ int i;
180
+
181
+ /* allocated but not initialized ? */
182
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
183
+
184
+ pg_typemap_mark(&this->typemap);
185
+ for( i=0; i<this->nfields; i++){
186
+ t_pg_coder *p_coder = this->convs[i].cconv;
187
+ if( p_coder )
188
+ rb_gc_mark_movable(p_coder->coder_obj);
189
+ }
190
+ }
191
+
192
+ static size_t
193
+ pg_tmbc_memsize( const void *_this )
194
+ {
195
+ const t_tmbc *this = (const t_tmbc *)_this;
196
+ return sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * this->nfields;
197
+ }
198
+
199
+ static void
200
+ pg_tmbc_compact( void *_this )
201
+ {
202
+ t_tmbc *this = (t_tmbc *)_this;
203
+ int i;
204
+
205
+ /* allocated but not initialized ? */
206
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
207
+
208
+ pg_typemap_compact(&this->typemap);
209
+ for( i=0; i<this->nfields; i++){
210
+ t_pg_coder *p_coder = this->convs[i].cconv;
211
+ if( p_coder )
212
+ pg_gc_location(p_coder->coder_obj);
213
+ }
214
+ }
215
+
216
+ static void
217
+ pg_tmbc_free( void *_this )
218
+ {
219
+ t_tmbc *this = (t_tmbc *)_this;
220
+ /* allocated but not initialized ? */
221
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
222
+ xfree( this );
223
+ }
224
+
225
+ static const rb_data_type_t pg_tmbc_type = {
226
+ "PG::TypeMapByColumn",
227
+ {
228
+ pg_tmbc_mark,
229
+ pg_tmbc_free,
230
+ pg_tmbc_memsize,
231
+ pg_compact_callback(pg_tmbc_compact),
232
+ },
233
+ &pg_typemap_type,
234
+ 0,
235
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
236
+ };
237
+
238
+ static VALUE
239
+ pg_tmbc_s_allocate( VALUE klass )
240
+ {
241
+ /* Use pg_typemap_funcs as interim struct until #initialize is called. */
242
+ return TypedData_Wrap_Struct( klass, &pg_tmbc_type, (t_tmbc *)&pg_typemap_funcs );
243
+ }
244
+
245
+ VALUE
246
+ pg_tmbc_allocate(void)
247
+ {
248
+ return pg_tmbc_s_allocate(rb_cTypeMapByColumn);
249
+ }
250
+
251
+ /*
252
+ * call-seq:
253
+ * PG::TypeMapByColumn.new( coders )
254
+ *
255
+ * Builds a new type map and assigns a list of coders for the given column.
256
+ * +coders+ must be an Array of PG::Coder objects or +nil+ values.
257
+ * The length of the Array corresponds to
258
+ * the number of columns or bind parameters this type map is usable for.
259
+ *
260
+ * A +nil+ value will forward the given field to the #default_type_map .
261
+ */
262
+ static VALUE
263
+ pg_tmbc_init(VALUE self, VALUE conv_ary)
264
+ {
265
+ long i;
266
+ t_tmbc *this;
267
+ int conv_ary_len;
268
+
269
+ rb_check_frozen(self);
270
+ Check_Type(conv_ary, T_ARRAY);
271
+ conv_ary_len = RARRAY_LENINT(conv_ary);
272
+ this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
273
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
274
+ this->nfields = 0;
275
+ this->typemap.funcs = pg_tmbc_funcs;
276
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
277
+ RTYPEDDATA_DATA(self) = this;
278
+
279
+ for(i=0; i<conv_ary_len; i++)
280
+ {
281
+ VALUE obj = rb_ary_entry(conv_ary, i);
282
+
283
+ if( obj == Qnil ){
284
+ /* no type cast */
285
+ this->convs[i].cconv = NULL;
286
+ } else {
287
+ t_pg_coder *p_coder;
288
+ /* Check argument type and store the coder pointer */
289
+ TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, p_coder);
290
+ RB_OBJ_WRITTEN(self, Qnil, p_coder->coder_obj);
291
+ this->convs[i].cconv = p_coder;
292
+ }
293
+ }
294
+
295
+ this->nfields = conv_ary_len;
296
+
297
+ return self;
298
+ }
299
+
300
+ /*
301
+ * call-seq:
302
+ * typemap.coders -> Array
303
+ *
304
+ * Array of PG::Coder objects. The length of the Array corresponds to
305
+ * the number of columns or bind parameters this type map is usable for.
306
+ */
307
+ static VALUE
308
+ pg_tmbc_coders(VALUE self)
309
+ {
310
+ int i;
311
+ t_tmbc *this = RTYPEDDATA_DATA( self );
312
+ VALUE ary_coders = rb_ary_new();
313
+
314
+ for( i=0; i<this->nfields; i++){
315
+ t_pg_coder *conv = this->convs[i].cconv;
316
+ if( conv ) {
317
+ rb_ary_push( ary_coders, conv->coder_obj );
318
+ } else {
319
+ rb_ary_push( ary_coders, Qnil );
320
+ }
321
+ }
322
+
323
+ return rb_obj_freeze(ary_coders);
324
+ }
325
+
326
+ void
327
+ init_pg_type_map_by_column(void)
328
+ {
329
+ s_id_decode = rb_intern("decode");
330
+ s_id_encode = rb_intern("encode");
331
+
332
+ /*
333
+ * Document-class: PG::TypeMapByColumn < PG::TypeMap
334
+ *
335
+ * This type map casts values by a coder assigned per field/column.
336
+ *
337
+ * Each PG::TypeMapByColumn has a fixed list of either encoders or decoders,
338
+ * that is defined at TypeMapByColumn.new . A type map with encoders is usable for type casting
339
+ * query bind parameters and COPY data for PG::Connection#put_copy_data .
340
+ * A type map with decoders is usable for type casting of result values and
341
+ * COPY data from PG::Connection#get_copy_data .
342
+ *
343
+ * PG::TypeMapByColumn objects are in particular useful in conjunction with prepared statements,
344
+ * since they can be cached alongside with the statement handle.
345
+ *
346
+ * This type map strategy is also used internally by PG::TypeMapByOid, when the
347
+ * number of rows of a result set exceeds a given limit.
348
+ */
349
+ rb_cTypeMapByColumn = rb_define_class_under( rb_mPG, "TypeMapByColumn", rb_cTypeMap );
350
+ rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
351
+ rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
352
+ rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
353
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
354
+ rb_include_module( rb_cTypeMapByColumn, rb_mDefaultTypeMappable );
355
+ }
@@ -0,0 +1,313 @@
1
+ /*
2
+ * pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
3
+ * $Id$
4
+ *
5
+ * This type map can be used to select value encoders based on the MRI-internal
6
+ * value type code.
7
+ *
8
+ */
9
+
10
+ #include "pg.h"
11
+
12
+ static VALUE rb_cTypeMapByMriType;
13
+
14
+ #define FOR_EACH_MRI_TYPE(func) \
15
+ func(T_FIXNUM) \
16
+ func(T_TRUE) \
17
+ func(T_FALSE) \
18
+ func(T_FLOAT) \
19
+ func(T_BIGNUM) \
20
+ func(T_COMPLEX) \
21
+ func(T_RATIONAL) \
22
+ func(T_ARRAY) \
23
+ func(T_STRING) \
24
+ func(T_SYMBOL) \
25
+ func(T_OBJECT) \
26
+ func(T_CLASS) \
27
+ func(T_MODULE) \
28
+ func(T_REGEXP) \
29
+ func(T_HASH) \
30
+ func(T_STRUCT) \
31
+ func(T_FILE) \
32
+ func(T_DATA)
33
+
34
+ #define DECLARE_CODER(type) \
35
+ t_pg_coder *coder_##type; \
36
+ VALUE ask_##type; \
37
+ VALUE coder_obj_##type;
38
+
39
+ typedef struct {
40
+ t_typemap typemap;
41
+ struct pg_tmbmt_converter {
42
+ FOR_EACH_MRI_TYPE( DECLARE_CODER )
43
+ } coders;
44
+ } t_tmbmt;
45
+
46
+
47
+ #define CASE_AND_GET(type) \
48
+ case type: \
49
+ p_coder = this->coders.coder_##type; \
50
+ ask_for_coder = this->coders.ask_##type; \
51
+ break;
52
+
53
+ static t_pg_coder *
54
+ pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
55
+ {
56
+ t_tmbmt *this = (t_tmbmt *)p_typemap;
57
+ t_pg_coder *p_coder;
58
+ VALUE ask_for_coder;
59
+
60
+ switch(TYPE(param_value)){
61
+ FOR_EACH_MRI_TYPE( CASE_AND_GET )
62
+ default:
63
+ /* unknown MRI type */
64
+ p_coder = NULL;
65
+ ask_for_coder = Qnil;
66
+ }
67
+
68
+ if( !NIL_P(ask_for_coder) ){
69
+ /* No static Coder object, but proc/method given to ask for the Coder to use. */
70
+ VALUE obj;
71
+
72
+ obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
73
+
74
+ /* Check argument type and store the coder pointer */
75
+ TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, p_coder);
76
+ }
77
+
78
+ if( !p_coder ){
79
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
80
+ return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
81
+ }
82
+
83
+ return p_coder;
84
+ }
85
+
86
+ static VALUE
87
+ pg_tmbmt_fit_to_query( VALUE self, VALUE params )
88
+ {
89
+ t_tmbmt *this = (t_tmbmt *)RTYPEDDATA_DATA(self);
90
+ /* Nothing to check at this typemap, but ensure that the default type map fits. */
91
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
92
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
93
+ return self;
94
+ }
95
+
96
+ #define GC_MARK_AS_USED(type) \
97
+ rb_gc_mark_movable( this->coders.ask_##type ); \
98
+ rb_gc_mark_movable( this->coders.coder_obj_##type );
99
+
100
+ static void
101
+ pg_tmbmt_mark( void *_this )
102
+ {
103
+ t_tmbmt *this = (t_tmbmt *)_this;
104
+ pg_typemap_mark(&this->typemap);
105
+ FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
106
+ }
107
+
108
+ static size_t
109
+ pg_tmbmt_memsize( const void *_this )
110
+ {
111
+ const t_tmbmt *this = (const t_tmbmt *)_this;
112
+ return sizeof(*this);
113
+ }
114
+
115
+ #define GC_COMPACT(type) \
116
+ pg_gc_location( this->coders.ask_##type ); \
117
+ pg_gc_location( this->coders.coder_obj_##type );
118
+
119
+ static void
120
+ pg_tmbmt_compact( void *_this )
121
+ {
122
+ t_tmbmt *this = (t_tmbmt *)_this;
123
+ pg_typemap_compact(&this->typemap);
124
+ FOR_EACH_MRI_TYPE( GC_COMPACT );
125
+ }
126
+
127
+ static const rb_data_type_t pg_tmbmt_type = {
128
+ "PG::TypeMapByMriType",
129
+ {
130
+ pg_tmbmt_mark,
131
+ RUBY_TYPED_DEFAULT_FREE,
132
+ pg_tmbmt_memsize,
133
+ pg_compact_callback(pg_tmbmt_compact),
134
+ },
135
+ &pg_typemap_type,
136
+ 0,
137
+ RUBY_TYPED_FREE_IMMEDIATELY,
138
+ };
139
+
140
+ #define INIT_VARIABLES(type) \
141
+ this->coders.coder_##type = NULL; \
142
+ this->coders.ask_##type = Qnil; \
143
+ this->coders.coder_obj_##type = Qnil;
144
+
145
+ static VALUE
146
+ pg_tmbmt_s_allocate( VALUE klass )
147
+ {
148
+ t_tmbmt *this;
149
+ VALUE self;
150
+
151
+ self = TypedData_Make_Struct( klass, t_tmbmt, &pg_tmbmt_type, this );
152
+ this->typemap.funcs.fit_to_result = pg_typemap_fit_to_result;
153
+ this->typemap.funcs.fit_to_query = pg_tmbmt_fit_to_query;
154
+ this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
155
+ this->typemap.funcs.typecast_result_value = pg_typemap_result_value;
156
+ this->typemap.funcs.typecast_query_param = pg_tmbmt_typecast_query_param;
157
+ this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
158
+ this->typemap.default_typemap = pg_typemap_all_strings;
159
+
160
+ FOR_EACH_MRI_TYPE( INIT_VARIABLES );
161
+
162
+ return self;
163
+ }
164
+
165
+ #define COMPARE_AND_ASSIGN(type) \
166
+ else if(!strcmp(p_mri_type, #type)){ \
167
+ this->coders.coder_obj_##type = coder; \
168
+ if(NIL_P(coder)){ \
169
+ this->coders.coder_##type = NULL; \
170
+ this->coders.ask_##type = Qnil; \
171
+ }else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
172
+ TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, this->coders.coder_##type); \
173
+ this->coders.ask_##type = Qnil; \
174
+ }else if(RB_TYPE_P(coder, T_SYMBOL)){ \
175
+ this->coders.coder_##type = NULL; \
176
+ this->coders.ask_##type = rb_obj_method( self, coder ); \
177
+ }else{ \
178
+ this->coders.coder_##type = NULL; \
179
+ this->coders.ask_##type = coder; \
180
+ } \
181
+ }
182
+
183
+ /*
184
+ * call-seq:
185
+ * typemap.[mri_type] = coder
186
+ *
187
+ * Assigns a new PG::Coder object to the type map. The encoder
188
+ * is registered for type casts of the given +mri_type+ .
189
+ *
190
+ * +coder+ can be one of the following:
191
+ * * +nil+ - Values are forwarded to the #default_type_map .
192
+ * * a PG::Coder - Values are encoded by the given encoder
193
+ * * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
194
+ * It must return a PG::Coder.
195
+ * * a Proc - The Proc object is called for each value. It must return a PG::Coder.
196
+ *
197
+ * +mri_type+ must be one of the following strings:
198
+ * * +T_FIXNUM+
199
+ * * +T_TRUE+
200
+ * * +T_FALSE+
201
+ * * +T_FLOAT+
202
+ * * +T_BIGNUM+
203
+ * * +T_COMPLEX+
204
+ * * +T_RATIONAL+
205
+ * * +T_ARRAY+
206
+ * * +T_STRING+
207
+ * * +T_SYMBOL+
208
+ * * +T_OBJECT+
209
+ * * +T_CLASS+
210
+ * * +T_MODULE+
211
+ * * +T_REGEXP+
212
+ * * +T_HASH+
213
+ * * +T_STRUCT+
214
+ * * +T_FILE+
215
+ * * +T_DATA+
216
+ */
217
+ static VALUE
218
+ pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
219
+ {
220
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
221
+ char *p_mri_type;
222
+
223
+ p_mri_type = StringValueCStr(mri_type);
224
+
225
+ if(0){}
226
+ FOR_EACH_MRI_TYPE( COMPARE_AND_ASSIGN )
227
+ else{
228
+ VALUE mri_type_inspect = rb_inspect( mri_type );
229
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
230
+ }
231
+
232
+ return self;
233
+ }
234
+
235
+ #define COMPARE_AND_GET(type) \
236
+ else if(!strcmp(p_mri_type, #type)){ \
237
+ coder = this->coders.coder_obj_##type; \
238
+ }
239
+
240
+ /*
241
+ * call-seq:
242
+ * typemap.[mri_type] -> coder
243
+ *
244
+ * Returns the encoder object for the given +mri_type+
245
+ *
246
+ * See #[]= for allowed +mri_type+ .
247
+ */
248
+ static VALUE
249
+ pg_tmbmt_aref( VALUE self, VALUE mri_type )
250
+ {
251
+ VALUE coder;
252
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
253
+ char *p_mri_type;
254
+
255
+ p_mri_type = StringValueCStr(mri_type);
256
+
257
+ if(0){}
258
+ FOR_EACH_MRI_TYPE( COMPARE_AND_GET )
259
+ else{
260
+ VALUE mri_type_inspect = rb_inspect( mri_type );
261
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
262
+ }
263
+
264
+ return coder;
265
+ }
266
+
267
+ #define ADD_TO_HASH(type) \
268
+ rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.coder_obj_##type );
269
+
270
+
271
+ /*
272
+ * call-seq:
273
+ * typemap.coders -> Hash
274
+ *
275
+ * Returns all mri types and their assigned encoder object.
276
+ */
277
+ static VALUE
278
+ pg_tmbmt_coders( VALUE self )
279
+ {
280
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
281
+ VALUE hash_coders = rb_hash_new();
282
+
283
+ FOR_EACH_MRI_TYPE( ADD_TO_HASH );
284
+
285
+ return rb_obj_freeze(hash_coders);
286
+ }
287
+
288
+ void
289
+ init_pg_type_map_by_mri_type(void)
290
+ {
291
+ /*
292
+ * Document-class: PG::TypeMapByMriType < PG::TypeMap
293
+ *
294
+ * This type map casts values based on the Ruby object type code of the given value
295
+ * to be sent.
296
+ *
297
+ * This type map is usable for type casting query bind parameters and COPY data
298
+ * for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
299
+ * the #[]= method.
300
+ *
301
+ * _Note_ : This type map is not portable across Ruby implementations and is less flexible
302
+ * than PG::TypeMapByClass.
303
+ * It is kept only for performance comparisons, but PG::TypeMapByClass proved to be equally
304
+ * fast in almost all cases.
305
+ *
306
+ */
307
+ rb_cTypeMapByMriType = rb_define_class_under( rb_mPG, "TypeMapByMriType", rb_cTypeMap );
308
+ rb_define_alloc_func( rb_cTypeMapByMriType, pg_tmbmt_s_allocate );
309
+ rb_define_method( rb_cTypeMapByMriType, "[]=", pg_tmbmt_aset, 2 );
310
+ rb_define_method( rb_cTypeMapByMriType, "[]", pg_tmbmt_aref, 1 );
311
+ rb_define_method( rb_cTypeMapByMriType, "coders", pg_tmbmt_coders, 0 );
312
+ rb_include_module( rb_cTypeMapByMriType, rb_mDefaultTypeMappable );
313
+ }