yugabyte_ysql 0.1

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 +373 -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 +990 -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/yugabyte_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/yugabyte_ysql.gemspec +33 -0
  121. metadata +232 -0
@@ -0,0 +1,388 @@
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 | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
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
+ RB_OBJ_WRITE(self, &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
+ RB_OBJ_WRITE(self, &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
+ rb_check_frozen(self);
246
+ TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, p_coder);
247
+
248
+ if( p_coder->format < 0 || p_coder->format > 1 )
249
+ rb_raise(rb_eArgError, "invalid format code %d", p_coder->format);
250
+
251
+ /* Update cache entry */
252
+ p_ce = CACHE_LOOKUP(this, p_coder->format, p_coder->oid);
253
+ p_ce->oid = p_coder->oid;
254
+ p_ce->p_coder = p_coder;
255
+ /* Write coder into the hash of the given format */
256
+ hash = this->format[p_coder->format].oid_to_coder;
257
+ rb_hash_aset( hash, UINT2NUM(p_coder->oid), coder);
258
+
259
+ return self;
260
+ }
261
+
262
+ /*
263
+ * call-seq:
264
+ * typemap.rm_coder( format, oid )
265
+ *
266
+ * Removes a PG::Coder object from the type map based on the given
267
+ * oid and format codes.
268
+ *
269
+ * Returns the removed coder object.
270
+ */
271
+ static VALUE
272
+ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
273
+ {
274
+ VALUE hash;
275
+ VALUE coder;
276
+ t_tmbo *this = RTYPEDDATA_DATA( self );
277
+ int i_format = NUM2INT(format);
278
+ struct pg_tmbo_oid_cache_entry *p_ce;
279
+
280
+ rb_check_frozen(self);
281
+ if( i_format < 0 || i_format > 1 )
282
+ rb_raise(rb_eArgError, "invalid format code %d", i_format);
283
+
284
+ /* Mark the cache entry as empty */
285
+ p_ce = CACHE_LOOKUP(this, i_format, NUM2UINT(oid));
286
+ p_ce->oid = 0;
287
+ p_ce->p_coder = NULL;
288
+ hash = this->format[i_format].oid_to_coder;
289
+ coder = rb_hash_delete( hash, oid );
290
+
291
+ return coder;
292
+ }
293
+
294
+ /*
295
+ * call-seq:
296
+ * typemap.coders -> Array
297
+ *
298
+ * Array of all assigned PG::Coder objects.
299
+ */
300
+ static VALUE
301
+ pg_tmbo_coders( VALUE self )
302
+ {
303
+ t_tmbo *this = RTYPEDDATA_DATA( self );
304
+
305
+ return rb_ary_concat(
306
+ rb_funcall(this->format[0].oid_to_coder, rb_intern("values"), 0),
307
+ rb_funcall(this->format[1].oid_to_coder, rb_intern("values"), 0));
308
+ }
309
+
310
+ /*
311
+ * call-seq:
312
+ * typemap.max_rows_for_online_lookup = number
313
+ *
314
+ * Threshold for doing Hash lookups versus creation of a dedicated PG::TypeMapByColumn.
315
+ * The type map will do Hash lookups for each result value, if the number of rows
316
+ * is below or equal +number+.
317
+ *
318
+ */
319
+ static VALUE
320
+ pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
321
+ {
322
+ t_tmbo *this = RTYPEDDATA_DATA( self );
323
+ rb_check_frozen(self);
324
+ this->max_rows_for_online_lookup = NUM2INT(value);
325
+ return value;
326
+ }
327
+
328
+ /*
329
+ * call-seq:
330
+ * typemap.max_rows_for_online_lookup -> Integer
331
+ */
332
+ static VALUE
333
+ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
334
+ {
335
+ t_tmbo *this = RTYPEDDATA_DATA( self );
336
+ return INT2NUM(this->max_rows_for_online_lookup);
337
+ }
338
+
339
+ /*
340
+ * call-seq:
341
+ * typemap.build_column_map( result )
342
+ *
343
+ * This builds a PG::TypeMapByColumn that fits to the given PG::Result object
344
+ * based on it's type OIDs and binary/text format.
345
+ *
346
+ */
347
+ static VALUE
348
+ pg_tmbo_build_column_map( VALUE self, VALUE result )
349
+ {
350
+ t_tmbo *this = RTYPEDDATA_DATA( self );
351
+
352
+ if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
353
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
354
+ rb_obj_classname( result ) );
355
+ }
356
+
357
+ return pg_tmbo_build_type_map_for_result2( this, pgresult_get(result) );
358
+ }
359
+
360
+
361
+ void
362
+ init_pg_type_map_by_oid(void)
363
+ {
364
+ s_id_decode = rb_intern("decode");
365
+
366
+ /*
367
+ * Document-class: PG::TypeMapByOid < PG::TypeMap
368
+ *
369
+ * This type map casts values based on the type OID of the given column
370
+ * in the result set.
371
+ *
372
+ * This type map is only suitable to cast values from PG::Result objects.
373
+ * Therefore only decoders might be assigned by the #add_coder method.
374
+ *
375
+ * Fields with no match to any of the registered type OID / format combination
376
+ * are forwarded to the #default_type_map .
377
+ */
378
+ rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
379
+ rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
380
+ rb_define_method( rb_cTypeMapByOid, "add_coder", pg_tmbo_add_coder, 1 );
381
+ rb_define_method( rb_cTypeMapByOid, "rm_coder", pg_tmbo_rm_coder, 2 );
382
+ rb_define_method( rb_cTypeMapByOid, "coders", pg_tmbo_coders, 0 );
383
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
384
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
385
+ rb_define_method( rb_cTypeMapByOid, "build_column_map", pg_tmbo_build_column_map, 1 );
386
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
387
+ rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
388
+ }
@@ -0,0 +1,333 @@
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 | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
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
+ * Should return the expected number of columns or 0 if the number of columns is unknown.
214
+ * This number is only used for memory pre-allocation.
215
+ *
216
+ */
217
+ static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
218
+ #endif
219
+
220
+ static int
221
+ pg_tmir_fit_to_copy_get( VALUE self )
222
+ {
223
+ t_tmir *this = RTYPEDDATA_DATA( self );
224
+ t_typemap *default_tm;
225
+ VALUE num_columns = INT2NUM(0);
226
+
227
+ if( rb_respond_to(self, s_id_fit_to_copy_get) ){
228
+ num_columns = rb_funcall( self, s_id_fit_to_copy_get, 0 );
229
+ }
230
+
231
+ if ( !rb_obj_is_kind_of(num_columns, rb_cInteger) ) {
232
+ rb_raise( rb_eTypeError, "wrong return type from fit_to_copy_get: %s expected kind of Integer",
233
+ rb_obj_classname( num_columns ) );
234
+ }
235
+ /* Ensure that the default type map fits equally. */
236
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
237
+ default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
238
+
239
+ return NUM2INT(num_columns);;
240
+ }
241
+
242
+ static VALUE
243
+ pg_tmir_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
244
+ {
245
+ t_tmir *this = (t_tmir *) p_typemap;
246
+ rb_encoding *p_encoding = rb_enc_from_index(enc_idx);
247
+ VALUE enc = rb_enc_from_encoding(p_encoding);
248
+ /* field_str is reused in-place by pg_text_dec_copy_row(), so we need to make
249
+ * a copy of the string buffer for use in ruby space. */
250
+ VALUE field_str_copy = rb_str_dup(field_str);
251
+ rb_str_modify(field_str_copy);
252
+
253
+ return rb_funcall( this->self, s_id_typecast_copy_get, 4, field_str_copy, INT2NUM(fieldno), INT2NUM(format), enc );
254
+ }
255
+
256
+ /*
257
+ * call-seq:
258
+ * typemap.typecast_copy_get( field_str, fieldno, format, encoding )
259
+ *
260
+ * Cast a field string received by PG::Connection#get_copy_data.
261
+ *
262
+ * This method implementation uses the #default_type_map to cast field_str.
263
+ * It can be derived to change this behaviour.
264
+ *
265
+ * Parameters:
266
+ * * +field_str+ : The String received from the server.
267
+ * * +fieldno+ : The field number from left to right.
268
+ * * +format+ : The format code (0 = text, 1 = binary)
269
+ * * +encoding+ : The encoding of the connection and encoding the returned
270
+ * value should get.
271
+ *
272
+ */
273
+ static VALUE
274
+ pg_tmir_typecast_copy_get( VALUE self, VALUE field_str, VALUE fieldno, VALUE format, VALUE enc )
275
+ {
276
+ t_tmir *this = RTYPEDDATA_DATA( self );
277
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
278
+ int enc_idx = rb_to_encoding_index( enc );
279
+
280
+ return default_tm->funcs.typecast_copy_get( default_tm, field_str, NUM2INT(fieldno), NUM2INT(format), enc_idx );
281
+ }
282
+
283
+ static VALUE
284
+ pg_tmir_s_allocate( VALUE klass )
285
+ {
286
+ t_tmir *this;
287
+ VALUE self;
288
+
289
+ self = TypedData_Make_Struct( klass, t_tmir, &pg_tmir_type, this );
290
+
291
+ this->typemap.funcs.fit_to_result = pg_tmir_fit_to_result;
292
+ this->typemap.funcs.fit_to_query = pg_tmir_fit_to_query;
293
+ this->typemap.funcs.fit_to_copy_get = pg_tmir_fit_to_copy_get;
294
+ this->typemap.funcs.typecast_result_value = pg_tmir_result_value;
295
+ this->typemap.funcs.typecast_query_param = pg_tmir_query_param;
296
+ this->typemap.funcs.typecast_copy_get = pg_tmir_copy_get;
297
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
298
+ this->self = self;
299
+
300
+ return self;
301
+ }
302
+
303
+
304
+ void
305
+ init_pg_type_map_in_ruby(void)
306
+ {
307
+ s_id_fit_to_result = rb_intern("fit_to_result");
308
+ s_id_fit_to_query = rb_intern("fit_to_query");
309
+ s_id_fit_to_copy_get = rb_intern("fit_to_copy_get");
310
+ s_id_typecast_result_value = rb_intern("typecast_result_value");
311
+ s_id_typecast_query_param = rb_intern("typecast_query_param");
312
+ s_id_typecast_copy_get = rb_intern("typecast_copy_get");
313
+
314
+ /*
315
+ * Document-class: PG::TypeMapInRuby < PG::TypeMap
316
+ *
317
+ * This class can be used to implement a type map in ruby, typically as a
318
+ * #default_type_map in a type map chain.
319
+ *
320
+ * This API is EXPERIMENTAL and could change in the future.
321
+ *
322
+ */
323
+ rb_cTypeMapInRuby = rb_define_class_under( rb_mPG, "TypeMapInRuby", rb_cTypeMap );
324
+ rb_define_alloc_func( rb_cTypeMapInRuby, pg_tmir_s_allocate );
325
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_result", pg_tmir_fit_to_result, 1 ); */
326
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_query", pg_tmir_fit_to_query, 1 ); */
327
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_copy_get", pg_tmir_fit_to_copy_get_dummy, 0 ); */
328
+ rb_define_method( rb_cTypeMapInRuby, "typecast_result_value", pg_tmir_typecast_result_value, 3 );
329
+ rb_define_method( rb_cTypeMapInRuby, "typecast_query_param", pg_tmir_typecast_query_param, 2 );
330
+ rb_define_method( rb_cTypeMapInRuby, "typecast_copy_get", pg_tmir_typecast_copy_get, 4 );
331
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
332
+ rb_include_module( rb_cTypeMapInRuby, rb_mDefaultTypeMappable );
333
+ }