pg 1.0.0 → 1.2.3

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 (64) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +156 -0
  6. data/Manifest.txt +8 -2
  7. data/README-Windows.rdoc +4 -4
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +55 -9
  10. data/Rakefile +9 -7
  11. data/Rakefile.cross +58 -57
  12. data/ext/errorcodes.def +68 -0
  13. data/ext/errorcodes.rb +1 -1
  14. data/ext/errorcodes.txt +19 -2
  15. data/ext/extconf.rb +7 -5
  16. data/ext/pg.c +141 -98
  17. data/ext/pg.h +64 -21
  18. data/ext/pg_binary_decoder.c +82 -15
  19. data/ext/pg_binary_encoder.c +13 -12
  20. data/ext/pg_coder.c +73 -12
  21. data/ext/pg_connection.c +625 -346
  22. data/ext/pg_copy_coder.c +16 -8
  23. data/ext/pg_record_coder.c +491 -0
  24. data/ext/pg_result.c +571 -191
  25. data/ext/pg_text_decoder.c +606 -40
  26. data/ext/pg_text_encoder.c +185 -54
  27. data/ext/pg_tuple.c +549 -0
  28. data/ext/pg_type_map.c +1 -1
  29. data/ext/pg_type_map_all_strings.c +4 -4
  30. data/ext/pg_type_map_by_class.c +9 -4
  31. data/ext/pg_type_map_by_column.c +7 -6
  32. data/ext/pg_type_map_by_mri_type.c +1 -1
  33. data/ext/pg_type_map_by_oid.c +3 -2
  34. data/ext/pg_type_map_in_ruby.c +1 -1
  35. data/ext/{util.c → pg_util.c} +10 -10
  36. data/ext/{util.h → pg_util.h} +2 -2
  37. data/lib/pg.rb +8 -6
  38. data/lib/pg/basic_type_mapping.rb +121 -25
  39. data/lib/pg/binary_decoder.rb +23 -0
  40. data/lib/pg/coder.rb +23 -2
  41. data/lib/pg/connection.rb +22 -3
  42. data/lib/pg/constants.rb +2 -1
  43. data/lib/pg/exceptions.rb +2 -1
  44. data/lib/pg/result.rb +14 -2
  45. data/lib/pg/text_decoder.rb +21 -26
  46. data/lib/pg/text_encoder.rb +32 -8
  47. data/lib/pg/tuple.rb +30 -0
  48. data/lib/pg/type_map_by_column.rb +3 -2
  49. data/spec/helpers.rb +52 -20
  50. data/spec/pg/basic_type_mapping_spec.rb +362 -37
  51. data/spec/pg/connection_spec.rb +376 -146
  52. data/spec/pg/connection_sync_spec.rb +41 -0
  53. data/spec/pg/result_spec.rb +240 -15
  54. data/spec/pg/tuple_spec.rb +333 -0
  55. data/spec/pg/type_map_by_class_spec.rb +2 -2
  56. data/spec/pg/type_map_by_column_spec.rb +6 -2
  57. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  58. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  59. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  60. data/spec/pg/type_map_spec.rb +1 -1
  61. data/spec/pg/type_spec.rb +363 -17
  62. data/spec/pg_spec.rb +1 -1
  63. metadata +47 -47
  64. metadata.gz.sig +0 -0
@@ -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
 
@@ -16,8 +32,8 @@ describe 'Basic type mapping' do
16
32
  # Encoding Examples
17
33
  #
18
34
 
