pg 1.2.1 → 1.3.0.rc2

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 +85 -0
  6. data/.github/workflows/source-gem.yml +130 -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 +93 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +8 -7
  18. data/Rakefile +31 -140
  19. data/Rakefile.cross +55 -56
  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 +59 -4
  27. data/ext/pg.h +18 -0
  28. data/ext/pg_coder.c +90 -24
  29. data/ext/pg_connection.c +606 -533
  30. data/ext/pg_copy_coder.c +45 -15
  31. data/ext/pg_record_coder.c +38 -9
  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 +50 -21
  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 +579 -57
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +38 -24
  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 +87 -227
  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 -382
  91. data/spec/pg/basic_type_mapping_spec.rb +0 -645
  92. data/spec/pg/connection_spec.rb +0 -1911
  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,645 +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
- def expect_to_typecase_result_value_warning
25
- warning = 'Warning: no type cast defined for type "name" with oid 19. '\
26
- "Please cast this type explicitly to TEXT to be safe for future changes.\n"\
27
- 'Warning: no type cast defined for type "regproc" with oid 24. '\
28
- "Please cast this type explicitly to TEXT to be safe for future changes.\n"
29
- expect { yield }.to output(warning).to_stderr
30
- end
31
-
32
- describe 'Basic type mapping' do
33
-
34
- describe PG::BasicTypeMapForQueries do
35
- let!(:basic_type_mapping) do
36
- PG::BasicTypeMapForQueries.new @conn
37
- end
38
-
39
- #
40
- # Encoding Examples
41
- #
42
-
43
- it "should do basic param encoding" do
44
- res = @conn.exec_params( "SELECT $1::int8, $2::float, $3, $4::TEXT",
45
- [1, 2.1, true, "b"], nil, basic_type_mapping )
46
-
47
- expect( res.values ).to eq( [
48
- [ "1", "2.1", "t", "b" ],
49
- ] )
50
-
51
- expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
52
- end
53
-
54
- it "should do basic Time encoding" do
55
- res = @conn.exec_params( "SELECT $1 AT TIME ZONE '-02'",
56
- [Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], nil, basic_type_mapping )
57
-
58
- expect( res.values ).to eq( [[ "2019-12-08 23:38:12.123" ]] )
59
- end
60
-
61
- it "should do basic param encoding of various float values" do
62
- 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",
63
- [0, 7, 9, 0.1, 0.9, -0.11, 10.11,
64
- 9876543210987654321e-400,
65
- 9876543210987654321e400,
66
- -1.234567890123456789e-280,
67
- -1.234567890123456789e280,
68
- 9876543210987654321e280
69
- ], nil, basic_type_mapping )
70
-
71
- expect( res.values[0][0, 9] ).to eq(
72
- [ "0", "7", "9", "0.1", "0.9", "-0.11", "10.11", "0", "Infinity" ]
73
- )
74
-
75
- expect( res.values[0][9] ).to match( /^-1\.2345678901234\d*e\-280$/ )
76
- expect( res.values[0][10] ).to match( /^-1\.2345678901234\d*e\+280$/ )
77
- expect( res.values[0][11] ).to match( /^9\.8765432109876\d*e\+298$/ )
78
-
79
- expect( result_typenames(res) ).to eq( ['double precision'] * 12 )
80
- end
81
-
82
- it "should do default array-as-array param encoding" do
83
- expect( basic_type_mapping.encode_array_as).to eq(:array)
84
- res = @conn.exec_params( "SELECT $1,$2,$3,$4,$5,$6", [
85
- [1, 2, 3], # Integer -> bigint[]
86
- [[1, 2], [3, nil]], # Integer two dimensions -> bigint[]
87
- [1.11, 2.21], # Float -> double precision[]
88
- ['/,"'.gsub("/", "\\"), nil, 'abcäöü'], # String -> text[]
89
- [BigDecimal("123.45")], # BigDecimal -> numeric[]
90
- [IPAddr.new('1234::5678')], # IPAddr -> inet[]
91
- ], nil, basic_type_mapping )
92
-
93
- expect( res.values ).to eq( [[
94
- '{1,2,3}',
95
- '{{1,2},{3,NULL}}',
96
- '{1.11,2.21}',
97
- '{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
98
- '{123.45}',
99
- '{1234::5678}',
100
- ]] )
101
-
102
- expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]', 'numeric[]', 'inet[]'] )
103
- end
104
-
105
- it "should do default array-as-array param encoding with Time objects" do
106
- res = @conn.exec_params( "SELECT $1", [
107
- [Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], # Time -> timestamptz[]
108
- ], nil, basic_type_mapping )
109
-
110
- expect( res.values[0][0] ).to match( /\{\"2019-12-08 \d\d:38:12.123[+-]\d\d\"\}/ )
111
- expect( result_typenames(res) ).to eq( ['timestamp with time zone[]'] )
112
- end
113
-
114
- it "should do array-as-json encoding" do
115
- basic_type_mapping.encode_array_as = :json
116
- expect( basic_type_mapping.encode_array_as).to eq(:json)
117
-
118
- res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
119
- [1, {a: 5}, true, ["a", 2], [3.4, nil]],
120
- ['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
121
- ], nil, basic_type_mapping )
122
-
123
- expect( res.values ).to eq( [[
124
- '[1,{"a":5},true,["a",2],[3.4,null]]',
125
- '["//,/"",null,"abcäöü"]'.gsub("/", "\\"),
126
- ]] )
127
-
128
- expect( result_typenames(res) ).to eq( ['json', 'json'] )
129
- end
130
-
131
- it "should do hash-as-json encoding" do
132
- res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
133
- {a: 5, b: ["a", 2], c: nil},
134
- {qu: '/,"'.gsub("/", "\\"), ni: nil, uml: 'abcäöü'},
135
- ], nil, basic_type_mapping )
136
-
137
- expect( res.values ).to eq( [[
138
- '{"a":5,"b":["a",2],"c":null}',
139
- '{"qu":"//,/"","ni":null,"uml":"abcäöü"}'.gsub("/", "\\"),
140
- ]] )
141
-
142
- expect( result_typenames(res) ).to eq( ['json', 'json'] )
143
- end
144
-
145
- describe "Record encoding" do
146
- before :all do
147
- @conn.exec("CREATE TYPE test_record1 AS (i int, d float, t text)")
148
- @conn.exec("CREATE TYPE test_record2 AS (i int, r test_record1)")
149
- end
150
-
151
- after :all do
152
- @conn.exec("DROP TYPE IF EXISTS test_record2 CASCADE")
153
- @conn.exec("DROP TYPE IF EXISTS test_record1 CASCADE")
154
- end
155
-
156
- it "should do array-as-record encoding" do
157
- basic_type_mapping.encode_array_as = :record
158
- expect( basic_type_mapping.encode_array_as).to eq(:record)
159
-
160
- res = @conn.exec_params( "SELECT $1::test_record1, $2::test_record2, $3::text", [
161
- [5, 3.4, "txt"],
162
- [1, [2, 4.5, "bcd"]],
163
- [4, 5, 6],
164
- ], nil, basic_type_mapping )
165
-
166
- expect( res.values ).to eq( [[
167
- '(5,3.4,txt)',
168
- '(1,"(2,4.5,bcd)")',
169
- '("4","5","6")',
170
- ]] )
171
-
172
- expect( result_typenames(res) ).to eq( ['test_record1', 'test_record2', 'text'] )
173
- end
174
- end
175
-
176
- it "should do bigdecimal param encoding" do
177
- large = ('123456790'*10) << '.' << ('012345679')
178
- res = @conn.exec_params( "SELECT $1::numeric,$2::numeric",
179
- [BigDecimal('1'), BigDecimal(large)], nil, basic_type_mapping )
180
-
181
- expect( res.values ).to eq( [
182
- [ "1.0", large ],
183
- ] )
184
-
185
- expect( result_typenames(res) ).to eq( ['numeric', 'numeric'] )
186
- end
187
-
188
- it "should do IPAddr param encoding" do
189
- res = @conn.exec_params( "SELECT $1::inet,$2::inet,$3::cidr,$4::cidr",
190
- ['1.2.3.4', IPAddr.new('1234::5678'), '1.2.3.4', IPAddr.new('1234:5678::/32')], nil, basic_type_mapping )
191
-
192
- expect( res.values ).to eq( [
193
- [ '1.2.3.4', '1234::5678', '1.2.3.4/32', '1234:5678::/32'],
194
- ] )
195
-
196
- expect( result_typenames(res) ).to eq( ['inet', 'inet', 'cidr', 'cidr'] )
197
- end
198
-
199
- it "should do array of string encoding on unknown classes" do
200
- iv = Class.new do
201
- def to_s
202
- "abc"
203
- end
204
- end.new
205
- res = @conn.exec_params( "SELECT $1", [
206
- [iv, iv], # Unknown -> text[]
207
- ], nil, basic_type_mapping )
208
-
209
- expect( res.values ).to eq( [[
210
- '{abc,abc}',
211
- ]] )
212
-
213
- expect( result_typenames(res) ).to eq( ['text[]'] )
214
- end
215
-
216
- end
217
-
218
-
219
-
220
- describe PG::BasicTypeMapForResults do
221
- let!(:basic_type_mapping) do
222
- PG::BasicTypeMapForResults.new @conn
223
- end
224
-
225
- #
226
- # Decoding Examples
227
- #
228
-
229
- it "should do OID based type conversions" do
230
- res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
231
- expect( res.map_types!(basic_type_mapping).values ).to eq( [
232
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
233
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
234
- ] )
235
- end
236
-
237
- #
238
- # Decoding Examples text+binary format converters
239
- #
240
-
241
- describe "connection wide type mapping" do
242
- before :each do
243
- @conn.type_map_for_results = basic_type_mapping
244
- end
245
-
246
- after :each do
247
- @conn.type_map_for_results = PG::TypeMapAllStrings.new
248
- end
249
-
250
- it "should do boolean type conversions" do
251
- [1, 0].each do |format|
252
- res = @conn.exec_params( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
253
- expect( res.values ).to eq( [[true, false, nil]] )
254
- end
255
- end
256
-
257
- it "should do binary type conversions" do
258
- [1, 0].each do |format|
259
- res = @conn.exec_params( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
260
- expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
261
- expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
262
- end
263
- end
264
-
265
- it "should do integer type conversions" do
266
- [1, 0].each do |format|
267
- res = @conn.exec_params( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
268
- expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
269
- end
270
- end
271
-
272
- it "should do string type conversions" do
273
- @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
274
- [1, 0].each do |format|
275
- res = @conn.exec_params( "SELECT 'abcäöü'::TEXT", [], format )
276
- expect( res.values ).to eq( [['abcäöü']] )
277
- expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
278
- end
279
- end
280
-
281
- it "should do float type conversions" do
282
- [1, 0].each do |format|
283
- res = @conn.exec_params( "SELECT -8.999e3::FLOAT4,
284
- 8.999e10::FLOAT4,
285
- -8999999999e-99::FLOAT8,
286
- NULL::FLOAT4,
287
- 'NaN'::FLOAT4,
288
- 'Infinity'::FLOAT4,
289
- '-Infinity'::FLOAT4
290
- ", [], format )
291
- expect( res.getvalue(0,0) ).to be_within(1e-2).of(-8.999e3)
292
- expect( res.getvalue(0,1) ).to be_within(1e5).of(8.999e10)
293
- expect( res.getvalue(0,2) ).to be_within(1e-109).of(-8999999999e-99)
294
- expect( res.getvalue(0,3) ).to be_nil
295
- expect( res.getvalue(0,4) ).to be_nan
296
- expect( res.getvalue(0,5) ).to eq( Float::INFINITY )
297
- expect( res.getvalue(0,6) ).to eq( -Float::INFINITY )
298
- end
299
- end
300
-
301
- it "should do text datetime without time zone type conversions" do
302
- # for backward compat text timestamps without time zone are treated as local times
303
- res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
304
- CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
305
- CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
306
- CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
307
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
308
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], 0 )
309
- expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
310
- expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
311
- expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
312
- expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
313
- expect( res.getvalue(0,4) ).to eq( 'infinity' )
314
- expect( res.getvalue(0,5) ).to eq( '-infinity' )
315
- end
316
-
317
- [1, 0].each do |format|
318
- it "should convert format #{format} timestamps per TimestampUtc" do
319
- restore_type("timestamp") do
320
- PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtc
321
- expect_to_typecase_result_value_warning do
322
- @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
323
- end
324
- res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
325
- CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
326
- CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
327
- CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
328
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
329
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
330
- expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).iso8601(3) )
331
- expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
332
- expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
333
- expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
334
- expect( res.getvalue(0,4) ).to eq( 'infinity' )
335
- expect( res.getvalue(0,5) ).to eq( '-infinity' )
336
- end
337
- end
338
- end
339
-
340
- [1, 0].each do |format|
341
- it "should convert format #{format} timestamps per TimestampUtcToLocal" do
342
- restore_type("timestamp") do
343
- PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtcToLocal
344
- PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtcToLocal
345
- expect_to_typecase_result_value_warning do
346
- @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
347
- end
348
- res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
349
- CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
350
- CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
351
- CAST('294276-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
352
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
353
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
354
- expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.utc(2013, 7, 31, 23, 58, 59).getlocal.iso8601(3) )
355
- expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.utc(1913, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
356
- expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.utc(-4713, 11, 24, 23, 58, 59.1231).getlocal.iso8601(3) )
357
- expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.utc(294276, 12, 31, 23, 58, 59.1231).getlocal.iso8601(3) )
358
- expect( res.getvalue(0,4) ).to eq( 'infinity' )
359
- expect( res.getvalue(0,5) ).to eq( '-infinity' )
360
- end
361
- end
362
- end
363
-
364
- [1, 0].each do |format|
365
- it "should convert format #{format} timestamps per TimestampLocal" do
366
- restore_type("timestamp") do
367
- PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampLocal
368
- PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampLocal
369
- expect_to_typecase_result_value_warning do
370
- @conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
371
- end
372
- res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59' AS TIMESTAMP WITHOUT TIME ZONE),
373
- CAST('1913-12-31 23:58:59.1231' AS TIMESTAMP WITHOUT TIME ZONE),
374
- CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
375
- CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITHOUT TIME ZONE),
376
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
377
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
378
- expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.new(2013, 7, 31, 23, 58, 59).iso8601(3) )
379
- expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231).iso8601(3) )
380
- expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231).iso8601(3) )
381
- expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231).iso8601(3) )
382
- expect( res.getvalue(0,4) ).to eq( 'infinity' )
383
- expect( res.getvalue(0,5) ).to eq( '-infinity' )
384
- end
385
- end
386
- end
387
-
388
- [0, 1].each do |format|
389
- it "should convert format #{format} timestamps with time zone" do
390
- res = @conn.exec_params( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
391
- CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITH TIME ZONE),
392
- CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITH TIME ZONE),
393
- CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITH TIME ZONE),
394
- CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
395
- CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
396
- expect( res.getvalue(0,0) ).to be_within(1e-3).of( Time.new(2013, 12, 31, 23, 58, 59, "+02:00").getlocal )
397
- expect( res.getvalue(0,1) ).to be_within(1e-3).of( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").getlocal )
398
- expect( res.getvalue(0,2) ).to be_within(1e-3).of( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").getlocal )
399
- expect( res.getvalue(0,3) ).to be_within(1e-3).of( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").getlocal )
400
- expect( res.getvalue(0,4) ).to eq( 'infinity' )
401
- expect( res.getvalue(0,5) ).to eq( '-infinity' )
402
- end
403
- end
404
-
405
- it "should do date type conversions" do
406
- [0].each do |format|
407
- res = @conn.exec_params( "SELECT CAST('2113-12-31' AS DATE),
408
- CAST('1913-12-31' AS DATE),
409
- CAST('infinity' AS DATE),
410
- CAST('-infinity' AS DATE)", [], format )
411
- expect( res.getvalue(0,0) ).to eq( Date.new(2113, 12, 31) )
412
- expect( res.getvalue(0,1) ).to eq( Date.new(1913, 12, 31) )
413
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
414
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
415
- end
416
- end
417
-
418
- it "should do numeric type conversions" do
419
- [0].each do |format|
420
- small = '123456790123.12'
421
- large = ('123456790'*10) << '.' << ('012345679')
422
- numerics = [
423
- '1',
424
- '1.0',
425
- '1.2',
426
- small,
427
- large,
428
- ]
429
- sql_numerics = numerics.map { |v| "CAST(#{v} AS numeric)" }
430
- res = @conn.exec_params( "SELECT #{sql_numerics.join(',')}", [], format )
431
- expect( res.getvalue(0,0) ).to eq( BigDecimal('1') )
432
- expect( res.getvalue(0,1) ).to eq( BigDecimal('1') )
433
- expect( res.getvalue(0,2) ).to eq( BigDecimal('1.2') )
434
- expect( res.getvalue(0,3) ).to eq( BigDecimal(small) )
435
- expect( res.getvalue(0,4) ).to eq( BigDecimal(large) )
436
- end
437
- end
438
-
439
- it "should do JSON conversions", :postgresql_94 do
440
- [0].each do |format|
441
- ['JSON', 'JSONB'].each do |type|
442
- res = @conn.exec_params( "SELECT CAST('123' AS #{type}),
443
- CAST('12.3' AS #{type}),
444
- CAST('true' AS #{type}),
445
- CAST('false' AS #{type}),
446
- CAST('null' AS #{type}),
447
- CAST('[1, \"a\", null]' AS #{type}),
448
- CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
449
- expect( res.getvalue(0,0) ).to eq( 123 )
450
- expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
451
- expect( res.getvalue(0,2) ).to eq( true )
452
- expect( res.getvalue(0,3) ).to eq( false )
453
- expect( res.getvalue(0,4) ).to eq( nil )
454
- expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
455
- expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
456
- end
457
- end
458
- end
459
-
460
- it "should do array type conversions" do
461
- [0].each do |format|
462
- res = @conn.exec_params( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
463
- CAST('{1,2,3}' AS INT4[]),
464
- CAST('{1,2,3}' AS INT8[]),
465
- CAST('{1,2,3}' AS TEXT[]),
466
- CAST('{1,2,3}' AS VARCHAR[]),
467
- CAST('{1,2,3}' AS FLOAT4[]),
468
- CAST('{1,2,3}' AS FLOAT8[])
469
- ", [], format )
470
- expect( res.getvalue(0,0) ).to eq( [1,2,3] )
471
- expect( res.getvalue(0,1) ).to eq( [[1,2],[3,4]] )
472
- expect( res.getvalue(0,2) ).to eq( [1,2,3] )
473
- expect( res.getvalue(0,3) ).to eq( [1,2,3] )
474
- expect( res.getvalue(0,4) ).to eq( ['1','2','3'] )
475
- expect( res.getvalue(0,5) ).to eq( ['1','2','3'] )
476
- expect( res.getvalue(0,6) ).to eq( [1.0,2.0,3.0] )
477
- expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
478
- end
479
- end
480
-
481
- it "should do inet type conversions" do
482
- [0].each do |format|
483
- vals = [
484
- '1.2.3.4',
485
- '0.0.0.0/0',
486
- '1.0.0.0/8',
487
- '1.2.0.0/16',
488
- '1.2.3.0/24',
489
- '1.2.3.4/24',
490
- '1.2.3.4/32',
491
- '1.2.3.128/25',
492
- '1234:3456:5678:789a:9abc:bced:edf0:f012',
493
- '::/0',
494
- '1234:3456::/32',
495
- '1234:3456:5678:789a::/64',
496
- '1234:3456:5678:789a:9abc:bced::/96',
497
- '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
498
- '1234:3456:5678:789a:9abc:bced:edf0:f012/0',
499
- '1234:3456:5678:789a:9abc:bced:edf0:f012/32',
500
- '1234:3456:5678:789a:9abc:bced:edf0:f012/64',
501
- '1234:3456:5678:789a:9abc:bced:edf0:f012/96',
502
- ]
503
- sql_vals = vals.map{|v| "CAST('#{v}' AS inet)"}
504
- res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
505
- vals.each_with_index do |v, i|
506
- val = res.getvalue(0,i)
507
- expect( res.getvalue(0,i) ).to eq( IPAddr.new(v) )
508
- end
509
- end
510
- end
511
-
512
- it "should do cidr type conversions" do
513
- [0].each do |format|
514
- vals = [
515
- '0.0.0.0/0',
516
- '1.0.0.0/8',
517
- '1.2.0.0/16',
518
- '1.2.3.0/24',
519
- '1.2.3.4/32',
520
- '1.2.3.128/25',
521
- '::/0',
522
- '1234:3456::/32',
523
- '1234:3456:5678:789a::/64',
524
- '1234:3456:5678:789a:9abc:bced::/96',
525
- '1234:3456:5678:789a:9abc:bced:edf0:f012/128',
526
- ]
527
- sql_vals = vals.map { |v| "CAST('#{v}' AS cidr)" }
528
- res = @conn.exec_params(("SELECT " + sql_vals.join(', ')), [], format )
529
- vals.each_with_index do |v, i|
530
- val = res.getvalue(0,i)
531
- ip, prefix = v.split('/', 2)
532
- expect( val.to_s ).to eq( ip )
533
- if val.respond_to?(:prefix)
534
- val_prefix = val.prefix
535
- else
536
- default_prefix = (val.family == Socket::AF_INET ? 32 : 128)
537
- range = val.to_range
538
- val_prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
539
- end
540
- if v.include?('/')
541
- expect( val_prefix ).to eq( prefix.to_i )
542
- elsif v.include?('.')
543
- expect( val_prefix ).to eq( 32 )
544
- else
545
- expect( val_prefix ).to eq( 128 )
546
- end
547
- end
548
- end
549
- end
550
- end
551
-
552
- context "with usage of result oids for copy decoder selection" do
553
- it "can type cast #copy_data output with explicit decoder" do
554
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
555
- @conn.exec( "INSERT INTO copytable VALUES ('a', 123, '{5,4,3}'), ('b', 234, '{2,3}')" )
556
-
557
- # Retrieve table OIDs per empty result.
558
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
559
- tm = basic_type_mapping.build_column_map( res )
560
- row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
561
-
562
- rows = []
563
- @conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
564
- while row=@conn.get_copy_data
565
- rows << row
566
- end
567
- end
568
- expect( rows ).to eq( [['a', 123, [5,4,3]], ['b', 234, [2,3]]] )
569
- end
570
- end
571
- end
572
-
573
-
574
- describe PG::BasicTypeMapBasedOnResult do
575
- let!(:basic_type_mapping) do
576
- PG::BasicTypeMapBasedOnResult.new @conn
577
- end
578
-
579
- context "with usage of result oids for bind params encoder selection" do
580
- it "can type cast query params" do
581
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
582
-
583
- # Retrieve table OIDs per empty result.
584
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
585
- tm = basic_type_mapping.build_column_map( res )
586
-
587
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['a', 123, [5,4,3]], 0, tm )
588
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['b', 234, [2,3]], 0, tm )
589
- res = @conn.exec( "SELECT * FROM copytable" )
590
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
591
- end
592
-
593
- it "can do JSON conversions", :postgresql_94 do
594
- ['JSON', 'JSONB'].each do |type|
595
- sql = "SELECT CAST('123' AS #{type}),
596
- CAST('12.3' AS #{type}),
597
- CAST('true' AS #{type}),
598
- CAST('false' AS #{type}),
599
- CAST('null' AS #{type}),
600
- CAST('[1, \"a\", null]' AS #{type}),
601
- CAST('{\"b\" : [2,3]}' AS #{type})"
602
-
603
- tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
604
- expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
605
-
606
- res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
607
- [ 123,
608
- 12.3,
609
- true,
610
- false,
611
- nil,
612
- [1, "a", nil],
613
- {"b" => [2, 3]},
614
- ], 0, tm )
615
-
616
- expect( res.getvalue(0,0) ).to eq( "123" )
617
- expect( res.getvalue(0,1) ).to eq( "12.3" )
618
- expect( res.getvalue(0,2) ).to eq( "true" )
619
- expect( res.getvalue(0,3) ).to eq( "false" )
620
- expect( res.getvalue(0,4) ).to eq( nil )
621
- expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
622
- expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
623
- end
624
- end
625
- end
626
-
627
- context "with usage of result oids for copy encoder selection" do
628
- it "can type cast #copy_data input with explicit encoder" do
629
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
630
-
631
- # Retrieve table OIDs per empty result set.
632
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
633
- tm = basic_type_mapping.build_column_map( res )
634
- row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
635
-
636
- @conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
637
- @conn.put_copy_data ['a', 123, [5,4,3]]
638
- @conn.put_copy_data ['b', 234, [2,3]]
639
- end
640
- res = @conn.exec( "SELECT * FROM copytable" )
641
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
642
- end
643
- end
644
- end
645
- end