pg 1.2.3 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +80 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +75 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +5 -5
  20. data/certs/ged.pem +24 -0
  21. data/ext/errorcodes.def +8 -0
  22. data/ext/errorcodes.txt +3 -1
  23. data/ext/extconf.rb +90 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +35 -1
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +538 -279
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +38 -10
  32. data/ext/pg_result.c +61 -31
  33. data/ext/pg_text_decoder.c +1 -1
  34. data/ext/pg_text_encoder.c +6 -6
  35. data/ext/pg_tuple.c +47 -21
  36. data/ext/pg_type_map.c +41 -8
  37. data/ext/pg_type_map_all_strings.c +14 -1
  38. data/ext/pg_type_map_by_class.c +49 -24
  39. data/ext/pg_type_map_by_column.c +64 -28
  40. data/ext/pg_type_map_by_mri_type.c +47 -18
  41. data/ext/pg_type_map_by_oid.c +52 -23
  42. data/ext/pg_type_map_in_ruby.c +50 -19
  43. data/ext/pg_util.c +2 -2
  44. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  45. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  46. data/lib/pg/basic_type_map_for_results.rb +81 -0
  47. data/lib/pg/basic_type_registry.rb +296 -0
  48. data/lib/pg/coder.rb +1 -1
  49. data/lib/pg/connection.rb +369 -56
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +38 -25
  52. data/misc/openssl-pg-segfault.rb +31 -0
  53. data/misc/postgres/History.txt +9 -0
  54. data/misc/postgres/Manifest.txt +5 -0
  55. data/misc/postgres/README.txt +21 -0
  56. data/misc/postgres/Rakefile +21 -0
  57. data/misc/postgres/lib/postgres.rb +16 -0
  58. data/misc/ruby-pg/History.txt +9 -0
  59. data/misc/ruby-pg/Manifest.txt +5 -0
  60. data/misc/ruby-pg/README.txt +21 -0
  61. data/misc/ruby-pg/Rakefile +21 -0
  62. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  63. data/pg.gemspec +32 -0
  64. data/sample/array_insert.rb +20 -0
  65. data/sample/async_api.rb +106 -0
  66. data/sample/async_copyto.rb +39 -0
  67. data/sample/async_mixed.rb +56 -0
  68. data/sample/check_conn.rb +21 -0
  69. data/sample/copydata.rb +71 -0
  70. data/sample/copyfrom.rb +81 -0
  71. data/sample/copyto.rb +19 -0
  72. data/sample/cursor.rb +21 -0
  73. data/sample/disk_usage_report.rb +177 -0
  74. data/sample/issue-119.rb +94 -0
  75. data/sample/losample.rb +69 -0
  76. data/sample/minimal-testcase.rb +17 -0
  77. data/sample/notify_wait.rb +72 -0
  78. data/sample/pg_statistics.rb +285 -0
  79. data/sample/replication_monitor.rb +222 -0
  80. data/sample/test_binary_values.rb +33 -0
  81. data/sample/wal_shipper.rb +434 -0
  82. data/sample/warehouse_partitions.rb +311 -0
  83. data.tar.gz.sig +0 -0
  84. metadata +79 -226
  85. metadata.gz.sig +0 -0
  86. data/ChangeLog +0 -0
  87. data/lib/pg/basic_type_mapping.rb +0 -522
  88. data/spec/data/expected_trace.out +0 -26
  89. data/spec/data/random_binary_data +0 -0
  90. data/spec/helpers.rb +0 -380
  91. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  92. data/spec/pg/connection_spec.rb +0 -1949
  93. data/spec/pg/connection_sync_spec.rb +0 -41
  94. data/spec/pg/result_spec.rb +0 -681
  95. data/spec/pg/tuple_spec.rb +0 -333
  96. data/spec/pg/type_map_by_class_spec.rb +0 -138
  97. data/spec/pg/type_map_by_column_spec.rb +0 -226
  98. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  99. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  100. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  101. data/spec/pg/type_map_spec.rb +0 -22
  102. data/spec/pg/type_spec.rb +0 -1123
  103. data/spec/pg_spec.rb +0 -50
