pg 0.17.1 → 0.18.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ChangeLog +2407 -2
- data/History.rdoc +68 -0
- data/Manifest.txt +29 -1
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +52 -2
- data/Rakefile +56 -18
- data/Rakefile.cross +77 -49
- data/ext/extconf.rb +33 -26
- data/ext/pg.c +142 -21
- data/ext/pg.h +242 -6
- data/ext/pg_binary_decoder.c +162 -0
- data/ext/pg_binary_encoder.c +162 -0
- data/ext/pg_coder.c +479 -0
- data/ext/pg_connection.c +858 -553
- data/ext/pg_copy_coder.c +561 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +479 -128
- data/ext/pg_text_decoder.c +421 -0
- data/ext/pg_text_encoder.c +663 -0
- data/ext/pg_type_map.c +159 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +312 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +355 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +399 -0
- data/lib/pg/coder.rb +83 -0
- data/lib/pg/connection.rb +81 -29
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +44 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/lib/pg.rb +12 -2
- data/spec/{lib/helpers.rb → helpers.rb} +101 -39
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +516 -218
- data/spec/pg/result_spec.rb +216 -112
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +222 -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 +697 -0
- data/spec/pg_spec.rb +24 -18
- data.tar.gz.sig +0 -0
- metadata +111 -45
- metadata.gz.sig +0 -0
data/spec/pg/result_spec.rb
CHANGED
@@ -1,59 +1,103 @@
|
|
1
1
|
#!/usr/bin/env 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'
|
17
7
|
|
8
|
+
|
18
9
|
describe PG::Result do
|
19
10
|
|
20
|
-
|
21
|
-
|
11
|
+
it "acts as an array of hashes" do
|
12
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
13
|
+
expect( res[0]['a'] ).to eq( '1' )
|
14
|
+
expect( res[0]['b'] ).to eq( '2' )
|
22
15
|
end
|
23
16
|
|
24
|
-
|
25
|
-
@conn.exec(
|
17
|
+
it "yields a row as an array" do
|
18
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
19
|
+
list = []
|
20
|
+
res.each_row { |r| list << r }
|
21
|
+
expect( list ).to eq [['1', '2']]
|
26
22
|
end
|
27
23
|
|
28
|
-
|
29
|
-
@conn.exec(
|
24
|
+
it "yields a row as an Enumerator" do
|
25
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
26
|
+
e = res.each_row
|
27
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
28
|
+
pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
|
29
|
+
expect( e.size ).to eq( 1 )
|
30
|
+
expect( e.to_a ).to eq [['1', '2']]
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
it "yields a row as an Enumerator of hashs" do
|
34
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
35
|
+
e = res.each
|
36
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
37
|
+
pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
|
38
|
+
expect( e.size ).to eq( 1 )
|
39
|
+
expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
|
40
|
+
end
|
41
|
+
|
42
|
+
context "result streaming", :postgresql_92 do
|
43
|
+
it "can iterate over all tuples in single row mode" do
|
44
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
45
|
+
@conn.set_single_row_mode
|
46
|
+
expect(
|
47
|
+
@conn.get_result.stream_each.to_a
|
48
|
+
).to eq(
|
49
|
+
[{'a'=>"2"}, {'a'=>"3"}, {'a'=>"4"}]
|
50
|
+
)
|
51
|
+
expect(
|
52
|
+
@conn.get_result.enum_for(:stream_each).to_a
|
53
|
+
).to eq(
|
54
|
+
[{'b'=>"1", 'c'=>"5"}, {'b'=>"1", 'c'=>"6"}]
|
55
|
+
)
|
56
|
+
expect( @conn.get_result ).to be_nil
|
57
|
+
end
|
35
58
|
|
59
|
+
it "can iterate over all rows in single row mode" do
|
60
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
61
|
+
@conn.set_single_row_mode
|
62
|
+
expect(
|
63
|
+
@conn.get_result.enum_for(:stream_each_row).to_a
|
64
|
+
).to eq(
|
65
|
+
[["2"], ["3"], ["4"]]
|
66
|
+
)
|
67
|
+
expect(
|
68
|
+
@conn.get_result.stream_each_row.to_a
|
69
|
+
).to eq(
|
70
|
+
[["1", "5"], ["1", "6"]]
|
71
|
+
)
|
72
|
+
expect( @conn.get_result ).to be_nil
|
73
|
+
end
|
36
74
|
|
37
|
-
|
38
|
-
|
39
|
-
|
75
|
+
it "complains when not in single row mode" do
|
76
|
+
@conn.send_query( "SELECT generate_series(2,4)" )
|
77
|
+
expect{
|
78
|
+
@conn.get_result.stream_each_row.to_a
|
79
|
+
}.to raise_error(PG::InvalidResultStatus, /not in single row mode/)
|
80
|
+
end
|
40
81
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
82
|
+
it "complains when intersected with get_result" do
|
83
|
+
@conn.send_query( "SELECT 1" )
|
84
|
+
@conn.set_single_row_mode
|
85
|
+
expect{
|
86
|
+
@conn.get_result.stream_each_row.each{ @conn.get_result }
|
87
|
+
}.to raise_error(PG::NoResultError, /no result received/)
|
88
|
+
end
|
46
89
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
90
|
+
it "raises server errors" do
|
91
|
+
@conn.send_query( "SELECT 0/0" )
|
92
|
+
expect{
|
93
|
+
@conn.get_result.stream_each_row.to_a
|
94
|
+
}.to raise_error(PG::DivisionByZero)
|
95
|
+
end
|
52
96
|
end
|
53
97
|
|
54
|
-
it "
|
98
|
+
it "inserts nil AS NULL and return NULL as nil" do
|
55
99
|
res = @conn.exec("SELECT $1::int AS n", [nil])
|
56
|
-
res[0]['n'].
|
100
|
+
expect( res[0]['n'] ).to be_nil()
|
57
101
|
end
|
58
102
|
|
59
103
|
it "encapsulates errors in a PGError object" do
|
@@ -66,20 +110,25 @@ describe PG::Result do
|
|
66
110
|
|
67
111
|
result = exception.result
|
68
112
|
|
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
|
-
|
113
|
+
expect( result ).to be_a( described_class() )
|
114
|
+
expect( result.error_field(PG::PG_DIAG_SEVERITY) ).to eq( 'ERROR' )
|
115
|
+
expect( result.error_field(PG::PG_DIAG_SQLSTATE) ).to eq( '42P01' )
|
116
|
+
expect(
|
117
|
+
result.error_field(PG::PG_DIAG_MESSAGE_PRIMARY)
|
118
|
+
).to eq( 'relation "nonexistant_table" does not exist' )
|
119
|
+
expect( result.error_field(PG::PG_DIAG_MESSAGE_DETAIL) ).to be_nil()
|
120
|
+
expect( result.error_field(PG::PG_DIAG_MESSAGE_HINT) ).to be_nil()
|
121
|
+
expect( result.error_field(PG::PG_DIAG_STATEMENT_POSITION) ).to eq( '15' )
|
122
|
+
expect( result.error_field(PG::PG_DIAG_INTERNAL_POSITION) ).to be_nil()
|
123
|
+
expect( result.error_field(PG::PG_DIAG_INTERNAL_QUERY) ).to be_nil()
|
124
|
+
expect( result.error_field(PG::PG_DIAG_CONTEXT) ).to be_nil()
|
125
|
+
expect(
|
126
|
+
result.error_field(PG::PG_DIAG_SOURCE_FILE)
|
127
|
+
).to match( /parse_relation\.c$|namespace\.c$/ )
|
128
|
+
expect( result.error_field(PG::PG_DIAG_SOURCE_LINE) ).to match( /^\d+$/ )
|
129
|
+
expect(
|
130
|
+
result.error_field(PG::PG_DIAG_SOURCE_FUNCTION)
|
131
|
+
).to match( /^parserOpenTable$|^RangeVarGetRelid$/ )
|
83
132
|
end
|
84
133
|
|
85
134
|
it "encapsulates database object names for integrity constraint violations", :postgresql_93 do
|
@@ -92,54 +141,54 @@ describe PG::Result do
|
|
92
141
|
end
|
93
142
|
result = exception.result
|
94
143
|
|
95
|
-
result.error_field(
|
96
|
-
result.error_field(
|
97
|
-
result.error_field(
|
98
|
-
result.error_field(
|
99
|
-
result.error_field(
|
144
|
+
expect( result.error_field(PG::PG_DIAG_SCHEMA_NAME) ).to eq( 'public' )
|
145
|
+
expect( result.error_field(PG::PG_DIAG_TABLE_NAME) ).to eq( 'integrity' )
|
146
|
+
expect( result.error_field(PG::PG_DIAG_COLUMN_NAME) ).to eq( 'id' )
|
147
|
+
expect( result.error_field(PG::PG_DIAG_DATATYPE_NAME) ).to be_nil
|
148
|
+
expect( result.error_field(PG::PG_DIAG_CONSTRAINT_NAME) ).to be_nil
|
100
149
|
end
|
101
150
|
|
102
|
-
it "
|
151
|
+
it "detects division by zero as SQLSTATE 22012" do
|
103
152
|
sqlstate = nil
|
104
153
|
begin
|
105
154
|
res = @conn.exec("SELECT 1/0")
|
106
155
|
rescue PGError => e
|
107
156
|
sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
|
108
157
|
end
|
109
|
-
sqlstate.
|
158
|
+
expect( sqlstate ).to eq( 22012 )
|
110
159
|
end
|
111
160
|
|
112
|
-
it "
|
161
|
+
it "returns the same bytes in binary format that are sent in binary format" do
|
113
162
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
114
163
|
bytes = File.open(binary_file, 'rb').read
|
115
164
|
res = @conn.exec('VALUES ($1::bytea)',
|
116
165
|
[ { :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].
|
166
|
+
expect( res[0]['column1'] ).to eq( bytes )
|
167
|
+
expect( res.getvalue(0,0) ).to eq( bytes )
|
168
|
+
expect( res.values[0][0] ).to eq( bytes )
|
169
|
+
expect( res.column_values(0)[0] ).to eq( bytes )
|
121
170
|
end
|
122
171
|
|
123
|
-
it "
|
172
|
+
it "returns the same bytes in binary format that are sent as inline text" do
|
124
173
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
125
174
|
bytes = File.open(binary_file, 'rb').read
|
126
175
|
@conn.exec("SET standard_conforming_strings=on")
|
127
176
|
res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
|
128
|
-
res[0]['column1'].
|
129
|
-
res.getvalue(0,0).
|
130
|
-
res.values[0][0].
|
131
|
-
res.column_values(0)[0].
|
177
|
+
expect( res[0]['column1'] ).to eq( bytes )
|
178
|
+
expect( res.getvalue(0,0) ).to eq( bytes )
|
179
|
+
expect( res.values[0][0] ).to eq( bytes )
|
180
|
+
expect( res.column_values(0)[0] ).to eq( bytes )
|
132
181
|
end
|
133
182
|
|
134
|
-
it "
|
183
|
+
it "returns the same bytes in text format that are sent in binary format" do
|
135
184
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
136
185
|
bytes = File.open(binary_file, 'rb').read
|
137
186
|
res = @conn.exec('VALUES ($1::bytea)',
|
138
187
|
[ { :value => bytes, :format => 1 } ])
|
139
|
-
PG::Connection.unescape_bytea(res[0]['column1']).
|
188
|
+
expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
|
140
189
|
end
|
141
190
|
|
142
|
-
it "
|
191
|
+
it "returns the same bytes in text format that are sent as inline text" do
|
143
192
|
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
144
193
|
in_bytes = File.open(binary_file, 'rb').read
|
145
194
|
|
@@ -147,73 +196,76 @@ describe PG::Result do
|
|
147
196
|
@conn.exec("SET standard_conforming_strings=on")
|
148
197
|
res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
|
149
198
|
out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
|
150
|
-
out_bytes.
|
199
|
+
expect( out_bytes ).to eq( in_bytes )
|
151
200
|
end
|
152
201
|
|
153
|
-
it "
|
202
|
+
it "returns the parameter type of the specified prepared statement parameter", :postgresql_92 do
|
154
203
|
query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
|
155
204
|
@conn.prepare( 'queryfinder', query )
|
156
205
|
res = @conn.describe_prepared( 'queryfinder' )
|
157
206
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
207
|
+
expect(
|
208
|
+
@conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
|
209
|
+
).to eq( 'name' )
|
210
|
+
expect(
|
211
|
+
@conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
|
212
|
+
).to eq( 'text' )
|
162
213
|
end
|
163
214
|
|
164
|
-
it "
|
215
|
+
it "raises an exception when a negative index is given to #fformat" do
|
165
216
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
166
217
|
expect {
|
167
218
|
res.fformat( -1 )
|
168
219
|
}.to raise_error( ArgumentError, /column number/i )
|
169
220
|
end
|
170
221
|
|
171
|
-
it "
|
222
|
+
it "raises an exception when a negative index is given to #fmod" do
|
172
223
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
173
224
|
expect {
|
174
225
|
res.fmod( -1 )
|
175
226
|
}.to raise_error( ArgumentError, /column number/i )
|
176
227
|
end
|
177
228
|
|
178
|
-
it "
|
229
|
+
it "raises an exception when a negative index is given to #[]" do
|
179
230
|
res = @conn.exec('SELECT * FROM pg_stat_activity')
|
180
231
|
expect {
|
181
232
|
res[ -1 ]
|
182
233
|
}.to raise_error( IndexError, /-1 is out of range/i )
|
183
234
|
end
|
184
235
|
|
185
|
-
it "
|
236
|
+
it "raises allow for conversion to an array of arrays" do
|
186
237
|
@conn.exec( 'CREATE TABLE valuestest ( foo varchar(33) )' )
|
187
238
|
@conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar\')' )
|
188
239
|
@conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar2\')' )
|
189
240
|
|
190
241
|
res = @conn.exec( 'SELECT * FROM valuestest' )
|
191
|
-
res.values.
|
242
|
+
expect( res.values ).to eq( [ ["bar"], ["bar2"] ] )
|
192
243
|
end
|
193
244
|
|
194
245
|
# PQfmod
|
195
246
|
it "can return the type modifier for a result column" do
|
196
247
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
197
248
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
198
|
-
res.fmod(
|
249
|
+
expect( res.fmod(0) ).to eq( 33 + 4 ) # Column length + varlena size (4)
|
199
250
|
end
|
200
251
|
|
201
|
-
it "
|
252
|
+
it "raises an exception when an invalid index is passed to PG::Result#fmod" do
|
202
253
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
203
254
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
204
255
|
expect { res.fmod(1) }.to raise_error( ArgumentError )
|
205
256
|
end
|
206
257
|
|
207
|
-
it "
|
258
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#fmod" do
|
208
259
|
@conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
|
209
260
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
210
261
|
expect { res.fmod(-11) }.to raise_error( ArgumentError )
|
211
262
|
end
|
212
263
|
|
213
|
-
it "
|
264
|
+
it "doesn't raise an exception when a valid index is passed to PG::Result#fmod for a" +
|
265
|
+
" column with no typemod" do
|
214
266
|
@conn.exec( 'CREATE TABLE fmodtest ( foo text )' )
|
215
267
|
res = @conn.exec( 'SELECT * FROM fmodtest' )
|
216
|
-
res.fmod(
|
268
|
+
expect( res.fmod(0) ).to eq( -1 )
|
217
269
|
end
|
218
270
|
|
219
271
|
# PQftable
|
@@ -221,28 +273,28 @@ describe PG::Result do
|
|
221
273
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
222
274
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
223
275
|
|
224
|
-
res.ftable(
|
276
|
+
expect( res.ftable(0) ).to be_nonzero()
|
225
277
|
end
|
226
278
|
|
227
|
-
it "
|
279
|
+
it "raises an exception when an invalid index is passed to PG::Result#ftable" do
|
228
280
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
229
281
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
230
282
|
|
231
283
|
expect { res.ftable(18) }.to raise_error( ArgumentError )
|
232
284
|
end
|
233
285
|
|
234
|
-
it "
|
286
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#ftable" do
|
235
287
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
236
288
|
res = @conn.exec( 'SELECT * FROM ftabletest' )
|
237
289
|
|
238
290
|
expect { res.ftable(-2) }.to raise_error( ArgumentError )
|
239
291
|
end
|
240
292
|
|
241
|
-
it "
|
293
|
+
it "doesn't raise an exception when a valid index is passed to PG::Result#ftable for a " +
|
242
294
|
"column with no corresponding table" do
|
243
295
|
@conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
|
244
296
|
res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftabletest' )
|
245
|
-
res.ftable(
|
297
|
+
expect( res.ftable(1) ).to eq( PG::INVALID_OID )
|
246
298
|
end
|
247
299
|
|
248
300
|
# PQftablecol
|
@@ -250,29 +302,29 @@ describe PG::Result do
|
|
250
302
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
251
303
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
252
304
|
|
253
|
-
res.ftablecol(
|
254
|
-
res.ftablecol(
|
305
|
+
expect( res.ftablecol(0) ).to eq( 1 )
|
306
|
+
expect( res.ftablecol(1) ).to eq( 2 )
|
255
307
|
end
|
256
308
|
|
257
|
-
it "
|
309
|
+
it "raises an exception when an invalid index is passed to PG::Result#ftablecol" do
|
258
310
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
259
311
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
260
312
|
|
261
313
|
expect { res.ftablecol(32) }.to raise_error( ArgumentError )
|
262
314
|
end
|
263
315
|
|
264
|
-
it "
|
316
|
+
it "raises an exception when an invalid (negative) index is passed to PG::Result#ftablecol" do
|
265
317
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
|
266
318
|
res = @conn.exec( 'SELECT * FROM ftablecoltest' )
|
267
319
|
|
268
320
|
expect { res.ftablecol(-1) }.to raise_error( ArgumentError )
|
269
321
|
end
|
270
322
|
|
271
|
-
it "
|
323
|
+
it "doesnn't raise an exception when a valid index is passed to PG::Result#ftablecol for a " +
|
272
324
|
"column with no corresponding table" do
|
273
325
|
@conn.exec( 'CREATE TABLE ftablecoltest ( foo text )' )
|
274
326
|
res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftablecoltest' )
|
275
|
-
res.ftablecol(1).
|
327
|
+
expect( res.ftablecol(1) ).to eq( 0 )
|
276
328
|
end
|
277
329
|
|
278
330
|
it "can be manually checked for failed result status (async API)" do
|
@@ -285,41 +337,41 @@ describe PG::Result do
|
|
285
337
|
|
286
338
|
it "can return the values of a single field" do
|
287
339
|
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{ res.field_values(
|
291
|
-
expect{ res.field_values(
|
340
|
+
expect( res.field_values('x') ).to eq( ['1', '2'] )
|
341
|
+
expect( res.field_values('y') ).to eq( ['a', 'b'] )
|
342
|
+
expect{ res.field_values('') }.to raise_error(IndexError)
|
343
|
+
expect{ res.field_values(:x) }.to raise_error(TypeError)
|
292
344
|
end
|
293
345
|
|
294
|
-
it "
|
346
|
+
it "raises a proper exception for a nonexistant table" do
|
295
347
|
expect {
|
296
348
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
297
349
|
}.to raise_error( PG::UndefinedTable, /relation "nonexistant_table" does not exist/ )
|
298
350
|
end
|
299
351
|
|
300
|
-
it "
|
352
|
+
it "raises a more generic exception for an unknown SQLSTATE" do
|
301
353
|
old_error = PG::ERROR_CLASSES.delete('42P01')
|
302
354
|
begin
|
303
355
|
expect {
|
304
356
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
305
357
|
}.to raise_error{|error|
|
306
|
-
error.
|
307
|
-
error.to_s.
|
358
|
+
expect( error ).to be_an_instance_of(PG::SyntaxErrorOrAccessRuleViolation)
|
359
|
+
expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
|
308
360
|
}
|
309
361
|
ensure
|
310
362
|
PG::ERROR_CLASSES['42P01'] = old_error
|
311
363
|
end
|
312
364
|
end
|
313
365
|
|
314
|
-
it "
|
366
|
+
it "raises a ServerError for an unknown SQLSTATE class" do
|
315
367
|
old_error1 = PG::ERROR_CLASSES.delete('42P01')
|
316
368
|
old_error2 = PG::ERROR_CLASSES.delete('42')
|
317
369
|
begin
|
318
370
|
expect {
|
319
371
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
320
372
|
}.to raise_error{|error|
|
321
|
-
error.
|
322
|
-
error.to_s.
|
373
|
+
expect( error ).to be_an_instance_of(PG::ServerError)
|
374
|
+
expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
|
323
375
|
}
|
324
376
|
ensure
|
325
377
|
PG::ERROR_CLASSES['42P01'] = old_error1
|
@@ -327,19 +379,71 @@ describe PG::Result do
|
|
327
379
|
end
|
328
380
|
end
|
329
381
|
|
330
|
-
it "
|
382
|
+
it "raises a proper exception for a nonexistant schema" do
|
331
383
|
expect {
|
332
384
|
@conn.exec( "DROP SCHEMA nonexistant_schema" )
|
333
385
|
}.to raise_error( PG::InvalidSchemaName, /schema "nonexistant_schema" does not exist/ )
|
334
386
|
end
|
335
387
|
|
336
|
-
it "the raised result
|
388
|
+
it "the raised result is nil in case of a connection error" do
|
337
389
|
c = PGconn.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
338
390
|
expect {
|
339
391
|
c.exec "select 1"
|
340
|
-
}.to raise_error{|error|
|
341
|
-
error.
|
342
|
-
error.result.
|
392
|
+
}.to raise_error {|error|
|
393
|
+
expect( error ).to be_an_instance_of(PG::UnableToSend)
|
394
|
+
expect( error.result ).to eq( nil )
|
343
395
|
}
|
344
396
|
end
|
397
|
+
|
398
|
+
it "does not clear the result itself" do
|
399
|
+
r = @conn.exec "select 1"
|
400
|
+
expect( r.autoclear? ).to eq(false)
|
401
|
+
expect( r.cleared? ).to eq(false)
|
402
|
+
r.clear
|
403
|
+
expect( r.cleared? ).to eq(true)
|
404
|
+
end
|
405
|
+
|
406
|
+
context 'result value conversions with TypeMapByColumn' do
|
407
|
+
let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
|
408
|
+
let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
|
409
|
+
|
410
|
+
it "should allow reading, assigning and diabling type conversions" do
|
411
|
+
res = @conn.exec( "SELECT 123" )
|
412
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
413
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
414
|
+
expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
|
415
|
+
expect( res.type_map.coders ).to eq( [textdec_int] )
|
416
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_float]
|
417
|
+
expect( res.type_map.coders ).to eq( [textdec_float] )
|
418
|
+
res.type_map = PG::TypeMapAllStrings.new
|
419
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
420
|
+
end
|
421
|
+
|
422
|
+
it "should be applied to all value retrieving methods" do
|
423
|
+
res = @conn.exec( "SELECT 123 as f" )
|
424
|
+
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
425
|
+
expect( res.values ).to eq( [[123]] )
|
426
|
+
expect( res.getvalue(0,0) ).to eq( 123 )
|
427
|
+
expect( res[0] ).to eq( {'f' => 123 } )
|
428
|
+
expect( res.enum_for(:each_row).to_a ).to eq( [[123]] )
|
429
|
+
expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
|
430
|
+
expect( res.column_values(0) ).to eq( [123] )
|
431
|
+
expect( res.field_values('f') ).to eq( [123] )
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should be usable for several querys" do
|
435
|
+
colmap = PG::TypeMapByColumn.new [textdec_int]
|
436
|
+
res = @conn.exec( "SELECT 123" )
|
437
|
+
res.type_map = colmap
|
438
|
+
expect( res.values ).to eq( [[123]] )
|
439
|
+
res = @conn.exec( "SELECT 456" )
|
440
|
+
res.type_map = colmap
|
441
|
+
expect( res.values ).to eq( [[456]] )
|
442
|
+
end
|
443
|
+
|
444
|
+
it "shouldn't allow invalid type maps" do
|
445
|
+
res = @conn.exec( "SELECT 1" )
|
446
|
+
expect{ res.type_map = 1 }.to raise_error(TypeError)
|
447
|
+
end
|
448
|
+
end
|
345
449
|
end
|