pg 0.18.4 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -5911
  6. data/History.rdoc +240 -0
  7. data/Manifest.txt +8 -20
  8. data/README-Windows.rdoc +4 -4
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +64 -15
  11. data/Rakefile +20 -21
  12. data/Rakefile.cross +67 -69
  13. data/ext/errorcodes.def +101 -0
  14. data/ext/errorcodes.rb +1 -1
  15. data/ext/errorcodes.txt +33 -2
  16. data/ext/extconf.rb +26 -36
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +27 -39
  19. data/ext/pg.c +156 -145
  20. data/ext/pg.h +74 -98
  21. data/ext/pg_binary_decoder.c +82 -15
  22. data/ext/pg_binary_encoder.c +20 -19
  23. data/ext/pg_coder.c +103 -21
  24. data/ext/pg_connection.c +917 -523
  25. data/ext/pg_copy_coder.c +50 -12
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +590 -208
  28. data/ext/pg_text_decoder.c +606 -40
  29. data/ext/pg_text_encoder.c +245 -94
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +14 -7
  32. data/ext/pg_type_map_all_strings.c +4 -4
  33. data/ext/pg_type_map_by_class.c +9 -4
  34. data/ext/pg_type_map_by_column.c +7 -6
  35. data/ext/pg_type_map_by_mri_type.c +1 -1
  36. data/ext/pg_type_map_by_oid.c +3 -2
  37. data/ext/pg_type_map_in_ruby.c +1 -1
  38. data/ext/{util.c → pg_util.c} +10 -10
  39. data/ext/{util.h → pg_util.h} +2 -2
  40. data/lib/pg.rb +23 -13
  41. data/lib/pg/basic_type_mapping.rb +155 -32
  42. data/lib/pg/binary_decoder.rb +23 -0
  43. data/lib/pg/coder.rb +23 -2
  44. data/lib/pg/connection.rb +73 -13
  45. data/lib/pg/constants.rb +2 -1
  46. data/lib/pg/exceptions.rb +2 -1
  47. data/lib/pg/result.rb +24 -7
  48. data/lib/pg/text_decoder.rb +24 -22
  49. data/lib/pg/text_encoder.rb +40 -8
  50. data/lib/pg/tuple.rb +30 -0
  51. data/lib/pg/type_map_by_column.rb +3 -2
  52. data/spec/helpers.rb +61 -36
  53. data/spec/pg/basic_type_mapping_spec.rb +415 -36
  54. data/spec/pg/connection_spec.rb +732 -327
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +253 -21
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +4 -4
  59. data/spec/pg/type_map_by_column_spec.rb +6 -2
  60. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  61. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  62. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  63. data/spec/pg/type_map_spec.rb +1 -1
  64. data/spec/pg/type_spec.rb +446 -20
  65. data/spec/pg_spec.rb +2 -2
  66. metadata +63 -72
  67. metadata.gz.sig +0 -0
  68. data/sample/array_insert.rb +0 -20
  69. data/sample/async_api.rb +0 -106
  70. data/sample/async_copyto.rb +0 -39
  71. data/sample/async_mixed.rb +0 -56
  72. data/sample/check_conn.rb +0 -21
  73. data/sample/copyfrom.rb +0 -81
  74. data/sample/copyto.rb +0 -19
  75. data/sample/cursor.rb +0 -21
  76. data/sample/disk_usage_report.rb +0 -186
  77. data/sample/issue-119.rb +0 -94
  78. data/sample/losample.rb +0 -69
  79. data/sample/minimal-testcase.rb +0 -17
  80. data/sample/notify_wait.rb +0 -72
  81. data/sample/pg_statistics.rb +0 -294
  82. data/sample/replication_monitor.rb +0 -231
  83. data/sample/test_binary_values.rb +0 -33
  84. data/sample/wal_shipper.rb +0 -434
  85. data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,41 @@
