pg 0.17.1 → 0.18.4
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 +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
|