pg 0.21.0 → 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 (55) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/History.rdoc +98 -0
  5. data/Manifest.txt +5 -1
  6. data/README.rdoc +14 -4
  7. data/Rakefile +4 -5
  8. data/Rakefile.cross +17 -21
  9. data/ext/errorcodes.def +12 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +4 -1
  12. data/ext/extconf.rb +14 -32
  13. data/ext/gvl_wrappers.c +4 -0
  14. data/ext/gvl_wrappers.h +23 -39
  15. data/ext/pg.c +23 -50
  16. data/ext/pg.h +51 -81
  17. data/ext/pg_binary_decoder.c +73 -6
  18. data/ext/pg_coder.c +52 -3
  19. data/ext/pg_connection.c +369 -219
  20. data/ext/pg_copy_coder.c +10 -5
  21. data/ext/pg_result.c +343 -119
  22. data/ext/pg_text_decoder.c +597 -37
  23. data/ext/pg_text_encoder.c +6 -7
  24. data/ext/pg_tuple.c +541 -0
  25. data/ext/util.c +6 -6
  26. data/ext/util.h +2 -2
  27. data/lib/pg.rb +5 -7
  28. data/lib/pg/basic_type_mapping.rb +40 -7
  29. data/lib/pg/binary_decoder.rb +22 -0
  30. data/lib/pg/coder.rb +1 -1
  31. data/lib/pg/connection.rb +27 -3
  32. data/lib/pg/constants.rb +1 -1
  33. data/lib/pg/exceptions.rb +1 -1
  34. data/lib/pg/result.rb +1 -1
  35. data/lib/pg/text_decoder.rb +19 -23
  36. data/lib/pg/text_encoder.rb +35 -1
  37. data/lib/pg/tuple.rb +30 -0
  38. data/lib/pg/type_map_by_column.rb +1 -1
  39. data/spec/helpers.rb +49 -21
  40. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  41. data/spec/pg/connection_spec.rb +473 -277
  42. data/spec/pg/connection_sync_spec.rb +41 -0
  43. data/spec/pg/result_spec.rb +48 -13
  44. data/spec/pg/tuple_spec.rb +280 -0
  45. data/spec/pg/type_map_by_class_spec.rb +1 -1
  46. data/spec/pg/type_map_by_column_spec.rb +1 -1
  47. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  48. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  49. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  50. data/spec/pg/type_map_spec.rb +1 -1
  51. data/spec/pg/type_spec.rb +184 -12
  52. data/spec/pg_spec.rb +2 -2
  53. metadata +37 -33
  54. metadata.gz.sig +0 -0
  55. data/lib/pg/deprecated_constants.rb +0 -21
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
@@ -1,11 +1,13 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pathname'
4
4
  require 'rspec'
5
5
  require 'shellwords'
6
6
  require 'pg'
7
7
 
8
- TEST_DIRECTORY = Pathname.getwd + "tmp_test_specs"
8
+ DEFAULT_TEST_DIR_STR = File.join(Dir.pwd, "tmp_test_specs")
9
+ TEST_DIR_STR = ENV['RUBY_PG_TEST_DIR'] || DEFAULT_TEST_DIR_STR
10
+ TEST_DIRECTORY = Pathname.new(TEST_DIR_STR)
9
11
 
10
12
  module PG::TestingHelpers
11
13
 
@@ -20,12 +22,11 @@ module PG::TestingHelpers
20
22
 
21
23
  mod.around( :each ) do |example|
22
24
  begin
25
+ @conn.set_default_encoding
23
26
  @conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
24
- if PG.respond_to?( :library_version )
25
- desc = example.source_location.join(':')
26
- @conn.exec_params %Q{SET application_name TO '%s'} %
27
- [@conn.escape_string(desc.slice(-60))]
28
- end
27
+ desc = example.source_location.join(':')
28
+ @conn.exec %Q{SET application_name TO '%s'} %
29
+ [@conn.escape_string(desc.slice(-60))]
29
30
  example.run
30
31
  ensure
31
32
  @conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
@@ -197,8 +198,8 @@ module PG::TestingHelpers
197
198
  @test_pgdata = TEST_DIRECTORY + 'data'
198
199
  @test_pgdata.mkpath
199
200
 
