pg 0.15.1 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -3022
  6. data/History.rdoc +370 -4
  7. data/Manifest.txt +39 -19
  8. data/README-Windows.rdoc +17 -28
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +113 -14
  11. data/Rakefile +97 -36
  12. data/Rakefile.cross +109 -83
  13. data/ext/errorcodes.def +1032 -0
  14. data/ext/errorcodes.rb +45 -0
  15. data/ext/errorcodes.txt +494 -0
  16. data/ext/extconf.rb +55 -52
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +94 -38
  19. data/ext/pg.c +273 -121
  20. data/ext/pg.h +292 -50
  21. data/ext/pg_binary_decoder.c +229 -0
  22. data/ext/pg_binary_encoder.c +163 -0
  23. data/ext/pg_coder.c +561 -0
  24. data/ext/pg_connection.c +1811 -1051
  25. data/ext/pg_copy_coder.c +599 -0
  26. data/ext/pg_errors.c +95 -0
  27. data/ext/pg_record_coder.c +491 -0
  28. data/ext/pg_result.c +917 -203
  29. data/ext/pg_text_decoder.c +987 -0
  30. data/ext/pg_text_encoder.c +814 -0
  31. data/ext/pg_tuple.c +549 -0
  32. data/ext/pg_type_map.c +166 -0
  33. data/ext/pg_type_map_all_strings.c +116 -0
  34. data/ext/pg_type_map_by_class.c +244 -0
  35. data/ext/pg_type_map_by_column.c +313 -0
  36. data/ext/pg_type_map_by_mri_type.c +284 -0
  37. data/ext/pg_type_map_by_oid.c +356 -0
  38. data/ext/pg_type_map_in_ruby.c +299 -0
  39. data/ext/pg_util.c +149 -0
  40. data/ext/pg_util.h +65 -0
  41. data/lib/pg.rb +31 -9
  42. data/lib/pg/basic_type_mapping.rb +522 -0
  43. data/lib/pg/binary_decoder.rb +23 -0
  44. data/lib/pg/coder.rb +104 -0
  45. data/lib/pg/connection.rb +235 -30
  46. data/lib/pg/constants.rb +2 -1
  47. data/lib/pg/exceptions.rb +2 -1
  48. data/lib/pg/result.rb +33 -6
  49. data/lib/pg/text_decoder.rb +46 -0
  50. data/lib/pg/text_encoder.rb +59 -0
  51. data/lib/pg/tuple.rb +30 -0
  52. data/lib/pg/type_map_by_column.rb +16 -0
  53. data/spec/{lib/helpers.rb → helpers.rb} +154 -52
  54. data/spec/pg/basic_type_mapping_spec.rb +630 -0
  55. data/spec/pg/connection_spec.rb +1352 -426
  56. data/spec/pg/connection_sync_spec.rb +41 -0
  57. data/spec/pg/result_spec.rb +508 -105
  58. data/spec/pg/tuple_spec.rb +333 -0
  59. data/spec/pg/type_map_by_class_spec.rb +138 -0
  60. data/spec/pg/type_map_by_column_spec.rb +226 -0
  61. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  62. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  63. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  64. data/spec/pg/type_map_spec.rb +22 -0
  65. data/spec/pg/type_spec.rb +1123 -0
  66. data/spec/pg_spec.rb +35 -16
  67. metadata +163 -84
  68. metadata.gz.sig +0 -0
  69. data/sample/array_insert.rb +0 -20
  70. data/sample/async_api.rb +0 -106
  71. data/sample/async_copyto.rb +0 -39
  72. data/sample/async_mixed.rb +0 -56
  73. data/sample/check_conn.rb +0 -21
  74. data/sample/copyfrom.rb +0 -81
  75. data/sample/copyto.rb +0 -19
  76. data/sample/cursor.rb +0 -21
  77. data/sample/disk_usage_report.rb +0 -186
  78. data/sample/issue-119.rb +0 -94
  79. data/sample/losample.rb +0 -69
  80. data/sample/minimal-testcase.rb +0 -17
  81. data/sample/notify_wait.rb +0 -72
  82. data/sample/pg_statistics.rb +0 -294
  83. data/sample/replication_monitor.rb +0 -231
  84. data/sample/test_binary_values.rb +0 -33
  85. data/sample/wal_shipper.rb +0 -434
  86. 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