pg 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gemtest +0 -0
  5. data/BSDL +22 -0
  6. data/ChangeLog +6595 -0
  7. data/Contributors.rdoc +46 -0
  8. data/History.rdoc +492 -0
  9. data/LICENSE +56 -0
  10. data/Manifest.txt +72 -0
  11. data/POSTGRES +23 -0
  12. data/README-OS_X.rdoc +68 -0
  13. data/README-Windows.rdoc +56 -0
  14. data/README.ja.rdoc +14 -0
  15. data/README.rdoc +178 -0
  16. data/Rakefile +215 -0
  17. data/Rakefile.cross +298 -0
  18. data/ext/errorcodes.def +968 -0
  19. data/ext/errorcodes.rb +45 -0
  20. data/ext/errorcodes.txt +478 -0
  21. data/ext/extconf.rb +94 -0
  22. data/ext/gvl_wrappers.c +17 -0
  23. data/ext/gvl_wrappers.h +241 -0
  24. data/ext/pg.c +640 -0
  25. data/ext/pg.h +365 -0
  26. data/ext/pg_binary_decoder.c +229 -0
  27. data/ext/pg_binary_encoder.c +162 -0
  28. data/ext/pg_coder.c +549 -0
  29. data/ext/pg_connection.c +4252 -0
  30. data/ext/pg_copy_coder.c +596 -0
  31. data/ext/pg_errors.c +95 -0
  32. data/ext/pg_result.c +1501 -0
  33. data/ext/pg_text_decoder.c +981 -0
  34. data/ext/pg_text_encoder.c +682 -0
  35. data/ext/pg_tuple.c +541 -0
  36. data/ext/pg_type_map.c +166 -0
  37. data/ext/pg_type_map_all_strings.c +116 -0
  38. data/ext/pg_type_map_by_class.c +239 -0
  39. data/ext/pg_type_map_by_column.c +312 -0
  40. data/ext/pg_type_map_by_mri_type.c +284 -0
  41. data/ext/pg_type_map_by_oid.c +355 -0
  42. data/ext/pg_type_map_in_ruby.c +299 -0
  43. data/ext/util.c +149 -0
  44. data/ext/util.h +65 -0
  45. data/ext/vc/pg.sln +26 -0
  46. data/ext/vc/pg_18/pg.vcproj +216 -0
  47. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  48. data/lib/pg.rb +74 -0
  49. data/lib/pg/basic_type_mapping.rb +459 -0
  50. data/lib/pg/binary_decoder.rb +22 -0
  51. data/lib/pg/coder.rb +83 -0
  52. data/lib/pg/connection.rb +291 -0
  53. data/lib/pg/constants.rb +11 -0
  54. data/lib/pg/exceptions.rb +11 -0
  55. data/lib/pg/result.rb +31 -0
  56. data/lib/pg/text_decoder.rb +47 -0
  57. data/lib/pg/text_encoder.rb +69 -0
  58. data/lib/pg/tuple.rb +30 -0
  59. data/lib/pg/type_map_by_column.rb +15 -0
  60. data/spec/data/expected_trace.out +26 -0
  61. data/spec/data/random_binary_data +0 -0
  62. data/spec/helpers.rb +380 -0
  63. data/spec/pg/basic_type_mapping_spec.rb +508 -0
  64. data/spec/pg/connection_spec.rb +1872 -0
  65. data/spec/pg/connection_sync_spec.rb +41 -0
  66. data/spec/pg/result_spec.rb +491 -0
  67. data/spec/pg/tuple_spec.rb +280 -0
  68. data/spec/pg/type_map_by_class_spec.rb +138 -0
  69. data/spec/pg/type_map_by_column_spec.rb +222 -0
  70. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  71. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  72. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  73. data/spec/pg/type_map_spec.rb +22 -0
  74. data/spec/pg/type_spec.rb +949 -0
  75. data/spec/pg_spec.rb +50 -0
  76. metadata +322 -0
  77. metadata.gz.sig +0 -0