1
+ # -*- rspec -*-
2
+ #encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ context "running with sync_* methods" do
7
+ before :each do
8
+ PG::Connection.async_api = false
9
+ end
10
+
11
+ after :each do
12
+ PG::Connection.async_api = true
13
+ end
14
+
15
+ fname = File.expand_path("../connection_spec.rb", __FILE__)
16
+ eval File.read(fname, encoding: __ENCODING__), binding, fname
17
+
18
+
19
+ it "enables/disables async/sync methods by #async_api" do
20
+ [true, false].each do |async|
21
+ PG::Connection.async_api = async
22
+
23
+ start = Time.now
24
+ t = Thread.new do
25
+ @conn.exec( 'select pg_sleep(1)' )
26
+ end
27
+ sleep 0.1
28
+
29
+ t.kill
30
+ t.join
31
+ dt = Time.now - start
32
+
33
+ if async
34
+ expect( dt ).to be < 1.0
35
+ else
36
+ expect( dt ).to be >= 1.0
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -1,19 +1,68 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
5
5
 
6
6
  require 'pg'
7
+ require 'objspace'
7
8
 
8
9
 
9
10
  describe PG::Result do
10
11
 
12
+ describe :field_name_type do
13
+ let!(:res) { @conn.exec('SELECT 1 AS a, 2 AS "B"') }
14
+
15
+ it "uses string field names per default" do
16
+ expect(res.field_name_type).to eq(:string)
17
+ end
18
+
19
+ it "can set string field names" do
20
+ res.field_name_type = :string
21
+ expect(res.field_name_type).to eq(:string)
22
+ end
23
+
24
+ it "can set symbol field names" do
25
+ res.field_name_type = :symbol
26
+ expect(res.field_name_type).to eq(:symbol)
27
+ end
28
+
29
+ it "can set static_symbol field names" do
30
+ res.field_name_type = :static_symbol
31
+ expect(res.field_name_type).to eq(:static_symbol)
32
+ end
33
+
34
+ it "can't set symbol field names after #fields" do
35
+ res.fields
36
+ expect{ res.field_name_type = :symbol }.to raise_error(ArgumentError, /already materialized/)
37
+ expect(res.field_name_type).to eq(:string)
38
+ end
39
+
40
+ it "can't set invalid values" do
41
+ expect{ res.field_name_type = :sym }.to raise_error(ArgumentError, /invalid argument :sym/)
42
+ expect{ res.field_name_type = "symbol" }.to raise_error(ArgumentError, /invalid argument "symbol"/)
43
+ end
44
+ end
45
+
11
46
  it "acts as an array of hashes" do
12
47
  res = @conn.exec("SELECT 1 AS a, 2 AS b")
13
48
  expect( res[0]['a'] ).to eq( '1' )
14
49
  expect( res[0]['b'] ).to eq( '2' )
15
50
  end
16
51
 
52
+ it "acts as an array of hashes with symbols" do
53
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
54
+ res.field_name_type = :symbol
55
+ expect( res[0][:a] ).to eq( '1' )
56
+ expect( res[0][:b] ).to eq( '2' )
57
+ end
58
+
59
+ it "acts as an array of hashes with static_symbols" do
60
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
61
+ res.field_name_type = :static_symbol
62
+ expect( res[0][:a] ).to eq( '1' )
63
+ expect( res[0][:b] ).to eq( '2' )
64
+ end
65
+
17
66
  it "yields a row as an array" do
18
67
  res = @conn.exec("SELECT 1 AS a, 2 AS b")
19
68
  list = []
@@ -25,7 +74,6 @@ describe PG::Result do
25
74
  res = @conn.exec("SELECT 1 AS a, 2 AS b")
26
75
  e = res.each_row
27
76
  expect( e ).to be_a_kind_of(Enumerator)
28
- pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
29
77
  expect( e.size ).to eq( 1 )
30
78
  expect( e.to_a ).to eq [['1', '2']]
31
79
  end
@@ -34,13 +82,20 @@ describe PG::Result do
34
82
  res = @conn.exec("SELECT 1 AS a, 2 AS b")
