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.
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