pg 1.3.0.rc2-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.gemtest +0 -0
  6. data/.github/workflows/binary-gems.yml +85 -0
  7. data/.github/workflows/source-gem.yml +130 -0
  8. data/.gitignore +13 -0
  9. data/.hgsigs +34 -0
  10. data/.hgtags +41 -0
  11. data/.irbrc +23 -0
  12. data/.pryrc +23 -0
  13. data/.tm_properties +21 -0
  14. data/.travis.yml +49 -0
  15. data/BSDL +22 -0
  16. data/Contributors.rdoc +46 -0
  17. data/Gemfile +14 -0
  18. data/History.rdoc +648 -0
  19. data/LICENSE +56 -0
  20. data/Manifest.txt +72 -0
  21. data/POSTGRES +23 -0
  22. data/README-OS_X.rdoc +68 -0
  23. data/README-Windows.rdoc +56 -0
  24. data/README.ja.rdoc +13 -0
  25. data/README.rdoc +214 -0
  26. data/Rakefile +106 -0
  27. data/Rakefile.cross +300 -0
  28. data/certs/ged.pem +24 -0
  29. data/ext/errorcodes.def +1040 -0
  30. data/ext/errorcodes.rb +45 -0
  31. data/ext/errorcodes.txt +496 -0
  32. data/ext/extconf.rb +165 -0
  33. data/ext/gvl_wrappers.c +21 -0
  34. data/ext/gvl_wrappers.h +264 -0
  35. data/ext/pg.c +732 -0
  36. data/ext/pg.h +385 -0
  37. data/ext/pg_binary_decoder.c +229 -0
  38. data/ext/pg_binary_encoder.c +163 -0
  39. data/ext/pg_coder.c +615 -0
  40. data/ext/pg_connection.c +4415 -0
  41. data/ext/pg_copy_coder.c +628 -0
  42. data/ext/pg_errors.c +95 -0
  43. data/ext/pg_record_coder.c +519 -0
  44. data/ext/pg_result.c +1683 -0
  45. data/ext/pg_text_decoder.c +987 -0
  46. data/ext/pg_text_encoder.c +814 -0
  47. data/ext/pg_tuple.c +575 -0
  48. data/ext/pg_type_map.c +199 -0
  49. data/ext/pg_type_map_all_strings.c +129 -0
  50. data/ext/pg_type_map_by_class.c +269 -0
  51. data/ext/pg_type_map_by_column.c +349 -0
  52. data/ext/pg_type_map_by_mri_type.c +313 -0
  53. data/ext/pg_type_map_by_oid.c +385 -0
  54. data/ext/pg_type_map_in_ruby.c +330 -0
  55. data/ext/pg_util.c +149 -0
  56. data/ext/pg_util.h +65 -0
  57. data/ext/vc/pg.sln +26 -0
  58. data/ext/vc/pg_18/pg.vcproj +216 -0
  59. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  60. data/lib/3.1/pg_ext.so +0 -0
  61. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  62. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  63. data/lib/pg/basic_type_map_for_results.rb +81 -0
  64. data/lib/pg/basic_type_registry.rb +296 -0
  65. data/lib/pg/binary_decoder.rb +23 -0
  66. data/lib/pg/coder.rb +104 -0
  67. data/lib/pg/connection.rb +813 -0
  68. data/lib/pg/constants.rb +12 -0
  69. data/lib/pg/exceptions.rb +12 -0
  70. data/lib/pg/result.rb +43 -0
  71. data/lib/pg/text_decoder.rb +46 -0
  72. data/lib/pg/text_encoder.rb +59 -0
  73. data/lib/pg/tuple.rb +30 -0
  74. data/lib/pg/type_map_by_column.rb +16 -0
  75. data/lib/pg/version.rb +4 -0
  76. data/lib/pg.rb +87 -0
  77. data/lib/x64-mingw-ucrt/libpq.dll +0 -0
  78. data/misc/openssl-pg-segfault.rb +31 -0
  79. data/misc/postgres/History.txt +9 -0
  80. data/misc/postgres/Manifest.txt +5 -0
  81. data/misc/postgres/README.txt +21 -0
  82. data/misc/postgres/Rakefile +21 -0
  83. data/misc/postgres/lib/postgres.rb +16 -0
  84. data/misc/ruby-pg/History.txt +9 -0
  85. data/misc/ruby-pg/Manifest.txt +5 -0
  86. data/misc/ruby-pg/README.txt +21 -0
  87. data/misc/ruby-pg/Rakefile +21 -0
  88. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  89. data/pg.gemspec +32 -0
  90. data/sample/array_insert.rb +20 -0
  91. data/sample/async_api.rb +106 -0
  92. data/sample/async_copyto.rb +39 -0
  93. data/sample/async_mixed.rb +56 -0
  94. data/sample/check_conn.rb +21 -0
  95. data/sample/copydata.rb +71 -0
  96. data/sample/copyfrom.rb +81 -0
  97. data/sample/copyto.rb +19 -0
  98. data/sample/cursor.rb +21 -0
  99. data/sample/disk_usage_report.rb +177 -0
  100. data/sample/issue-119.rb +94 -0
  101. data/sample/losample.rb +69 -0
  102. data/sample/minimal-testcase.rb +17 -0
  103. data/sample/notify_wait.rb +72 -0
  104. data/sample/pg_statistics.rb +285 -0
  105. data/sample/replication_monitor.rb +222 -0
  106. data/sample/test_binary_values.rb +33 -0
  107. data/sample/wal_shipper.rb +434 -0
  108. data/sample/warehouse_partitions.rb +311 -0
  109. data.tar.gz.sig +0 -0
  110. metadata +188 -0
  111. metadata.gz.sig +0 -0