@@ -1,630 +0,0 @@
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" 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 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[]
83
- ], nil, basic_type_mapping )
84
-
85
- expect( res.values ).to eq( [[
86
- '{1,2,3}',
87
- '{{1,2},{3,NULL}}',
88
- '{1.11,2.21}',
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}',
203
- ]] )
204
-
205
- expect( result_typenames(res) ).to eq( ['text[]'] )
206
- end
207
-
208
- end
209
-
210
-
211
-
212
- describe PG::BasicTypeMapForResults do
213
- let!(:basic_type_mapping) do
214
- PG::BasicTypeMapForResults.new @conn
215
- end
216
-
217
- #
218
- # Decoding Examples
219
- #
220
-
221
- it "should do OID based type conversions" do
222
- res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
223
- expect( res.map_types!(basic_type_mapping).values ).to eq( [
224
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
225
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
226
- ] )
227
- end
228
-
229
- #
230
- # Decoding Examples text+binary format converters
231
- #
232
-
233
- describe "connection wide type mapping" do
234
- before :each do
235
- @conn.type_map_for_results = basic_type_mapping
236
- end
237
-
238
- after :each do
239
- @conn.type_map_for_results = PG::TypeMapAllStrings.new
240
- end
241
-
242
- it "should do boolean type conversions" do
243
- [1, 0].each do |format|
244
- res = @conn.exec_params( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
245
- expect( res.values ).to eq( [[true, false, nil]] )
246
- end
247
- end
248
-
249
- it "should do binary type conversions" do
250
- [1, 0].each do |format|
251
- res = @conn.exec_params( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
252
- expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
253
- expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
254
- end
255
- end
256
-
257
- it "should do integer type conversions" do
258
- [1, 0].each do |format|
259
- res = @conn.exec_params( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
260
- expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
261
- end
262
- end
263
-
264
- it "should do string type conversions" do
265
- @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
266
- [1, 0].each do |format|
267
- res = @conn.exec_params( "SELECT 'abcäöü'::TEXT", [], format )
268
- expect( res.values ).to eq( [['abcäöü']] )
269
- expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
270
- end
271
- end
272
-
273
- it "should do float type conversions" do
274
- [1, 0].each do |format|
275
- res = @conn.exec_params( "SELECT -8.999e3::FLOAT4,
276
- 8.999e10::FLOAT4,
277
- -8999999999e-99::FLOAT8,
278
- NULL::FLOAT4,
279
- 'NaN'::FLOAT4,
280
- 'Infinity'::FLOAT4,
281
- '-Infinity'::FLOAT4
282
- ", [], format )
283
- expect( res.getvalue(0,0) ).to be_within(1e-2).of(-8.999e3)
284
- expect( res.getvalue(0,1) ).to be_within(1e5).of(8.999e10)
285
- expect( res.getvalue(0,2) ).to be_within(1e-109).of(-8999999999e-99)
286
- expect( res.getvalue(0,3) ).to be_nil
287
- expect( res.getvalue(0,4) ).to be_nan
288
- expect( res.getvalue(0,5) ).to eq( Float::INFINITY )
289
- expect( res.getvalue(0,6) ).to eq( -Float::INFINITY )
290
- end
291
- end
292
-
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
327
- end
328
- end
329
-
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),
380
- CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
381
- CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
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' )
388
- end
389
- end
390
-
391
- it "should do date type conversions" do
392
- [0].each do |format|
393
- res = @conn.exec_params( "SELECT CAST('2113-12-31' AS DATE),
394
- CAST('1913-12-31' AS DATE),
395
- CAST('infinity' AS DATE),
396
- CAST('-infinity' AS DATE)", [], format )
397
- expect( res.getvalue(0,0) ).to eq( Date.new(2113, 12, 31) )
398
- expect( res.getvalue(0,1) ).to eq( Date.new(1913, 12, 31) )
399
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
400
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
401
- end
402
- end
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
-
425
- it "should do JSON conversions", :postgresql_94 do
426
- [0].each do |format|
427
- ['JSON', 'JSONB'].each do |type|
428
- res = @conn.exec_params( "SELECT CAST('123' AS #{type}),
429
- CAST('12.3' AS #{type}),
430
- CAST('true' AS #{type}),
431
- CAST('false' AS #{type}),
432
- CAST('null' AS #{type}),
433
- CAST('[1, \"a\", null]' AS #{type}),
434
- CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
435
- expect( res.getvalue(0,0) ).to eq( 123 )
436
- expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
437
- expect( res.getvalue(0,2) ).to eq( true )
438
- expect( res.getvalue(0,3) ).to eq( false )
439
- expect( res.getvalue(0,4) ).to eq( nil )
440
- expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
441
- expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
442
- end
443
- end
444
- end
445
-
446
- it "should do array type conversions" do
447
- [0].each do |format|
448
- res = @conn.exec_params( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
449
- CAST('{1,2,3}' AS INT4[]),
450
- CAST('{1,2,3}' AS INT8[]),
451
- CAST('{1,2,3}' AS TEXT[]),
452
- CAST('{1,2,3}' AS VARCHAR[]),
453
- CAST('{1,2,3}' AS FLOAT4[]),
454
- CAST('{1,2,3}' AS FLOAT8[])
455
- ", [], format )
456
- expect( res.getvalue(0,0) ).to eq( [1,2,3] )
457
- expect( res.getvalue(0,1) ).to eq( [[1,2],[3,4]] )
458
- expect( res.getvalue(0,2) ).to eq( [1,2,3] )
459
- expect( res.getvalue(0,3) ).to eq( [1,2,3] )
460
- expect( res.getvalue(0,4) ).to eq( ['1','2','3'] )
461
- expect( res.getvalue(0,5) ).to eq( ['1','2','3'] )
462
- expect( res.getvalue(0,6) ).to eq( [1.0,2.0,3.0] )
463
- expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
464
- end
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
535
- end
536
-
537
- context "with usage of result oids for copy decoder selection" do
538
- it "can type cast #copy_data output with explicit decoder" do
539
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
540
- @conn.exec( "INSERT INTO copytable VALUES ('a', 123, '{5,4,3}'), ('b', 234, '{2,3}')" )
541
-
542
- # Retrieve table OIDs per empty result.
543
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
544
- tm = basic_type_mapping.build_column_map( res )
545
- row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
546
-
547
- rows = []
548
- @conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
549
- while row=@conn.get_copy_data
550
- rows << row
551
- end
552
- end
553
- expect( rows ).to eq( [['a', 123, [5,4,3]], ['b', 234, [2,3]]] )
554
- end
555
- end
556
- end
557
-
558
-
559
- describe PG::BasicTypeMapBasedOnResult do
560
- let!(:basic_type_mapping) do
561
- PG::BasicTypeMapBasedOnResult.new @conn
562
- end
563
-
564
- context "with usage of result oids for bind params encoder selection" do
565
- it "can type cast query params" do
566
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
567
-
568
- # Retrieve table OIDs per empty result.
569
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
570
- tm = basic_type_mapping.build_column_map( res )
571
-
572
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['a', 123, [5,4,3]], 0, tm )
573
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['b', 234, [2,3]], 0, tm )
574
- res = @conn.exec( "SELECT * FROM copytable" )
575
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
576
- end
577
-
578
- it "can do JSON conversions", :postgresql_94 do
579
- ['JSON', 'JSONB'].each do |type|
580
- sql = "SELECT CAST('123' AS #{type}),
581
- CAST('12.3' AS #{type}),
582
- CAST('true' AS #{type}),
583
- CAST('false' AS #{type}),
584
- CAST('null' AS #{type}),
585
- CAST('[1, \"a\", null]' AS #{type}),
586
- CAST('{\"b\" : [2,3]}' AS #{type})"
587
-
588
- tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
589
- expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
590
-
591
- res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
592
- [ 123,
593
- 12.3,
594
- true,
595
- false,
596
- nil,
597
- [1, "a", nil],
598
- {"b" => [2, 3]},
599
- ], 0, tm )
600
-
601
- expect( res.getvalue(0,0) ).to eq( "123" )
602
- expect( res.getvalue(0,1) ).to eq( "12.3" )
603
- expect( res.getvalue(0,2) ).to eq( "true" )
604
- expect( res.getvalue(0,3) ).to eq( "false" )
605
- expect( res.getvalue(0,4) ).to eq( nil )
606
- expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
607
- expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
608
- end
609
- end
610
- end
611
-
612
- context "with usage of result oids for copy encoder selection" do
613
- it "can type cast #copy_data input with explicit encoder" do
614
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
615
-
616
- # Retrieve table OIDs per empty result set.
617
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
618
- tm = basic_type_mapping.build_column_map( res )
619
- row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
620
-
621
- @conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
622
- @conn.put_copy_data ['a', 123, [5,4,3]]
623
- @conn.put_copy_data ['b', 234, [2,3]]
624
- end
625
- res = @conn.exec( "SELECT * FROM copytable" )
626
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
627
- end
628
- end
629
- end
630
- end