pg 0.17.1 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +2407 -2
  4. data/History.rdoc +68 -0
  5. data/Manifest.txt +29 -1
  6. data/README-Windows.rdoc +15 -26
  7. data/README.rdoc +52 -2
  8. data/Rakefile +56 -18
  9. data/Rakefile.cross +77 -49
  10. data/ext/extconf.rb +33 -26
  11. data/ext/pg.c +142 -21
  12. data/ext/pg.h +242 -6
  13. data/ext/pg_binary_decoder.c +162 -0
  14. data/ext/pg_binary_encoder.c +162 -0
  15. data/ext/pg_coder.c +479 -0
  16. data/ext/pg_connection.c +858 -553
  17. data/ext/pg_copy_coder.c +561 -0
  18. data/ext/pg_errors.c +6 -0
  19. data/ext/pg_result.c +479 -128
  20. data/ext/pg_text_decoder.c +421 -0
  21. data/ext/pg_text_encoder.c +663 -0
  22. data/ext/pg_type_map.c +159 -0
  23. data/ext/pg_type_map_all_strings.c +116 -0
  24. data/ext/pg_type_map_by_class.c +239 -0
  25. data/ext/pg_type_map_by_column.c +312 -0
  26. data/ext/pg_type_map_by_mri_type.c +284 -0
  27. data/ext/pg_type_map_by_oid.c +355 -0
  28. data/ext/pg_type_map_in_ruby.c +299 -0
  29. data/ext/util.c +149 -0
  30. data/ext/util.h +65 -0
  31. data/lib/pg/basic_type_mapping.rb +399 -0
  32. data/lib/pg/coder.rb +83 -0
  33. data/lib/pg/connection.rb +81 -29
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +44 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/pg.rb +12 -2
  39. data/spec/{lib/helpers.rb → helpers.rb} +101 -39
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +516 -218
  42. data/spec/pg/result_spec.rb +216 -112
  43. data/spec/pg/type_map_by_class_spec.rb +138 -0
  44. data/spec/pg/type_map_by_column_spec.rb +222 -0
  45. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  46. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  47. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  48. data/spec/pg/type_map_spec.rb +22 -0
  49. data/spec/pg/type_spec.rb +697 -0
  50. data/spec/pg_spec.rb +24 -18
  51. data.tar.gz.sig +0 -0
  52. metadata +111 -45
  53. metadata.gz.sig +0 -0
@@ -1,59 +1,103 @@
1
1
  #!/usr/bin/env rspec
2
2
  # encoding: utf-8
3
3
 
