pg 0.18.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +1221 -4
  5. data/History.rdoc +200 -0
  6. data/Manifest.txt +5 -18
  7. data/README-Windows.rdoc +15 -26
  8. data/README.rdoc +27 -10
  9. data/Rakefile +33 -24
  10. data/Rakefile.cross +57 -39
  11. data/ext/errorcodes.def +37 -0
  12. data/ext/errorcodes.rb +1 -1
  13. data/ext/errorcodes.txt +16 -1
  14. data/ext/extconf.rb +29 -35
  15. data/ext/gvl_wrappers.c +4 -0
  16. data/ext/gvl_wrappers.h +27 -39
  17. data/ext/pg.c +27 -53
  18. data/ext/pg.h +66 -83
  19. data/ext/pg_binary_decoder.c +75 -6
  20. data/ext/pg_binary_encoder.c +14 -12
  21. data/ext/pg_coder.c +83 -13
  22. data/ext/pg_connection.c +627 -351
  23. data/ext/pg_copy_coder.c +44 -9
  24. data/ext/pg_result.c +364 -134
  25. data/ext/pg_text_decoder.c +605 -46
  26. data/ext/pg_text_encoder.c +95 -76
  27. data/ext/pg_tuple.c +541 -0
  28. data/ext/pg_type_map.c +20 -13
  29. data/ext/pg_type_map_by_column.c +7 -7
  30. data/ext/pg_type_map_by_mri_type.c +2 -2
  31. data/ext/pg_type_map_in_ruby.c +4 -7
  32. data/ext/util.c +7 -7
  33. data/ext/util.h +3 -3
  34. data/lib/pg/basic_type_mapping.rb +105 -45
  35. data/lib/pg/binary_decoder.rb +22 -0
  36. data/lib/pg/coder.rb +1 -1
  37. data/lib/pg/connection.rb +109 -39
  38. data/lib/pg/constants.rb +1 -1
  39. data/lib/pg/exceptions.rb +1 -1
  40. data/lib/pg/result.rb +11 -6
  41. data/lib/pg/text_decoder.rb +25 -20
  42. data/lib/pg/text_encoder.rb +43 -1
  43. data/lib/pg/tuple.rb +30 -0
  44. data/lib/pg/type_map_by_column.rb +1 -1
  45. data/lib/pg.rb +21 -11
  46. data/spec/helpers.rb +50 -25
  47. data/spec/pg/basic_type_mapping_spec.rb +287 -30
  48. data/spec/pg/connection_spec.rb +695 -282
  49. data/spec/pg/connection_sync_spec.rb +41 -0
  50. data/spec/pg/result_spec.rb +59 -17
  51. data/spec/pg/tuple_spec.rb +280 -0
  52. data/spec/pg/type_map_by_class_spec.rb +3 -3
  53. data/spec/pg/type_map_by_column_spec.rb +1 -1
  54. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  55. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  56. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  57. data/spec/pg/type_map_spec.rb +1 -1
  58. data/spec/pg/type_spec.rb +319 -35
  59. data/spec/pg_spec.rb +2 -2
  60. data.tar.gz.sig +0 -0
  61. metadata +68 -68
  62. metadata.gz.sig +0 -0
  63. data/sample/array_insert.rb +0 -20
  64. data/sample/async_api.rb +0 -106
  65. data/sample/async_copyto.rb +0 -39
  66. data/sample/async_mixed.rb +0 -56
  67. data/sample/check_conn.rb +0 -21
  68. data/sample/copyfrom.rb +0 -81
  69. data/sample/copyto.rb +0 -19
  70. data/sample/cursor.rb +0 -21
  71. data/sample/disk_usage_report.rb +0 -186
  72. data/sample/issue-119.rb +0 -94
  73. data/sample/losample.rb +0 -69
  74. data/sample/minimal-testcase.rb +0 -17
  75. data/sample/notify_wait.rb +0 -72
  76. data/sample/pg_statistics.rb +0 -294
  77. data/sample/replication_monitor.rb +0 -231
  78. data/sample/test_binary_values.rb +0 -33
  79. data/sample/wal_shipper.rb +0 -434
  80. data/sample/warehouse_partitions.rb +0 -320
@@ -1,9 +1,25 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
5
5
 
6
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
7
23
 
8
24
  describe 'Basic type mapping' do
9
25
 
@@ -42,6 +58,30 @@ describe 'Basic type mapping' do
42
58
 
43
59
  expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
44
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
+
45
85
  end
46
86
 
47
87
 
@@ -58,8 +98,8 @@ describe 'Basic type mapping' do
58
98
  it "should do OID based type conversions", :ruby_19 do
59
99
  res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
