pg 0.17.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +0 -3506
  5. data/History.rdoc +308 -0
  6. data/Manifest.txt +35 -19
  7. data/README-Windows.rdoc +17 -28
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +113 -14
  10. data/Rakefile +67 -30
  11. data/Rakefile.cross +109 -83
  12. data/ext/errorcodes.def +101 -0
  13. data/ext/errorcodes.rb +1 -1
  14. data/ext/errorcodes.txt +33 -2
  15. data/ext/extconf.rb +55 -58
  16. data/ext/gvl_wrappers.c +4 -0
  17. data/ext/gvl_wrappers.h +27 -39
  18. data/ext/pg.c +262 -130
  19. data/ext/pg.h +266 -54
  20. data/ext/pg_binary_decoder.c +229 -0
  21. data/ext/pg_binary_encoder.c +163 -0
  22. data/ext/pg_coder.c +561 -0
  23. data/ext/pg_connection.c +1689 -990
  24. data/ext/pg_copy_coder.c +599 -0
  25. data/ext/pg_errors.c +6 -0
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +897 -164
  28. data/ext/pg_text_decoder.c +987 -0
  29. data/ext/pg_text_encoder.c +814 -0
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +166 -0
  32. data/ext/pg_type_map_all_strings.c +116 -0
  33. data/ext/pg_type_map_by_class.c +244 -0
  34. data/ext/pg_type_map_by_column.c +313 -0
  35. data/ext/pg_type_map_by_mri_type.c +284 -0
  36. data/ext/pg_type_map_by_oid.c +356 -0
  37. data/ext/pg_type_map_in_ruby.c +299 -0
  38. data/ext/pg_util.c +149 -0
  39. data/ext/pg_util.h +65 -0
  40. data/lib/pg/basic_type_mapping.rb +522 -0
  41. data/lib/pg/binary_decoder.rb +23 -0
  42. data/lib/pg/coder.rb +104 -0
  43. data/lib/pg/connection.rb +153 -41
  44. data/lib/pg/constants.rb +2 -1
  45. data/lib/pg/exceptions.rb +2 -1
  46. data/lib/pg/result.rb +33 -6
  47. data/lib/pg/text_decoder.rb +46 -0
  48. data/lib/pg/text_encoder.rb +59 -0
  49. data/lib/pg/tuple.rb +30 -0
  50. data/lib/pg/type_map_by_column.rb +16 -0
  51. data/lib/pg.rb +29 -9
  52. data/spec/{lib/helpers.rb → helpers.rb} +151 -64
  53. data/spec/pg/basic_type_mapping_spec.rb +630 -0
  54. data/spec/pg/connection_spec.rb +1180 -477
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +456 -120
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +138 -0
  59. data/spec/pg/type_map_by_column_spec.rb +226 -0
  60. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  61. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  62. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  63. data/spec/pg/type_map_spec.rb +22 -0
  64. data/spec/pg/type_spec.rb +1123 -0
  65. data/spec/pg_spec.rb +26 -20
  66. data.tar.gz.sig +0 -0
  67. metadata +148 -91
  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,226 @@
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
@@ -0,0 +1,136 @@
1
+ # -*- rspec -*-
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ require 'pg'
7
+
8
+
9
+ describe PG::TypeMapByMriType 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::TypeMapByMriType.new
29
+ tm['T_FIXNUM'] = binaryenc_int
30
+ tm['T_FLOAT'] = textenc_float
31
+ tm['T_SYMBOL'] = pass_through_type
32
+ tm
33
+ end
34
+
35
+ let!(:derived_tm) do
36
+ tm = Class.new(PG::TypeMapByMriType) do
37
+ def array_type_map_for(value)
38
+ PG::TextEncoder::Array.new name: '_INT4', oid: 1007, elements_type: PG::TextEncoder::Integer.new
39
+ end
40
+ end.new
41
+ tm['T_FIXNUM'] = proc{|value| textenc_int }
42
+ tm['T_REGEXP'] = proc{|value| :invalid }
43
+ tm['T_ARRAY'] = :array_type_map_for
44
+ tm
45
+ end
46
+
47
+ it "should retrieve all conversions" do
48
+ expect( tm.coders ).to eq( {
49
+ "T_FIXNUM" => binaryenc_int,
50
+ "T_FLOAT" => textenc_float,
51
+ "T_SYMBOL" => pass_through_type,
52
+ "T_HASH" => nil,
53
+ "T_ARRAY" => nil,
54
+ "T_BIGNUM" => nil,
55
+ "T_CLASS" => nil,
56
+ "T_COMPLEX" => nil,
57
+ "T_DATA" => nil,
58
+ "T_FALSE" => nil,
59
+ "T_FILE" => nil,
60
+ "T_MODULE" => nil,
61
+ "T_OBJECT" => nil,
62
+ "T_RATIONAL" => nil,
63
+ "T_REGEXP" => nil,
64
+ "T_STRING" => nil,
65
+ "T_STRUCT" => nil,
66
+ "T_TRUE" => nil,
67
+ } )
68
+ end
69
+
70
+ it "should retrieve particular conversions" do
71
+ expect( tm['T_FIXNUM'] ).to eq(binaryenc_int)
72
+ expect( tm['T_FLOAT'] ).to eq(textenc_float)
73
+ expect( tm['T_BIGNUM'] ).to be_nil
74
+ expect( derived_tm['T_REGEXP'] ).to be_kind_of(Proc)
75
+ expect( derived_tm['T_ARRAY'] ).to eq(:array_type_map_for)
76
+ end
77
+
78
+ it "should allow deletion of coders" do
79
+ tm['T_FIXNUM'] = nil
80
+ expect( tm['T_FIXNUM'] ).to be_nil
81
+ end
82
+
83
+ it "should check MRI type key" do
84
+ expect{ tm['NO_TYPE'] }.to raise_error(ArgumentError)
85
+ expect{ tm[123] }.to raise_error(TypeError)
86
+ expect{ tm['NO_TYPE'] = textenc_float }.to raise_error(ArgumentError)
87
+ expect{ tm[123] = textenc_float }.to raise_error(TypeError)
88
+ end
89
+
90
+ it "forwards query param conversions to the #default_type_map" do
91
+ tm1 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] )
92
+
93
+ tm2 = PG::TypeMapByMriType.new
94
+ tm2['T_FIXNUM'] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
95
+ tm2.default_type_map = tm1
96
+
97
+ res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", ['1', 2, 3], 0, tm2 )
98
+
99
+ expect( res.ftype(0) ).to eq( 23 ) # tm1
100
+ expect( res.ftype(1) ).to eq( 21 ) # tm2
101
+ expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
102
+ end
103
+
104
+ #
105
+ # Decoding Examples
106
+ #
107
+
108
+ it "should raise an error when used for results" do
109
+ res = @conn.exec_params( "SELECT 1", [], 1 )
110
+ expect{ res.type_map = tm }.to raise_error(NotImplementedError, /not suitable to map result values/)
111
+ end
112
+
113
+ #
114
+ # Encoding Examples
115
+ #
116
+
117
+ it "should allow mixed type conversions" do
118
+ res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
119
+ expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
120
+ expect( res.ftype(0) ).to eq(20)
121
+ end
122
+
123
+ it "should allow mixed type conversions with derived type map" do
124
+ res = @conn.exec_params( "SELECT $1, $2", [6, [7]], 0, derived_tm )
125
+ expect( res.values ).to eq([['6', '{7}']])
126
+ expect( res.ftype(0) ).to eq(23)
127
+ expect( res.ftype(1) ).to eq(1007)
128
+ end
129
+
130
+ it "should raise TypeError with derived type map" do
131
+ expect{
132
+ @conn.exec_params( "SELECT $1", [//], 0, derived_tm )
133
+ }.to raise_error(TypeError, /argument 1/)
134
+ end
135
+
136
+ end
@@ -0,0 +1,149 @@
1
+ # -*- rspec -*-
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ require 'pg'
7
+
8
+
9
+ describe PG::TypeMapByOid do
10
+
11
+ let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
12
+ let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT8', oid: 701 }
13
+ let!(:textdec_string){ PG::TextDecoder::String.new name: 'TEXT', oid: 25 }
14
+ let!(:textdec_bytea){ PG::TextDecoder::Bytea.new name: 'BYTEA', oid: 17 }
15
+ let!(:binarydec_float){ PG::BinaryDecoder::Float.new name: 'FLOAT8', oid: 701, format: 1 }
16
+ let!(:pass_through_type) do
17
+ type = Class.new(PG::SimpleDecoder) do
18
+ def decode(*v)
19
+ v
20
+ end
21
+ end.new
22
+ type.oid = 1082
23
+ type.format = 0
24
+ type.name = 'pass_through'
25
+ type
26
+ end
27
+
28
+ let!(:tm) do
29
+ tm = PG::TypeMapByOid.new
30
+ tm.add_coder textdec_int
31
+ tm.add_coder textdec_float
32
+ tm.add_coder binarydec_float
33
+ tm.add_coder pass_through_type
34
+ tm
35
+ end
36
+
37
+ it "should retrieve it's conversions" do
38
+ expect( tm.coders ).to eq( [
39
+ textdec_int,
40
+ textdec_float,
41
+ pass_through_type,
42
+ binarydec_float,
43
+ ] )
44
+ end
45
+
46
+ it "should allow deletion of coders" do
47
+ expect( tm.rm_coder 0, 701 ).to eq(textdec_float)
48
+ expect( tm.rm_coder 0, 701 ).to eq(nil)
49
+ expect( tm.rm_coder 1, 701 ).to eq(binarydec_float)
50
+ expect( tm.coders ).to eq( [
51
+ textdec_int,
52
+ pass_through_type,
53
+ ] )
54
+ end
55
+
56
+ it "should check format when deleting coders" do
57
+ expect{ tm.rm_coder(2, 123) }.to raise_error(ArgumentError)
58
+ expect{ tm.rm_coder(-1, 123) }.to raise_error(ArgumentError)
59
+ end
60
+
61
+ it "should check format when adding coders" do
62
+ textdec_int.format = 2
63
+ expect{ tm.add_coder textdec_int }.to raise_error(ArgumentError)
64
+ textdec_int.format = -1
65
+ expect{ tm.add_coder textdec_int }.to raise_error(ArgumentError)
66
+ end
67
+
68
+ it "should check coder type when adding coders" do
69
+ expect{ tm.add_coder :dummy }.to raise_error(ArgumentError)
70
+ end
71
+
72
+ it "should allow reading and writing max_rows_for_online_lookup" do
73
+ expect( tm.max_rows_for_online_lookup ).to eq(10)
74
+ tm.max_rows_for_online_lookup = 5
75
+ expect( tm.max_rows_for_online_lookup ).to eq(5)
76
+ end
77
+
78
+ it "should allow building new TypeMapByColumn for a given result" do
79
+ res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, '2013-06-30'::DATE" )
80
+ tm2 = tm.build_column_map(res)
81
+ expect( tm2 ).to be_a_kind_of(PG::TypeMapByColumn)
82
+ expect( tm2.coders ).to eq( [textdec_int, nil, textdec_float, pass_through_type] )
83
+ end
84
+
85
+ it "forwards result value conversions to another TypeMapByOid as #default_type_map" do
86
+ # One run with implicit built TypeMapByColumn and another with online lookup
87
+ # for each type map.
88
+ [[0, 0], [0, 10], [10, 0], [10, 10]].each do |max_rows1, max_rows2|
89
+ tm1 = PG::TypeMapByOid.new
90
+ tm1.add_coder PG::TextDecoder::Integer.new name: 'INT2', oid: 21
91
+ tm1.max_rows_for_online_lookup = max_rows1
92
+
93
+ tm2 = PG::TypeMapByOid.new
94
+ tm2.add_coder PG::TextDecoder::Integer.new name: 'INT4', oid: 23
95
+ tm2.max_rows_for_online_lookup = max_rows2
96
+ tm2.default_type_map = tm1
97
+
98
+ res = @conn.exec( "SELECT '1'::INT4, '2'::INT2, '3'::INT8" ).map_types!( tm2 )
99
+
100
+ expect( res.getvalue(0,0) ).to eq( 1 ) # tm2
101
+ expect( res.getvalue(0,1) ).to eq( 2 ) # tm1
102
+ expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
103
+ end
104
+ end
105
+
106
+ #
107
+ # Decoding Examples text format
108
+ #
109
+
110
+ it "should allow mixed type conversions in text format" do
111
+ res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, '2013-06-30'::DATE" )
112
+ res.type_map = tm
113
+ expect( res.values ).to eq( [[1, 'a', 2.0, ['2013-06-30', 0, 3] ]] )
114
+ end
115
+
116
+ it "should build a TypeMapByColumn when assigned and the number of rows is high enough" do
117
+ res = @conn.exec( "SELECT generate_series(1,20), 'a', 2.0::FLOAT, '2013-06-30'::DATE" )
118
+ res.type_map = tm
119
+ expect( res.type_map ).to be_kind_of( PG::TypeMapByColumn )
120
+ expect( res.type_map.coders ).to eq( [textdec_int, nil, textdec_float, pass_through_type] )
121
+ end
122
+
123
+ it "should use TypeMapByOid for online lookup and the number of rows is low enough" do
124
+ res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, '2013-06-30'::DATE" )
125
+ res.type_map = tm
126
+ expect( res.type_map ).to be_kind_of( PG::TypeMapByOid )
127
+ end
128
+
129
+ #
130
+ # Decoding Examples binary format
131
+ #
132
+
133
+ it "should allow mixed type conversions in binary format" do
134
+ res = @conn.exec_params( "SELECT 1, 2.0::FLOAT", [], 1 )
135
+ res.type_map = tm
136
+ expect( res.values ).to eq( [["\x00\x00\x00\x01", 2.0 ]] )
137
+ end
138
+
139
+ #
140
+ # Encoding Examples
141
+ #
142
+
143
+ it "should raise an error used for query params" do
144
+ expect{
145
+ @conn.exec_params( "SELECT $1", [5], 0, tm )
146
+ }.to raise_error(NotImplementedError, /not suitable to map query params/)
147
+ end
148
+
149
+ end