pg 1.2.3 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +80 -0
  6. data/.github/workflows/source-gem.yml +129 -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 +75 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +5 -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 +90 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +35 -1
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +538 -279
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +38 -10
  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 +369 -56
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +38 -25
  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 +79 -226
  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