60
100
  expect( res.map_types!(basic_type_mapping).values ).to eq( [
61
- [ 1, 'a', 2.0, true, Time.new(2013,6,30), 4 ],
62
- [ 1, 'a', 2.0, true, Time.new(2013,6,30), 5 ],
101
+ [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
102
+ [ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
63
103
  ] )
64
104
  end
65
105
 
@@ -78,14 +118,14 @@ describe 'Basic type mapping' do
78
118
 
79
119
  it "should do boolean type conversions" do
80
120
  [1, 0].each do |format|
81
- res = @conn.exec( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
121
+ res = @conn.exec_params( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
82
122
  expect( res.values ).to eq( [[true, false, nil]] )
83
123
  end
84
124
  end
85
125
 
86
126
  it "should do binary type conversions" do
87
127
  [1, 0].each do |format|
88
- res = @conn.exec( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
128
+ res = @conn.exec_params( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
89
129
  expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
90
130
  expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
91
131
  end
@@ -93,7 +133,7 @@ describe 'Basic type mapping' do
93
133
 
94
134
  it "should do integer type conversions" do
95
135
  [1, 0].each do |format|
96
- res = @conn.exec( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
136
+ res = @conn.exec_params( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
97
137
  expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
98
138
  end
99
139
  end
@@ -101,7 +141,7 @@ describe 'Basic type mapping' do
101
141
  it "should do string type conversions" do
102
142
  @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
103
143
  [1, 0].each do |format|
104
- res = @conn.exec( "SELECT 'abcäöü'::TEXT", [], format )
144
+ res = @conn.exec_params( "SELECT 'abcäöü'::TEXT", [], format )
105
145
  expect( res.values ).to eq( [['abcäöü']] )
106
146
  expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
107
147
  end
@@ -109,7 +149,7 @@ describe 'Basic type mapping' do
109
149
 
110
150
  it "should do float type conversions" do
111
151
  [1, 0].each do |format|
112
- res = @conn.exec( "SELECT -8.999e3::FLOAT4,
152
+ res = @conn.exec_params( "SELECT -8.999e3::FLOAT4,
113
153
  8.999e10::FLOAT4,
114
154
  -8999999999e-99::FLOAT8,
115
155
  NULL::FLOAT4,
@@ -127,48 +167,162 @@ describe 'Basic type mapping' do
127
167
  end
128
168
  end
129
169
 
130
- it "should do datetime without time zone type conversions" do
131
- [0].each do |format|
132
- res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
133
- CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITHOUT TIME ZONE),
134
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
135
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
136
- expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
137
- expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123))
138
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
139
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
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
140
204
  end
141
205
  end
142
206
 
143
- it "should do datetime with time zone type conversions" do
144
- [0].each do |format|
145
- res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
146
- CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITH TIME ZONE),
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),
147
257
  CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
148
258
  CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
149
- expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59, "+02:00") )
150
- expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123, "-03:00"))
151
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
152
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
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' )
153
265
  end
154
266
  end
155
267
 
156
268
  it "should do date type conversions" do
157
269
  [0].each do |format|
158
- res = @conn.exec( "SELECT CAST('2113-12-31' AS DATE),
270
+ res = @conn.exec_params( "SELECT CAST('2113-12-31' AS DATE),
159
271
  CAST('1913-12-31' AS DATE),
160
272
  CAST('infinity' AS DATE),
161
273
  CAST('-infinity' AS DATE)", [], format )
162
- expect( res.getvalue(0,0) ).to eq( Time.new(2113, 12, 31) )
163
- expect( res.getvalue(0,1) ).to eq( Time.new(1913, 12, 31) )
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) )
164
276
  expect( res.getvalue(0,2) ).to eq( 'infinity' )
165
277
  expect( res.getvalue(0,3) ).to eq( '-infinity' )
166
278
  end
167
279
  end
168
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
+
169
323
  it "should do array type conversions" do
170
324
  [0].each do |format|
171
- res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
325
+ res = @conn.exec_params( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
172
326
  CAST('{1,2,3}' AS INT4[]),
173
327
  CAST('{1,2,3}' AS INT8[]),
174
328
  CAST('{1,2,3}' AS TEXT[]),
@@ -186,6 +340,76 @@ describe 'Basic type mapping' do
186
340
  expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
187
341
  end
188
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
189
413
  end
190
414
 
191
415
  context "with usage of result oids for copy decoder selection" do
@@ -228,6 +452,39 @@ describe 'Basic type mapping' do
228
452
  res = @conn.exec( "SELECT * FROM copytable" )
229
453
  expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
230
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
231
488
  end
232
489
 
233
490
  context "with usage of result oids for copy encoder selection" do