19
- it "should do basic param encoding", :ruby_19 do
20
- res = @conn.exec_params( "SELECT $1::int8,$2::float,$3,$4::TEXT",
35
+ it "should do basic param encoding" do
36
+ res = @conn.exec_params( "SELECT $1::int8, $2::float, $3, $4::TEXT",
21
37
  [1, 2.1, true, "b"], nil, basic_type_mapping )
22
38
 
23
39
  expect( res.values ).to eq( [
@@ -27,21 +43,168 @@ describe 'Basic type mapping' do
27
43
  expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
28
44
  end
29
45
 
30
- it "should do array param encoding" do
31
- res = @conn.exec_params( "SELECT $1,$2,$3,$4", [
32
- [1, 2, 3], [[1, 2], [3, nil]],
33
- [1.11, 2.21],
34
- ['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
46
+ it "should do basic Time encoding" do
47
+ res = @conn.exec_params( "SELECT $1 AT TIME ZONE '-02'",
48
+ [Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], nil, basic_type_mapping )
49
+
50
+ expect( res.values ).to eq( [[ "2019-12-08 23:38:12.123" ]] )
51
+ end
52
+
53
+ it "should do basic param encoding of various float values" do
54
+ res = @conn.exec_params( "SELECT $1::float, $2::float, $3::float, $4::float, $5::float, $6::float, $7::float, $8::float, $9::float, $10::float, $11::float, $12::float",
55
+ [0, 7, 9, 0.1, 0.9, -0.11, 10.11,
56
+ 9876543210987654321e-400,
57
+ 9876543210987654321e400,
58
+ -1.234567890123456789e-280,
59
+ -1.234567890123456789e280,
60
+ 9876543210987654321e280
61
+ ], nil, basic_type_mapping )
62
+
63
+ expect( res.values[0][0, 9] ).to eq(
64
+ [ "0", "7", "9", "0.1", "0.9", "-0.11", "10.11", "0", "Infinity" ]
65
+ )
66
+
67
+ expect( res.values[0][9] ).to match( /^-1\.2345678901234\d*e\-280$/ )
68
+ expect( res.values[0][10] ).to match( /^-1\.2345678901234\d*e\+280$/ )
69
+ expect( res.values[0][11] ).to match( /^9\.8765432109876\d*e\+298$/ )
70
+
71
+ expect( result_typenames(res) ).to eq( ['double precision'] * 12 )
72
+ end
73
+
74
+ it "should do default array-as-array param encoding" do
75
+ expect( basic_type_mapping.encode_array_as).to eq(:array)
76
+ res = @conn.exec_params( "SELECT $1,$2,$3,$4,$5,$6", [
77
+ [1, 2, 3], # Integer -> bigint[]
78
+ [[1, 2], [3, nil]], # Integer two dimensions -> bigint[]
79
+ [1.11, 2.21], # Float -> double precision[]
80
+ ['/,"'.gsub("/", "\\"), nil, 'abcäöü'], # String -> text[]
81
+ [BigDecimal("123.45")], # BigDecimal -> numeric[]
82
+ [IPAddr.new('1234::5678')], # IPAddr -> inet[]
35
83
  ], nil, basic_type_mapping )
36
84
 
37
85
  expect( res.values ).to eq( [[
38
- '{1,2,3}', '{{1,2},{3,NULL}}',
86
+ '{1,2,3}',
87
+ '{{1,2},{3,NULL}}',
39
88
  '{1.11,2.21}',
40
89
  '{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
90
+ '{123.45}',
91
+ '{1234::5678}',
92
+ ]] )
93
+
94
+ expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]', 'numeric[]', 'inet[]'] )
95
+ end
96
+
97
+ it "should do default array-as-array param encoding with Time objects" do
98
+ res = @conn.exec_params( "SELECT $1", [
99
+ [Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], # Time -> timestamptz[]
100
+ ], nil, basic_type_mapping )
101
+
102
+ expect( res.values[0][0] ).to match( /\{\"2019-12-08 \d\d:38:12.123[+-]\d\d\"\}/ )
103
+ expect( result_typenames(res) ).to eq( ['timestamp with time zone[]'] )
104
+ end
105
+
106
+ it "should do array-as-json encoding" do
107
+ basic_type_mapping.encode_array_as = :json
108
+ expect( basic_type_mapping.encode_array_as).to eq(:json)
109
+
110
+ res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
111
+ [1, {a: 5}, true, ["a", 2], [3.4, nil]],
112
+ ['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
113
+ ], nil, basic_type_mapping )
114
+
115
+ expect( res.values ).to eq( [[
116
+ '[1,{"a":5},true,["a",2],[3.4,null]]',
117
+ '["//,/"",null,"abcäöü"]'.gsub("/", "\\"),
118
+ ]] )
119
+
120
+ expect( result_typenames(res) ).to eq( ['json', 'json'] )
121
+ end
122
+
123
+ it "should do hash-as-json encoding" do
124
+ res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
125
+ {a: 5, b: ["a", 2], c: nil},
126
+ {qu: '/,"'.gsub("/", "\\"), ni: nil, uml: 'abcäöü'},
127
+ ], nil, basic_type_mapping )
128
+
129
+ expect( res.values ).to eq( [[
130
+ '{"a":5,"b":["a",2],"c":null}',
131
+ '{"qu":"//,/"","ni":null,"uml":"abcäöü"}'.gsub("/", "\\"),
132
+ ]] )
133
+
134
+ expect( result_typenames(res) ).to eq( ['json', 'json'] )
135
+ end
136
+
137
+ describe "Record encoding" do
138
+ before :all do
139
+ @conn.exec("CREATE TYPE test_record1 AS (i int, d float, t text)")
140
+ @conn.exec("CREATE TYPE test_record2 AS (i int, r test_record1)")
141
+ end
142
+
143
+ after :all do
144
+ @conn.exec("DROP TYPE IF EXISTS test_record2 CASCADE")
145
+ @conn.exec("DROP TYPE IF EXISTS test_record1 CASCADE")
146
+ end
147
+
148
+ it "should do array-as-record encoding" do
149
+ basic_type_mapping.encode_array_as = :record
150
+ expect( basic_type_mapping.encode_array_as).to eq(:record)
151
+
152
+ res = @conn.exec_params( "SELECT $1::test_record1, $2::test_record2, $3::text", [
153
+ [5, 3.4, "txt"],
154
+ [1, [2, 4.5, "bcd"]],
155
+ [4, 5, 6],
156
+ ], nil, basic_type_mapping )
157
+
158
+ expect( res.values ).to eq( [[
159
+ '(5,3.4,txt)',
160
+ '(1,"(2,4.5,bcd)")',
161
+ '("4","5","6")',
162
+ ]] )
163
+
164
+ expect( result_typenames(res) ).to eq( ['test_record1', 'test_record2', 'text'] )
165
+ end
166
+ end
167
+
168
+ it "should do bigdecimal param encoding" do
169
+ large = ('123456790'*10) << '.' << ('012345679')
170
+ res = @conn.exec_params( "SELECT $1::numeric,$2::numeric",
171
+ [BigDecimal('1'), BigDecimal(large)], nil, basic_type_mapping )
172
+
173
+ expect( res.values ).to eq( [
174
+ [ "1.0", large ],
175
+ ] )
176
+
177
+ expect( result_typenames(res) ).to eq( ['numeric', 'numeric'] )
178
+ end
179
+
180
+ it "should do IPAddr param encoding" do
181
+ res = @conn.exec_params( "SELECT $1::inet,$2::inet,$3::cidr,$4::cidr",
182
+ ['1.2.3.4', IPAddr.new('1234::5678'), '1.2.3.4', IPAddr.new('1234:5678::/32')], nil, basic_type_mapping )
183
+
184
+ expect( res.values ).to eq( [
185
+ [ '1.2.3.4', '1234::5678', '1.2.3.4/32', '1234:5678::/32'],
186
+ ] )
187
+
188
+ expect( result_typenames(res) ).to eq( ['inet', 'inet', 'cidr', 'cidr'] )
189
+ end
190
+
191
+ it "should do array of string encoding on unknown classes" do
192
+ iv = Class.new do
193
+ def to_s
194
+ "abc"
195
+ end
196
+ end.new
197
+ res = @conn.exec_params( "SELECT $1", [
198
+ [iv, iv], # Unknown -> text[]
199
+ ], nil, basic_type_mapping )
200
+
201
+ expect( res.values ).to eq( [[
202
+ '{abc,abc}',
41
203
  ]] )
42
204
 
43
- expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
205
+ expect( result_typenames(res) ).to eq( ['text[]'] )
44
206
  end
207
+
45
208
  end
46
209
 
47
210
 
@@ -55,7 +218,7 @@ describe 'Basic type mapping' do
55
218
  # Decoding Examples
56
219
  #
57
220
 
58
- it "should do OID based type conversions", :ruby_19 do
221
+ it "should do OID based type conversions" do
59
222
  res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
60
223
  expect( res.map_types!(basic_type_mapping).values ).to eq( [
61
224
  [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
@@ -78,14 +241,14 @@ describe 'Basic type mapping' do
78
241
 
79
242
  it "should do boolean type conversions" do
80
243
  [1, 0].each do |format|
81
- res = @conn.exec( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
244
+ res = @conn.exec_params( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
82
245
  expect( res.values ).to eq( [[true, false, nil]] )
83
246
  end
84
247
  end
85
248
 
86
249
  it "should do binary type conversions" do
87
250
  [1, 0].each do |format|
88
- res = @conn.exec( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
251
+ res = @conn.exec_params( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
89
252
  expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
90
253
  expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
91
254
  end
@@ -93,7 +256,7 @@ describe 'Basic type mapping' do
93
256
 
94
257
  it "should do integer type conversions" do
95
258
  [1, 0].each do |format|
96
- res = @conn.exec( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
259
+ res = @conn.exec_params( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
97
260
  expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
98
261
  end
99
262
  end
@@ -101,7 +264,7 @@ describe 'Basic type mapping' do
101
264
  it "should do string type conversions" do
102
265
  @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
103
266
  [1, 0].each do |format|
104
- res = @conn.exec( "SELECT 'abcäöü'::TEXT", [], format )
267
+ res = @conn.exec_params( "SELECT 'abcäöü'::TEXT", [], format )
105
268
  expect( res.values ).to eq( [['abcäöü']] )
106
269
  expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
107
270
  end
@@ -109,7 +272,7 @@ describe 'Basic type mapping' do
109
272
 
110
273
  it "should do float type conversions" do
111
274
  [1, 0].each do |format|
112
- res = @conn.exec( "SELECT -8.999e3::FLOAT4,
275
+ res = @conn.exec_params( "SELECT -8.999e3::FLOAT4,
113
276
  8.999e10::FLOAT4,
114
277
  -8999999999e-99::FLOAT8,
115
278
  NULL::FLOAT4,
@@ -127,35 +290,107 @@ describe 'Basic type mapping' do
127
290
  end
128
291
  end
129
292
 
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' )
293
+ it "should do text datetime without time zone type conversions" do
294
+ # for backward compat text timestamps without time zone are treated as local times
295
+ res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
296
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
297
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
298
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
299
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
300
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], 0 )
301
+ expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
302
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
303
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
304
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
305
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
306
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
307
+ end
308
+
309
+ [1, 0].each do |format|
310
+ it "should convert format #{format} timestamps per TimestampUtc" do
311
+ restore_type("timestamp") do
312
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtc
313
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
314
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
315
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
316
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
317
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
318
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
319
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
320
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).iso8601(3) )
321
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
322
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
323
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
324
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
325
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
326
+ end
140
327
  end
141
328
  end
142
329
 
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),
330
+ [1, 0].each do |format|
331
+ it "should convert format #{format} timestamps per TimestampUtcToLocal" do
332
+ restore_type("timestamp") do
333
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtcToLocal
334
+ PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtcToLocal
335
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
336
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
337
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
338
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
339
+ CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
340
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
341
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
342
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).getlocal.iso8601(3) )
343
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
344
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).getlocal.iso8601(3) )
345
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
346
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
347
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
348
+ end
349
+ end
350
+ end
351
+
352
+ [1, 0].each do |format|
353
+ it "should convert format #{format} timestamps per TimestampLocal" do
354
+ restore_type("timestamp") do
355
+ PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampLocal
356
+ PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampLocal
357
+ @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
358
+ res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59' AS TIMESTAMP WITHOUT TIME ZONE),
359
+ CAST('1913-12-31 23:58:59.1231' AS TIMESTAMP WITHOUT TIME ZONE),
360
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
361
+ CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITHOUT TIME ZONE),
362
+ CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
363
+ CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
364
+ expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.new(2013, 7, 31, 23, 58, 59).iso8601(3) )
365
+ expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
366
+ expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
367
+ expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
368
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
369
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
370
+ end
371
+ end
372
+ end
373
+
374
+ [0, 1].each do |format|
375
+ it "should convert format #{format} timestamps with time zone" do
376
+ res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
377
+ CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITH TIME ZONE),
378
+ CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITH TIME ZONE),
379
+ CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITH TIME ZONE),
147
380
  CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
148
381
  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' )
382
+ expect( res.getvalue(0,0) ).to be_within(1e-3).of( Time.new(2013, 12, 31, 23, 58, 59, "+02:00").getlocal )
383
+ expect( res.getvalue(0,1) ).to be_within(1e-3).of( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").getlocal )
384
+ expect( res.getvalue(0,2) ).to be_within(1e-3).of( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").getlocal )
385
+ expect( res.getvalue(0,3) ).to be_within(1e-3).of( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").getlocal )
386
+ expect( res.getvalue(0,4) ).to eq( 'infinity' )
387
+ expect( res.getvalue(0,5) ).to eq( '-infinity' )
153
388
  end
154
389
  end
155
390
 
156
391
  it "should do date type conversions" do
157
392
  [0].each do |format|
158
- res = @conn.exec( "SELECT CAST('2113-12-31' AS DATE),
393
+ res = @conn.exec_params( "SELECT CAST('2113-12-31' AS DATE),
159
394
  CAST('1913-12-31' AS DATE),
160
395
  CAST('infinity' AS DATE),
161
396
  CAST('-infinity' AS DATE)", [], format )
@@ -166,10 +401,31 @@ describe 'Basic type mapping' do
166
401
  end
167
402
  end
168
403
 
404
+ it "should do numeric type conversions" do
405
+ [0].each do |format|
406
+ small = '123456790123.12'
407
+ large = ('123456790'*10) << '.' << ('012345679')
408
+ numerics = [
409
+ '1',
410
+ '1.0',
411
+ '1.2',
412
+ small,
413
+ large,
414
+ ]
415
+ sql_numerics = numerics.map { |v| "CAST(#{v} AS numeric)" }
416
+ res = @conn.exec_params( "SELECT #{sql_numerics.join(',')}", [], format )
417
+ expect( res.getvalue(0,0) ).to eq( BigDecimal('1') )
418
+ expect( res.getvalue(0,1) ).to eq( BigDecimal('1') )
419
+ expect( res.getvalue(0,2) ).to eq( BigDecimal('1.2') )
420
+ expect( res.getvalue(0,3) ).to eq( BigDecimal(small) )
421
+ expect( res.getvalue(0,4) ).to eq( BigDecimal(large) )
422
+ end
423
+ end
424
+
169
425
  it "should do JSON conversions", :postgresql_94 do
170
426
  [0].each do |format|
171
427
  ['JSON', 'JSONB'].each do |type|
172
- res = @conn.exec( "SELECT CAST('123' AS #{type}),
428
+ res = @conn.exec_params( "SELECT CAST('123' AS #{type}),
173
429
  CAST('12.3' AS #{type}),
174
430
  CAST('true' AS #{type}),
175
431
  CAST('false' AS #{type}),
@@ -189,7 +445,7 @@ describe 'Basic type mapping' do
189
445
 
190
446
  it "should do array type conversions" do
191
447
  [0].each do |format|
192
- res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
448
+ res = @conn.exec_params( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
193
449
  CAST('{1,2,3}' AS INT4[]),
194
450
  CAST('{1,2,3}' AS INT8[]),
195
451
  CAST('{1,2,3}' AS TEXT[]),
@@ -207,6 +463,75 @@ describe 'Basic type mapping' do
207
463
  expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
208
464
  end
209
465
  end
466
+
467
+ it "should do inet type conversions" do
468
+ [0].each do |format|
469
+ vals = [
470
+ '1.2.3.4',
471
+ '0.0.0.0/0',
472
+ '1.0.0.0/8',
473
+ '1.2.0.0/16',
474
+ '1.2.3.0/24',
475
+ '1.2.3.4/24',
476
+ '1.2.3.4/32',
477
+ '1.2.3.128/25',
478
+ '1234:3456:5678:789a:9abc:bced:edf0:f012',
479
+ '::/0',
480
+ '1234:3456::/32',
481
+ '1234:3456:5678:789a::/64',
482
+ '1234:3456:5678:789a:9abc:bced::/96',
483
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
484
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/0',
485
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/32',
486
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/64',
487
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/96',
488
+ ]
489
+ sql_vals = vals.map{|v| "CAST('#{v}' AS inet)"}
490
+ res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
491
+ vals.each_with_index do |v, i|
492
+ expect( res.getvalue(0,i) ).to eq( IPAddr.new(v) )
493
+ end
494
+ end
495
+ end
496
+
497
+ it "should do cidr type conversions" do
498
+ [0].each do |format|
499
+ vals = [
500
+ '0.0.0.0/0',
501
+ '1.0.0.0/8',
502
+ '1.2.0.0/16',
503
+ '1.2.3.0/24',
504
+ '1.2.3.4/32',
505
+ '1.2.3.128/25',
506
+ '::/0',
507
+ '1234:3456::/32',
508
+ '1234:3456:5678:789a::/64',
509
+ '1234:3456:5678:789a:9abc:bced::/96',
510
+ '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
511
+ ]
512
+ sql_vals = vals.map { |v| "CAST('#{v}' AS cidr)" }
513
+ res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
514
+ vals.each_with_index do |v, i|
515
+ val = res.getvalue(0,i)
516
+ ip, prefix = v.split('/', 2)
517
+ expect( val.to_s ).to eq( ip )
518
+ if val.respond_to?(:prefix)
519
+ val_prefix = val.prefix
520
+ else
521
+ default_prefix = (val.family == Socket::AF_INET ? 32 : 128)
522
+ range = val.to_range
523
+ val_prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
524
+ end
525
+ if v.include?('/')
526
+ expect( val_prefix ).to eq( prefix.to_i )
527
+ elsif v.include?('.')
528
+ expect( val_prefix ).to eq( 32 )
529
+ else
530
+ expect( val_prefix ).to eq( 128 )
531
+ end
532
+ end
533
+ end
534
+ end
210
535
  end
211
536
 
212
537
  context "with usage of result oids for copy decoder selection" do