@@ -0,0 +1,385 @@
1
+ /*
2
+ * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ static VALUE rb_cTypeMapByOid;
10
+ static ID s_id_decode;
11
+
12
+ typedef struct {
13
+ t_typemap typemap;
14
+ int max_rows_for_online_lookup;
15
+
16
+ struct pg_tmbo_converter {
17
+ VALUE oid_to_coder;
18
+
19
+ struct pg_tmbo_oid_cache_entry {
20
+ Oid oid;
21
+ t_pg_coder *p_coder;
22
+ } cache_row[0x100];
23
+ } format[2];
24
+ } t_tmbo;
25
+
26
+ static VALUE pg_tmbo_s_allocate( VALUE klass );
27
+
28
+
29
+ /*
30
+ * We use the OID's minor 8 Bits as index to a 256 entry cache. This avoids full ruby hash lookups
31
+ * for each value in most cases.
32
+ */
33
+ #define CACHE_LOOKUP(this, form, oid) ( &this->format[(form)].cache_row[(oid) & 0xff] )
34
+
35
+ static t_pg_coder *
36
+ pg_tmbo_lookup_oid(t_tmbo *this, int format, Oid oid)
37
+ {
38
+ t_pg_coder *conv;
39
+ struct pg_tmbo_oid_cache_entry *p_ce;
40
+
41
+ p_ce = CACHE_LOOKUP(this, format, oid);
42
+
43
+ /* Has the entry the expected OID and is it a non empty entry? */
44
+ if( p_ce->oid == oid && (oid || p_ce->p_coder) ) {
45
+ conv = p_ce->p_coder;
46
+ } else {
47
+ VALUE obj = rb_hash_lookup( this->format[format].oid_to_coder, UINT2NUM( oid ));
48
+ /* obj must be nil or some kind of PG::Coder, this is checked at insertion */
49
+ conv = NIL_P(obj) ? NULL : RTYPEDDATA_DATA(obj);
50
+ /* Write the retrieved coder to the cache */
51
+ p_ce->oid = oid;
52
+ p_ce->p_coder = conv;
53
+ }
54
+ return conv;
55
+ }
56
+
57
+ /* Build a TypeMapByColumn that fits to the given result */
58
+ static VALUE
59
+ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
60
+ {
61
+ t_tmbc *p_colmap;
62
+ int i;
63
+ VALUE colmap;
64
+ int nfields = PQnfields( pgresult );
65
+
66
+ p_colmap = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields);
67
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
68
+ p_colmap->nfields = 0;
69
+ p_colmap->typemap.funcs = pg_tmbc_funcs;
70
+ p_colmap->typemap.default_typemap = pg_typemap_all_strings;
71
+
72
+ colmap = pg_tmbc_allocate();
73
+ RTYPEDDATA_DATA(colmap) = p_colmap;
74
+
75
+ for(i=0; i<nfields; i++)
76
+ {
77
+ int format = PQfformat(pgresult, i);
78
+
79
+ if( format < 0 || format > 1 )
80
+ rb_raise(rb_eArgError, "result field %d has unsupported format code %d", i+1, format);
81
+
82
+ p_colmap->convs[i].cconv = pg_tmbo_lookup_oid( this, format, PQftype(pgresult, i) );
83
+ }
84
+
85
+ p_colmap->nfields = nfields;
86
+
87
+ return colmap;
88
+ }
89
+
90
+ static VALUE
91
+ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
92
+ {
93
+ int format;
94
+ t_pg_coder *p_coder;
95
+ t_pg_result *p_result = pgresult_get_this(result);
96
+ t_tmbo *this = (t_tmbo*) p_typemap;
97
+ t_typemap *default_tm;
98
+
99
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
100
+ return Qnil;
101
+ }
102
+
103
+ format = PQfformat( p_result->pgresult, field );
104
+
105
+ if( format < 0 || format > 1 )
106
+ rb_raise(rb_eArgError, "result field %d has unsupported format code %d", field+1, format);
107
+
108
+ p_coder = pg_tmbo_lookup_oid( this, format, PQftype(p_result->pgresult, field) );
109
+ if( p_coder ){
110
+ char * val = PQgetvalue( p_result->pgresult, tuple, field );
111
+ int len = PQgetlength( p_result->pgresult, tuple, field );
112
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func( p_coder, format );
113
+ return dec_func( p_coder, val, len, tuple, field, p_result->enc_idx );
114
+ }
115
+
116
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
117
+ return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
118
+ }
119
+
120
+ static VALUE
121
+ pg_tmbo_fit_to_result( VALUE self, VALUE result )
122
+ {
123
+ t_tmbo *this = RTYPEDDATA_DATA( self );
124
+ PGresult *pgresult = pgresult_get( result );
125
+
126
+ /* Ensure that the default type map fits equally. */
127
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
128
+ VALUE sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
129
+
130
+ if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
131
+ /* Do a hash lookup for each result value in pg_tmbc_result_value() */
132
+
133
+ /* Did the default type return the same object ? */
134
+ if( sub_typemap == this->typemap.default_typemap ){
135
+ return self;
136
+ } else {
137
+ /* The default type map built a new object, so we need to propagate it
138
+ * and build a copy of this type map. */
139
+ VALUE new_typemap = pg_tmbo_s_allocate( rb_cTypeMapByOid );
140
+ t_tmbo *p_new_typemap = RTYPEDDATA_DATA(new_typemap);
141
+ *p_new_typemap = *this;
142
+ p_new_typemap->typemap.default_typemap = sub_typemap;
143
+ return new_typemap;
144
+ }
145
+ }else{
146
+ /* Build a new TypeMapByColumn that fits to the given result and
147
+ * uses a fast array lookup.
148
+ */
149
+ VALUE new_typemap = pg_tmbo_build_type_map_for_result2( this, pgresult );
150
+ t_tmbo *p_new_typemap = RTYPEDDATA_DATA(new_typemap);
151
+ p_new_typemap->typemap.default_typemap = sub_typemap;
152
+ return new_typemap;
153
+ }
154
+ }
155
+
156
+ static void
157
+ pg_tmbo_mark( void *_this )
158
+ {
159
+ t_tmbo *this = (t_tmbo *)_this;
160
+ int i;
161
+
162
+ pg_typemap_mark(&this->typemap);
163
+ for( i=0; i<2; i++){
164
+ rb_gc_mark_movable(this->format[i].oid_to_coder);
165
+ }
166
+ }
167
+
168
+ static size_t
169
+ pg_tmbo_memsize( const void *_this )
170
+ {
171
+ const t_tmbo *this = (const t_tmbo *)_this;
172
+ return sizeof(*this);
173
+ }
174
+
175
+ static void
176
+ pg_tmbo_compact( void *_this )
177
+ {
178
+ t_tmbo *this = (t_tmbo *)_this;
179
+ int i;
180
+
181
+ pg_typemap_compact(&this->typemap);
182
+ for( i=0; i<2; i++){
183
+ pg_gc_location(this->format[i].oid_to_coder);
184
+ }
185
+ }
186
+
187
+ static const rb_data_type_t pg_tmbo_type = {
188
+ "PG::TypeMapByOid",
189
+ {
190
+ pg_tmbo_mark,
191
+ RUBY_TYPED_DEFAULT_FREE,
192
+ pg_tmbo_memsize,
193
+ pg_compact_callback(pg_tmbo_compact),
194
+ },
195
+ &pg_typemap_type,
196
+ 0,
197
+ RUBY_TYPED_FREE_IMMEDIATELY,
198
+ };
199
+
200
+ static VALUE
201
+ pg_tmbo_s_allocate( VALUE klass )
202
+ {
203
+ t_tmbo *this;
204
+ VALUE self;
205
+ int i;
206
+
207
+ self = TypedData_Make_Struct( klass, t_tmbo, &pg_tmbo_type, this );
208
+
209
+ this->typemap.funcs.fit_to_result = pg_tmbo_fit_to_result;
210
+ this->typemap.funcs.fit_to_query = pg_typemap_fit_to_query;
211
+ this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
212
+ this->typemap.funcs.typecast_result_value = pg_tmbo_result_value;
213
+ this->typemap.funcs.typecast_query_param = pg_typemap_typecast_query_param;
214
+ this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
215
+ this->typemap.default_typemap = pg_typemap_all_strings;
216
+ this->max_rows_for_online_lookup = 10;
217
+
218
+ for( i=0; i<2; i++){
219
+ this->format[i].oid_to_coder = rb_hash_new();
220
+ }
221
+
222
+ return self;
223
+ }
224
+
225
+ /*
226
+ * call-seq:
227
+ * typemap.add_coder( coder )
228
+ *
229
+ * Assigns a new PG::Coder object to the type map. The decoder
230
+ * is registered for type casts based on it's PG::Coder#oid and
231
+ * PG::Coder#format attributes.
232
+ *
233
+ * Later changes of the oid or format code within the coder object
234
+ * will have no effect to the type map.
235
+ *
236
+ */
237
+ static VALUE
238
+ pg_tmbo_add_coder( VALUE self, VALUE coder )
239
+ {
240
+ VALUE hash;
241
+ t_tmbo *this = RTYPEDDATA_DATA( self );
242
+ t_pg_coder *p_coder;
243
+ struct pg_tmbo_oid_cache_entry *p_ce;
244
+
245
+ TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, p_coder);
246
+
247
+ if( p_coder->format < 0 || p_coder->format > 1 )
248
+ rb_raise(rb_eArgError, "invalid format code %d", p_coder->format);
249
+
250
+ /* Update cache entry */
251
+ p_ce = CACHE_LOOKUP(this, p_coder->format, p_coder->oid);
252
+ p_ce->oid = p_coder->oid;
253
+ p_ce->p_coder = p_coder;
254
+ /* Write coder into the hash of the given format */
255
+ hash = this->format[p_coder->format].oid_to_coder;
256
+ rb_hash_aset( hash, UINT2NUM(p_coder->oid), coder);
257
+
258
+ return self;
259
+ }
260
+
261
+ /*
262
+ * call-seq:
263
+ * typemap.rm_coder( format, oid )
264
+ *
265
+ * Removes a PG::Coder object from the type map based on the given
266
+ * oid and format codes.
267
+ *
268
+ * Returns the removed coder object.
269
+ */
270
+ static VALUE
271
+ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
272
+ {
273
+ VALUE hash;
274
+ VALUE coder;
275
+ t_tmbo *this = RTYPEDDATA_DATA( self );
276
+ int i_format = NUM2INT(format);
277
+ struct pg_tmbo_oid_cache_entry *p_ce;
278
+
279
+ if( i_format < 0 || i_format > 1 )
280
+ rb_raise(rb_eArgError, "invalid format code %d", i_format);
281
+
282
+ /* Mark the cache entry as empty */
283
+ p_ce = CACHE_LOOKUP(this, i_format, NUM2UINT(oid));
284
+ p_ce->oid = 0;
285
+ p_ce->p_coder = NULL;
286
+ hash = this->format[i_format].oid_to_coder;
287
+ coder = rb_hash_delete( hash, oid );
288
+
289
+ return coder;
290
+ }
291
+
292
+ /*
293
+ * call-seq:
294
+ * typemap.coders -> Array
295
+ *
296
+ * Array of all assigned PG::Coder objects.
297
+ */
298
+ static VALUE
299
+ pg_tmbo_coders( VALUE self )
300
+ {
301
+ t_tmbo *this = RTYPEDDATA_DATA( self );
302
+
303
+ return rb_ary_concat(
304
+ rb_funcall(this->format[0].oid_to_coder, rb_intern("values"), 0),
305
+ rb_funcall(this->format[1].oid_to_coder, rb_intern("values"), 0));
306
+ }
307
+
308
+ /*
309
+ * call-seq:
310
+ * typemap.max_rows_for_online_lookup = number
311
+ *
312
+ * Threshold for doing Hash lookups versus creation of a dedicated PG::TypeMapByColumn.
313
+ * The type map will do Hash lookups for each result value, if the number of rows
314
+ * is below or equal +number+.
315
+ *
316
+ */
317
+ static VALUE
318
+ pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
319
+ {
320
+ t_tmbo *this = RTYPEDDATA_DATA( self );
321
+ this->max_rows_for_online_lookup = NUM2INT(value);
322
+ return value;
323
+ }
324
+
325
+ /*
326
+ * call-seq:
327
+ * typemap.max_rows_for_online_lookup -> Integer
328
+ */
329
+ static VALUE
330
+ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
331
+ {
332
+ t_tmbo *this = RTYPEDDATA_DATA( self );
333
+ return INT2NUM(this->max_rows_for_online_lookup);
334
+ }
335
+
336
+ /*
337
+ * call-seq:
338
+ * typemap.build_column_map( result )
339
+ *
340
+ * This builds a PG::TypeMapByColumn that fits to the given PG::Result object
341
+ * based on it's type OIDs.
342
+ *
343
+ */
344
+ static VALUE
345
+ pg_tmbo_build_column_map( VALUE self, VALUE result )
346
+ {
347
+ t_tmbo *this = RTYPEDDATA_DATA( self );
348
+
349
+ if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
350
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
351
+ rb_obj_classname( result ) );
352
+ }
353
+
354
+ return pg_tmbo_build_type_map_for_result2( this, pgresult_get(result) );
355
+ }
356
+
357
+
358
+ void
359
+ init_pg_type_map_by_oid()
360
+ {
361
+ s_id_decode = rb_intern("decode");
362
+
363
+ /*
364
+ * Document-class: PG::TypeMapByOid < PG::TypeMap
365
+ *
366
+ * This type map casts values based on the type OID of the given column
367
+ * in the result set.
368
+ *
369
+ * This type map is only suitable to cast values from PG::Result objects.
370
+ * Therefore only decoders might be assigned by the #add_coder method.
371
+ *
372
+ * Fields with no match to any of the registered type OID / format combination
373
+ * are forwarded to the #default_type_map .
374
+ */
375
+ rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
376
+ rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
377
+ rb_define_method( rb_cTypeMapByOid, "add_coder", pg_tmbo_add_coder, 1 );
378
+ rb_define_method( rb_cTypeMapByOid, "rm_coder", pg_tmbo_rm_coder, 2 );
379
+ rb_define_method( rb_cTypeMapByOid, "coders", pg_tmbo_coders, 0 );
380
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
381
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
382
+ rb_define_method( rb_cTypeMapByOid, "build_column_map", pg_tmbo_build_column_map, 1 );
383
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
384
+ rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
385
+ }
@@ -0,0 +1,330 @@
1
+ /*
2
+ * pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ VALUE rb_cTypeMapInRuby;
10
+ static VALUE s_id_fit_to_result;
11
+ static VALUE s_id_fit_to_query;
12
+ static VALUE s_id_fit_to_copy_get;
13
+ static VALUE s_id_typecast_result_value;
14
+ static VALUE s_id_typecast_query_param;
15
+ static VALUE s_id_typecast_copy_get;
16
+
17
+ typedef struct {
18
+ t_typemap typemap;
19
+ VALUE self;
20
+ } t_tmir;
21
+
22
+ static size_t
23
+ pg_tmir_memsize( const void *_this )
24
+ {
25
+ const t_tmir *this = (const t_tmir *)_this;
26
+ return sizeof(*this);
27
+ }
28
+
29
+ static void
30
+ pg_tmir_compact( void *_this )
31
+ {
32
+ t_tmir *this = (t_tmir *)_this;
33
+ pg_typemap_compact(&this->typemap);
34
+ pg_gc_location(this->self);
35
+ }
36
+
37
+ static const rb_data_type_t pg_tmir_type = {
38
+ "PG::TypeMapInRuby",
39
+ {
40
+ pg_typemap_mark,
41
+ RUBY_TYPED_DEFAULT_FREE,
42
+ pg_tmir_memsize,
43
+ pg_compact_callback(pg_tmir_compact),
44
+ },
45
+ &pg_typemap_type,
46
+ 0,
47
+ RUBY_TYPED_FREE_IMMEDIATELY,
48
+ };
49
+
50
+ /*
51
+ * call-seq:
52
+ * typemap.fit_to_result( result )
53
+ *
54
+ * Check that the type map fits to the result.
55
+ *
56
+ * This method is called, when a type map is assigned to a result.
57
+ * It must return a PG::TypeMap object or raise an Exception.
58
+ * This can be +self+ or some other type map that fits to the result.
59
+ *
60
+ */
61
+ static VALUE
62
+ pg_tmir_fit_to_result( VALUE self, VALUE result )
63
+ {
64
+ t_tmir *this = RTYPEDDATA_DATA( self );
65
+ t_typemap *default_tm;
66
+ t_typemap *p_new_typemap;
67
+ VALUE sub_typemap;
68
+ VALUE new_typemap;
69
+
70
+ if( rb_respond_to(self, s_id_fit_to_result) ){
71
+ t_typemap *tm;
72
+ UNUSED(tm);
73
+ new_typemap = rb_funcall( self, s_id_fit_to_result, 1, result );
74
+
75
+ if ( !rb_obj_is_kind_of(new_typemap, rb_cTypeMap) ) {
76
+ /* TypedData_Get_Struct() raises "wrong argument type", which is misleading,
77
+ * so we better raise our own message */
78
+ rb_raise( rb_eTypeError, "wrong return type from fit_to_result: %s expected kind of PG::TypeMap",
79
+ rb_obj_classname( new_typemap ) );
80
+ }
81
+ TypedData_Get_Struct(new_typemap, t_typemap, &pg_typemap_type, tm);
82
+ } else {
83
+ new_typemap = self;
84
+ }
85
+
86
+ /* Ensure that the default type map fits equally. */
87
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
88
+ sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
89
+
90
+ if( sub_typemap != this->typemap.default_typemap ){
91
+ new_typemap = rb_obj_dup( new_typemap );
92
+ }
93
+
94
+ p_new_typemap = RTYPEDDATA_DATA(new_typemap);
95
+ p_new_typemap->default_typemap = sub_typemap;
96
+ return new_typemap;
97
+ }
98
+
99
+ static VALUE
100
+ pg_tmir_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
101
+ {
102
+ t_tmir *this = (t_tmir *) p_typemap;
103
+
104
+ return rb_funcall( this->self, s_id_typecast_result_value, 3, result, INT2NUM(tuple), INT2NUM(field) );
105
+ }
106
+
107
+ /*
108
+ * call-seq:
109
+ * typemap.typecast_result_value( result, tuple, field )
110
+ *
111
+ * Retrieve and cast a field of the given result.
112
+ *
113
+ * This method implementation uses the #default_type_map to get the
114
+ * field value. It can be derived to change this behaviour.
115
+ *
116
+ * Parameters:
117
+ * * +result+ : The PG::Result received from the database.
118
+ * * +tuple+ : The row number to retrieve.
119
+ * * +field+ : The column number to retrieve.
120
+ *
121
+ * Note: Calling any value retrieving methods of +result+ will result
122
+ * in an (endless) recursion. Instead super() can be used to retrieve
123
+ * the value using the default_typemap.
124
+ *
125
+ */
126
+ static VALUE
127
+ pg_tmir_typecast_result_value( VALUE self, VALUE result, VALUE tuple, VALUE field )
128
+ {
129
+ t_tmir *this = RTYPEDDATA_DATA( self );
130
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
131
+ return default_tm->funcs.typecast_result_value( default_tm, result, NUM2INT(tuple), NUM2INT(field) );
132
+ }
133
+
134
+ /*
135
+ * call-seq:
136
+ * typemap.fit_to_query( params )
137
+ *
138
+ * Check that the type map fits to the given user values.
139
+ *
140
+ * This method is called, when a type map is used for sending a query
141
+ * and for encoding of copy data, before the value is casted.
142
+ *
143
+ */
144
+ static VALUE
145
+ pg_tmir_fit_to_query( VALUE self, VALUE params )
146
+ {
147
+ t_tmir *this = RTYPEDDATA_DATA( self );
148
+ t_typemap *default_tm;
149
+
150
+ if( rb_respond_to(self, s_id_fit_to_query) ){
151
+ rb_funcall( self, s_id_fit_to_query, 1, params );
152
+ }
153
+
154
+ /* Ensure that the default type map fits equally. */
155
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
156
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
157
+
158
+ return self;
159
+ }
160
+
161
+ static t_pg_coder *
162
+ pg_tmir_query_param( t_typemap *p_typemap, VALUE param_value, int field )
163
+ {
164
+ t_tmir *this = (t_tmir *) p_typemap;
165
+
166
+ VALUE coder = rb_funcall( this->self, s_id_typecast_query_param, 2, param_value, INT2NUM(field) );
167
+
168
+ if ( NIL_P(coder) ){
169
+ return NULL;
170
+ } else if( rb_obj_is_kind_of(coder, rb_cPG_Coder) ) {
171
+ return RTYPEDDATA_DATA(coder);
172
+ } else {
173
+ rb_raise( rb_eTypeError, "wrong return type from typecast_query_param: %s expected nil or kind of PG::Coder",
174
+ rb_obj_classname( coder ) );
175
+ }
176
+ }
177
+
178
+ /*
179
+ * call-seq:
180
+ * typemap.typecast_query_param( param_value, field )
181
+ *
182
+ * Cast a field string for transmission to the server.
183
+ *
184
+ * This method implementation uses the #default_type_map to cast param_value.
185
+ * It can be derived to change this behaviour.
186
+ *
187
+ * Parameters:
188
+ * * +param_value+ : The value from the user.
189
+ * * +field+ : The field number from left to right.
190
+ *
191
+ */
192
+ static VALUE
193
+ pg_tmir_typecast_query_param( VALUE self, VALUE param_value, VALUE field )
194
+ {
195
+ t_tmir *this = RTYPEDDATA_DATA( self );
196
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
197
+ t_pg_coder *p_coder = default_tm->funcs.typecast_query_param( default_tm, param_value, NUM2INT(field) );
198
+
199
+ return p_coder ? p_coder->coder_obj : Qnil;
200
+ }
201
+
202
+ /* This is to fool rdoc's C parser */
203
+ #if 0
204
+ /*
205
+ * call-seq:
206
+ * typemap.fit_to_copy_get()
207
+ *
208
+ * Check that the type map can be used for PG::Connection#get_copy_data.
209
+ *
210
+ * This method is called, when a type map is used for decoding copy data,
211
+ * before the value is casted.
212
+ *
213
+ */
214
+ static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
215
+ #endif
216
+
217
+ static int
218
+ pg_tmir_fit_to_copy_get( VALUE self )
219
+ {
220
+ t_tmir *this = RTYPEDDATA_DATA( self );
221
+ t_typemap *default_tm;
222
+ VALUE num_columns = INT2NUM(0);
223
+
224
+ if( rb_respond_to(self, s_id_fit_to_copy_get) ){
225
+ num_columns = rb_funcall( self, s_id_fit_to_copy_get, 0 );
226
+ }
227
+
228
+ if ( !rb_obj_is_kind_of(num_columns, rb_cInteger) ) {
229
+ rb_raise( rb_eTypeError, "wrong return type from fit_to_copy_get: %s expected kind of Integer",
230
+ rb_obj_classname( num_columns ) );
231
+ }
232
+ /* Ensure that the default type map fits equally. */
233
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
234
+ default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
235
+
236
+ return NUM2INT(num_columns);;
237
+ }
238
+
239
+ static VALUE
240
+ pg_tmir_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
241
+ {
242
+ t_tmir *this = (t_tmir *) p_typemap;
243
+ rb_encoding *p_encoding = rb_enc_from_index(enc_idx);
244
+ VALUE enc = rb_enc_from_encoding(p_encoding);
245
+ /* field_str is reused in-place by pg_text_dec_copy_row(), so we need to make
246
+ * a copy of the string buffer for use in ruby space. */
247
+ VALUE field_str_copy = rb_str_dup(field_str);
248
+ rb_str_modify(field_str_copy);
249
+
250
+ return rb_funcall( this->self, s_id_typecast_copy_get, 4, field_str_copy, INT2NUM(fieldno), INT2NUM(format), enc );
251
+ }
252
+
253
+ /*
254
+ * call-seq:
255
+ * typemap.typecast_copy_get( field_str, fieldno, format, encoding )
256
+ *
257
+ * Cast a field string received by PG::Connection#get_copy_data.
258
+ *
259
+ * This method implementation uses the #default_type_map to cast field_str.
260
+ * It can be derived to change this behaviour.
261
+ *
262
+ * Parameters:
263
+ * * +field_str+ : The String received from the server.
264
+ * * +fieldno+ : The field number from left to right.
265
+ * * +format+ : The format code (0 = text, 1 = binary)
266
+ * * +encoding+ : The encoding of the connection and encoding the returned
267
+ * value should get.
268
+ *
269
+ */
270
+ static VALUE
271
+ pg_tmir_typecast_copy_get( VALUE self, VALUE field_str, VALUE fieldno, VALUE format, VALUE enc )
272
+ {
273
+ t_tmir *this = RTYPEDDATA_DATA( self );
274
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
275
+ int enc_idx = rb_to_encoding_index( enc );
276
+
277
+ return default_tm->funcs.typecast_copy_get( default_tm, field_str, NUM2INT(fieldno), NUM2INT(format), enc_idx );
278
+ }
279
+
280
+ static VALUE
281
+ pg_tmir_s_allocate( VALUE klass )
282
+ {
283
+ t_tmir *this;
284
+ VALUE self;
285
+
286
+ self = TypedData_Make_Struct( klass, t_tmir, &pg_tmir_type, this );
287
+
288
+ this->typemap.funcs.fit_to_result = pg_tmir_fit_to_result;
289
+ this->typemap.funcs.fit_to_query = pg_tmir_fit_to_query;
290
+ this->typemap.funcs.fit_to_copy_get = pg_tmir_fit_to_copy_get;
291
+ this->typemap.funcs.typecast_result_value = pg_tmir_result_value;
292
+ this->typemap.funcs.typecast_query_param = pg_tmir_query_param;
293
+ this->typemap.funcs.typecast_copy_get = pg_tmir_copy_get;
294
+ this->typemap.default_typemap = pg_typemap_all_strings;
295
+ this->self = self;
296
+
297
+ return self;
298
+ }
299
+
300
+
301
+ void
302
+ init_pg_type_map_in_ruby()
303
+ {
304
+ s_id_fit_to_result = rb_intern("fit_to_result");
305
+ s_id_fit_to_query = rb_intern("fit_to_query");
306
+ s_id_fit_to_copy_get = rb_intern("fit_to_copy_get");
307
+ s_id_typecast_result_value = rb_intern("typecast_result_value");
308
+ s_id_typecast_query_param = rb_intern("typecast_query_param");
309
+ s_id_typecast_copy_get = rb_intern("typecast_copy_get");
310
+
311
+ /*
312
+ * Document-class: PG::TypeMapInRuby < PG::TypeMap
313
+ *
314
+ * This class can be used to implement a type map in ruby, typically as a
315
+ * #default_type_map in a type map chain.
316
+ *
317
+ * This API is EXPERIMENTAL and could change in the future.
318
+ *
319
+ */
320
+ rb_cTypeMapInRuby = rb_define_class_under( rb_mPG, "TypeMapInRuby", rb_cTypeMap );
321
+ rb_define_alloc_func( rb_cTypeMapInRuby, pg_tmir_s_allocate );
322
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_result", pg_tmir_fit_to_result, 1 ); */
323
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_query", pg_tmir_fit_to_query, 1 ); */
324
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_copy_get", pg_tmir_fit_to_copy_get_dummy, 0 ); */
325
+ rb_define_method( rb_cTypeMapInRuby, "typecast_result_value", pg_tmir_typecast_result_value, 3 );
326
+ rb_define_method( rb_cTypeMapInRuby, "typecast_query_param", pg_tmir_typecast_query_param, 2 );
327
+ rb_define_method( rb_cTypeMapInRuby, "typecast_copy_get", pg_tmir_typecast_copy_get, 4 );
328
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
329
+ rb_include_module( rb_cTypeMapInRuby, rb_mDefaultTypeMappable );
330
+ }