pg 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
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