pg 1.2.3 → 1.3.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +130 -0
- data/.gitignore +13 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.rdoc +95 -7
- data/Manifest.txt +0 -1
- data/README.rdoc +7 -6
- data/Rakefile +27 -138
- data/Rakefile.cross +6 -5
- data/certs/ged.pem +24 -0
- data/ext/errorcodes.def +8 -0
- data/ext/errorcodes.txt +3 -1
- data/ext/extconf.rb +91 -19
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +59 -4
- data/ext/pg.h +18 -1
- data/ext/pg_coder.c +82 -28
- data/ext/pg_connection.c +673 -493
- data/ext/pg_copy_coder.c +45 -16
- data/ext/pg_record_coder.c +39 -11
- data/ext/pg_result.c +61 -31
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +6 -6
- data/ext/pg_tuple.c +47 -21
- data/ext/pg_type_map.c +41 -8
- data/ext/pg_type_map_all_strings.c +14 -1
- data/ext/pg_type_map_by_class.c +49 -24
- data/ext/pg_type_map_by_column.c +64 -28
- data/ext/pg_type_map_by_mri_type.c +47 -18
- data/ext/pg_type_map_by_oid.c +52 -23
- data/ext/pg_type_map_in_ruby.c +50 -19
- data/ext/pg_util.c +2 -2
- data/lib/pg/basic_type_map_based_on_result.rb +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +296 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +588 -58
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +47 -32
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +32 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +106 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +76 -217
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -0
- data/lib/pg/basic_type_mapping.rb +0 -522
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -380
- data/spec/pg/basic_type_mapping_spec.rb +0 -630
- data/spec/pg/connection_spec.rb +0 -1949
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -681
- data/spec/pg/tuple_spec.rb +0 -333
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -226
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -1123
- data/spec/pg_spec.rb +0 -50
data/spec/pg/tuple_spec.rb
DELETED
@@ -1,333 +0,0 @@
|
|
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,138 +0,0 @@
|
|
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
|
@@ -1,226 +0,0 @@
|
|
1
|
-
# -*- rspec -*-
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
require_relative '../helpers'
|
5
|
-
|
6
|
-
require 'pg'
|
7
|
-
|
8
|
-
|
9
|
-
describe PG::TypeMapByColumn do
|
10
|
-
|
11
|
-
let!(:textenc_int){ PG::TextEncoder::Integer.new name: 'INT4', oid: 23 }
|
12
|
-
let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
|
13
|
-
let!(:textenc_float){ PG::TextEncoder::Float.new name: 'FLOAT4', oid: 700 }
|
14
|
-
let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
|
15
|
-
let!(:textenc_string){ PG::TextEncoder::String.new name: 'TEXT', oid: 25 }
|
16
|
-
let!(:textdec_string){ PG::TextDecoder::String.new name: 'TEXT', oid: 25 }
|
17
|
-
let!(:textdec_bytea){ PG::TextDecoder::Bytea.new name: 'BYTEA', oid: 17 }
|
18
|
-
let!(:binaryenc_bytea){ PG::BinaryEncoder::Bytea.new name: 'BYTEA', oid: 17, format: 1 }
|
19
|
-
let!(:binarydec_bytea){ PG::BinaryDecoder::Bytea.new name: 'BYTEA', oid: 17, format: 1 }
|
20
|
-
let!(:pass_through_type) do
|
21
|
-
type = Class.new(PG::SimpleDecoder) do
|
22
|
-
def decode(*v)
|
23
|
-
v
|
24
|
-
end
|
25
|
-
end.new
|
26
|
-
type.oid = 123456
|
27
|
-
type.format = 1
|
28
|
-
type.name = 'pass_through'
|
29
|
-
type
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should retrieve it's conversions" do
|
33
|
-
cm = PG::TypeMapByColumn.new( [textdec_int, textenc_string, textdec_float, pass_through_type, nil] )
|
34
|
-
expect( cm.coders ).to eq( [
|
35
|
-
textdec_int,
|
36
|
-
textenc_string,
|
37
|
-
textdec_float,
|
38
|
-
pass_through_type,
|
39
|
-
nil
|
40
|
-
] )
|
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>" )
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should retrieve it's oids" do
|
49
|
-
cm = PG::TypeMapByColumn.new( [textdec_int, textdec_string, textdec_float, pass_through_type, nil] )
|
50
|
-
expect( cm.oids ).to eq( [23, 25, 700, 123456, nil] )
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should gracefully handle not initialized state" do
|
54
|
-
# PG::TypeMapByColumn is not initialized in allocate function, like other
|
55
|
-
# type maps, but in #initialize. So it might be not called by derived classes.
|
56
|
-
|
57
|
-
not_init = Class.new(PG::TypeMapByColumn) do
|
58
|
-
def initialize
|
59
|
-
# no super call
|
60
|
-
end
|
61
|
-
end.new
|
62
|
-
|
63
|
-
expect{ @conn.exec_params( "SELECT $1", [ 0 ], 0, not_init ) }.to raise_error(NotImplementedError)
|
64
|
-
|
65
|
-
res = @conn.exec( "SELECT 1" )
|
66
|
-
expect{ res.type_map = not_init }.to raise_error(NotImplementedError)
|
67
|
-
|
68
|
-
@conn.copy_data("COPY (SELECT 1) TO STDOUT") do
|
69
|
-
decoder = PG::TextDecoder::CopyRow.new(type_map: not_init)
|
70
|
-
expect{ @conn.get_copy_data(false, decoder) }.to raise_error(NotImplementedError)
|
71
|
-
@conn.get_copy_data
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
#
|
77
|
-
# Encoding Examples
|
78
|
-
#
|
79
|
-
|
80
|
-
it "should encode integer params" do
|
81
|
-
col_map = PG::TypeMapByColumn.new( [textenc_int]*3 )
|
82
|
-
res = @conn.exec_params( "SELECT $1, $2, $3", [ 0, nil, "-999" ], 0, col_map )
|
83
|
-
expect( res.values ).to eq( [
|
84
|
-
[ "0", nil, "-999" ],
|
85
|
-
] )
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should encode bytea params" do
|
89
|
-
data = "'\u001F\\"
|
90
|
-
col_map = PG::TypeMapByColumn.new( [binaryenc_bytea]*2 )
|
91
|
-
res = @conn.exec_params( "SELECT $1, $2", [ data, nil ], 0, col_map )
|
92
|
-
res.type_map = PG::TypeMapByColumn.new( [textdec_bytea]*2 )
|
93
|
-
expect( res.values ).to eq( [
|
94
|
-
[ data, nil ],
|
95
|
-
] )
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
it "should allow hash form parameters for default encoder" do
|
100
|
-
col_map = PG::TypeMapByColumn.new( [nil, nil] )
|
101
|
-
hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
|
102
|
-
hash_param_nil = { value: nil, type: 17, format: 1 }
|
103
|
-
res = @conn.exec_params( "SELECT $1, $2",
|
104
|
-
[ hash_param_bin, hash_param_nil ], 0, col_map )
|
105
|
-
expect( res.values ).to eq( [["\\x00ff", nil]] )
|
106
|
-
expect( result_typenames(res) ).to eq( ['bytea', 'bytea'] )
|
107
|
-
end
|
108
|
-
|
109
|
-
it "should convert hash form parameters to string when using string encoders" do
|
110
|
-
col_map = PG::TypeMapByColumn.new( [textenc_string, textenc_string] )
|
111
|
-
hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
|
112
|
-
hash_param_nil = { value: nil, type: 17, format: 1 }
|
113
|
-
res = @conn.exec_params( "SELECT $1::text, $2::text",
|
114
|
-
[ hash_param_bin, hash_param_nil ], 0, col_map )
|
115
|
-
expect( res.values ).to eq( [["{:value=>\"\\x00\\xFF\", :type=>17, :format=>1}", "{:value=>nil, :type=>17, :format=>1}"]] )
|
116
|
-
end
|
117
|
-
|
118
|
-
it "shouldn't allow param mappings with different number of fields" do
|
119
|
-
expect{
|
120
|
-
@conn.exec_params( "SELECT $1", [ 123 ], 0, PG::TypeMapByColumn.new([]) )
|
121
|
-
}.to raise_error(ArgumentError, /mapped columns/)
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should verify the default type map for query params as well" do
|
125
|
-
tm1 = PG::TypeMapByColumn.new([])
|
126
|
-
expect{
|
127
|
-
@conn.exec_params( "SELECT $1", [ 123 ], 0, PG::TypeMapByColumn.new([nil]).with_default_type_map(tm1) )
|
128
|
-
}.to raise_error(ArgumentError, /mapped columns/)
|
129
|
-
end
|
130
|
-
|
131
|
-
it "forwards query param conversions to the #default_type_map" do
|
132
|
-
tm1 = PG::TypeMapByClass.new
|
133
|
-
tm1[Integer] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
|
134
|
-
|
135
|
-
tm2 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] ).with_default_type_map( tm1 )
|
136
|
-
res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", [1, 2, :abc], 0, tm2 )
|
137
|
-
|
138
|
-
expect( res.ftype(0) ).to eq( 23 ) # tm2
|
139
|
-
expect( res.ftype(1) ).to eq( 21 ) # tm1
|
140
|
-
expect( res.getvalue(0,2) ).to eq( "abc" ) # TypeMapAllStrings
|
141
|
-
end
|
142
|
-
|
143
|
-
#
|
144
|
-
# Decoding Examples
|
145
|
-
#
|
146
|
-
|
147
|
-
class Exception_in_decode < PG::SimpleDecoder
|
148
|
-
def decode(res, tuple, field)
|
149
|
-
raise "no type decoder defined for tuple #{tuple} field #{field}"
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
it "should raise an error from decode method of type converter" do
|
154
|
-
res = @conn.exec( "SELECT now()" )
|
155
|
-
types = Array.new( res.nfields, Exception_in_decode.new )
|
156
|
-
res.type_map = PG::TypeMapByColumn.new( types )
|
157
|
-
expect{ res.values }.to raise_error(/no type decoder defined/)
|
158
|
-
end
|
159
|
-
|
160
|
-
it "should raise an error for invalid params" do
|
161
|
-
expect{ PG::TypeMapByColumn.new( :WrongType ) }.to raise_error(TypeError, /wrong argument type/)
|
162
|
-
expect{ PG::TypeMapByColumn.new( [123] ) }.to raise_error(ArgumentError, /invalid/)
|
163
|
-
end
|
164
|
-
|
165
|
-
it "shouldn't allow result mappings with different number of fields" do
|
166
|
-
res = @conn.exec( "SELECT 1" )
|
167
|
-
expect{ res.type_map = PG::TypeMapByColumn.new([]) }.to raise_error(ArgumentError, /mapped columns/)
|
168
|
-
end
|
169
|
-
|
170
|
-
it "should verify the default type map for result values as well" do
|
171
|
-
res = @conn.exec( "SELECT 1" )
|
172
|
-
tm1 = PG::TypeMapByColumn.new([])
|
173
|
-
expect{
|
174
|
-
res.type_map = PG::TypeMapByColumn.new([nil]).with_default_type_map(tm1)
|
175
|
-
}.to raise_error(ArgumentError, /mapped columns/)
|
176
|
-
end
|
177
|
-
|
178
|
-
it "forwards result value conversions to a TypeMapByOid as #default_type_map" do
|
179
|
-
# One run with implicit built TypeMapByColumn and another with online lookup
|
180
|
-
[0, 10].each do |max_rows|
|
181
|
-
tm1 = PG::TypeMapByOid.new
|
182
|
-
tm1.add_coder PG::TextDecoder::Integer.new name: 'INT2', oid: 21
|
183
|
-
tm1.max_rows_for_online_lookup = max_rows
|
184
|
-
|
185
|
-
tm2 = PG::TypeMapByColumn.new( [textdec_int, nil, nil] ).with_default_type_map( tm1 )
|
186
|
-
res = @conn.exec( "SELECT '1'::INT4, '2'::INT2, '3'::INT8" ).map_types!( tm2 )
|
187
|
-
|
188
|
-
expect( res.getvalue(0,0) ).to eq( 1 ) # tm2
|
189
|
-
expect( res.getvalue(0,1) ).to eq( 2 ) # tm1
|
190
|
-
expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
it "forwards get_copy_data conversions to another TypeMapByColumn as #default_type_map" do
|
195
|
-
tm1 = PG::TypeMapByColumn.new( [textdec_int, nil, nil] )
|
196
|
-
tm2 = PG::TypeMapByColumn.new( [nil, textdec_int, nil] ).with_default_type_map( tm1 )
|
197
|
-
decoder = PG::TextDecoder::CopyRow.new(type_map: tm2)
|
198
|
-
@conn.copy_data("COPY (SELECT 1, 2, 3) TO STDOUT", decoder) do
|
199
|
-
expect( @conn.get_copy_data ).to eq( [1, 2, '3'] )
|
200
|
-
@conn.get_copy_data
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
it "will deny copy queries with different column count" do
|
205
|
-
[[2, 2], [2, 3], [3, 2]].each do |cols1, cols2|
|
206
|
-
tm1 = PG::TypeMapByColumn.new( [textdec_int, nil, nil][0, cols1] )
|
207
|
-
tm2 = PG::TypeMapByColumn.new( [nil, textdec_int, nil][0, cols2] ).with_default_type_map( tm1 )
|
208
|
-
decoder = PG::TextDecoder::CopyRow.new(type_map: tm2)
|
209
|
-
@conn.copy_data("COPY (SELECT 1, 2, 3) TO STDOUT", decoder) do
|
210
|
-
expect{ @conn.get_copy_data }.to raise_error(ArgumentError, /number of copy fields/)
|
211
|
-
@conn.get_copy_data
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
#
|
217
|
-
# Decoding Examples text format
|
218
|
-
#
|
219
|
-
|
220
|
-
it "should allow mixed type conversions" do
|
221
|
-
res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, '2013-06-30'::DATE, 3" )
|
222
|
-
res.type_map = PG::TypeMapByColumn.new( [textdec_int, textdec_string, textdec_float, pass_through_type, nil] )
|
223
|
-
expect( res.values ).to eq( [[1, 'a', 2.0, ['2013-06-30', 0, 3], '3' ]] )
|
224
|
-
end
|
225
|
-
|
226
|
-
end
|