200
- @port = 54321
201
- ENV['PGPORT'] = @port.to_s
201
+ ENV['PGPORT'] ||= "54321"
202
+ @port = ENV['PGPORT'].to_i
202
203
  ENV['PGHOST'] = 'localhost'
203
204
  @conninfo = "host=localhost port=#{@port} dbname=test"
204
205
 
@@ -251,7 +252,7 @@ module PG::TestingHelpers
251
252
 
252
253
  def check_for_lingering_connections( conn )
253
254
  conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
254
- conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
255
+ conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid && ["client backend", nil].include?(row["backend_type"]) }
255
256
  unless conns.empty?
256
257
  puts "Lingering connections remain:"
257
258
  conns.each do |row|
@@ -264,7 +265,7 @@ module PG::TestingHelpers
264
265
 
265
266
  # Retrieve the names of the column types of a given result set.
266
267
  def result_typenames(res)
267
- @conn.exec( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
268
+ @conn.exec_params( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
268
269
  res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
269
270
  values[0]
270
271
  end
@@ -318,6 +319,39 @@ module PG::TestingHelpers
318
319
  return ConnStillUsableMatcher.new
319
320
  end
320
321
 
322
+ def wait_for_polling_ok(conn, meth = :connect_poll)
323
+ status = conn.send(meth)
324
+
325
+ while status != PG::PGRES_POLLING_OK
326
+ if status == PG::PGRES_POLLING_READING
327
+ select( [conn.socket_io], [], [], 5.0 ) or
328
+ raise "Asynchronous connection timed out!"
329
+
330
+ elsif status == PG::PGRES_POLLING_WRITING
331
+ select( [], [conn.socket_io], [], 5.0 ) or
332
+ raise "Asynchronous connection timed out!"
333
+ end
334
+ status = conn.send(meth)
335
+ end
336
+ end
337
+
338
+ def wait_for_query_result(conn)
339
+ result = nil
340
+ loop do
341
+ # Buffer any incoming data on the socket until a full result is ready.
342
+ conn.consume_input
343
+ while conn.is_busy
344
+ select( [conn.socket_io], nil, nil, 5.0 ) or
345
+ raise "Timeout waiting for query response."
346
+ conn.consume_input
347
+ end
348
+
349
+ # Fetch the next result. If there isn't one, the query is finished
350
+ result = conn.get_result || break
351
+ end
352
+ result
353
+ end
354
+
321
355
  end
322
356
 
323
357
 
@@ -339,14 +373,8 @@ RSpec.configure do |config|
339
373
  config.filter_run_excluding :socket_io unless
340
374
  PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
341
375
 
342
- if PG.library_version < 90200
343
- config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94, :postgresql_95 )
344
- elsif PG.library_version < 90300
345
- config.filter_run_excluding( :postgresql_93, :postgresql_94, :postgresql_95 )
346
- elsif PG.library_version < 90400
347
- config.filter_run_excluding( :postgresql_94, :postgresql_95 )
348
- elsif PG.library_version < 90500
349
- config.filter_run_excluding( :postgresql_95 )
350
- end
376
+ config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
377
+ config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
378
+ config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
379
+ config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
351
380
  end
352
-
@@ -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
 
@@ -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,35 +167,107 @@ 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 )
@@ -166,10 +278,31 @@ describe 'Basic type mapping' do
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
+
169
302
  it "should do JSON conversions", :postgresql_94 do
170
303
  [0].each do |format|
171
304
  ['JSON', 'JSONB'].each do |type|
172
- res = @conn.exec( "SELECT CAST('123' AS #{type}),
305
+ res = @conn.exec_params( "SELECT CAST('123' AS #{type}),
173
306
  CAST('12.3' AS #{type}),
174
307
  CAST('true' AS #{type}),
175
308
  CAST('false' AS #{type}),
@@ -189,7 +322,7 @@ describe 'Basic type mapping' do
189
322
 
190
323
  it "should do array type conversions" do
191
324
  [0].each do |format|
192
- 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[][]),
193
326
  CAST('{1,2,3}' AS INT4[]),
194
327
  CAST('{1,2,3}' AS INT8[]),
195
328
  CAST('{1,2,3}' AS TEXT[]),
@@ -207,6 +340,76 @@ describe 'Basic type mapping' do
207
340
  expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
208
341
  end
209
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
210
413
  end
211
414
 
212
415
  context "with usage of result oids for copy decoder selection" do