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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +130 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +95 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +6 -5
  20. data/certs/ged.pem +24 -0
  21. data/ext/errorcodes.def +8 -0
  22. data/ext/errorcodes.txt +3 -1
  23. data/ext/extconf.rb +91 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +59 -4
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +673 -493
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +39 -11
  32. data/ext/pg_result.c +61 -31
  33. data/ext/pg_text_decoder.c +1 -1
  34. data/ext/pg_text_encoder.c +6 -6
  35. data/ext/pg_tuple.c +47 -21
  36. data/ext/pg_type_map.c +41 -8
  37. data/ext/pg_type_map_all_strings.c +14 -1
  38. data/ext/pg_type_map_by_class.c +49 -24
  39. data/ext/pg_type_map_by_column.c +64 -28
  40. data/ext/pg_type_map_by_mri_type.c +47 -18
  41. data/ext/pg_type_map_by_oid.c +52 -23
  42. data/ext/pg_type_map_in_ruby.c +50 -19
  43. data/ext/pg_util.c +2 -2
  44. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  45. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  46. data/lib/pg/basic_type_map_for_results.rb +81 -0
  47. data/lib/pg/basic_type_registry.rb +296 -0
  48. data/lib/pg/coder.rb +1 -1
  49. data/lib/pg/connection.rb +588 -58
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +47 -32
  52. data/misc/openssl-pg-segfault.rb +31 -0
  53. data/misc/postgres/History.txt +9 -0
  54. data/misc/postgres/Manifest.txt +5 -0
  55. data/misc/postgres/README.txt +21 -0
  56. data/misc/postgres/Rakefile +21 -0
  57. data/misc/postgres/lib/postgres.rb +16 -0
  58. data/misc/ruby-pg/History.txt +9 -0
  59. data/misc/ruby-pg/Manifest.txt +5 -0
  60. data/misc/ruby-pg/README.txt +21 -0
  61. data/misc/ruby-pg/Rakefile +21 -0
  62. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  63. data/pg.gemspec +32 -0
  64. data/sample/array_insert.rb +20 -0
  65. data/sample/async_api.rb +106 -0
  66. data/sample/async_copyto.rb +39 -0
  67. data/sample/async_mixed.rb +56 -0
  68. data/sample/check_conn.rb +21 -0
  69. data/sample/copydata.rb +71 -0
  70. data/sample/copyfrom.rb +81 -0
  71. data/sample/copyto.rb +19 -0
  72. data/sample/cursor.rb +21 -0
  73. data/sample/disk_usage_report.rb +177 -0
  74. data/sample/issue-119.rb +94 -0
  75. data/sample/losample.rb +69 -0
  76. data/sample/minimal-testcase.rb +17 -0
  77. data/sample/notify_wait.rb +72 -0
  78. data/sample/pg_statistics.rb +285 -0
  79. data/sample/replication_monitor.rb +222 -0
  80. data/sample/test_binary_values.rb +33 -0
  81. data/sample/wal_shipper.rb +434 -0
  82. data/sample/warehouse_partitions.rb +311 -0
  83. data.tar.gz.sig +0 -0
  84. metadata +76 -217
  85. metadata.gz.sig +0 -0
  86. data/ChangeLog +0 -0
  87. data/lib/pg/basic_type_mapping.rb +0 -522
  88. data/spec/data/expected_trace.out +0 -26
  89. data/spec/data/random_binary_data +0 -0
  90. data/spec/helpers.rb +0 -380
  91. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  92. data/spec/pg/connection_spec.rb +0 -1949
  93. data/spec/pg/connection_sync_spec.rb +0 -41
  94. data/spec/pg/result_spec.rb +0 -681
  95. data/spec/pg/tuple_spec.rb +0 -333
  96. data/spec/pg/type_map_by_class_spec.rb +0 -138
  97. data/spec/pg/type_map_by_column_spec.rb +0 -226
  98. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  99. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  100. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  101. data/spec/pg/type_map_spec.rb +0 -22
  102. data/spec/pg/type_spec.rb +0 -1123
  103. data/spec/pg_spec.rb +0 -50
@@ -1,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