pg 0.17.1 → 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/BSDL +2 -2
- data/ChangeLog +0 -3506
- data/History.rdoc +308 -0
- data/Manifest.txt +35 -19
- data/README-Windows.rdoc +17 -28
- data/README.ja.rdoc +1 -2
- data/README.rdoc +113 -14
- data/Rakefile +67 -30
- data/Rakefile.cross +109 -83
- data/ext/errorcodes.def +101 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +33 -2
- data/ext/extconf.rb +55 -58
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +262 -130
- data/ext/pg.h +266 -54
- data/ext/pg_binary_decoder.c +229 -0
- data/ext/pg_binary_encoder.c +163 -0
- data/ext/pg_coder.c +561 -0
- data/ext/pg_connection.c +1689 -990
- data/ext/pg_copy_coder.c +599 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +897 -164
- data/ext/pg_text_decoder.c +987 -0
- data/ext/pg_text_encoder.c +814 -0
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +166 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +244 -0
- data/ext/pg_type_map_by_column.c +313 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +356 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/pg_util.c +149 -0
- data/ext/pg_util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +522 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +104 -0
- data/lib/pg/connection.rb +153 -41
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +33 -6
- data/lib/pg/text_decoder.rb +46 -0
- data/lib/pg/text_encoder.rb +59 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +16 -0
- data/lib/pg.rb +29 -9
- data/spec/{lib/helpers.rb → helpers.rb} +151 -64
- data/spec/pg/basic_type_mapping_spec.rb +630 -0
- data/spec/pg/connection_spec.rb +1180 -477
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +456 -120
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +226 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +1123 -0
- data/spec/pg_spec.rb +26 -20
- data.tar.gz.sig +0 -0
- metadata +148 -91
- 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
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# -*- rspec -*-
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require_relative '../helpers'
|
5
|
+
|
6
|
+
require 'pg'
|
7
|
+
|
8
|
+
|
9
|
+
describe PG::TypeMapByClass do
|
10
|
+
|
11
|
+
let!(:textenc_int){ PG::TextEncoder::Integer.new name: 'INT4', oid: 23 }
|
12
|
+
let!(:textenc_float){ PG::TextEncoder::Float.new name: 'FLOAT8', oid: 701 }
|
13
|
+
let!(:textenc_string){ PG::TextEncoder::String.new name: 'TEXT', oid: 25 }
|
14
|
+
let!(:binaryenc_int){ PG::BinaryEncoder::Int8.new name: 'INT8', oid: 20, format: 1 }
|
15
|
+
let!(:pass_through_type) do
|
16
|
+
type = Class.new(PG::SimpleEncoder) do
|
17
|
+
def encode(*v)
|
18
|
+
v.inspect
|
19
|
+
end
|
20
|
+
end.new
|
21
|
+
type.oid = 25
|
22
|
+
type.format = 0
|
23
|
+
type.name = 'pass_through'
|
24
|
+
type
|
25
|
+
end
|
26
|
+
|
27
|
+
let!(:tm) do
|
28
|
+
tm = PG::TypeMapByClass.new
|
29
|
+
tm[Integer] = binaryenc_int
|
30
|
+
tm[Float] = textenc_float
|
31
|
+
tm[Symbol] = pass_through_type
|
32
|
+
tm
|
33
|
+
end
|
34
|
+
|
35
|
+
let!(:raise_class) do
|
36
|
+
Class.new
|
37
|
+
end
|
38
|
+
|
39
|
+
let!(:derived_tm) do
|
40
|
+
tm = Class.new(PG::TypeMapByClass) do
|
41
|
+
def array_type_map_for(value)
|
42
|
+
PG::TextEncoder::Array.new name: '_INT4', oid: 1007, elements_type: PG::TextEncoder::Integer.new
|
43
|
+
end
|
44
|
+
end.new
|
45
|
+
tm[Integer] = proc{|value| textenc_int }
|
46
|
+
tm[raise_class] = proc{|value| /invalid/ }
|
47
|
+
tm[Array] = :array_type_map_for
|
48
|
+
tm
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should retrieve all conversions" do
|
52
|
+
expect( tm.coders ).to eq( {
|
53
|
+
Integer => binaryenc_int,
|
54
|
+
Float => textenc_float,
|
55
|
+
Symbol => pass_through_type,
|
56
|
+
} )
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should retrieve particular conversions" do
|
60
|
+
expect( tm[Integer] ).to eq(binaryenc_int)
|
61
|
+
expect( tm[Float] ).to eq(textenc_float)
|
62
|
+
expect( tm[Range] ).to be_nil
|
63
|
+
expect( derived_tm[raise_class] ).to be_kind_of(Proc)
|
64
|
+
expect( derived_tm[Array] ).to eq(:array_type_map_for)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should allow deletion of coders" do
|
68
|
+
tm[Integer] = nil
|
69
|
+
expect( tm[Integer] ).to be_nil
|
70
|
+
expect( tm.coders ).to eq( {
|
71
|
+
Float => textenc_float,
|
72
|
+
Symbol => pass_through_type,
|
73
|
+
} )
|
74
|
+
end
|
75
|
+
|
76
|
+
it "forwards query param conversions to the #default_type_map" do
|
77
|
+
tm1 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] )
|
78
|
+
|
79
|
+
tm2 = PG::TypeMapByClass.new
|
80
|
+
tm2[Integer] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
|
81
|
+
tm2.default_type_map = tm1
|
82
|
+
|
83
|
+
res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", ['1', 2, 3], 0, tm2 )
|
84
|
+
|
85
|
+
expect( res.ftype(0) ).to eq( 23 ) # tm1
|
86
|
+
expect( res.ftype(1) ).to eq( 21 ) # tm2
|
87
|
+
expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Decoding Examples
|
92
|
+
#
|
93
|
+
|
94
|
+
it "should raise an error when used for results" do
|
95
|
+
res = @conn.exec_params( "SELECT 1", [], 1 )
|
96
|
+
expect{ res.type_map = tm }.to raise_error(NotImplementedError, /not suitable to map result values/)
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Encoding Examples
|
101
|
+
#
|
102
|
+
|
103
|
+
it "should allow mixed type conversions" do
|
104
|
+
res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
|
105
|
+
expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
|
106
|
+
expect( res.ftype(0) ).to eq(20)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should expire the cache after changes to the coders" do
|
110
|
+
res = @conn.exec_params( "SELECT $1", [5], 0, tm )
|
111
|
+
expect( res.ftype(0) ).to eq(20)
|
112
|
+
|
113
|
+
tm[Integer] = textenc_int
|
114
|
+
|
115
|
+
res = @conn.exec_params( "SELECT $1", [5], 0, tm )
|
116
|
+
expect( res.ftype(0) ).to eq(23)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should allow mixed type conversions with derived type map" do
|
120
|
+
res = @conn.exec_params( "SELECT $1, $2", [6, [7]], 0, derived_tm )
|
121
|
+
expect( res.values ).to eq([['6', '{7}']])
|
122
|
+
expect( res.ftype(0) ).to eq(23)
|
123
|
+
expect( res.ftype(1) ).to eq(1007)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise TypeError with derived type map" do
|
127
|
+
expect{
|
128
|
+
@conn.exec_params( "SELECT $1", [raise_class.new], 0, derived_tm )
|
129
|
+
}.to raise_error(TypeError, /invalid type Regexp/)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should raise error on invalid coder object" do
|
133
|
+
tm[TrueClass] = "dummy"
|
134
|
+
expect{
|
135
|
+
@conn.exec_params( "SELECT $1", [true], 0, tm )
|
136
|
+
}.to raise_error(NoMethodError, /undefined method.*call/)
|
137
|
+
end
|
138
|
+
end
|