35
83
  e = res.each
36
84
  expect( e ).to be_a_kind_of(Enumerator)
37
- pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
38
85
  expect( e.size ).to eq( 1 )
39
86
  expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
40
87
  end
41
88
 
42
- context "result streaming", :postgresql_92 do
43
- it "can iterate over all tuples in single row mode" do
89
+ it "yields a row as an Enumerator of hashs with symbols" do
90
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
91
+ res.field_name_type = :symbol
92
+ expect( res.each.to_a ).to eq [{:a=>'1', :b=>'2'}]
93
+ end
94
+
95
+ context "result streaming in single row mode" do
96
+ let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
97
+
98
+ it "can iterate over all rows as Hash" do
44
99
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
45
100
  @conn.set_single_row_mode
46
101
  expect(
@@ -56,7 +111,32 @@ describe PG::Result do
56
111
  expect( @conn.get_result ).to be_nil
57
112
  end
58
113
 
59
- it "can iterate over all rows in single row mode" do
114
+ it "can iterate over all rows as Hash with symbols and typemap" do
115
+ @conn.send_query( "SELECT generate_series(2,4) AS a" )
116
+ @conn.set_single_row_mode
117
+ res = @conn.get_result.field_names_as(:symbol)
118
+ res.type_map = PG::TypeMapByColumn.new [textdec_int]
119
+ expect(
120
+ res.stream_each.to_a
121
+ ).to eq(
122
+ [{:a=>2}, {:a=>3}, {:a=>4}]
123
+ )
124
+ expect( @conn.get_result ).to be_nil
125
+ end
126
+
127
+ it "keeps last result on error while iterating stream_each" do
128
+ @conn.send_query( "SELECT generate_series(2,4) AS a" )
129
+ @conn.set_single_row_mode
130
+ res = @conn.get_result
131
+ expect do
132
+ res.stream_each_row do
133
+ raise ZeroDivisionError
134
+ end
135
+ end.to raise_error(ZeroDivisionError)
136
+ expect( res.values ).to eq([["2"]])
137
+ end
138
+
139
+ it "can iterate over all rows as Array" do
60
140
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
61
141
  @conn.set_single_row_mode
62
142
  expect(
@@ -72,6 +152,64 @@ describe PG::Result do
72
152
  expect( @conn.get_result ).to be_nil
73
153
  end
74
154
 
155
+ it "keeps last result on error while iterating stream_each_row" do
156
+ @conn.send_query( "SELECT generate_series(2,4) AS a" )
157
+ @conn.set_single_row_mode
158
+ res = @conn.get_result
159
+ expect do
160
+ res.stream_each_row do
161
+ raise ZeroDivisionError
162
+ end
163
+ end.to raise_error(ZeroDivisionError)
164
+ expect( res.values ).to eq([["2"]])
165
+ end
166
+
167
+ it "can iterate over all rows as PG::Tuple" do
168
+ @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
169
+ @conn.set_single_row_mode
170
+ tuples = @conn.get_result.stream_each_tuple.to_a
171
+ expect( tuples[0][0] ).to eq( "2" )
172
+ expect( tuples[1]["a"] ).to eq( "3" )
173
+ expect( tuples.size ).to eq( 3 )
174
+
175
+ tuples = @conn.get_result.enum_for(:stream_each_tuple).to_a
176
+ expect( tuples[-1][-1] ).to eq( "6" )
177
+ expect( tuples[-2]["b"] ).to eq( "1" )
178
+ expect( tuples.size ).to eq( 2 )
179
+
180
+ expect( @conn.get_result ).to be_nil
181
+ end
182
+
183
+ it "clears result on error while iterating stream_each_tuple" do
184
+ @conn.send_query( "SELECT generate_series(2,4) AS a" )
185
+ @conn.set_single_row_mode
186
+ res = @conn.get_result
187
+ expect do
188
+ res.stream_each_tuple do
189
+ raise ZeroDivisionError
190
+ end
191
+ end.to raise_error(ZeroDivisionError)
192
+ expect( res.cleared? ).to eq(true)
193
+ end
194
+
195
+ it "should reuse field names in stream_each_tuple" do
196
+ @conn.send_query( "SELECT generate_series(2,3) AS a" )
197
+ @conn.set_single_row_mode
198
+ tuple1, tuple2 = *@conn.get_result.stream_each_tuple.to_a
199
+ expect( tuple1.keys[0].object_id ).to eq(tuple2.keys[0].object_id)
200
+ end
201
+
202
+ it "can iterate over all rows as PG::Tuple with symbols and typemap" do
203
+ @conn.send_query( "SELECT generate_series(2,4) AS a" )
204
+ @conn.set_single_row_mode
205
+ res = @conn.get_result.field_names_as(:symbol)
206
+ res.type_map = PG::TypeMapByColumn.new [textdec_int]
207
+ tuples = res.stream_each_tuple.to_a
208
+ expect( tuples[0][0] ).to eq( 2 )
209
+ expect( tuples[1][:a] ).to eq( 3 )
210
+ expect( @conn.get_result ).to be_nil
211
+ end
212
+
75
213
  it "complains when not in single row mode" do
76
214
  @conn.send_query( "SELECT generate_series(2,4)" )
77
215
  expect{
@@ -96,15 +234,15 @@ describe PG::Result do
96
234
  end
97
235
 
98
236
  it "inserts nil AS NULL and return NULL as nil" do
99
- res = @conn.exec("SELECT $1::int AS n", [nil])
237
+ res = @conn.exec_params("SELECT $1::int AS n", [nil])
100
238
  expect( res[0]['n'] ).to be_nil()
101
239
  end
102
240
 
103
- it "encapsulates errors in a PGError object" do
241
+ it "encapsulates errors in a PG::Error object" do
104
242
  exception = nil
105
243
  begin
106
244
  @conn.exec( "SELECT * FROM nonexistant_table" )
107
- rescue PGError => err
245
+ rescue PG::Error => err
108
246
  exception = err
109
247
  end
110
248
 
@@ -131,12 +269,23 @@ describe PG::Result do
131
269
  ).to match( /^parserOpenTable$|^RangeVarGetRelid$/ )
132
270
  end
133
271
 
272
+ it "encapsulates PG_DIAG_SEVERITY_NONLOCALIZED error in a PG::Error object", :postgresql_96 do
273
+ result = nil
274
+ begin
275
+ @conn.exec( "SELECT * FROM nonexistant_table" )
276
+ rescue PG::Error => err
277
+ result = err.result
278
+ end
279
+
280
+ expect( result.error_field(PG::PG_DIAG_SEVERITY_NONLOCALIZED) ).to eq( 'ERROR' )
281
+ end
282
+
134
283
  it "encapsulates database object names for integrity constraint violations", :postgresql_93 do
135
284
  @conn.exec( "CREATE TABLE integrity (id SERIAL PRIMARY KEY)" )
136
285
  exception = nil
137
286
  begin
138
287
  @conn.exec( "INSERT INTO integrity VALUES (NULL)" )
139
- rescue PGError => err
288
+ rescue PG::Error => err
140
289
  exception = err
141
290
  end
142
291
  result = exception.result
@@ -151,17 +300,39 @@ describe PG::Result do
151
300
  it "detects division by zero as SQLSTATE 22012" do
152
301
  sqlstate = nil
153
302
  begin
154
- res = @conn.exec("SELECT 1/0")
155
- rescue PGError => e
303
+ @conn.exec("SELECT 1/0")
304
+ rescue PG::Error => e
156
305
  sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
157
306
  end
158
307
  expect( sqlstate ).to eq( 22012 )
159
308
  end
160
309
 
310
+ it "provides the error message" do
311
+ @conn.send_query("SELECT xyz")
312
+ res = @conn.get_result; @conn.get_result
313
+ expect( res.error_message ).to match(/"xyz"/)
314
+ expect( res.result_error_message ).to match(/"xyz"/)
315
+ end
316
+
317
+ it "provides a verbose error message", :postgresql_96 do
318
+ @conn.send_query("SELECT xyz")
319
+ res = @conn.get_result; @conn.get_result
320
+ # PQERRORS_TERSE should give a single line result
321
+ expect( res.verbose_error_message(PG::PQERRORS_TERSE, PG::PQSHOW_CONTEXT_ALWAYS) ).to match(/\A.*\n\z/)
322
+ # PQERRORS_VERBOSE should give a multi line result
323
+ expect( res.result_verbose_error_message(PG::PQERRORS_VERBOSE, PG::PQSHOW_CONTEXT_NEVER) ).to match(/\n.*\n/)
324
+ end
325
+
326
+ it "provides a verbose error message with SQLSTATE", :postgresql_12 do
327
+ @conn.send_query("SELECT xyz")
328
+ res = @conn.get_result; @conn.get_result
329
+ expect( res.verbose_error_message(PG::PQERRORS_SQLSTATE, PG::PQSHOW_CONTEXT_NEVER) ).to match(/42703/)
330
+ end
331
+
161
332
  it "returns the same bytes in binary format that are sent in binary format" do
162
333
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
163
334
  bytes = File.open(binary_file, 'rb').read
164
- res = @conn.exec('VALUES ($1::bytea)',
335
+ res = @conn.exec_params('VALUES ($1::bytea)',
165
336
  [ { :value => bytes, :format => 1 } ], 1)
166
337
  expect( res[0]['column1'] ).to eq( bytes )
167
338
  expect( res.getvalue(0,0) ).to eq( bytes )
@@ -173,7 +344,7 @@ describe PG::Result do
173
344
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
174
345
  bytes = File.open(binary_file, 'rb').read
175
346
  @conn.exec("SET standard_conforming_strings=on")
176
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
347
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
177
348
  expect( res[0]['column1'] ).to eq( bytes )
178
349
  expect( res.getvalue(0,0) ).to eq( bytes )
179
350
  expect( res.values[0][0] ).to eq( bytes )
@@ -183,7 +354,7 @@ describe PG::Result do
183
354
  it "returns the same bytes in text format that are sent in binary format" do
184
355
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
185
356
  bytes = File.open(binary_file, 'rb').read
186
- res = @conn.exec('VALUES ($1::bytea)',
357
+ res = @conn.exec_params('VALUES ($1::bytea)',
187
358
  [ { :value => bytes, :format => 1 } ])
188
359
  expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
189
360
  end
@@ -194,21 +365,21 @@ describe PG::Result do
194
365
 
195
366
  out_bytes = nil
196
367
  @conn.exec("SET standard_conforming_strings=on")
197
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
368
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
198
369
  out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
199
370
  expect( out_bytes ).to eq( in_bytes )
200
371
  end
201
372
 
202
- it "returns the parameter type of the specified prepared statement parameter", :postgresql_92 do
373
+ it "returns the parameter type of the specified prepared statement parameter" do
203
374
  query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
204
375
  @conn.prepare( 'queryfinder', query )
205
376
  res = @conn.describe_prepared( 'queryfinder' )
206
377
 
207
378
  expect(
208
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
379
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
209
380
  ).to eq( 'name' )
210
381
  expect(
211
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
382
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
212
383
  ).to eq( 'text' )
213
384
  end
214
385
 
@@ -242,6 +413,32 @@ describe PG::Result do
242
413
  expect( res.values ).to eq( [ ["bar"], ["bar2"] ] )
243
414
  end
244
415
 
416
+ it "can retrieve field names" do
417
+ res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
418
+ expect(res.fields).to eq(["a", "B"])
419
+ end
420
+
421
+ it "can retrieve field names as symbols" do
422
+ res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
423
+ res.field_name_type = :symbol
424
+ expect(res.fields).to eq([:a, :B])
425
+ end
426
+
427
+ it "can retrieve single field names" do
428
+ res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
429
+ expect(res.fname(0)).to eq("a")
430
+ expect(res.fname(1)).to eq("B")
431
+ expect{res.fname(2)}.to raise_error(ArgumentError)
432
+ end
433
+
434
+ it "can retrieve single field names as symbol" do
435
+ res = @conn.exec('SELECT 1 AS a, 2 AS "B"')
436
+ res.field_name_type = :symbol
437
+ expect(res.fname(0)).to eq(:a)
438
+ expect(res.fname(1)).to eq(:B)
439
+ expect{res.fname(2)}.to raise_error(ArgumentError)
440
+ end
441
+
245
442
  # PQfmod
246
443
  it "can return the type modifier for a result column" do
247
444
  @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
@@ -339,8 +536,27 @@ describe PG::Result do
339
536
  res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
340
537
  expect( res.field_values('x') ).to eq( ['1', '2'] )
341
538
  expect( res.field_values('y') ).to eq( ['a', 'b'] )
539
+ expect( res.field_values(:x) ).to eq( ['1', '2'] )
342
540
  expect{ res.field_values('') }.to raise_error(IndexError)
343
- expect{ res.field_values(:x) }.to raise_error(TypeError)
541
+ expect{ res.field_values(0) }.to raise_error(TypeError)
542
+ end
543
+
544
+ it "can return the values of a single tuple" do
545
+ res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
546
+ expect( res.tuple_values(0) ).to eq( ['1', 'a'] )
547
+ expect( res.tuple_values(1) ).to eq( ['2', 'b'] )
548
+ expect{ res.tuple_values(2) }.to raise_error(IndexError)
549
+ expect{ res.tuple_values(-1) }.to raise_error(IndexError)
550
+ expect{ res.tuple_values("x") }.to raise_error(TypeError)
551
+ end
552
+
553
+ it "can return the values of a single vary lazy tuple" do
554
+ res = @conn.exec( "VALUES(1),(2)" )
555
+ expect( res.tuple(0) ).to be_kind_of( PG::Tuple )
556
+ expect( res.tuple(1) ).to be_kind_of( PG::Tuple )
557
+ expect{ res.tuple(2) }.to raise_error(IndexError)
558
+ expect{ res.tuple(-1) }.to raise_error(IndexError)
559
+ expect{ res.tuple("x") }.to raise_error(TypeError)
344
560
  end
345
561
 
346
562
  it "raises a proper exception for a nonexistant table" do
@@ -386,7 +602,7 @@ describe PG::Result do
386
602
  end
387
603
 
388
604
  it "the raised result is nil in case of a connection error" do
389
- c = PGconn.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
605
+ c = PG::Connection.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
390
606
  expect {
391
607
  c.exec "select 1"
392
608
  }.to raise_error {|error|
@@ -403,6 +619,20 @@ describe PG::Result do
403
619
  expect( r.cleared? ).to eq(true)
404
620
  end
405
621
 
622
+ it "can be inspected before and after clear" do
623
+ r = @conn.exec "select 1"
624
+ expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
625
+ r.clear
626
+ expect( r.inspect ).to match(/cleared/)
627
+ end
628
+
629
+ it "should give account about memory usage" do
630
+ r = @conn.exec "select 1"
631
+ expect( ObjectSpace.memsize_of(r) ).to be > 1000
632
+ r.clear
633
+ expect( ObjectSpace.memsize_of(r) ).to be < 100
634
+ end
635
+
406
636
  context 'result value conversions with TypeMapByColumn' do
407
637
  let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
408
638
  let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
@@ -429,6 +659,8 @@ describe PG::Result do
429
659
  expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
430
660
  expect( res.column_values(0) ).to eq( [123] )
431
661
  expect( res.field_values('f') ).to eq( [123] )
662
+ expect( res.field_values(:f) ).to eq( [123] )
663
+ expect( res.tuple_values(0) ).to eq( [123] )
432
664
  end
433
665
 
434
666
  it "should be usable for several querys" do