4
- BEGIN {
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
- before( :all ) do
21
- @conn = setup_testing_db( "PG_Result" )
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
- before( :each ) do
25
- @conn.exec( 'BEGIN' )
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
- after( :each ) do
29
- @conn.exec( 'ROLLBACK' )
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
- after( :all ) do
33
- teardown_testing_db( @conn )
34
- end
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
- # Examples
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
- it "should act as an array of hashes" do
42
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
43
- res[0]['a'].should== '1'
44
- res[0]['b'].should== '2'
45
- end
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
- it "should yield a row as an array" do
48
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
49
- list = []
50
- res.each_row { |r| list << r }
51
- list.should eq [['1', '2']]
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 "should insert nil AS NULL and return NULL as nil" do
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'].should be_nil()
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.should be_a( described_class() )
70
- result.error_field( PG::PG_DIAG_SEVERITY ).should == 'ERROR'
71
- result.error_field( PG::PG_DIAG_SQLSTATE ).should == '42P01'
72
- result.error_field( PG::PG_DIAG_MESSAGE_PRIMARY ).
73
- should == 'relation "nonexistant_table" does not exist'
74
- result.error_field( PG::PG_DIAG_MESSAGE_DETAIL ).should be_nil()
75
- result.error_field( PG::PG_DIAG_MESSAGE_HINT ).should be_nil()
76
- result.error_field( PG::PG_DIAG_STATEMENT_POSITION ).should == '15'
77
- result.error_field( PG::PG_DIAG_INTERNAL_POSITION ).should be_nil()
78
- result.error_field( PG::PG_DIAG_INTERNAL_QUERY ).should be_nil()
79
- result.error_field( PG::PG_DIAG_CONTEXT ).should be_nil()
80
- result.error_field( PG::PG_DIAG_SOURCE_FILE ).should =~ /parse_relation\.c$|namespace\.c$/
81
- result.error_field( PG::PG_DIAG_SOURCE_LINE ).should =~ /^\d+$/
82
- result.error_field( PG::PG_DIAG_SOURCE_FUNCTION ).should =~ /^parserOpenTable$|^RangeVarGetRelid$/
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( PG::PG_DIAG_SCHEMA_NAME ).should == 'public'
96
- result.error_field( PG::PG_DIAG_TABLE_NAME ).should == 'integrity'
97
- result.error_field( PG::PG_DIAG_COLUMN_NAME ).should == 'id'
98
- result.error_field( PG::PG_DIAG_DATATYPE_NAME ).should be_nil
99
- result.error_field( PG::PG_DIAG_CONSTRAINT_NAME ).should be_nil
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 "should detect division by zero as SQLSTATE 22012" do
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.should == 22012
158
+ expect( sqlstate ).to eq( 22012 )
110
159
  end
111
160
 
112
- it "should return the same bytes in binary format that are sent in binary format" do
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'].should== bytes
118
- res.getvalue(0,0).should == bytes
119
- res.values[0][0].should == bytes
120
- res.column_values(0)[0].should == bytes
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 "should return the same bytes in binary format that are sent as inline text" do
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'].should == bytes
129
- res.getvalue(0,0).should == bytes
130
- res.values[0][0].should == bytes
131
- res.column_values(0)[0].should == bytes
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 "should return the same bytes in text format that are sent in binary format" do
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']).should== bytes
188
+ expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
140
189
  end
141
190
 
142
- it "should return the same bytes in text format that are sent as inline text" do
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.should == in_bytes
199
+ expect( out_bytes ).to eq( in_bytes )
151
200
  end
152
201
 
153
- it "should return the parameter type of the specified prepared statement parameter", :postgresql_92 do
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
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 ).
159
- should == 'name'
160
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 ).
161
- should == 'text'
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 "should raise an exception when a negative index is given to #fformat" do
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 "should raise an exception when a negative index is given to #fmod" do
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 "should raise an exception when a negative index is given to #[]" do
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 "should raise allow for conversion to an array of arrays" do
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.should == [ ["bar"], ["bar2"] ]
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( 0 ).should == 33 + 4 # Column length + varlena size (4)
249
+ expect( res.fmod(0) ).to eq( 33 + 4 ) # Column length + varlena size (4)
199
250
  end
200
251
 
201
- it "should raise an exception when an invalid index is passed to PG::Result#fmod" do
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 "should raise an exception when an invalid (negative) index is passed to PG::Result#fmod" do
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 "shouldn't raise an exception when a valid index is passed to PG::Result#fmod for a column with no typemod" do
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( 0 ).should == -1 # and it shouldn't raise an exception, either
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( 0 ).should == be_nonzero()
276
+ expect( res.ftable(0) ).to be_nonzero()
225
277
  end
226
278
 
227
- it "should raise an exception when an invalid index is passed to PG::Result#ftable" do
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 "should raise an exception when an invalid (negative) index is passed to PG::Result#ftable" do
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 "shouldn't raise an exception when a valid index is passed to PG::Result#ftable for a " +
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( 1 ).should == PG::INVALID_OID # and it shouldn't raise an exception, either
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( 0 ).should == 1
254
- res.ftablecol( 1 ).should == 2
305
+ expect( res.ftablecol(0) ).to eq( 1 )
306
+ expect( res.ftablecol(1) ).to eq( 2 )
255
307
  end
256
308
 
257
- it "should raise an exception when an invalid index is passed to PG::Result#ftablecol" do
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 "should raise an exception when an invalid (negative) index is passed to PG::Result#ftablecol" do
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 "shouldn't raise an exception when a valid index is passed to PG::Result#ftablecol for a " +
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).should == 0 # and it shouldn't raise an exception, either
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( 'x' ).should == ['1', '2']
289
- res.field_values( 'y' ).should == ['a', 'b']
290
- expect{ res.field_values( '' ) }.to raise_error(IndexError)
291
- expect{ res.field_values( :x ) }.to raise_error(TypeError)
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 "should raise a proper exception for a nonexistant table" do
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 "should raise a more generic exception for an unknown SQLSTATE" do
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.should be_an_instance_of(PG::SyntaxErrorOrAccessRuleViolation)
307
- error.to_s.should match(/relation "nonexistant_table" does not exist/)
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 "should raise a ServerError for an unknown SQLSTATE class" do
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.should be_an_instance_of(PG::ServerError)
322
- error.to_s.should match(/relation "nonexistant_table" does not exist/)
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 "should raise a proper exception for a nonexistant schema" do
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 should be nil in case of a connection error" do
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.should be_an_instance_of(PG::UnableToSend)
342
- error.result.should == nil
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