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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +0 -3506
- data/History.rdoc +308 -0
- data/Manifest.txt +35 -19
- data/README-Windows.rdoc +17 -28
- data/README.ja.rdoc +1 -2
- data/README.rdoc +113 -14
- data/Rakefile +67 -30
- data/Rakefile.cross +109 -83
- data/ext/errorcodes.def +101 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +33 -2
- data/ext/extconf.rb +55 -58
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +262 -130
- data/ext/pg.h +266 -54
- data/ext/pg_binary_decoder.c +229 -0
- data/ext/pg_binary_encoder.c +163 -0
- data/ext/pg_coder.c +561 -0
- data/ext/pg_connection.c +1689 -990
- data/ext/pg_copy_coder.c +599 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +897 -164
- data/ext/pg_text_decoder.c +987 -0
- data/ext/pg_text_encoder.c +814 -0
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +166 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +244 -0
- data/ext/pg_type_map_by_column.c +313 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +356 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/pg_util.c +149 -0
- data/ext/pg_util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +522 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +104 -0
- data/lib/pg/connection.rb +153 -41
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +33 -6
- data/lib/pg/text_decoder.rb +46 -0
- data/lib/pg/text_encoder.rb +59 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +16 -0
- data/lib/pg.rb +29 -9
- data/spec/{lib/helpers.rb → helpers.rb} +151 -64
- data/spec/pg/basic_type_mapping_spec.rb +630 -0
- data/spec/pg/connection_spec.rb +1180 -477
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +456 -120
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +226 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +1123 -0
- data/spec/pg_spec.rb +26 -20
- data.tar.gz.sig +0 -0
- metadata +148 -91
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- rspec -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require_relative '../helpers'
|
5
|
+
|
6
|
+
context "running with sync_* methods" do
|
7
|
+
before :each do
|
8
|
+
PG::Connection.async_api = false
|
9
|
+
end
|
10
|
+
|
11
|
+
after :each do
|
12
|
+
PG::Connection.async_api = true
|
13
|
+
end
|
14
|
+
|
15
|
+
fname = File.expand_path("../connection_spec.rb", __FILE__)
|
16
|
+
eval File.read(fname, encoding: __ENCODING__), binding, fname
|
17
|
+
|
18
|
+
|
19
|
+
it "enables/disables async/sync methods by #async_api" do
|
20
|
+
[true, false].each do |async|
|
21
|
+
PG::Connection.async_api = async
|
22
|
+
|
23
|
+
start = Time.now
|
24
|
+
t = Thread.new do
|
25
|
+
@conn.exec( 'select pg_sleep(1)' )
|
26
|
+
end
|
27
|
+
sleep 0.1
|
28
|
+
|
29
|
+
t.kill
|
30
|
+
t.join
|
31
|
+
dt = Time.now - start
|
32
|
+
|
33
|
+
if async
|
34
|
+
expect( dt ).to be < 1.0
|
35
|
+
else
|
36
|
+
expect( dt ).to be >= 1.0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/spec/pg/result_spec.rb
CHANGED
@@ -1,85 +1,283 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
|
-
|
5
|
-
require 'pathname'
|
4
|
+
require_relative '../helpers'
|
6
5
|
|
7
|
-
basedir = Pathname( __FILE__ ).dirname.parent.parent
|
8
|
-
libdir = basedir + 'lib'
|
9
|
-
|
10
|
-
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
11
|
-
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
12
|
-
}
|
13
|
-
|
14
|
-
require 'rspec'
|
15
|
-
require 'spec/lib/helpers'
|
16
6
|
require 'pg'
|
7
|
+
require 'objspace'
|
8
|
+
|
17
9
|
|
18
10
|
describe PG::Result do
|
19
11
|
|
20
|
-
|
21
|
-
@conn
|
12
|
+
describe :field_name_type do
|
13
|
+
let!(:res) { @conn.exec('SELECT 1 AS a, 2 AS "B"') }
|
14
|
+
|
15
|
+
it "uses string field names per default" do
|
16
|
+
expect(res.field_name_type).to eq(:string)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can set string field names" do
|
20
|
+
res.field_name_type = :string
|
21
|
+
expect(res.field_name_type).to eq(:string)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can set symbol field names" do
|
25
|
+
res.field_name_type = :symbol
|
26
|
+
expect(res.field_name_type).to eq(:symbol)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can set static_symbol field names" do
|
30
|
+
res.field_name_type = :static_symbol
|
31
|
+
expect(res.field_name_type).to eq(:static_symbol)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can't set symbol field names after #fields" do
|
35
|
+
res.fields
|
36
|
+
expect{ res.field_name_type = :symbol }.to raise_error(ArgumentError, /already materialized/)
|
37
|
+
expect(res.field_name_type).to eq(:string)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can't set invalid values" do
|
41
|
+
expect{ res.field_name_type = :sym }.to raise_error(ArgumentError, /invalid argument :sym/)
|
42
|
+
expect{ res.field_name_type = "symbol" }.to raise_error(ArgumentError, /invalid argument "symbol"/)
|
43
|
+
end
|
22
44
|
end
|
23
45
|
|
24
|
-
|
25
|
-
@conn.exec(
|
46
|
+
it "acts as an array of hashes" do
|
47
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
48
|
+
expect( res[0]['a'] ).to eq( '1' )
|
49
|
+
expect( res[0]['b'] ).to eq( '2' )
|
26
50
|
end
|
27
51
|
|
28
|
-
|
29
|
-
@conn.exec(
|
52
|
+
it "acts as an array of hashes with symbols" do
|
53
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
54
|
+
res.field_name_type = :symbol
|
55
|
+
expect( res[0][:a] ).to eq( '1' )
|
56
|
+
expect( res[0][:b] ).to eq( '2' )
|
30
57
|
end
|
31
58
|
|
32
|
-
|
33
|
-
|
59
|
+
it "acts as an array of hashes with static_symbols" do
|
60
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
61
|
+
res.field_name_type = :static_symbol
|
62
|
+
expect( res[0][:a] ).to eq( '1' )
|
63
|
+
expect( res[0][:b] ).to eq( '2' )
|
34
64
|
end
|
35
65
|
|
66
|
+
it "yields a row as an array" do
|
67
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
68
|
+
list = []
|
69
|
+
res.each_row { |r| list << r }
|
70
|
+
expect( list ).to eq [['1', '2']]
|
71
|
+
end
|
36
72
|
|
37
|
-
|
38
|
-
|
39
|
-
|
73
|
+
it "yields a row as an Enumerator" do
|
74
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
75
|
+
e = res.each_row
|
76
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
77
|
+
expect( e.size ).to eq( 1 )
|
78
|
+
expect( e.to_a ).to eq [['1', '2']]
|
79
|
+
end
|
40
80
|
|
41
|
-
it "
|
81
|
+
it "yields a row as an Enumerator of hashs" do
|
42
82
|
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
43
|
-
res
|
44
|
-
|
83
|
+
e = res.each
|
84
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
85
|
+
expect( e.size ).to eq( 1 )
|
86
|
+
expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
|
45
87
|
end
|
46
88
|
|
47
|
-
it "
|
89
|
+
it "yields a row as an Enumerator of hashs with symbols" do
|
48
90
|
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
49
|
-
|
50
|
-
res.
|
51
|
-
|
91
|
+
res.field_name_type = :symbol
|
92
|
+
expect( res.each.to_a ).to eq [{:a=>'1', :b=>'2'}]
|
93
|
+
end
|
94
|
+
|
95
|
+
context "result streaming in single row mode" do
|
96
|
+
let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
|
97
|
+
|
98
|
+
it "can iterate over all rows as Hash" do
|
99
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
100
|
+
@conn.set_single_row_mode
|
101
|
+
expect(
|
102
|
+
@conn.get_result.stream_each.to_a
|
103
|
+
).to eq(
|
104
|
+
[{'a'=>"2"}, {'a'=>"3"}, {'a'=>"4"}]
|
105
|
+
)
|
106
|
+
expect(
|
107
|
+
@conn.get_result.enum_for(:stream_each).to_a
|
108
|
+
).to eq(
|
109
|
+
[{'b'=>"1", 'c'=>"5"}, {'b'=>"1", 'c'=>"6"}]
|
110
|
+
)
|
111
|
+
expect( @conn.get_result ).to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it "can iterate over all rows as Hash with symbols and typemap" do
|
115
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a" )
|
116
|
+
@conn.set_single_row_mode
|
117
|
+
res = @conn.get_result.field_names_as(:symbol)
|
118
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
119
|
+
expect(
|
120
|
+
res.stream_each.to_a
|
121
|
+
).to eq(
|
122
|
+
[{:a=>2}, {:a=>3}, {:a=>4}]
|
123
|
+
)
|
124
|
+
expect( @conn.get_result ).to be_nil
|
125
|
+
end
|
126
|
+
|
127
|
+
it "keeps last result on error while iterating stream_each" do
|
128
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a" )
|
129
|
+
@conn.set_single_row_mode
|
130
|
+
res = @conn.get_result
|
131
|
+
expect do
|
132
|
+
res.stream_each_row do
|
133
|
+
raise ZeroDivisionError
|
134
|
+
end
|
135
|
+
end.to raise_error(ZeroDivisionError)
|
136
|
+
expect( res.values ).to eq([["2"]])
|
137
|
+
end
|
138
|
+
|
139
|
+
it "can iterate over all rows as Array" do
|
140
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
141
|
+
@conn.set_single_row_mode
|
142
|
+
expect(
|
143
|
+
@conn.get_result.enum_for(:stream_each_row).to_a
|
144
|
+
).to eq(
|
145
|
+
[["2"], ["3"], ["4"]]
|
146
|
+
)
|
147
|
+
expect(
|
148
|
+
@conn.get_result.stream_each_row.to_a
|
149
|
+
).to eq(
|
150
|
+
[["1", "5"], ["1", "6"]]
|
151
|
+
)
|
152
|
+
expect( @conn.get_result ).to be_nil
|
153
|
+
end
|
154
|
+
|
155
|
+
it "keeps last result on error while iterating stream_each_row" do
|
156
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a" )
|
157
|
+
@conn.set_single_row_mode
|
158
|
+
res = @conn.get_result
|
159
|
+
expect do
|
160
|
+
res.stream_each_row do
|
161
|
+
raise ZeroDivisionError
|
162
|
+
end
|
163
|
+
end.to raise_error(ZeroDivisionError)
|
164
|
+
expect( res.values ).to eq([["2"]])
|
165
|
+
end
|
166
|
+
|
167
|
+
it "can iterate over all rows as PG::Tuple" do
|
168
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
169
|
+
@conn.set_single_row_mode
|
170
|
+
tuples = @conn.get_result.stream_each_tuple.to_a
|
171
|
+
expect( tuples[0][0] ).to eq( "2" )
|
172
|
+
expect( tuples[1]["a"] ).to eq( "3" )
|
173
|
+
expect( tuples.size ).to eq( 3 )
|
174
|
+
|
175
|
+
tuples = @conn.get_result.enum_for(:stream_each_tuple).to_a
|
176
|
+
expect( tuples[-1][-1] ).to eq( "6" )
|
177
|
+
expect( tuples[-2]["b"] ).to eq( "1" )
|
178
|
+
expect( tuples.size ).to eq( 2 )
|
179
|
+
|
180
|
+
expect( @conn.get_result ).to be_nil
|
181
|
+
end
|
182
|
+
|
183
|
+
it "clears result on error while iterating stream_each_tuple" do
|
184
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a" )
|
185
|
+
@conn.set_single_row_mode
|
186
|
+
res = @conn.get_result
|
187
|
+
expect do
|
188
|
+
res.stream_each_tuple do
|
189
|
+
raise ZeroDivisionError
|
190
|
+
end
|
191
|
+
end.to raise_error(ZeroDivisionError)
|
192
|
+
expect( res.cleared? ).to eq(true)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should reuse field names in stream_each_tuple" do
|
196
|
+
@conn.send_query( "SELECT generate_series(2,3) AS a" )
|
197
|
+
@conn.set_single_row_mode
|
198
|
+
tuple1, tuple2 = *@conn.get_result.stream_each_tuple.to_a
|
199
|
+
expect( tuple1.keys[0].object_id ).to eq(tuple2.keys[0].object_id)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "can iterate over all rows as PG::Tuple with symbols and typemap" do
|
203
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a" )
|
204
|
+
@conn.set_single_row_mode
|
205
|
+
res = @conn.get_result.field_names_as(:symbol)
|
206
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
207
|
+
tuples = res.stream_each_tuple.to_a
|
208
|
+
expect( tuples[0][0] ).to eq( 2 )
|
209
|
+
expect( tuples[1][:a] ).to eq( 3 )
|
210
|
+
expect( @conn.get_result ).to be_nil
|
211
|
+
end
|
212
|
+
|
213
|
+
it "complains when not in single row mode" do
|
214
|
+
@conn.send_query( "SELECT generate_series(2,4)" )
|
215
|
+
expect{
|
216
|
+
@conn.get_result.stream_each_row.to_a
|
217
|
+
}.to raise_error(PG::InvalidResultStatus, /not in single row mode/)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "complains when intersected with get_result" do
|
221
|
+
@conn.send_query( "SELECT 1" )
|
222
|
+
@conn.set_single_row_mode
|
223
|
+
expect{
|
224
|
+
@conn.get_result.stream_each_row.each{ @conn.get_result }
|
225
|
+
}.to raise_error(PG::NoResultError, /no result received/)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "raises server errors" do
|
229
|
+
@conn.send_query( "SELECT 0/0" )
|
230
|
+
expect{
|
231
|
+
@conn.get_result.stream_each_row.to_a
|
232
|
+
}.to raise_error(PG::DivisionByZero)
|
233
|
+
end
|
52
234
|
end
|
53
235
|
|
54
|
-
it "
|
55
|
-
res = @conn.
|
56
|
-
res[0]['n'].
|
236
|
+
it "inserts nil AS NULL and return NULL as nil" do
|
237
|
+
res = @conn.exec_params("SELECT $1::int AS n", [nil])
|
238
|
+
expect( res[0]['n'] ).to be_nil()
|
57
239
|
end
|
58
240
|
|
59
|
-
it "encapsulates errors in a
|
241
|
+
it "encapsulates errors in a PG::Error object" do
|
60
242
|
exception = nil
|
61
243
|
begin
|
62
244
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
63
|
-
rescue
|
245
|
+
rescue PG::Error => err
|
64
246
|
exception = err
|
65
247
|
end
|
66
248
|
|
67
249
|
result = exception.result
|
68
250
|
|
69
|
-
result.
|
70
|
-
result.error_field(
|
71
|
-
result.error_field(
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
result.error_field(
|
76
|
-
result.error_field(
|
77
|
-
result.error_field(
|
78
|
-
result.error_field(
|
79
|
-
result.error_field(
|
80
|
-
result.error_field(
|
81
|
-
|
82
|
-
|
251
|
+
expect( result ).to be_a( described_class() )
|
252
|
+
expect( result.error_field(PG::PG_DIAG_SEVERITY) ).to eq( 'ERROR' )
|
253
|
+
expect( result.error_field(PG::PG_DIAG_SQLSTATE) ).to eq( '42P01' )
|
254
|
+
expect(
|
255
|
+
result.error_field(PG::PG_DIAG_MESSAGE_PRIMARY)
|
256
|
+
).to eq( 'relation "nonexistant_table" does not exist' )
|
257
|
+
expect( result.error_field(PG::PG_DIAG_MESSAGE_DETAIL) ).to be_nil()
|
258
|
+
expect( result.error_field(PG::PG_DIAG_MESSAGE_HINT) ).to be_nil()
|
259
|
+
expect( result.error_field(PG::PG_DIAG_STATEMENT_POSITION) ).to eq( '15' )
|
260
|
+
expect( result.error_field(PG::PG_DIAG_INTERNAL_POSITION) ).to be_nil()
|
261
|
+
expect( result.error_field(PG::PG_DIAG_INTERNAL_QUERY) ).to be_nil()
|
262
|
+
expect( result.error_field(PG::PG_DIAG_CONTEXT) ).to be_nil()
|
263
|
+
expect(
|
264
|
+
result.error_field(PG::PG_DIAG_SOURCE_FILE)
|
265
|
+
).to match( /parse_relation\.c$|namespace\.c$/ )
|
266
|
+
expect( result.error_field(PG::PG_DIAG_SOURCE_LINE) ).to match( /^\d+$/ )
|
267
|
+
expect(
|
268
|
+
result.error_field(PG::PG_DIAG_SOURCE_FUNCTION)
|
269
|
+
).to match( /^parserOpenTable$|^RangeVarGetRelid$/ )
|
270
|
+
end
|
271
|
+
|
272
|
+
it "encapsulates PG_DIAG_SEVERITY_NONLOCALIZED error in a PG::Error object", :postgresql_96 do
|
273
|
+
result = nil
|
274
|
+
begin
|
275
|
+
@conn.exec( "SELECT * FROM nonexistant_table" )
|
276
|
+
rescue PG::Error => err
|
277
|
+
result = err.result
|
278
|
+
end
|
279
|
+
|
280
|
+
expect( result.error_field(PG::PG_DIAG_SEVERITY_NONLOCALIZED) ).to eq( 'ERROR' )
|
83
281
|
end
|
84
282
|
|
85
283
|
it "encapsulates database object names for integrity constraint violations", :postgresql_93 do
|
@@ -87,133 +285,184 @@ describe PG::Result do
|
|
87
285
|
exception = nil
|
88
286
|
begin
|
89
287
|
@conn.exec( "INSERT INTO integrity VALUES (NULL)" )
|
90
|
-
rescue
|
288
|
+
rescue PG::Error => err
|
91
289
|
exception = err
|
92
290
|
end
|
93
291
|
result = exception.result
|
94
292
|
|
95
|
-
result.error_field(
|
96
|
-
result.error_field(
|
97
|
-
result.error_field(
|
98
|
-
result.error_field(
|
99
|
-
result.error_field(
|
293
|
+
expect( result.error_field(PG::PG_DIAG_SCHEMA_NAME) ).to eq( 'public' )
|
294
|
+
expect( result.error_field(PG::PG_DIAG_TABLE_NAME) ).to eq( 'integrity' )
|
295
|
+
expect( result.error_field(PG::PG_DIAG_COLUMN_NAME) ).to eq( 'id' )
|
296
|
+
expect( result.error_field(PG::PG_DIAG_DATATYPE_NAME) ).to be_nil
|
297
|
+
expect( result.error_field(PG::PG_DIAG_CONSTRAINT_NAME) ).to be_nil
|
100
298
|
end
|
101
299
|
|
102
|
-
it "
|
300
|
+
it "detects division by zero as SQLSTATE 22012" do
|
103
301
|
sqlstate = nil
|
104
302
|
begin
|
105
|
-
|
106
|
-
rescue
|
303
|
+
@conn.exec("SELECT 1/0")
|
304
|
+
rescue PG::Error => e
|
107
305
|
sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
|
108
306
|
end
|
109
|
-
sqlstate.
|
307
|
+
expect( sqlstate ).to eq( 22012 )
|
308
|
+
end
|
309
|
+
|
310
|
+
it "provides the error message" do
|
311
|
+
@conn.send_query("SELECT xyz")
|
312
|
+
res = @conn.get_result; @conn.get_result
|
313
|
+
expect( res.error_message ).to match(/"xyz"/)
|
314
|
+
expect( res.result_error_message ).to match(/"xyz"/)
|
110
315
|
end
|
111
316
|
|
112
|
-
it "
|
317
|
+
it "provides a verbose error message", :postgresql_96 do
|
318
|
+
@conn.send_query("SELECT xyz")
|
319
|
+
res = @conn.get_result; @conn.get_result
|
320
|
+
# PQERRORS_TERSE should give a single line result
|
321
|
+
expect( res.verbose_error_message(PG::PQERRORS_TERSE, PG::PQSHOW_CONTEXT_ALWAYS) ).to match(/\A.*\n\z/)
|
322
|
+
# PQERRORS_VERBOSE should give a multi line result
|
323
|
+
expect( res.result_verbose_error_message(PG::PQERRORS_VERBOSE, PG::PQSHOW_CONTEXT_NEVER) ).to match(/\n.*\n/)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "provides a verbose error message with SQLSTATE", :postgresql_12 do
|
327
|
+
@conn.send_query("SELECT xyz")
|
328
|
+
res = @conn.get_result; @conn.get_result
|
329
|
+
expect( res.verbose_error_message(PG::PQERRORS_SQLSTATE, PG::PQSHOW_CONTEXT_NEVER) ).to match(/42703/)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "returns the same bytes in binary format that are sent in binary format" do
|
113
333
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
114
334
|
bytes = File.open(binary_file, 'rb').read
|
115
|
-
res = @conn.
|
335
|
+
res = @conn.exec_params('VALUES ($1::bytea)',
|
116
336
|
[ { :value => bytes, :format => 1 } ], 1)
|
117
|
-
res[0]['column1'].
|
118
|
-
res.getvalue(0,0).
|
119
|
-
res.values[0][0].
|
120
|
-
res.column_values(0)[0].
|
337
|
+
expect( res[0]['column1'] ).to eq( bytes )
|
338
|
+
expect( res.getvalue(0,0) ).to eq( bytes )
|
339
|
+
expect( res.values[0][0] ).to eq( bytes )
|
340
|
+
expect( res.column_values(0)[0] ).to eq( bytes )
|
121
341
|
end
|
122
342
|
|
123
|
-
it "
|
343
|
+
it "returns the same bytes in binary format that are sent as inline text" do
|
124
344
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
125
345
|
bytes = File.open(binary_file, 'rb').read
|
126
346
|
@conn.exec("SET standard_conforming_strings=on")
|
127
|
-
res = @conn.
|
128
|
-
res[0]['column1'].
|
129
|
-
res.getvalue(0,0).
|
130
|
-
res.values[0][0].
|
131
|
-
res.column_values(0)[0].
|
347
|
+
res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
|
348
|
+
expect( res[0]['column1'] ).to eq( bytes )
|
349
|
+
expect( res.getvalue(0,0) ).to eq( bytes )
|
350
|
+
expect( res.values[0][0] ).to eq( bytes )
|
351
|
+
expect( res.column_values(0)[0] ).to eq( bytes )
|
132
352
|
end
|
133
353
|
|
134
|
-
it "
|
354
|
+
it "returns the same bytes in text format that are sent in binary format" do
|
135
355
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
136
356
|
bytes = File.open(binary_file, 'rb').read
|
137
|
-
res = @conn.
|
357
|
+
res = @conn.exec_params('VALUES ($1::bytea)',
|
138
358
|
[ { :value => bytes, :format => 1 } ])
|
139
|
-
PG::Connection.unescape_bytea(res[0]['column1']).
|
359
|
+
expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
|
140
360
|
end
|
141
361
|
|
142
|
-
it "
|
362
|
+
it "returns the same bytes in text format that are sent as inline text" do
|
143
363
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
144
364
|
in_bytes = File.open(binary_file, 'rb').read
|
145
365
|
|
146
366
|
out_bytes = nil
|
147
367
|
@conn.exec("SET standard_conforming_strings=on")
|
148
|
-
res = @conn.
|
368
|
+
res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
|
149
369
|
out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
|
150
|
-
out_bytes.
|
370
|
+
expect( out_bytes ).to eq( in_bytes )
|
151
371
|
end
|
152
372
|
|
153
|
-
it "
|
373
|
+
it "returns the parameter type of the specified prepared statement parameter" do
|
154
374
|
query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
|
155
375
|
@conn.prepare( 'queryfinder', query )
|
156
376
|
res = @conn.describe_prepared( 'queryfinder' )
|
157
377
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
378
|
+
expect(
|
379
|
+
@conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
|
380
|
+
).to eq( 'name' )
|
381
|
+
expect(
|
382
|
+
@conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
|
383
|
+
).to eq( 'text' )
|
162
384
|
end
|
163
385
|
|
164
|
-
it "
|
386
|
+
it "raises an exception when a negative index is given to #fformat" do
|
165
387
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
166
388
|
expect {
|
167
389
|
res.fformat( -1 )
|
168
390
|
}.to raise_error( ArgumentError, /column number/i )
|
169
391
|
end
|
170
392
|
|
171
|
-
it "
|
393
|
+
it "raises an exception when a negative index is given to #fmod" do
|
172
394
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
173
395
|
expect {
|
174
396
|
res.fmod( -1 )
|
175
397
|
}.to raise_error( ArgumentError, /column number/i )
|
176
398
|
end
|
177
399
|
|
178
|
-
it "
|
400
|
+
it "raises an exception when a negative index is given to #[]" do
|
179
401
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
180
402
|
expect {
|
181
403
|
res[ -1 ]
|
182
404
|
}.to raise_error( IndexError, /-1 is out of range/i )
|
183
405
|
end
|
184
406
|
|
185
|
-
it "
|
407
|
+
it "raises allow for conversion to an array of arrays" do
|
186
408
|
@conn.exec( 'CREATE TABLE valuestest ( foo varchar(33) )' )
|
187
409
|
@conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar\')' )
|
188
410
|
@conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar2\')' )
|
189
411
|
|
190
412
|
res = @conn.exec( 'SELECT * FROM valuestest' )
|
191
|
-
res.values.
|
413
|
+
expect( res.values ).to eq( [ ["bar"], ["bar2"] ] )
|
414
|
+
end
|
415
|
+
|
416
|
+
it "can retrieve field names" do
|
417
|
+
res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
|
418
|
+
expect(res.fields).to eq(["a", "B"])
|
419
|
+
end
|
420
|
+
|
421
|
+
it "can retrieve field names as symbols" do
|
422
|
+
res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
|
423
|
+
res.field_name_type = :symbol
|
424
|
+
expect(res.fields).to eq([:a, :B])
|
425
|
+
end
|
426
|
+
|
427
|
+
it "can retrieve single field names" do
|
428
|
+
res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
|
429
|
+
expect(res.fname(0)).to eq("a")
|
430
|
+
expect(res.fname(1)).to eq("B")
|
431
|
+
expect{res.fname(2)}.to raise_error(ArgumentError)
|
432
|
+
end
|
433
|
+
|
434
|
+
it "can retrieve single field names as symbol" do
|
435
|
+
res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
|
436
|
+
res.field_name_type = :symbol
|
437
|
+
expect(res.fname(0)).to eq(:a)
|
438
|
+
expect(res.fname(1)).to eq(:B)
|
439
|
+
expect{res.fname(2)}.to raise_error(ArgumentError)
|
192
440
|
end
|
193
441
|
|
194
442
|
# PQfmod
|
195
443
|
it "can return the type modifier for a result column" do
|
196
444
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
197
445
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
198
|
-
res.fmod(
|
446
|
+
expect( res.fmod(0) ).to eq( 33 + 4 ) # Column length + varlena size (4)
|
199
447
|
end
|
200
448
|
|
201
|
-
it "
|
449
|
+
it "raises an exception when an invalid index is passed to PG::Result#fmod" do
|
202
450
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
203
451
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
204
452
|
expect { res.fmod(1) }.to raise_error( ArgumentError )
|
205
453
|
end
|
206
454
|
|
207
|
-
it "
|
455
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#fmod" do
|
208
456
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
209
457
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
210
458
|
expect { res.fmod(-11) }.to raise_error( ArgumentError )
|
211
459
|
end
|
212
460
|
|
213
|
-
it "
|
461
|
+
it "doesn't raise an exception when a valid index is passed to PG::Result#fmod for a" +
|
462
|
+
" column with no typemod" do
|
214
463
|
@conn.exec( 'CREATE TABLE fmodtest ( foo text )' )
|
215
464
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
216
|
-
res.fmod(
|
465
|
+
expect( res.fmod(0) ).to eq( -1 )
|
217
466
|
end
|
218
467
|
|
219
468
|
# PQftable
|
@@ -221,28 +470,28 @@ describe PG::Result do
|
|
221
470
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
222
471
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
223
472
|
|
224
|
-
res.ftable(
|
473
|
+
expect( res.ftable(0) ).to be_nonzero()
|
225
474
|
end
|
226
475
|
|
227
|
-
it "
|
476
|
+
it "raises an exception when an invalid index is passed to PG::Result#ftable" do
|
228
477
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
229
478
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
230
479
|
|
231
480
|
expect { res.ftable(18) }.to raise_error( ArgumentError )
|
232
481
|
end
|
233
482
|
|
234
|
-
it "
|
483
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#ftable" do
|
235
484
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
236
485
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
237
486
|
|
238
487
|
expect { res.ftable(-2) }.to raise_error( ArgumentError )
|
239
488
|
end
|
240
489
|
|
241
|
-
it "
|
490
|
+
it "doesn't raise an exception when a valid index is passed to PG::Result#ftable for a " +
|
242
491
|
"column with no corresponding table" do
|
243
492
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
244
493
|
res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftabletest' )
|
245
|
-
res.ftable(
|
494
|
+
expect( res.ftable(1) ).to eq( PG::INVALID_OID )
|
246
495
|
end
|
247
496
|
|
248
497
|
# PQftablecol
|
@@ -250,29 +499,29 @@ describe PG::Result do
|
|
250
499
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
251
500
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
252
501
|
|
253
|
-
res.ftablecol(
|
254
|
-
res.ftablecol(
|
502
|
+
expect( res.ftablecol(0) ).to eq( 1 )
|
503
|
+
expect( res.ftablecol(1) ).to eq( 2 )
|
255
504
|
end
|
256
505
|
|
257
|
-
it "
|
506
|
+
it "raises an exception when an invalid index is passed to PG::Result#ftablecol" do
|
258
507
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
259
508
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
260
509
|
|
261
510
|
expect { res.ftablecol(32) }.to raise_error( ArgumentError )
|
262
511
|
end
|
263
512
|
|
264
|
-
it "
|
513
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#ftablecol" do
|
265
514
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
266
515
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
267
516
|
|
268
517
|
expect { res.ftablecol(-1) }.to raise_error( ArgumentError )
|
269
518
|
end
|
270
519
|
|
271
|
-
it "
|
520
|
+
it "doesnn't raise an exception when a valid index is passed to PG::Result#ftablecol for a " +
|
272
521
|
"column with no corresponding table" do
|
273
522
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text )' )
|
274
523
|
res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftablecoltest' )
|
275
|
-
res.ftablecol(1).
|
524
|
+
expect( res.ftablecol(1) ).to eq( 0 )
|
276
525
|
end
|
277
526
|
|
278
527
|
it "can be manually checked for failed result status (async API)" do
|
@@ -285,41 +534,60 @@ describe PG::Result do
|
|
285
534
|
|
286
535
|
it "can return the values of a single field" do
|
287
536
|
res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
|
288
|
-
res.field_values(
|
289
|
-
res.field_values(
|
290
|
-
expect
|
291
|
-
expect{ res.field_values(
|
537
|
+
expect( res.field_values('x') ).to eq( ['1', '2'] )
|
538
|
+
expect( res.field_values('y') ).to eq( ['a', 'b'] )
|
539
|
+
expect( res.field_values(:x) ).to eq( ['1', '2'] )
|
540
|
+
expect{ res.field_values('') }.to raise_error(IndexError)
|
541
|
+
expect{ res.field_values(0) }.to raise_error(TypeError)
|
542
|
+
end
|
543
|
+
|
544
|
+
it "can return the values of a single tuple" do
|
545
|
+
res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
|
546
|
+
expect( res.tuple_values(0) ).to eq( ['1', 'a'] )
|
547
|
+
expect( res.tuple_values(1) ).to eq( ['2', 'b'] )
|
548
|
+
expect{ res.tuple_values(2) }.to raise_error(IndexError)
|
549
|
+
expect{ res.tuple_values(-1) }.to raise_error(IndexError)
|
550
|
+
expect{ res.tuple_values("x") }.to raise_error(TypeError)
|
551
|
+
end
|
552
|
+
|
553
|
+
it "can return the values of a single vary lazy tuple" do
|
554
|
+
res = @conn.exec( "VALUES(1),(2)" )
|
555
|
+
expect( res.tuple(0) ).to be_kind_of( PG::Tuple )
|
556
|
+
expect( res.tuple(1) ).to be_kind_of( PG::Tuple )
|
557
|
+
expect{ res.tuple(2) }.to raise_error(IndexError)
|
558
|
+
expect{ res.tuple(-1) }.to raise_error(IndexError)
|
559
|
+
expect{ res.tuple("x") }.to raise_error(TypeError)
|
292
560
|
end
|
293
561
|
|
294
|
-
it "
|
562
|
+
it "raises a proper exception for a nonexistant table" do
|
295
563
|
expect {
|
296
564
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
297
565
|
}.to raise_error( PG::UndefinedTable, /relation "nonexistant_table" does not exist/ )
|
298
566
|
end
|
299
567
|
|
300
|
-
it "
|
568
|
+
it "raises a more generic exception for an unknown SQLSTATE" do
|
301
569
|
old_error = PG::ERROR_CLASSES.delete('42P01')
|
302
570
|
begin
|
303
571
|
expect {
|
304
572
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
305
573
|
}.to raise_error{|error|
|
306
|
-
error.
|
307
|
-
error.to_s.
|
574
|
+
expect( error ).to be_an_instance_of(PG::SyntaxErrorOrAccessRuleViolation)
|
575
|
+
expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
|
308
576
|
}
|
309
577
|
ensure
|
310
578
|
PG::ERROR_CLASSES['42P01'] = old_error
|
311
579
|
end
|
312
580
|
end
|
313
581
|
|
314
|
-
it "
|
582
|
+
it "raises a ServerError for an unknown SQLSTATE class" do
|
315
583
|
old_error1 = PG::ERROR_CLASSES.delete('42P01')
|
316
584
|
old_error2 = PG::ERROR_CLASSES.delete('42')
|
317
585
|
begin
|
318
586
|
expect {
|
319
587
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
320
588
|
}.to raise_error{|error|
|
321
|
-
error.
|
322
|
-
error.to_s.
|
589
|
+
expect( error ).to be_an_instance_of(PG::ServerError)
|
590
|
+
expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
|
323
591
|
}
|
324
592
|
ensure
|
325
593
|
PG::ERROR_CLASSES['42P01'] = old_error1
|
@@ -327,19 +595,87 @@ describe PG::Result do
|
|
327
595
|
end
|
328
596
|
end
|
329
597
|
|
330
|
-
it "
|
598
|
+
it "raises a proper exception for a nonexistant schema" do
|
331
599
|
expect {
|
332
600
|
@conn.exec( "DROP SCHEMA nonexistant_schema" )
|
333
601
|
}.to raise_error( PG::InvalidSchemaName, /schema "nonexistant_schema" does not exist/ )
|
334
602
|
end
|
335
603
|
|
336
|
-
it "the raised result
|
337
|
-
c =
|
604
|
+
it "the raised result is nil in case of a connection error" do
|
605
|
+
c = PG::Connection.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
338
606
|
expect {
|
339
607
|
c.exec "select 1"
|
340
|
-
}.to raise_error{|error|
|
341
|
-
error.
|
342
|
-
error.result.
|
608
|
+
}.to raise_error {|error|
|
609
|
+
expect( error ).to be_an_instance_of(PG::UnableToSend)
|
610
|
+
expect( error.result ).to eq( nil )
|
343
611
|
}
|
344
612
|
end
|
613
|
+
|
614
|
+
it "does not clear the result itself" do
|
615
|
+
r = @conn.exec "select 1"
|
616
|
+
expect( r.autoclear? ).to eq(false)
|
617
|
+
expect( r.cleared? ).to eq(false)
|
618
|
+
r.clear
|
619
|
+
expect( r.cleared? ).to eq(true)
|
620
|
+
end
|
621
|
+
|
622
|
+
it "can be inspected before and after clear" do
|
623
|
+
r = @conn.exec "select 1"
|
624
|
+
expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
|
625
|
+
r.clear
|
626
|
+
expect( r.inspect ).to match(/cleared/)
|
627
|
+
end
|
628
|
+
|
629
|
+
it "should give account about memory usage" do
|
630
|
+
r = @conn.exec "select 1"
|
631
|
+
expect( ObjectSpace.memsize_of(r) ).to be > 1000
|
632
|
+
r.clear
|
633
|
+
expect( ObjectSpace.memsize_of(r) ).to be < 100
|
634
|
+
end
|
635
|
+
|
636
|
+
context 'result value conversions with TypeMapByColumn' do
|
637
|
+
let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
|
638
|
+
let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
|
639
|
+
|
640
|
+
it "should allow reading, assigning and diabling type conversions" do
|
641
|
+
res = @conn.exec( "SELECT 123" )
|
642
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
643
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
644
|
+
expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
|
645
|
+
expect( res.type_map.coders ).to eq( [textdec_int] )
|
646
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_float]
|
647
|
+
expect( res.type_map.coders ).to eq( [textdec_float] )
|
648
|
+
res.type_map = PG::TypeMapAllStrings.new
|
649
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
650
|
+
end
|
651
|
+
|
652
|
+
it "should be applied to all value retrieving methods" do
|
653
|
+
res = @conn.exec( "SELECT 123 as f" )
|
654
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
655
|
+
expect( res.values ).to eq( [[123]] )
|
656
|
+
expect( res.getvalue(0,0) ).to eq( 123 )
|
657
|
+
expect( res[0] ).to eq( {'f' => 123 } )
|
658
|
+
expect( res.enum_for(:each_row).to_a ).to eq( [[123]] )
|
659
|
+
expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
|
660
|
+
expect( res.column_values(0) ).to eq( [123] )
|
661
|
+
expect( res.field_values('f') ).to eq( [123] )
|
662
|
+
expect( res.field_values(:f) ).to eq( [123] )
|
663
|
+
expect( res.tuple_values(0) ).to eq( [123] )
|
664
|
+
end
|
665
|
+
|
666
|
+
it "should be usable for several querys" do
|
667
|
+
colmap = PG::TypeMapByColumn.new [textdec_int]
|
668
|
+
res = @conn.exec( "SELECT 123" )
|
669
|
+
res.type_map = colmap
|
670
|
+
expect( res.values ).to eq( [[123]] )
|
671
|
+
res = @conn.exec( "SELECT 456" )
|
672
|
+
res.type_map = colmap
|
673
|
+
expect( res.values ).to eq( [[456]] )
|
674
|
+
end
|
675
|
+
|
676
|
+
it "shouldn't allow invalid type maps" do
|
677
|
+
res = @conn.exec( "SELECT 1" )
|
678
|
+
expect{ res.type_map = 1 }.to raise_error(TypeError)
|
679
|
+
end
|
680
|
+
end
|
345
681
|
end
|