pg 0.18.4 → 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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +0 -5911
- data/History.rdoc +240 -0
- data/Manifest.txt +8 -20
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +64 -15
- data/Rakefile +20 -21
- data/Rakefile.cross +67 -69
- data/ext/errorcodes.def +101 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +33 -2
- data/ext/extconf.rb +26 -36
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +156 -145
- data/ext/pg.h +74 -98
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +20 -19
- data/ext/pg_coder.c +103 -21
- data/ext/pg_connection.c +917 -523
- data/ext/pg_copy_coder.c +50 -12
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +590 -208
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +245 -94
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +14 -7
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +9 -4
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +23 -13
- data/lib/pg/basic_type_mapping.rb +155 -32
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +73 -13
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +24 -7
- data/lib/pg/text_decoder.rb +24 -22
- data/lib/pg/text_encoder.rb +40 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +61 -36
- data/spec/pg/basic_type_mapping_spec.rb +415 -36
- data/spec/pg/connection_spec.rb +732 -327
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +253 -21
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +4 -4
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +446 -20
- data/spec/pg_spec.rb +2 -2
- metadata +63 -72
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,333 @@
|
|
1
|
+
# -*- rspec -*-
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require_relative '../helpers'
|
5
|
+
require 'pg'
|
6
|
+
require 'objspace'
|
7
|
+
|
8
|
+
describe PG::Tuple do
|
9
|
+
let!(:typemap) { PG::BasicTypeMapForResults.new(@conn) }
|
10
|
+
let!(:result2x2) { @conn.exec( "VALUES(1, 'a'), (2, 'b')" ) }
|
11
|
+
let!(:result2x2sym) { @conn.exec( "VALUES(1, 'a'), (2, 'b')" ).field_names_as(:symbol) }
|
12
|
+
let!(:result2x3cast) do
|
13
|
+
@conn.exec( "SELECT * FROM (VALUES(1, TRUE, '3'), (2, FALSE, '4')) AS m (a, b, b)" )
|
14
|
+
.map_types!(typemap)
|
15
|
+
end
|
16
|
+
let!(:result2x3symcast) do
|
17
|
+
@conn.exec( "SELECT * FROM (VALUES(1, TRUE, '3'), (2, FALSE, '4')) AS m (a, b, b)" )
|
18
|
+
.map_types!(typemap)
|
19
|
+
.field_names_as(:symbol)
|
20
|
+
end
|
21
|
+
let!(:tuple0) { result2x2.tuple(0) }
|
22
|
+
let!(:tuple0sym) { result2x2sym.tuple(0) }
|
23
|
+
let!(:tuple1) { result2x2.tuple(1) }
|
24
|
+
let!(:tuple1sym) { result2x2sym.tuple(1) }
|
25
|
+
let!(:tuple2) { result2x3cast.tuple(0) }
|
26
|
+
let!(:tuple2sym) { result2x3symcast.tuple(0) }
|
27
|
+
let!(:tuple3) { str = Marshal.dump(result2x3cast.tuple(1)); Marshal.load(str) }
|
28
|
+
let!(:tuple_empty) { PG::Tuple.new }
|
29
|
+
|
30
|
+
describe "[]" do
|
31
|
+
it "returns nil for invalid keys" do
|
32
|
+
expect( tuple0["x"] ).to be_nil
|
33
|
+
expect( tuple0[0.5] ).to be_nil
|
34
|
+
expect( tuple0[2] ).to be_nil
|
35
|
+
expect( tuple0[-3] ).to be_nil
|
36
|
+
expect( tuple2[-4] ).to be_nil
|
37
|
+
expect{ tuple_empty[0] }.to raise_error(TypeError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "supports array like access" do
|
41
|
+
expect( tuple0[0] ).to eq( "1" )
|
42
|
+
expect( tuple0[1] ).to eq( "a" )
|
43
|
+
expect( tuple1[0] ).to eq( "2" )
|
44
|
+
expect( tuple1[1] ).to eq( "b" )
|
45
|
+
expect( tuple2[0] ).to eq( 1 )
|
46
|
+
expect( tuple2[1] ).to eq( true )
|
47
|
+
expect( tuple2[2] ).to eq( "3" )
|
48
|
+
expect( tuple3[0] ).to eq( 2 )
|
49
|
+
expect( tuple3[1] ).to eq( false )
|
50
|
+
expect( tuple3[2] ).to eq( "4" )
|
51
|
+
end
|
52
|
+
|
53
|
+
it "supports negative indices" do
|
54
|
+
expect( tuple0[-2] ).to eq( "1" )
|
55
|
+
expect( tuple0[-1] ).to eq( "a" )
|
56
|
+
expect( tuple2[-3] ).to eq( 1 )
|
57
|
+
expect( tuple2[-2] ).to eq( true )
|
58
|
+
expect( tuple2[-1] ).to eq( "3" )
|
59
|
+
end
|
60
|
+
|
61
|
+
it "supports hash like access" do
|
62
|
+
expect( tuple0["column1"] ).to eq( "1" )
|
63
|
+
expect( tuple0["column2"] ).to eq( "a" )
|
64
|
+
expect( tuple2["a"] ).to eq( 1 )
|
65
|
+
expect( tuple2["b"] ).to eq( "3" )
|
66
|
+
expect( tuple0[:b] ).to be_nil
|
67
|
+
expect( tuple0["x"] ).to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it "supports hash like access with symbols" do
|
71
|
+
expect( tuple0sym[:column1] ).to eq( "1" )
|
72
|
+
expect( tuple0sym[:column2] ).to eq( "a" )
|
73
|
+
expect( tuple2sym[:a] ).to eq( 1 )
|
74
|
+
expect( tuple2sym[:b] ).to eq( "3" )
|
75
|
+
expect( tuple2sym["b"] ).to be_nil
|
76
|
+
expect( tuple0sym[:x] ).to be_nil
|
77
|
+
end
|
78
|
+
|
79
|
+
it "casts lazy and caches result" do
|
80
|
+
a = []
|
81
|
+
deco = Class.new(PG::SimpleDecoder) do
|
82
|
+
define_method(:decode) do |*args|
|
83
|
+
a << args
|
84
|
+
args.last
|
85
|
+
end
|
86
|
+
end.new
|
87
|
+
|
88
|
+
result2x2.map_types!(PG::TypeMapByColumn.new([deco, deco]))
|
89
|
+
t = result2x2.tuple(1)
|
90
|
+
|
91
|
+
# cast and cache at first call to [0]
|
92
|
+
a.clear
|
93
|
+
expect( t[0] ).to eq( 0 )
|
94
|
+
expect( a ).to eq([["2", 1, 0]])
|
95
|
+
|
96
|
+
# use cache at second call to [0]
|
97
|
+
a.clear
|
98
|
+
expect( t[0] ).to eq( 0 )
|
99
|
+
expect( a ).to eq([])
|
100
|
+
|
101
|
+
# cast and cache at first call to [1]
|
102
|
+
a.clear
|
103
|
+
expect( t[1] ).to eq( 1 )
|
104
|
+
expect( a ).to eq([["b", 1, 1]])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "fetch" do
|
109
|
+
it "raises proper errors for invalid keys" do
|
110
|
+
expect{ tuple0.fetch("x") }.to raise_error(KeyError)
|
111
|
+
expect{ tuple0.fetch(0.5) }.to raise_error(KeyError)
|
112
|
+
expect{ tuple0.fetch(2) }.to raise_error(IndexError)
|
113
|
+
expect{ tuple0.fetch(-3) }.to raise_error(IndexError)
|
114
|
+
expect{ tuple0.fetch(-3) }.to raise_error(IndexError)
|
115
|
+
expect{ tuple2.fetch(-4) }.to raise_error(IndexError)
|
116
|
+
expect{ tuple_empty[0] }.to raise_error(TypeError)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "supports array like access" do
|
120
|
+
expect( tuple0.fetch(0) ).to eq( "1" )
|
121
|
+
expect( tuple0.fetch(1) ).to eq( "a" )
|
122
|
+
expect( tuple2.fetch(0) ).to eq( 1 )
|
123
|
+
expect( tuple2.fetch(1) ).to eq( true )
|
124
|
+
expect( tuple2.fetch(2) ).to eq( "3" )
|
125
|
+
end
|
126
|
+
|
127
|
+
it "supports default value for indices" do
|
128
|
+
expect( tuple0.fetch(2, 42) ).to eq( 42 )
|
129
|
+
expect( tuple0.fetch(2){43} ).to eq( 43 )
|
130
|
+
end
|
131
|
+
|
132
|
+
it "supports negative indices" do
|
133
|
+
expect( tuple0.fetch(-2) ).to eq( "1" )
|
134
|
+
expect( tuple0.fetch(-1) ).to eq( "a" )
|
135
|
+
expect( tuple2.fetch(-3) ).to eq( 1 )
|
136
|
+
expect( tuple2.fetch(-2) ).to eq( true )
|
137
|
+
expect( tuple2.fetch(-1) ).to eq( "3" )
|
138
|
+
end
|
139
|
+
|
140
|
+
it "supports hash like access" do
|
141
|
+
expect( tuple0.fetch("column1") ).to eq( "1" )
|
142
|
+
expect( tuple0.fetch("column2") ).to eq( "a" )
|
143
|
+
expect( tuple2.fetch("a") ).to eq( 1 )
|
144
|
+
expect( tuple2.fetch("b") ).to eq( "3" )
|
145
|
+
end
|
146
|
+
|
147
|
+
it "supports default value for name keys" do
|
148
|
+
expect( tuple0.fetch("x", "defa") ).to eq("defa")
|
149
|
+
expect( tuple0.fetch("x"){"defa"} ).to eq("defa")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "each" do
|
154
|
+
it "can be used as an enumerator" do
|
155
|
+
expect( tuple0.each ).to be_kind_of(Enumerator)
|
156
|
+
expect( tuple0.each.to_a ).to eq( [["column1", "1"], ["column2", "a"]] )
|
157
|
+
expect( tuple1.each.to_a ).to eq( [["column1", "2"], ["column2", "b"]] )
|
158
|
+
expect( tuple2.each.to_a ).to eq( [["a", 1], ["b", true], ["b", "3"]] )
|
159
|
+
expect( tuple3.each.to_a ).to eq( [["a", 2], ["b", false], ["b", "4"]] )
|
160
|
+
expect{ tuple_empty.each }.to raise_error(TypeError)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "can be used as an enumerator with symbols" do
|
164
|
+
expect( tuple0sym.each ).to be_kind_of(Enumerator)
|
165
|
+
expect( tuple0sym.each.to_a ).to eq( [[:column1, "1"], [:column2, "a"]] )
|
166
|
+
expect( tuple2sym.each.to_a ).to eq( [[:a, 1], [:b, true], [:b, "3"]] )
|
167
|
+
end
|
168
|
+
|
169
|
+
it "can be used with block" do
|
170
|
+
a = []
|
171
|
+
tuple0.each do |*v|
|
172
|
+
a << v
|
173
|
+
end
|
174
|
+
expect( a ).to eq( [["column1", "1"], ["column2", "a"]] )
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "each_value" do
|
179
|
+
it "can be used as an enumerator" do
|
180
|
+
expect( tuple0.each_value ).to be_kind_of(Enumerator)
|
181
|
+
expect( tuple0.each_value.to_a ).to eq( ["1", "a"] )
|
182
|
+
expect( tuple1.each_value.to_a ).to eq( ["2", "b"] )
|
183
|
+
expect( tuple2.each_value.to_a ).to eq( [1, true, "3"] )
|
184
|
+
expect( tuple3.each_value.to_a ).to eq( [2, false, "4"] )
|
185
|
+
expect{ tuple_empty.each_value }.to raise_error(TypeError)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "can be used with block" do
|
189
|
+
a = []
|
190
|
+
tuple0.each_value do |v|
|
191
|
+
a << v
|
192
|
+
end
|
193
|
+
expect( a ).to eq( ["1", "a"] )
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
it "responds to values" do
|
198
|
+
expect( tuple0.values ).to eq( ["1", "a"] )
|
199
|
+
expect( tuple3.values ).to eq( [2, false, "4"] )
|
200
|
+
expect{ tuple_empty.values }.to raise_error(TypeError)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "responds to key?" do
|
204
|
+
expect( tuple1.key?("column1") ).to eq( true )
|
205
|
+
expect( tuple1.key?(:column1) ).to eq( false )
|
206
|
+
expect( tuple1.key?("other") ).to eq( false )
|
207
|
+
expect( tuple1.has_key?("column1") ).to eq( true )
|
208
|
+
expect( tuple1.has_key?("other") ).to eq( false )
|
209
|
+
end
|
210
|
+
|
211
|
+
it "responds to key? as symbol" do
|
212
|
+
expect( tuple1sym.key?(:column1) ).to eq( true )
|
213
|
+
expect( tuple1sym.key?("column1") ).to eq( false )
|
214
|
+
expect( tuple1sym.key?(:other) ).to eq( false )
|
215
|
+
expect( tuple1sym.has_key?(:column1) ).to eq( true )
|
216
|
+
expect( tuple1sym.has_key?(:other) ).to eq( false )
|
217
|
+
end
|
218
|
+
|
219
|
+
it "responds to keys" do
|
220
|
+
expect( tuple0.keys ).to eq( ["column1", "column2"] )
|
221
|
+
expect( tuple2.keys ).to eq( ["a", "b", "b"] )
|
222
|
+
end
|
223
|
+
|
224
|
+
it "responds to keys as symbol" do
|
225
|
+
expect( tuple0sym.keys ).to eq( [:column1, :column2] )
|
226
|
+
expect( tuple2sym.keys ).to eq( [:a, :b, :b] )
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "each_key" do
|
230
|
+
it "can be used as an enumerator" do
|
231
|
+
expect( tuple0.each_key ).to be_kind_of(Enumerator)
|
232
|
+
expect( tuple0.each_key.to_a ).to eq( ["column1", "column2"] )
|
233
|
+
expect( tuple2.each_key.to_a ).to eq( ["a", "b", "b"] )
|
234
|
+
end
|
235
|
+
|
236
|
+
it "can be used with block" do
|
237
|
+
a = []
|
238
|
+
tuple0.each_key do |v|
|
239
|
+
a << v
|
240
|
+
end
|
241
|
+
expect( a ).to eq( ["column1", "column2"] )
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "responds to length" do
|
246
|
+
expect( tuple0.length ).to eq( 2 )
|
247
|
+
expect( tuple0.size ).to eq( 2 )
|
248
|
+
expect( tuple2.size ).to eq( 3 )
|
249
|
+
end
|
250
|
+
|
251
|
+
it "responds to index" do
|
252
|
+
expect( tuple0.index("column1") ).to eq( 0 )
|
253
|
+
expect( tuple0.index(:column1) ).to eq( nil )
|
254
|
+
expect( tuple0.index("column2") ).to eq( 1 )
|
255
|
+
expect( tuple0.index("x") ).to eq( nil )
|
256
|
+
expect( tuple2.index("a") ).to eq( 0 )
|
257
|
+
expect( tuple2.index("b") ).to eq( 2 )
|
258
|
+
end
|
259
|
+
|
260
|
+
it "responds to index with symbol" do
|
261
|
+
expect( tuple0sym.index(:column1) ).to eq( 0 )
|
262
|
+
expect( tuple0sym.index("column1") ).to eq( nil )
|
263
|
+
expect( tuple0sym.index(:column2) ).to eq( 1 )
|
264
|
+
expect( tuple0sym.index(:x) ).to eq( nil )
|
265
|
+
expect( tuple2sym.index(:a) ).to eq( 0 )
|
266
|
+
expect( tuple2sym.index(:b) ).to eq( 2 )
|
267
|
+
end
|
268
|
+
|
269
|
+
it "can be used as Enumerable" do
|
270
|
+
expect( tuple0.to_a ).to eq( [["column1", "1"], ["column2", "a"]] )
|
271
|
+
expect( tuple1.to_a ).to eq( [["column1", "2"], ["column2", "b"]] )
|
272
|
+
expect( tuple2.to_a ).to eq( [["a", 1], ["b", true], ["b", "3"]] )
|
273
|
+
expect( tuple3.to_a ).to eq( [["a", 2], ["b", false], ["b", "4"]] )
|
274
|
+
end
|
275
|
+
|
276
|
+
it "can be marshaled" do
|
277
|
+
[tuple0, tuple1, tuple2, tuple3, tuple0sym, tuple2sym].each do |t1|
|
278
|
+
str = Marshal.dump(t1)
|
279
|
+
t2 = Marshal.load(str)
|
280
|
+
|
281
|
+
expect( t2 ).to be_kind_of(t1.class)
|
282
|
+
expect( t2 ).not_to equal(t1)
|
283
|
+
expect( t2.to_a ).to eq(t1.to_a)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
it "passes instance variables when marshaled" do
|
288
|
+
t1 = tuple0
|
289
|
+
t1.instance_variable_set("@a", 4711)
|
290
|
+
str = Marshal.dump(t1)
|
291
|
+
t2 = Marshal.load(str)
|
292
|
+
|
293
|
+
expect( t2.instance_variable_get("@a") ).to eq( 4711 )
|
294
|
+
end
|
295
|
+
|
296
|
+
it "can't be marshaled when empty" do
|
297
|
+
expect{ Marshal.dump(tuple_empty) }.to raise_error(TypeError)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should give account about memory usage" do
|
301
|
+
expect( ObjectSpace.memsize_of(tuple0) ).to be > 40
|
302
|
+
expect( ObjectSpace.memsize_of(tuple_empty) ).to be > 0
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should override #inspect" do
|
306
|
+
expect( tuple1.inspect ).to eq('#<PG::Tuple column1: "2", column2: "b">')
|
307
|
+
expect( tuple2.inspect ).to eq('#<PG::Tuple a: 1, b: true, b: "3">')
|
308
|
+
expect( tuple2sym.inspect ).to eq('#<PG::Tuple a: 1, b: true, b: "3">')
|
309
|
+
expect{ tuple_empty.inspect }.to raise_error(TypeError)
|
310
|
+
end
|
311
|
+
|
312
|
+
context "with cleared result" do
|
313
|
+
it "should raise an error when non-materialized fields are used" do
|
314
|
+
r = result2x2
|
315
|
+
t = r.tuple(0)
|
316
|
+
t[0] # materialize first field only
|
317
|
+
r.clear
|
318
|
+
|
319
|
+
# second column should fail
|
320
|
+
expect{ t[1] }.to raise_error(PG::Error)
|
321
|
+
expect{ t.fetch(1) }.to raise_error(PG::Error)
|
322
|
+
expect{ t.fetch("column2") }.to raise_error(PG::Error)
|
323
|
+
|
324
|
+
# first column should succeed
|
325
|
+
expect( t[0] ).to eq( "1" )
|
326
|
+
expect( t.fetch(0) ).to eq( "1" )
|
327
|
+
expect( t.fetch("column1") ).to eq( "1" )
|
328
|
+
|
329
|
+
# should fail due to the second column
|
330
|
+
expect{ t.values }.to raise_error(PG::Error)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -59,7 +59,7 @@ describe PG::TypeMapByClass do
|
|
59
59
|
it "should retrieve particular conversions" do
|
60
60
|
expect( tm[Integer] ).to eq(binaryenc_int)
|
61
61
|
expect( tm[Float] ).to eq(textenc_float)
|
62
|
-
expect( tm[
|
62
|
+
expect( tm[Range] ).to be_nil
|
63
63
|
expect( derived_tm[raise_class] ).to be_kind_of(Proc)
|
64
64
|
expect( derived_tm[Array] ).to eq(:array_type_map_for)
|
65
65
|
end
|
@@ -102,7 +102,7 @@ describe PG::TypeMapByClass do
|
|
102
102
|
|
103
103
|
it "should allow mixed type conversions" do
|
104
104
|
res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
|
105
|
-
expect( res.values ).to eq([['5', '1.23',
|
105
|
+
expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
|
106
106
|
expect( res.ftype(0) ).to eq(20)
|
107
107
|
end
|
108
108
|
|
@@ -132,7 +132,7 @@ describe PG::TypeMapByClass do
|
|
132
132
|
it "should raise error on invalid coder object" do
|
133
133
|
tm[TrueClass] = "dummy"
|
134
134
|
expect{
|
135
|
-
|
135
|
+
@conn.exec_params( "SELECT $1", [true], 0, tm )
|
136
136
|
}.to raise_error(NoMethodError, /undefined method.*call/)
|
137
137
|
end
|
138
138
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -38,7 +38,11 @@ describe PG::TypeMapByColumn do
|
|
38
38
|
pass_through_type,
|
39
39
|
nil
|
40
40
|
] )
|
41
|
-
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should respond to inspect" do
|
44
|
+
cm = PG::TypeMapByColumn.new( [textdec_int, textenc_string, textdec_float, pass_through_type, PG::TextEncoder::Float.new, nil] )
|
45
|
+
expect( cm.inspect ).to eq( "#<PG::TypeMapByColumn INT4:TD TEXT:TE FLOAT4:TD pass_through:BD PG::TextEncoder::Float:TE nil>" )
|
42
46
|
end
|
43
47
|
|
44
48
|
it "should retrieve it's oids" do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -116,7 +116,7 @@ describe PG::TypeMapByMriType do
|
|
116
116
|
|
117
117
|
it "should allow mixed type conversions" do
|
118
118
|
res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
|
119
|
-
expect( res.values ).to eq([['5', '1.23',
|
119
|
+
expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
|
120
120
|
expect( res.ftype(0) ).to eq(20)
|
121
121
|
end
|
122
122
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -54,8 +54,8 @@ describe PG::TypeMapByOid do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should check format when deleting coders" do
|
57
|
-
expect{ tm.rm_coder
|
58
|
-
expect{ tm.rm_coder
|
57
|
+
expect{ tm.rm_coder(2, 123) }.to raise_error(ArgumentError)
|
58
|
+
expect{ tm.rm_coder(-1, 123) }.to raise_error(ArgumentError)
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should check format when adding coders" do
|
data/spec/pg/type_map_spec.rb
CHANGED
data/spec/pg/type_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require 'pg'
|
5
|
+
require 'time'
|
5
6
|
|
6
7
|
|
7
8
|
describe "PG::Type derivations" do
|
@@ -11,10 +12,14 @@ describe "PG::Type derivations" do
|
|
11
12
|
let!(:textdec_boolean) { PG::TextDecoder::Boolean.new }
|
12
13
|
let!(:textenc_float) { PG::TextEncoder::Float.new }
|
13
14
|
let!(:textdec_float) { PG::TextDecoder::Float.new }
|
15
|
+
let!(:textenc_numeric) { PG::TextEncoder::Numeric.new }
|
14
16
|
let!(:textenc_string) { PG::TextEncoder::String.new }
|
15
17
|
let!(:textdec_string) { PG::TextDecoder::String.new }
|
16
18
|
let!(:textenc_timestamp) { PG::TextEncoder::TimestampWithoutTimeZone.new }
|
17
19
|
let!(:textdec_timestamp) { PG::TextDecoder::TimestampWithoutTimeZone.new }
|
20
|
+
let!(:textenc_timestamputc) { PG::TextEncoder::TimestampUtc.new }
|
21
|
+
let!(:textdec_timestamputc) { PG::TextDecoder::TimestampUtc.new }
|
22
|
+
let!(:textdec_timestampul) { PG::TextDecoder::TimestampUtcToLocal.new }
|
18
23
|
let!(:textenc_timestamptz) { PG::TextEncoder::TimestampWithTimeZone.new }
|
19
24
|
let!(:textdec_timestamptz) { PG::TextDecoder::TimestampWithTimeZone.new }
|
20
25
|
let!(:textenc_bytea) { PG::TextEncoder::Bytea.new }
|
@@ -39,6 +44,14 @@ describe "PG::Type derivations" do
|
|
39
44
|
end.new
|
40
45
|
end
|
41
46
|
|
47
|
+
let!(:intenc_incrementer_with_encoding) do
|
48
|
+
Class.new(PG::SimpleEncoder) do
|
49
|
+
def encode(value, encoding)
|
50
|
+
r = (value.to_i + 1).to_s + " #{encoding}"
|
51
|
+
r.encode!(encoding)
|
52
|
+
end
|
53
|
+
end.new
|
54
|
+
end
|
42
55
|
let!(:intenc_incrementer_with_int_result) do
|
43
56
|
Class.new(PG::SimpleEncoder) do
|
44
57
|
def encode(value)
|
@@ -67,7 +80,7 @@ describe "PG::Type derivations" do
|
|
67
80
|
expect( intdec_incrementer.decode("3") ).to eq( 4 )
|
68
81
|
end
|
69
82
|
|
70
|
-
it "should decode integers of different lengths
|
83
|
+
it "should decode integers of different lengths from text format" do
|
71
84
|
30.times do |zeros|
|
72
85
|
expect( textdec_int.decode("1" + "0"*zeros) ).to eq( 10 ** zeros )
|
73
86
|
expect( textdec_int.decode(zeros==0 ? "0" : "9"*zeros) ).to eq( 10 ** zeros - 1 )
|
@@ -88,21 +101,41 @@ describe "PG::Type derivations" do
|
|
88
101
|
end
|
89
102
|
|
90
103
|
context 'timestamps' do
|
91
|
-
it 'decodes timestamps without timezone' do
|
92
|
-
expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456') ).
|
93
|
-
to
|
104
|
+
it 'decodes timestamps without timezone as local time' do
|
105
|
+
expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
106
|
+
to eq( Time.new(2016,1,2, 23,23,59.123456).iso8601(5) )
|
107
|
+
expect( textdec_timestamp.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
108
|
+
to eq( Time.new(2016,8,2, 23,23,59.123456).iso8601(5) )
|
109
|
+
end
|
110
|
+
it 'decodes timestamps with UTC time and returns UTC timezone' do
|
111
|
+
expect( textdec_timestamputc.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
112
|
+
to eq( Time.utc(2016,1,2, 23,23,59.123456).iso8601(5) )
|
113
|
+
expect( textdec_timestamputc.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
114
|
+
to eq( Time.utc(2016,8,2, 23,23,59.123456).iso8601(5) )
|
115
|
+
end
|
116
|
+
it 'decodes timestamps with UTC time and returns local timezone' do
|
117
|
+
expect( textdec_timestampul.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
118
|
+
to eq( Time.utc(2016,1,2, 23,23,59.123456).getlocal.iso8601(5) )
|
119
|
+
expect( textdec_timestampul.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
120
|
+
to eq( Time.utc(2016,8,2, 23,23,59.123456).getlocal.iso8601(5) )
|
94
121
|
end
|
95
122
|
it 'decodes timestamps with hour timezone' do
|
96
|
-
expect( textdec_timestamptz.decode('
|
97
|
-
to
|
98
|
-
expect( textdec_timestamptz.decode('
|
99
|
-
to
|
123
|
+
expect( textdec_timestamptz.decode('2016-01-02 23:23:59.123456-04').iso8601(5) ).
|
124
|
+
to eq( Time.new(2016,1,2, 23,23,59.123456, "-04:00").iso8601(5) )
|
125
|
+
expect( textdec_timestamptz.decode('2016-08-02 23:23:59.123456+10').iso8601(5) ).
|
126
|
+
to eq( Time.new(2016,8,2, 23,23,59.123456, "+10:00").iso8601(5) )
|
127
|
+
expect( textdec_timestamptz.decode('1913-12-31 23:58:59.1231-03').iso8601(5) ).
|
128
|
+
to eq( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
129
|
+
expect( textdec_timestamptz.decode('4714-11-24 23:58:59.1231-03 BC').iso8601(5) ).
|
130
|
+
to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
131
|
+
expect( textdec_timestamptz.decode('294276-12-31 23:58:59.1231+03').iso8601(5) ).
|
132
|
+
to eq( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").iso8601(5) )
|
100
133
|
end
|
101
134
|
it 'decodes timestamps with hour:minute timezone' do
|
102
135
|
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04:15') ).
|
103
136
|
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:15") )
|
104
|
-
expect( textdec_timestamptz.decode('2015-
|
105
|
-
to be_within(0.000001).of( Time.new(2015,
|
137
|
+
expect( textdec_timestamptz.decode('2015-07-26 17:26:42.691511-04:30') ).
|
138
|
+
to be_within(0.000001).of( Time.new(2015,07,26, 17, 26, 42.691511, "-04:30") )
|
106
139
|
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10:45') ).
|
107
140
|
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:45") )
|
108
141
|
end
|
@@ -113,6 +146,81 @@ describe "PG::Type derivations" do
|
|
113
146
|
expect( textdec_timestamptz.decode('1916-01-01 00:00:00-00:25:21') ).
|
114
147
|
to be_within(0.000001).of( Time.new(1916, 1, 1, 0, 0, 0, "-00:25:21") )
|
115
148
|
end
|
149
|
+
it 'decodes timestamps with date before 1823' do
|
150
|
+
expect( textdec_timestamp.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
151
|
+
to eq( Time.new(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
152
|
+
expect( textdec_timestamputc.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
153
|
+
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
154
|
+
expect( textdec_timestampul.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
155
|
+
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
156
|
+
expect( textdec_timestamptz.decode('1822-01-02 23:23:59.123456+04').iso8601(5) ).
|
157
|
+
to eq( Time.new(1822,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
158
|
+
end
|
159
|
+
it 'decodes timestamps with date after 2116' do
|
160
|
+
expect( textdec_timestamp.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
161
|
+
to eq( Time.new(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
162
|
+
expect( textdec_timestamputc.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
163
|
+
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
164
|
+
expect( textdec_timestampul.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
165
|
+
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
166
|
+
expect( textdec_timestamptz.decode('2117-01-02 23:23:59.123456+04').iso8601(5) ).
|
167
|
+
to eq( Time.new(2117,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
168
|
+
end
|
169
|
+
it 'decodes timestamps with variable number of digits for the useconds part' do
|
170
|
+
sec = "59.12345678912345"
|
171
|
+
(4..sec.length).each do |i|
|
172
|
+
expect( textdec_timestamp.decode("2016-01-02 23:23:#{sec[0,i]}") ).
|
173
|
+
to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, sec[0,i].to_f) )
|
174
|
+
end
|
175
|
+
end
|
176
|
+
it 'decodes timestamps with leap-second' do
|
177
|
+
expect( textdec_timestamp.decode('1998-12-31 23:59:60.1234') ).
|
178
|
+
to be_within(0.000001).of( Time.new(1998,12,31, 23, 59, 60.1234) )
|
179
|
+
end
|
180
|
+
|
181
|
+
def textdec_timestamptz_decode_should_fail(str)
|
182
|
+
expect(textdec_timestamptz.decode(str)).to eq(str)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'fails when the timestamp is an empty string' do
|
186
|
+
textdec_timestamptz_decode_should_fail('')
|
187
|
+
end
|
188
|
+
it 'fails when the timestamp contains values with less digits than expected' do
|
189
|
+
textdec_timestamptz_decode_should_fail('2016-0-02 23:23:59.123456+00:25:21')
|
190
|
+
textdec_timestamptz_decode_should_fail('2016-01-0 23:23:59.123456+00:25:21')
|
191
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 2:23:59.123456+00:25:21')
|
192
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:2:59.123456+00:25:21')
|
193
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:5.123456+00:25:21')
|
194
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.+00:25:21')
|
195
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+0:25:21')
|
196
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:2:21')
|
197
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:2')
|
198
|
+
end
|
199
|
+
it 'fails when the timestamp contains values with more digits than expected' do
|
200
|
+
textdec_timestamptz_decode_should_fail('2016-011-02 23:23:59.123456+00:25:21')
|
201
|
+
textdec_timestamptz_decode_should_fail('2016-01-022 23:23:59.123456+00:25:21')
|
202
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 233:23:59.123456+00:25:21')
|
203
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:233:59.123456+00:25:21')
|
204
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:599.123456+00:25:21')
|
205
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+000:25:21')
|
206
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:255:21')
|
207
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:211')
|
208
|
+
end
|
209
|
+
it 'fails when the timestamp contains values with invalid characters' do
|
210
|
+
str = '2013-01-02 23:23:59.123456+00:25:21'
|
211
|
+
str.length.times do |i|
|
212
|
+
textdec_timestamptz_decode_should_fail(str[0,i] + "x" + str[i+1..-1])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
it 'fails when the timestamp contains leading characters' do
|
216
|
+
textdec_timestamptz_decode_should_fail(' 2016-01-02 23:23:59.123456')
|
217
|
+
end
|
218
|
+
it 'fails when the timestamp contains trailing characters' do
|
219
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456 ')
|
220
|
+
end
|
221
|
+
it 'fails when the timestamp contains non ASCII character' do
|
222
|
+
textdec_timestamptz_decode_should_fail('2016-01ª02 23:23:59.123456')
|
223
|
+
end
|
116
224
|
end
|
117
225
|
|
118
226
|
context 'identifier quotation' do
|
@@ -127,6 +235,13 @@ describe "PG::Type derivations" do
|
|
127
235
|
expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
|
128
236
|
expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
|
129
237
|
end
|
238
|
+
|
239
|
+
it 'should split identifier string with correct character encoding' do
|
240
|
+
quoted_type = PG::TextDecoder::Identifier.new
|
241
|
+
v = quoted_type.decode(%[Héllo].encode("iso-8859-1")).first
|
242
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
243
|
+
expect( v ).to eq( %[Héllo].encode(Encoding::ISO_8859_1) )
|
244
|
+
end
|
130
245
|
end
|
131
246
|
|
132
247
|
it "should raise when decode method is called with wrong args" do
|
@@ -140,6 +255,11 @@ describe "PG::Type derivations" do
|
|
140
255
|
expect( textdec_string.decode( nil )).to be_nil
|
141
256
|
expect( textdec_int.decode( nil )).to be_nil
|
142
257
|
end
|
258
|
+
|
259
|
+
it "should be defined on an encoder but not on a decoder instance" do
|
260
|
+
expect( textdec_int.respond_to?(:decode) ).to be_truthy
|
261
|
+
expect( textenc_int.respond_to?(:decode) ).to be_falsey
|
262
|
+
end
|
143
263
|
end
|
144
264
|
|
145
265
|
describe '#encode' do
|
@@ -190,16 +310,98 @@ describe "PG::Type derivations" do
|
|
190
310
|
end
|
191
311
|
end
|
192
312
|
|
313
|
+
it "should encode floats" do
|
314
|
+
expect( textenc_float.encode(0) ).to eq( "0.0" )
|
315
|
+
expect( textenc_float.encode(-1) ).to eq( "-1.0" )
|
316
|
+
expect( textenc_float.encode(-1.234567890123456789) ).to eq( "-1.234567890123457" )
|
317
|
+
expect( textenc_float.encode(9) ).to eq( "9.0" )
|
318
|
+
expect( textenc_float.encode(10) ).to eq( "10.0" )
|
319
|
+
expect( textenc_float.encode(-99) ).to eq( "-99.0" )
|
320
|
+
expect( textenc_float.encode(-100) ).to eq( "-100.0" )
|
321
|
+
expect( textenc_float.encode(999) ).to eq( "999.0" )
|
322
|
+
expect( textenc_float.encode(-1000) ).to eq( "-1000.0" )
|
323
|
+
expect( textenc_float.encode(1234.567890123456789) ).to eq( "1234.567890123457" )
|
324
|
+
expect( textenc_float.encode(-9999) ).to eq( "-9999.0" )
|
325
|
+
expect( textenc_float.encode(10000) ).to eq( "10000.0" )
|
326
|
+
expect( textenc_float.encode(99999) ).to eq( "99999.0" )
|
327
|
+
expect( textenc_float.encode(-100000) ).to eq( "-100000.0" )
|
328
|
+
expect( textenc_float.encode(-999999) ).to eq( "-999999.0" )
|
329
|
+
expect( textenc_float.encode(1000000) ).to eq( "1000000.0" )
|
330
|
+
expect( textenc_float.encode(9999999) ).to eq( "9999999.0" )
|
331
|
+
expect( textenc_float.encode(-100000000000000) ).to eq( "-100000000000000.0" )
|
332
|
+
expect( textenc_float.encode(123456789012345) ).to eq( "123456789012345.0" )
|
333
|
+
expect( textenc_float.encode(-999999999999999) ).to eq( "-999999999999999.0" )
|
334
|
+
expect( textenc_float.encode(1000000000000000) ).to eq( "1e15" )
|
335
|
+
expect( textenc_float.encode(-1234567890123456) ).to eq( "-1.234567890123456e15" )
|
336
|
+
expect( textenc_float.encode(9999999999999999) ).to eq( "1e16" )
|
337
|
+
|
338
|
+
expect( textenc_float.encode(-0.0) ).to eq( "0.0" )
|
339
|
+
expect( textenc_float.encode(0.1) ).to eq( "0.1" )
|
340
|
+
expect( textenc_float.encode(0.1234567890123456789) ).to eq( "0.1234567890123457" )
|
341
|
+
expect( textenc_float.encode(-0.9) ).to eq( "-0.9" )
|
342
|
+
expect( textenc_float.encode(-0.01234567890123456789) ).to eq( "-0.01234567890123457" )
|
343
|
+
expect( textenc_float.encode(0.09) ).to eq( "0.09" )
|
344
|
+
expect( textenc_float.encode(0.001234567890123456789) ).to eq( "0.001234567890123457" )
|
345
|
+
expect( textenc_float.encode(-0.009) ).to eq( "-0.009" )
|
346
|
+
expect( textenc_float.encode(-0.0001234567890123456789) ).to eq( "-0.0001234567890123457" )
|
347
|
+
expect( textenc_float.encode(0.0009) ).to eq( "0.0009" )
|
348
|
+
expect( textenc_float.encode(0.00001) ).to eq( "1e-5" )
|
349
|
+
expect( textenc_float.encode(0.00001234567890123456789) ).to eq( "1.234567890123457e-5" )
|
350
|
+
expect( textenc_float.encode(-0.00009) ).to eq( "-9e-5" )
|
351
|
+
expect( textenc_float.encode(-0.11) ).to eq( "-0.11" )
|
352
|
+
expect( textenc_float.encode(10.11) ).to eq( "10.11" )
|
353
|
+
expect( textenc_float.encode(-1.234567890123456789E-280) ).to eq( "-1.234567890123457e-280" )
|
354
|
+
expect( textenc_float.encode(-1.234567890123456789E280) ).to eq( "-1.234567890123457e280" )
|
355
|
+
expect( textenc_float.encode(9876543210987654321E280) ).to eq( "9.87654321098765e298" )
|
356
|
+
expect( textenc_float.encode(9876543210987654321E-400) ).to eq( "0.0" )
|
357
|
+
expect( textenc_float.encode(9876543210987654321E400) ).to eq( "Infinity" )
|
358
|
+
end
|
359
|
+
|
193
360
|
it "should encode special floats equally to Float#to_s" do
|
194
361
|
expect( textenc_float.encode(Float::INFINITY) ).to eq( Float::INFINITY.to_s )
|
195
362
|
expect( textenc_float.encode(-Float::INFINITY) ).to eq( (-Float::INFINITY).to_s )
|
196
363
|
expect( textenc_float.encode(-Float::NAN) ).to eq( Float::NAN.to_s )
|
197
364
|
end
|
198
365
|
|
366
|
+
it "should encode various inputs to numeric format" do
|
367
|
+
expect( textenc_numeric.encode(0) ).to eq( "0" )
|
368
|
+
expect( textenc_numeric.encode(1) ).to eq( "1" )
|
369
|
+
expect( textenc_numeric.encode(-12345678901234567890123) ).to eq( "-12345678901234567890123" )
|
370
|
+
expect( textenc_numeric.encode(0.0) ).to eq( "0.0" )
|
371
|
+
expect( textenc_numeric.encode(1.0) ).to eq( "1.0" )
|
372
|
+
expect( textenc_numeric.encode(-1.23456789012e45) ).to eq( "-1.23456789012e45" )
|
373
|
+
expect( textenc_numeric.encode(Float::NAN) ).to eq( Float::NAN.to_s )
|
374
|
+
expect( textenc_numeric.encode(BigDecimal(0)) ).to eq( "0.0" )
|
375
|
+
expect( textenc_numeric.encode(BigDecimal(1)) ).to eq( "1.0" )
|
376
|
+
expect( textenc_numeric.encode(BigDecimal("-12345678901234567890.1234567")) ).to eq( "-12345678901234567890.1234567" )
|
377
|
+
expect( textenc_numeric.encode(" 123 ") ).to eq( " 123 " )
|
378
|
+
end
|
379
|
+
|
199
380
|
it "encodes binary string to bytea" do
|
200
381
|
expect( textenc_bytea.encode("\x00\x01\x02\x03\xef".b) ).to eq( "\\x00010203ef" )
|
201
382
|
end
|
202
383
|
|
384
|
+
context 'timestamps' do
|
385
|
+
it 'encodes timestamps without timezone' do
|
386
|
+
expect( textenc_timestamp.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
387
|
+
to match( /^2016-01-02 23:23:59.12345\d+$/ )
|
388
|
+
expect( textenc_timestamp.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
389
|
+
to match( /^2016-08-02 23:23:59.12345\d+$/ )
|
390
|
+
end
|
391
|
+
it 'encodes timestamps with UTC timezone' do
|
392
|
+
expect( textenc_timestamputc.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
393
|
+
to match( /^2016-01-02 20:23:59.12345\d+$/ )
|
394
|
+
expect( textenc_timestamputc.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
395
|
+
to match( /^2016-08-02 20:23:59.12345\d+$/ )
|
396
|
+
end
|
397
|
+
it 'encodes timestamps with hour timezone' do
|
398
|
+
expect( textenc_timestamptz.encode(Time.new(2016,1,02, 23, 23, 59.123456, -4*60*60)) ).
|
399
|
+
to match( /^2016-01-02 23:23:59.12345\d+ \-04:00$/ )
|
400
|
+
expect( textenc_timestamptz.encode(Time.new(2016,8,02, 23, 23, 59.123456, 10*60*60)) ).
|
401
|
+
to match( /^2016-08-02 23:23:59.12345\d+ \+10:00$/ )
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
203
405
|
context 'identifier quotation' do
|
204
406
|
it 'should quote and escape identifier' do
|
205
407
|
quoted_type = PG::TextEncoder::Identifier.new
|
@@ -209,6 +411,13 @@ describe "PG::Type derivations" do
|
|
209
411
|
expect( quoted_type.encode( nil ) ).to be_nil
|
210
412
|
end
|
211
413
|
|
414
|
+
it 'should quote identifiers with correct character encoding' do
|
415
|
+
quoted_type = PG::TextEncoder::Identifier.new
|
416
|
+
v = quoted_type.encode(['Héllo'], "iso-8859-1")
|
417
|
+
expect( v ).to eq( %["Héllo"].encode(Encoding::ISO_8859_1) )
|
418
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
419
|
+
end
|
420
|
+
|
212
421
|
it "will raise a TypeError for invalid arguments to quote_ident" do
|
213
422
|
quoted_type = PG::TextEncoder::Identifier.new
|
214
423
|
expect{ quoted_type.encode( [nil] ) }.to raise_error(TypeError)
|
@@ -220,6 +429,12 @@ describe "PG::Type derivations" do
|
|
220
429
|
expect( intenc_incrementer.encode(3) ).to eq( "4 " )
|
221
430
|
end
|
222
431
|
|
432
|
+
it "should encode with ruby encoder and given character encoding" do
|
433
|
+
r = intenc_incrementer_with_encoding.encode(3, Encoding::CP850)
|
434
|
+
expect( r ).to eq( "4 CP850" )
|
435
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
436
|
+
end
|
437
|
+
|
223
438
|
it "should return when ruby encoder returns non string values" do
|
224
439
|
expect( intenc_incrementer_with_int_result.encode(3) ).to eq( 4 )
|
225
440
|
end
|
@@ -228,6 +443,11 @@ describe "PG::Type derivations" do
|
|
228
443
|
expect( textenc_string.encode( nil )).to be_nil
|
229
444
|
expect( textenc_int.encode( nil )).to be_nil
|
230
445
|
end
|
446
|
+
|
447
|
+
it "should be defined on a decoder but not on an encoder instance" do
|
448
|
+
expect( textenc_int.respond_to?(:encode) ).to be_truthy
|
449
|
+
expect( textdec_int.respond_to?(:encode) ).to be_falsey
|
450
|
+
end
|
231
451
|
end
|
232
452
|
|
233
453
|
it "should be possible to marshal encoders" do
|
@@ -244,7 +464,7 @@ describe "PG::Type derivations" do
|
|
244
464
|
|
245
465
|
it "should respond to to_h" do
|
246
466
|
expect( textenc_int.to_h ).to eq( {
|
247
|
-
name: 'Integer', oid: 23, format: 0
|
467
|
+
name: 'Integer', oid: 23, format: 0, flags: 0
|
248
468
|
} )
|
249
469
|
end
|
250
470
|
|
@@ -275,6 +495,7 @@ describe "PG::Type derivations" do
|
|
275
495
|
describe "Array types" do
|
276
496
|
let!(:textenc_string_array) { PG::TextEncoder::Array.new elements_type: textenc_string }
|
277
497
|
let!(:textdec_string_array) { PG::TextDecoder::Array.new elements_type: textdec_string }
|
498
|
+
let!(:textdec_string_array_raise) { PG::TextDecoder::Array.new elements_type: textdec_string, flags: PG::Coder:: FORMAT_ERROR_TO_RAISE }
|
278
499
|
let!(:textenc_int_array) { PG::TextEncoder::Array.new elements_type: textenc_int, needs_quotation: false }
|
279
500
|
let!(:textdec_int_array) { PG::TextDecoder::Array.new elements_type: textdec_int, needs_quotation: false }
|
280
501
|
let!(:textenc_float_array) { PG::TextEncoder::Array.new elements_type: textenc_float, needs_quotation: false }
|
@@ -340,6 +561,57 @@ describe "PG::Type derivations" do
|
|
340
561
|
it 'respects a different delimiter' do
|
341
562
|
expect( textdec_string_array_with_delimiter.decode(%[{1;2;3}]) ).to eq( ['1','2','3'] )
|
342
563
|
end
|
564
|
+
|
565
|
+
it 'ignores array dimensions' do
|
566
|
+
expect( textdec_string_array.decode(%[[2:4]={1,2,3}]) ).to eq( ['1','2','3'] )
|
567
|
+
expect( textdec_string_array.decode(%[[]={1,2,3}]) ).to eq( ['1','2','3'] )
|
568
|
+
expect( textdec_string_array.decode(%[ [-1:+2]= {4,3,2,1}]) ).to eq( ['4','3','2','1'] )
|
569
|
+
end
|
570
|
+
|
571
|
+
it 'ignores spaces after array' do
|
572
|
+
expect( textdec_string_array.decode(%[[2:4]={1,2,3} ]) ).to eq( ['1','2','3'] )
|
573
|
+
expect( textdec_string_array.decode(%[{1,2,3} ]) ).to eq( ['1','2','3'] )
|
574
|
+
end
|
575
|
+
|
576
|
+
describe "with malformed syntax are deprecated" do
|
577
|
+
it 'accepts broken array dimensions' do
|
578
|
+
expect( textdec_string_array.decode(%([2:4={1,2,3})) ).to eq([['1','2','3']])
|
579
|
+
expect( textdec_string_array.decode(%(2:4]={1,2,3})) ).to eq([['1','2','3']])
|
580
|
+
expect( textdec_string_array.decode(%(={1,2,3})) ).to eq([['1','2','3']])
|
581
|
+
expect( textdec_string_array.decode(%([x]={1,2,3})) ).to eq([['1','2','3']])
|
582
|
+
expect( textdec_string_array.decode(%([]{1,2,3})) ).to eq([['1','2','3']])
|
583
|
+
expect( textdec_string_array.decode(%(1,2,3)) ).to eq(['','2'])
|
584
|
+
end
|
585
|
+
|
586
|
+
it 'accepts malformed arrays' do
|
587
|
+
expect( textdec_string_array.decode(%({1,2,3)) ).to eq(['1','2'])
|
588
|
+
expect( textdec_string_array.decode(%({1,2,3}})) ).to eq(['1','2','3'])
|
589
|
+
expect( textdec_string_array.decode(%({1,2,3}x)) ).to eq(['1','2','3'])
|
590
|
+
expect( textdec_string_array.decode(%({{1,2},{2,3})) ).to eq([['1','2'],['2','3']])
|
591
|
+
expect( textdec_string_array.decode(%({{1,2},{2,3}}x)) ).to eq([['1','2'],['2','3']])
|
592
|
+
expect( textdec_string_array.decode(%({[1,2},{2,3}}})) ).to eq(['[1','2'])
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
describe "with malformed syntax are raised with pg-2.0+" do
|
597
|
+
it 'complains about broken array dimensions' do
|
598
|
+
expect{ textdec_string_array_raise.decode(%([2:4={1,2,3})) }.to raise_error(TypeError)
|
599
|
+
expect{ textdec_string_array_raise.decode(%(2:4]={1,2,3})) }.to raise_error(TypeError)
|
600
|
+
expect{ textdec_string_array_raise.decode(%(={1,2,3})) }.to raise_error(TypeError)
|
601
|
+
expect{ textdec_string_array_raise.decode(%([x]={1,2,3})) }.to raise_error(TypeError)
|
602
|
+
expect{ textdec_string_array_raise.decode(%([]{1,2,3})) }.to raise_error(TypeError)
|
603
|
+
expect{ textdec_string_array_raise.decode(%(1,2,3)) }.to raise_error(TypeError)
|
604
|
+
end
|
605
|
+
|
606
|
+
it 'complains about malformed array' do
|
607
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3)) }.to raise_error(TypeError)
|
608
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3}})) }.to raise_error(TypeError)
|
609
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3}x)) }.to raise_error(TypeError)
|
610
|
+
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3})) }.to raise_error(TypeError)
|
611
|
+
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3}}x)) }.to raise_error(TypeError)
|
612
|
+
expect{ textdec_string_array_raise.decode(%({[1,2},{2,3}}})) }.to raise_error(TypeError)
|
613
|
+
end
|
614
|
+
end
|
343
615
|
end
|
344
616
|
|
345
617
|
context 'bytea' do
|
@@ -424,7 +696,7 @@ describe "PG::Type derivations" do
|
|
424
696
|
expect( textenc_int_array.encode(['1',['2'],'3']) ).to eq( %[{1,{2},3}] )
|
425
697
|
end
|
426
698
|
it 'encodes an array of float8 with sub arrays' do
|
427
|
-
expect( textenc_float_array.encode([1000.11,[-0.
|
699
|
+
expect( textenc_float_array.encode([1000.11,[-0.00000221,[3.31,-441]],[nil,6.61],-7.71]) ).to match(Regexp.new(%[^{1000.1*,{-2.2*e-*6,{3.3*,-441.0}},{NULL,6.6*},-7.7*}$].gsub(/([\.\+\{\}\,])/, "\\\\\\1").gsub(/\*/, "\\d*")))
|
428
700
|
end
|
429
701
|
end
|
430
702
|
context 'two dimensional arrays' do
|
@@ -447,9 +719,18 @@ describe "PG::Type derivations" do
|
|
447
719
|
end
|
448
720
|
|
449
721
|
context 'array of types with encoder in ruby space' do
|
450
|
-
it 'encodes with quotation' do
|
722
|
+
it 'encodes with quotation and default character encoding' do
|
723
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
724
|
+
r = array_type.encode([3,4])
|
725
|
+
expect( r ).to eq( %[{"4 ","5 "}] )
|
726
|
+
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'encodes with quotation and given character encoding' do
|
451
730
|
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
452
|
-
|
731
|
+
r = array_type.encode([3,4], Encoding::CP850)
|
732
|
+
expect( r ).to eq( %[{"4 ","5 "}] )
|
733
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
453
734
|
end
|
454
735
|
|
455
736
|
it 'encodes without quotation' do
|
@@ -457,6 +738,20 @@ describe "PG::Type derivations" do
|
|
457
738
|
expect( array_type.encode([3,4]) ).to eq( %[{4 ,5 }] )
|
458
739
|
end
|
459
740
|
|
741
|
+
it 'encodes with default character encoding' do
|
742
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
743
|
+
r = array_type.encode([3,4])
|
744
|
+
expect( r ).to eq( %[{"4 ASCII-8BIT","5 ASCII-8BIT"}] )
|
745
|
+
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
746
|
+
end
|
747
|
+
|
748
|
+
it 'encodes with given character encoding' do
|
749
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
750
|
+
r = array_type.encode([3,4], Encoding::CP850)
|
751
|
+
expect( r ).to eq( %[{"4 CP850","5 CP850"}] )
|
752
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
753
|
+
end
|
754
|
+
|
460
755
|
it "should raise when ruby encoder returns non string values" do
|
461
756
|
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_int_result, needs_quotation: false
|
462
757
|
expect{ array_type.encode([3,4]) }.to raise_error(TypeError)
|
@@ -473,6 +768,13 @@ describe "PG::Type derivations" do
|
|
473
768
|
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
474
769
|
expect( quoted_type.encode(["'A\",","\\B'"]) ).to eq( %['{"''A\\",","\\\\B''"}'] )
|
475
770
|
end
|
771
|
+
|
772
|
+
it 'should quote literals with correct character encoding' do
|
773
|
+
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
774
|
+
v = quoted_type.encode(["Héllo"], "iso-8859-1")
|
775
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
776
|
+
expect( v ).to eq( %['{Héllo}'].encode(Encoding::ISO_8859_1) )
|
777
|
+
end
|
476
778
|
end
|
477
779
|
end
|
478
780
|
|
@@ -482,15 +784,15 @@ describe "PG::Type derivations" do
|
|
482
784
|
expect( lt.to_h ).to eq( textenc_int_array.to_h )
|
483
785
|
end
|
484
786
|
|
485
|
-
it "should be possible to marshal
|
486
|
-
mt = Marshal.dump(
|
787
|
+
it "should be possible to marshal decoders" do
|
788
|
+
mt = Marshal.dump(textdec_string_array_raise)
|
487
789
|
lt = Marshal.load(mt)
|
488
|
-
expect( lt.to_h ).to eq(
|
790
|
+
expect( lt.to_h ).to eq( textdec_string_array_raise.to_h )
|
489
791
|
end
|
490
792
|
|
491
793
|
it "should respond to to_h" do
|
492
794
|
expect( textenc_int_array.to_h ).to eq( {
|
493
|
-
name: nil, oid: 0, format: 0,
|
795
|
+
name: nil, oid: 0, format: 0, flags: 0,
|
494
796
|
elements_type: textenc_int, needs_quotation: false, delimiter: ','
|
495
797
|
} )
|
496
798
|
end
|
@@ -522,9 +824,19 @@ describe "PG::Type derivations" do
|
|
522
824
|
expect( e.encode("(\xFBm") ).to eq("KPtt")
|
523
825
|
end
|
524
826
|
|
827
|
+
it 'should encode Strings as base64 with correct character encoding' do
|
828
|
+
e = PG::TextEncoder::ToBase64.new
|
829
|
+
v = e.encode("Héllo".encode("utf-16le"), "iso-8859-1")
|
830
|
+
expect( v ).to eq("SOlsbG8=")
|
831
|
+
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
832
|
+
end
|
833
|
+
|
525
834
|
it "should encode Strings as base64 in BinaryDecoder" do
|
526
835
|
e = PG::BinaryDecoder::ToBase64.new
|
527
836
|
expect( e.decode("x") ).to eq("eA==")
|
837
|
+
v = e.decode("Héllo".encode("utf-16le"))
|
838
|
+
expect( v ).to eq("SADpAGwAbABvAA==")
|
839
|
+
expect( v.encoding ).to eq(Encoding::ASCII_8BIT)
|
528
840
|
end
|
529
841
|
|
530
842
|
it "should encode Integers as base64" do
|
@@ -611,6 +923,12 @@ describe "PG::Type derivations" do
|
|
611
923
|
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
612
924
|
to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
|
613
925
|
end
|
926
|
+
|
927
|
+
it 'should output a string with correct character encoding' do
|
928
|
+
v = encoder.encode(["Héllo"], "iso-8859-1")
|
929
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
930
|
+
expect( v ).to eq( "Héllo\n".encode(Encoding::ISO_8859_1) )
|
931
|
+
end
|
614
932
|
end
|
615
933
|
|
616
934
|
context "with TypeMapByClass" do
|
@@ -672,9 +990,15 @@ describe "PG::Type derivations" do
|
|
672
990
|
end
|
673
991
|
|
674
992
|
describe '#decode' do
|
675
|
-
it "should decode
|
993
|
+
it "should decode COPY text format to array of strings" do
|
676
994
|
expect( decoder.decode("123\t \0#\t#\n#\r#\\ \t234\t#\x01#\002\n".gsub("#", "\\"))).to eq( ["123", " \0\t\n\r\\ ", "234", "\x01\x02"] )
|
677
995
|
end
|
996
|
+
|
997
|
+
it 'should respect input character encoding' do
|
998
|
+
v = decoder.decode("Héllo\n".encode("iso-8859-1")).first
|
999
|
+
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
1000
|
+
expect( v ).to eq("Héllo".encode("iso-8859-1"))
|
1001
|
+
end
|
678
1002
|
end
|
679
1003
|
end
|
680
1004
|
|
@@ -694,4 +1018,106 @@ describe "PG::Type derivations" do
|
|
694
1018
|
end
|
695
1019
|
end
|
696
1020
|
end
|
1021
|
+
|
1022
|
+
describe PG::RecordCoder do
|
1023
|
+
describe PG::TextEncoder::Record do
|
1024
|
+
context "with default typemap" do
|
1025
|
+
let!(:encoder) do
|
1026
|
+
PG::TextEncoder::Record.new
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
it "should encode different types of Ruby objects" do
|
1030
|
+
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
1031
|
+
to eq('("xyz","123","2456","34567","456789","5678901","[1, 2, 3]","12.1","abcdefg",)')
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
it 'should output a string with correct character encoding' do
|
1035
|
+
v = encoder.encode(["Héllo"], "iso-8859-1")
|
1036
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
1037
|
+
expect( v ).to eq( '("Héllo")'.encode(Encoding::ISO_8859_1) )
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
context "with TypeMapByClass" do
|
1042
|
+
let!(:tm) do
|
1043
|
+
tm = PG::TypeMapByClass.new
|
1044
|
+
tm[Integer] = textenc_int
|
1045
|
+
tm[Float] = intenc_incrementer
|
1046
|
+
tm[Array] = PG::TextEncoder::Array.new elements_type: textenc_string
|
1047
|
+
tm
|
1048
|
+
end
|
1049
|
+
let!(:encoder) do
|
1050
|
+
PG::TextEncoder::Record.new type_map: tm
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
it "should have reasonable default values" do
|
1054
|
+
expect( encoder.name ).to be_nil
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
it "copies all attributes with #dup" do
|
1058
|
+
encoder.name = "test"
|
1059
|
+
encoder.type_map = PG::TypeMapByColumn.new []
|
1060
|
+
encoder2 = encoder.dup
|
1061
|
+
expect( encoder.object_id ).to_not eq( encoder2.object_id )
|
1062
|
+
expect( encoder2.name ).to eq( "test" )
|
1063
|
+
expect( encoder2.type_map ).to be_a_kind_of( PG::TypeMapByColumn )
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
describe '#encode' do
|
1067
|
+
it "should encode different types of Ruby objects" do
|
1068
|
+
expect( encoder.encode([]) ).to eq("()")
|
1069
|
+
expect( encoder.encode(["a"]) ).to eq('("a")')
|
1070
|
+
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
1071
|
+
to eq('("xyz","123","2456","34567","456789","5678901","{1,2,3}","13 ","abcdefg",)')
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
it "should escape special characters" do
|
1075
|
+
expect( encoder.encode([" \"\t\n\\\r"]) ).to eq("(\" \"\"\t\n##\r\")".gsub("#", "\\"))
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
describe PG::TextDecoder::Record do
|
1082
|
+
context "with default typemap" do
|
1083
|
+
let!(:decoder) do
|
1084
|
+
PG::TextDecoder::Record.new
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
describe '#decode' do
|
1088
|
+
it "should decode composite text format to array of strings" do
|
1089
|
+
expect( decoder.decode('("fuzzy dice",,"",42,)') ).to eq( ["fuzzy dice",nil, "", "42", nil] )
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
it 'should respect input character encoding' do
|
1093
|
+
v = decoder.decode("(Héllo)".encode("iso-8859-1")).first
|
1094
|
+
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
1095
|
+
expect( v ).to eq("Héllo".encode("iso-8859-1"))
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
it 'should raise an error on malformed input' do
|
1099
|
+
expect{ decoder.decode('') }.to raise_error(ArgumentError, /"" - Missing left parenthesis/)
|
1100
|
+
expect{ decoder.decode('(') }.to raise_error(ArgumentError, /"\(" - Unexpected end of input/)
|
1101
|
+
expect{ decoder.decode('(\\') }.to raise_error(ArgumentError, /"\(\\" - Unexpected end of input/)
|
1102
|
+
expect{ decoder.decode('()x') }.to raise_error(ArgumentError, /"\(\)x" - Junk after right parenthesis/)
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
context "with TypeMapByColumn" do
|
1108
|
+
let!(:tm) do
|
1109
|
+
PG::TypeMapByColumn.new [textdec_int, textdec_string, intdec_incrementer, nil]
|
1110
|
+
end
|
1111
|
+
let!(:decoder) do
|
1112
|
+
PG::TextDecoder::Record.new type_map: tm
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
describe '#decode' do
|
1116
|
+
it "should decode different types of Ruby objects" do
|
1117
|
+
expect( decoder.decode("(123,\" #,#\n#\r#\\ \",234,#\x01#\002)".gsub("#", "\\"))).to eq( [123, " ,\n\r\\ ", 235, "\x01\x02"] )
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
end
|
1121
|
+
end
|
1122
|
+
end
|
697
1123
|
end
|