@@ -0,0 +1,508 @@
1
+ # -*- rspec -*-
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ require 'pg'
7
+ require 'time'
8
+
9
+ def restore_type(types)
10
+ [0, 1].each do |format|
11
+ [types].flatten.each do |type|
12
+ PG::BasicTypeRegistry.alias_type(format, "restore_#{type}", type)
13
+ end
14
+ end
15
+ yield
16
+ ensure
17
+ [0, 1].each do |format|
18
+ [types].flatten.each do |type|
19
+ PG::BasicTypeRegistry.alias_type(format, type, "restore_#{type}")
20
+ end
21
+ end
22
+ end
23
+
24
+ describe 'Basic type mapping' do
25
+
26
+ describe PG::BasicTypeMapForQueries do
27
+ let!(:basic_type_mapping) do
28
+ PG::BasicTypeMapForQueries.new @conn
29
+ end
30
+
31
+ #
32
+ # Encoding Examples
33
+ #
34
+
35
+ it "should do basic param encoding", :ruby_19 do
36
+ res = @conn.exec_params( "SELECT $1::int8,$2::float,$3,$4::TEXT",
37
+ [1, 2.1, true, "b"], nil, basic_type_mapping )
38
+
39
+ expect( res.values ).to eq( [
40
+ [ "1", "2.1", "t", "b" ],
41
+ ] )
42
+
43
+ expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
44
+ end
45
+
46
+ it "should do array param encoding" do
47
+ res = @conn.exec_params( "SELECT $1,$2,$3,$4", [
48
+ [1, 2, 3], [[1, 2], [3, nil]],
49
+ [1.11, 2.21],
50
+ ['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
51
+ ], nil, basic_type_mapping )
52
+
53
+ expect( res.values ).to eq( [[
54
+ '{1,2,3}', '{{1,2},{3,NULL}}',
55
+ '{1.11,2.21}',
56
+ '{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
57
+ ]] )
58
+
59
+ expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
60
+ end
61
+
62
+ it "should do bigdecimal param encoding" do
63
+ large = ('123456790'*10) << '.' << ('012345679')
64
+ res = @conn.exec_params( "SELECT $1::numeric,$2::numeric",
65
+ [BigDecimal('1'), BigDecimal(large)], nil, basic_type_mapping )
66
+
67
+ expect( res.values ).to eq( [
68
+ [ "1.0", large ],
69
+ ] )
70
+
71
+ expect( result_typenames(res) ).to eq( ['numeric', 'numeric'] )
72
+ end
73
+
74
+ it "should do IPAddr param encoding" do
75
+ res = @conn.exec_params( "SELECT $1::inet,$2::inet,$3::cidr,$4::cidr",
76
+ ['1.2.3.4', IPAddr.new('1234::5678'), '1.2.3.4', IPAddr.new('1234:5678::/32')], nil, basic_type_mapping )
77
+
78
+ expect( res.values ).to eq( [
79
+ [ '1.2.3.4', '1234::5678', '1.2.3.4/32', '1234:5678::/32'],
80
+ ] )
81
+
82
+ expect( result_typenames(res) ).to eq( ['inet', 'inet', 'cidr', 'cidr'] )
83
+ end
84
+
85
+ end
86
+
87
+
88
+
89
+ describe PG::BasicTypeMapForResults do
90
+ let!(:basic_type_mapping) do
91
+ PG::BasicTypeMapForResults.new @conn
92
+ end
93
+
94
+ #
95
+ # Decoding Examples
96
+ #
97
+
98
+ it "should do OID based type conversions", :ruby_19 do
99
+ res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
100
+ expect( res.map_types!(basic_type_mapping).values ).to eq( [
101
+ [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
102
+ [ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
103
+ ] )
104
+ end
105
+
106
+ #
107
+ # Decoding Examples text+binary format converters
108
+ #
109
+
110
+ describe "connection wide type mapping" do
111
+ before :each do
112
+ @conn.type_map_for_results = basic_type_mapping
113
+ end
114
+
115
+ after :each do
116
+ @conn.type_map_for_results = PG::TypeMapAllStrings.new
117
+ end
118
+
119
+ it "should do boolean type conversions" do
120
+ [1, 0].each do |format|
121
+ res = @conn.exec_params( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
122
+ expect( res.values ).to eq( [[true, false, nil]] )
123
+ end
124
+ end
125
+
126
+ it "should do binary type conversions" do
127
+ [1, 0].each do |format|
128
+ res = @conn.exec_params( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
129
+ expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
130
+ expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
131
+ end
132
+ end
133
+
134
+ it "should do integer type conversions" do
135
+ [1, 0].each do |format|
136
+ res = @conn.exec_params( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
137
+ expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
138
+ end
139
+ end
140
+
141
+ it "should do string type conversions" do
142
+ @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
143
+ [1, 0].each do |format|
144
+ res = @conn.exec_params( "SELECT 'abcäöü'::TEXT", [], format )
145
+ expect( res.values ).to eq( [['abcäöü']] )
146
+ expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
147
+ end
148
+ end
149
+
150
+ it "should do float type conversions" do
151
+ [1, 0].each do |format|
152
+ res = @conn.exec_params( "SELECT -8.999e3::FLOAT4,
153
+ 8.999e10::FLOAT4,
154
+ -8999999999e-99::FLOAT8,
155
+ NULL::FLOAT4,
156
+ 'NaN'::FLOAT4,
157
+ 'Infinity'::FLOAT4,
158
+ '-Infinity'::FLOAT4
159
+ ", [], format )
160
+ expect( res.getvalue(0,0) ).to be_within(1e-2).of(-8.999e3)
161
+ expect( res.getvalue(0,1) ).to be_within(1e5).of(8.999e10)
162
+ expect( res.getvalue(0,2) ).to be_within(1e-109).of(-8999999999e-99)
163
+ expect( res.getvalue(0,3) ).to be_nil
164
+ expect( res.getvalue(0,4) ).to be_nan
165
+ expect( res.getvalue(0,5) ).to eq( Float::INFINITY )
166
+ expect( res.getvalue(0,6) ).to eq( -Float::INFINITY )
167
+ end
168
+ end
169
+
170
+ it "should do text datetime without time zone type conversions" do
171
+ # for backward compat text timestamps without time zone are treated as local times
172
+ res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
173
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
174
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
175
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
176
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
177
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], 0 )
178
+ expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
179
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
180
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
181
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
182
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
183
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
184
+ end
185
+
186
+ [1, 0].each do |format|
187
+ it "should convert format #{format} timestamps per TimestampUtc" do
188
+ restore_type("timestamp") do
189
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtc
190
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
191
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
192
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
193
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
194
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
195
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
196
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
197
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).iso8601(3) )
198
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
199
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
200
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
201
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
202
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
203
+ end
204
+ end
205
+ end
206
+
207
+ [1, 0].each do |format|
208
+ it "should convert format #{format} timestamps per TimestampUtcToLocal" do
209
+ restore_type("timestamp") do
210
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtcToLocal
211
+ PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtcToLocal
212
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
213
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
214
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
215
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
216
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
217
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
218
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
219
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).getlocal.iso8601(3) )
220
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
221
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).getlocal.iso8601(3) )
222
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
223
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
224
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
225
+ end
226
+ end
227
+ end
228
+
229
+ [1, 0].each do |format|
230
+ it "should convert format #{format} timestamps per TimestampLocal" do
231
+ restore_type("timestamp") do
232
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampLocal
233
+ PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampLocal
234
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
235
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59' AS TIMESTAMP WITHOUT TIME ZONE),
236
+ CAST('1913-12-31 23:58:59.1231' AS TIMESTAMP WITHOUT TIME ZONE),
237
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
238
+ CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITHOUT TIME ZONE),
239
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
240
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
241
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.new(2013, 7, 31, 23, 58, 59).iso8601(3) )
242
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
243
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
244
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
245
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
246
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
247
+ end
248
+ end
249
+ end
250
+
251
+ [0, 1].each do |format|
252
+ it "should convert format #{format} timestamps with time zone" do
253
+ res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
254
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITH TIME ZONE),
255
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITH TIME ZONE),
256
+ CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITH TIME ZONE),
257
+ CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
258
+ CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
259
+ expect( res.getvalue(0,0) ).to be_within(1e-3).of( Time.new(2013, 12, 31, 23, 58, 59, "+02:00").getlocal )
260
+ expect( res.getvalue(0,1) ).to be_within(1e-3).of( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").getlocal )
261
+ expect( res.getvalue(0,2) ).to be_within(1e-3).of( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").getlocal )
262
+ expect( res.getvalue(0,3) ).to be_within(1e-3).of( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").getlocal )
263
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
264
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
265
+ end
266
+ end
267
+
268
+ it "should do date type conversions" do
269
+ [0].each do |format|
270
+ res = @conn.exec_params( "SELECT CAST('2113-12-31' AS DATE),
271
+ CAST('1913-12-31' AS DATE),
272
+ CAST('infinity' AS DATE),
273
+ CAST('-infinity' AS DATE)", [], format )
274
+ expect( res.getvalue(0,0) ).to eq( Date.new(2113, 12, 31) )
275
+ expect( res.getvalue(0,1) ).to eq( Date.new(1913, 12, 31) )
276
+ expect( res.getvalue(0,2) ).to eq( 'infinity' )
277
+ expect( res.getvalue(0,3) ).to eq( '-infinity' )
278
+ end
279
+ end
280
+
281
+ it "should do numeric type conversions" do
282
+ [0].each do |format|
283
+ small = '123456790123.12'
284
+ large = ('123456790'*10) << '.' << ('012345679')
285
+ numerics = [
286
+ '1',
287
+ '1.0',
288
+ '1.2',
289
+ small,
290
+ large,
291
+ ]
292
+ sql_numerics = numerics.map { |v| "CAST(#{v} AS numeric)" }
293
+ res = @conn.exec_params( "SELECT #{sql_numerics.join(',')}", [], format )
294
+ expect( res.getvalue(0,0) ).to eq( BigDecimal('1') )
295
+ expect( res.getvalue(0,1) ).to eq( BigDecimal('1') )
296
+ expect( res.getvalue(0,2) ).to eq( BigDecimal('1.2') )
297
+ expect( res.getvalue(0,3) ).to eq( BigDecimal(small) )
298
+ expect( res.getvalue(0,4) ).to eq( BigDecimal(large) )
299
+ end
300
+ end
301
+
302
+ it "should do JSON conversions", :postgresql_94 do
303
+ [0].each do |format|
304
+ ['JSON', 'JSONB'].each do |type|
305
+ res = @conn.exec_params( "SELECT CAST('123' AS #{type}),
306
+ CAST('12.3' AS #{type}),
307
+ CAST('true' AS #{type}),
308
+ CAST('false' AS #{type}),
309
+ CAST('null' AS #{type}),
310
+ CAST('[1, \"a\", null]' AS #{type}),
311
+ CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
312
+ expect( res.getvalue(0,0) ).to eq( 123 )
313
+ expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
314
+ expect( res.getvalue(0,2) ).to eq( true )
315
+ expect( res.getvalue(0,3) ).to eq( false )
316
+ expect( res.getvalue(0,4) ).to eq( nil )
317
+ expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
318
+ expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
319
+ end
320
+ end
321
+ end
322
+
323
+ it "should do array type conversions" do
324
+ [0].each do |format|
325
+ res = @conn.exec_params( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
326
+ CAST('{1,2,3}' AS INT4[]),
327
+ CAST('{1,2,3}' AS INT8[]),
328
+ CAST('{1,2,3}' AS TEXT[]),
329
+ CAST('{1,2,3}' AS VARCHAR[]),
330
+ CAST('{1,2,3}' AS FLOAT4[]),
331
+ CAST('{1,2,3}' AS FLOAT8[])
332
+ ", [], format )
333
+ expect( res.getvalue(0,0) ).to eq( [1,2,3] )
334
+ expect( res.getvalue(0,1) ).to eq( [[1,2],[3,4]] )
335
+ expect( res.getvalue(0,2) ).to eq( [1,2,3] )
336
+ expect( res.getvalue(0,3) ).to eq( [1,2,3] )
337
+ expect( res.getvalue(0,4) ).to eq( ['1','2','3'] )
338
+ expect( res.getvalue(0,5) ).to eq( ['1','2','3'] )
339
+ expect( res.getvalue(0,6) ).to eq( [1.0,2.0,3.0] )
340
+ expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
341
+ end
342
+ end
343
+
344
+ it "should do inet type conversions" do
345
+ [0].each do |format|
346
+ vals = [
347
+ '1.2.3.4',
348
+ '0.0.0.0/0',
349
+ '1.0.0.0/8',
350
+ '1.2.0.0/16',
351
+ '1.2.3.0/24',
352
+ '1.2.3.4/24',
353
+ '1.2.3.4/32',
354
+ '1.2.3.128/25',
355
+ '1234:3456:5678:789a:9abc:bced:edf0:f012',
356
+ '::/0',
357
+ '1234:3456::/32',
358
+ '1234:3456:5678:789a::/64',
359
+ '1234:3456:5678:789a:9abc:bced::/96',
360
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
361
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/0',
362
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/32',
363
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/64',
364
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/96',
365
+ ]
366
+ sql_vals = vals.map{|v| "CAST('#{v}' AS inet)"}
367
+ res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
368
+ vals.each_with_index do |v, i|
369
+ val = res.getvalue(0,i)
370
+ expect( res.getvalue(0,i) ).to eq( IPAddr.new(v) )
371
+ end
372
+ end
373
+ end
374
+
375
+ it "should do cidr type conversions" do
376
+ [0].each do |format|
377
+ vals = [
378
+ '0.0.0.0/0',
379
+ '1.0.0.0/8',
380
+ '1.2.0.0/16',
381
+ '1.2.3.0/24',
382
+ '1.2.3.4/32',
383
+ '1.2.3.128/25',
384
+ '::/0',
385
+ '1234:3456::/32',
386
+ '1234:3456:5678:789a::/64',
387
+ '1234:3456:5678:789a:9abc:bced::/96',
388
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
389
+ ]
390
+ sql_vals = vals.map { |v| "CAST('#{v}' AS cidr)" }
391
+ res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
392
+ vals.each_with_index do |v, i|
393
+ val = res.getvalue(0,i)
394
+ ip, prefix = v.split('/', 2)
395
+ expect( val.to_s ).to eq( ip )
396
+ if val.respond_to?(:prefix)
397
+ val_prefix = val.prefix
398
+ else
399
+ default_prefix = (val.family == Socket::AF_INET ? 32 : 128)
400
+ range = val.to_range
401
+ val_prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
402
+ end
403
+ if v.include?('/')
404
+ expect( val_prefix ).to eq( prefix.to_i )
405
+ elsif v.include?('.')
406
+ expect( val_prefix ).to eq( 32 )
407
+ else
408
+ expect( val_prefix ).to eq( 128 )
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ context "with usage of result oids for copy decoder selection" do
416
+ it "can type cast #copy_data output with explicit decoder" do
417
+ @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
418
+ @conn.exec( "INSERT INTO copytable VALUES ('a', 123, '{5,4,3}'), ('b', 234, '{2,3}')" )
419
+
420
+ # Retrieve table OIDs per empty result.
421
+ res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
422
+ tm = basic_type_mapping.build_column_map( res )
423
+ row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
424
+
425
+ rows = []
426
+ @conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
427
+ while row=@conn.get_copy_data
428
+ rows << row
429
+ end
430
+ end
431
+ expect( rows ).to eq( [['a', 123, [5,4,3]], ['b', 234, [2,3]]] )
432
+ end
433
+ end
434
+ end
435
+
436
+
437
+ describe PG::BasicTypeMapBasedOnResult do
438
+ let!(:basic_type_mapping) do
439
+ PG::BasicTypeMapBasedOnResult.new @conn
440
+ end
441
+
442
+ context "with usage of result oids for bind params encoder selection" do
443
+ it "can type cast query params" do
444
+ @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
445
+
446
+ # Retrieve table OIDs per empty result.
447
+ res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
448
+ tm = basic_type_mapping.build_column_map( res )
449
+
450
+ @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['a', 123, [5,4,3]], 0, tm )
451
+ @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['b', 234, [2,3]], 0, tm )
452
+ res = @conn.exec( "SELECT * FROM copytable" )
453
+ expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
454
+ end
455
+
456
+ it "can do JSON conversions", :postgresql_94 do
457
+ ['JSON', 'JSONB'].each do |type|
458
+ sql = "SELECT CAST('123' AS #{type}),
459
+ CAST('12.3' AS #{type}),
460
+ CAST('true' AS #{type}),
461
+ CAST('false' AS #{type}),
462
+ CAST('null' AS #{type}),
463
+ CAST('[1, \"a\", null]' AS #{type}),
464
+ CAST('{\"b\" : [2,3]}' AS #{type})"
465
+
466
+ tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
467
+ expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
468
+
469
+ res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
470
+ [ 123,
471
+ 12.3,
472
+ true,
473
+ false,
474
+ nil,
475
+ [1, "a", nil],
476
+ {"b" => [2, 3]},
477
+ ], 0, tm )
478
+
479
+ expect( res.getvalue(0,0) ).to eq( "123" )
480
+ expect( res.getvalue(0,1) ).to eq( "12.3" )
481
+ expect( res.getvalue(0,2) ).to eq( "true" )
482
+ expect( res.getvalue(0,3) ).to eq( "false" )
483
+ expect( res.getvalue(0,4) ).to eq( nil )
484
+ expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
485
+ expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
486
+ end
487
+ end
488
+ end
489
+
490
+ context "with usage of result oids for copy encoder selection" do
491
+ it "can type cast #copy_data input with explicit encoder" do
492
+ @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
493
+
494
+ # Retrieve table OIDs per empty result set.
495
+ res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
496
+ tm = basic_type_mapping.build_column_map( res )
497
+ row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
498
+
499
+ @conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
500
+ @conn.put_copy_data ['a', 123, [5,4,3]]
501
+ @conn.put_copy_data ['b', 234, [2,3]]
502
+ end
503
+ res = @conn.exec( "SELECT * FROM copytable" )
504
+ expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
505
+ end
506
+ end
507
+ end
508
+ end