pg 0.19.0 → 1.1.0

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 (74) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +218 -1
  4. data/History.rdoc +106 -0
  5. data/Manifest.txt +5 -18
  6. data/README.rdoc +15 -5
  7. data/Rakefile +8 -9
  8. data/Rakefile.cross +19 -22
  9. data/ext/errorcodes.def +17 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +11 -1
  12. data/ext/extconf.rb +14 -32
  13. data/ext/gvl_wrappers.c +4 -0
  14. data/ext/gvl_wrappers.h +23 -39
  15. data/ext/pg.c +19 -48
  16. data/ext/pg.h +46 -81
  17. data/ext/pg_binary_decoder.c +69 -6
  18. data/ext/pg_coder.c +53 -4
  19. data/ext/pg_connection.c +401 -253
  20. data/ext/pg_copy_coder.c +10 -5
  21. data/ext/pg_result.c +359 -131
  22. data/ext/pg_text_decoder.c +597 -37
  23. data/ext/pg_text_encoder.c +6 -7
  24. data/ext/pg_tuple.c +541 -0
  25. data/ext/pg_type_map.c +14 -7
  26. data/ext/util.c +6 -6
  27. data/ext/util.h +2 -2
  28. data/lib/pg/basic_type_mapping.rb +40 -7
  29. data/lib/pg/binary_decoder.rb +22 -0
  30. data/lib/pg/coder.rb +1 -1
  31. data/lib/pg/connection.rb +27 -7
  32. data/lib/pg/constants.rb +1 -1
  33. data/lib/pg/exceptions.rb +1 -1
  34. data/lib/pg/result.rb +6 -5
  35. data/lib/pg/text_decoder.rb +19 -23
  36. data/lib/pg/text_encoder.rb +36 -2
  37. data/lib/pg/tuple.rb +30 -0
  38. data/lib/pg/type_map_by_column.rb +1 -1
  39. data/lib/pg.rb +21 -11
  40. data/spec/helpers.rb +47 -19
  41. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  42. data/spec/pg/connection_spec.rb +402 -275
  43. data/spec/pg/connection_sync_spec.rb +41 -0
  44. data/spec/pg/result_spec.rb +59 -17
  45. data/spec/pg/tuple_spec.rb +280 -0
  46. data/spec/pg/type_map_by_class_spec.rb +2 -2
  47. data/spec/pg/type_map_by_column_spec.rb +1 -1
  48. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  49. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  50. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  51. data/spec/pg/type_map_spec.rb +1 -1
  52. data/spec/pg/type_spec.rb +184 -12
  53. data/spec/pg_spec.rb +2 -2
  54. data.tar.gz.sig +0 -0
  55. metadata +47 -53
  56. metadata.gz.sig +0 -0
  57. data/sample/array_insert.rb +0 -20
  58. data/sample/async_api.rb +0 -106
  59. data/sample/async_copyto.rb +0 -39
  60. data/sample/async_mixed.rb +0 -56
  61. data/sample/check_conn.rb +0 -21
  62. data/sample/copyfrom.rb +0 -81
  63. data/sample/copyto.rb +0 -19
  64. data/sample/cursor.rb +0 -21
  65. data/sample/disk_usage_report.rb +0 -186
  66. data/sample/issue-119.rb +0 -94
  67. data/sample/losample.rb +0 -69
  68. data/sample/minimal-testcase.rb +0 -17
  69. data/sample/notify_wait.rb +0 -72
  70. data/sample/pg_statistics.rb +0 -294
  71. data/sample/replication_monitor.rb +0 -231
  72. data/sample/test_binary_values.rb +0 -33
  73. data/sample/wal_shipper.rb +0 -434
  74. 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,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -39,8 +39,8 @@ describe PG::Result do
39
39
  expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
40
40
  end
41
41
 
42
- context "result streaming", :postgresql_92 do
43
- it "can iterate over all tuples in single row mode" do
42
+ context "result streaming in single row mode" do
43
+ it "can iterate over all rows as Hash" do
44
44
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
45
45
  @conn.set_single_row_mode
46
46
  expect(
@@ -56,7 +56,7 @@ describe PG::Result do
56
56
  expect( @conn.get_result ).to be_nil
57
57
  end
58
58
 
59
- it "can iterate over all rows in single row mode" do
59
+ it "can iterate over all rows as Array" do
60
60
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
61
61
  @conn.set_single_row_mode
62
62
  expect(
@@ -72,6 +72,22 @@ describe PG::Result do
72
72
  expect( @conn.get_result ).to be_nil
73
73
  end
74
74
 
75
+ it "can iterate over all rows as PG::Tuple" do
76
+ @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
77
+ @conn.set_single_row_mode
78
+ tuples = @conn.get_result.stream_each_tuple.to_a
79
+ expect( tuples[0][0] ).to eq( "2" )
80
+ expect( tuples[1]["a"] ).to eq( "3" )
81
+ expect( tuples.size ).to eq( 3 )
82
+
83
+ tuples = @conn.get_result.enum_for(:stream_each_tuple).to_a
84
+ expect( tuples[-1][-1] ).to eq( "6" )
85
+ expect( tuples[-2]["b"] ).to eq( "1" )
86
+ expect( tuples.size ).to eq( 2 )
87
+
88
+ expect( @conn.get_result ).to be_nil
89
+ end
90
+
75
91
  it "complains when not in single row mode" do
76
92
  @conn.send_query( "SELECT generate_series(2,4)" )
77
93
  expect{
@@ -96,15 +112,15 @@ describe PG::Result do
96
112
  end
97
113
 
98
114
  it "inserts nil AS NULL and return NULL as nil" do
99
- res = @conn.exec("SELECT $1::int AS n", [nil])
115
+ res = @conn.exec_params("SELECT $1::int AS n", [nil])
100
116
  expect( res[0]['n'] ).to be_nil()
101
117
  end
102
118
 
103
- it "encapsulates errors in a PGError object" do
119
+ it "encapsulates errors in a PG::Error object" do
104
120
  exception = nil
105
121
  begin
106
122
  @conn.exec( "SELECT * FROM nonexistant_table" )
107
- rescue PGError => err
123
+ rescue PG::Error => err
108
124
  exception = err
109
125
  end
110
126
 
@@ -136,7 +152,7 @@ describe PG::Result do
136
152
  exception = nil
137
153
  begin
138
154
  @conn.exec( "INSERT INTO integrity VALUES (NULL)" )
139
- rescue PGError => err
155
+ rescue PG::Error => err
140
156
  exception = err
141
157
  end
142
158
  result = exception.result
@@ -152,7 +168,7 @@ describe PG::Result do
152
168
  sqlstate = nil
153
169
  begin
154
170
  res = @conn.exec("SELECT 1/0")
155
- rescue PGError => e
171
+ rescue PG::Error => e
156
172
  sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
157
173
  end
158
174
  expect( sqlstate ).to eq( 22012 )
@@ -161,7 +177,7 @@ describe PG::Result do
161
177
  it "returns the same bytes in binary format that are sent in binary format" do
162
178
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
163
179
  bytes = File.open(binary_file, 'rb').read
164
- res = @conn.exec('VALUES ($1::bytea)',
180
+ res = @conn.exec_params('VALUES ($1::bytea)',
165
181
  [ { :value => bytes, :format => 1 } ], 1)
166
182
  expect( res[0]['column1'] ).to eq( bytes )
167
183
  expect( res.getvalue(0,0) ).to eq( bytes )
@@ -173,7 +189,7 @@ describe PG::Result do
173
189
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
174
190
  bytes = File.open(binary_file, 'rb').read
175
191
  @conn.exec("SET standard_conforming_strings=on")
176
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
192
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
177
193
  expect( res[0]['column1'] ).to eq( bytes )
178
194
  expect( res.getvalue(0,0) ).to eq( bytes )
179
195
  expect( res.values[0][0] ).to eq( bytes )
@@ -183,7 +199,7 @@ describe PG::Result do
183
199
  it "returns the same bytes in text format that are sent in binary format" do
184
200
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
185
201
  bytes = File.open(binary_file, 'rb').read
186
- res = @conn.exec('VALUES ($1::bytea)',
202
+ res = @conn.exec_params('VALUES ($1::bytea)',
187
203
  [ { :value => bytes, :format => 1 } ])
188
204
  expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
189
205
  end
@@ -194,21 +210,21 @@ describe PG::Result do
194
210
 
195
211
  out_bytes = nil
196
212
  @conn.exec("SET standard_conforming_strings=on")
197
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
213
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
198
214
  out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
199
215
  expect( out_bytes ).to eq( in_bytes )
200
216
  end
201
217
 
202
- it "returns the parameter type of the specified prepared statement parameter", :postgresql_92 do
218
+ it "returns the parameter type of the specified prepared statement parameter" do
203
219
  query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
204
220
  @conn.prepare( 'queryfinder', query )
205
221
  res = @conn.describe_prepared( 'queryfinder' )
206
222
 
207
223
  expect(
208
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
224
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
209
225
  ).to eq( 'name' )
210
226
  expect(
211
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
227
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
212
228
  ).to eq( 'text' )
213
229
  end
214
230
 
@@ -343,6 +359,24 @@ describe PG::Result do
343
359
  expect{ res.field_values(:x) }.to raise_error(TypeError)
344
360
  end
345
361
 
362
+ it "can return the values of a single tuple" do
363
+ res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
364
+ expect( res.tuple_values(0) ).to eq( ['1', 'a'] )
365
+ expect( res.tuple_values(1) ).to eq( ['2', 'b'] )
366
+ expect{ res.tuple_values(2) }.to raise_error(IndexError)
367
+ expect{ res.tuple_values(-1) }.to raise_error(IndexError)
368
+ expect{ res.tuple_values("x") }.to raise_error(TypeError)
369
+ end
370
+
371
+ it "can return the values of a single vary lazy tuple" do
372
+ res = @conn.exec( "VALUES(1),(2)" )
373
+ expect( res.tuple(0) ).to be_kind_of( PG::Tuple )
374
+ expect( res.tuple(1) ).to be_kind_of( PG::Tuple )
375
+ expect{ res.tuple(2) }.to raise_error(IndexError)
376
+ expect{ res.tuple(-1) }.to raise_error(IndexError)
377
+ expect{ res.tuple("x") }.to raise_error(TypeError)
378
+ end
379
+
346
380
  it "raises a proper exception for a nonexistant table" do
347
381
  expect {
348
382
  @conn.exec( "SELECT * FROM nonexistant_table" )
@@ -386,7 +420,7 @@ describe PG::Result do
386
420
  end
387
421
 
388
422
  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" )
423
+ c = PG::Connection.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
390
424
  expect {
391
425
  c.exec "select 1"
392
426
  }.to raise_error {|error|
@@ -403,6 +437,13 @@ describe PG::Result do
403
437
  expect( r.cleared? ).to eq(true)
404
438
  end
405
439
 
440
+ it "can be inspected before and after clear" do
441
+ r = @conn.exec "select 1"
442
+ expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
443
+ r.clear
444
+ expect( r.inspect ).to match(/cleared/)
445
+ end
446
+
406
447
  context 'result value conversions with TypeMapByColumn' do
407
448
  let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
408
449
  let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
@@ -429,6 +470,7 @@ describe PG::Result do
429
470
  expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
430
471
  expect( res.column_values(0) ).to eq( [123] )
431
472
  expect( res.field_values('f') ).to eq( [123] )
473
+ expect( res.tuple_values(0) ).to eq( [123] )
432
474
  end
433
475
 
434
476
  it "should be usable for several querys" do
@@ -0,0 +1,280 @@
1
+ # -*- rspec -*-
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+ require 'pg'
6
+ require 'objspace'
7
+
8
+ describe PG::Tuple do
9
+ let!(:typemap) { PG::BasicTypeMapForResults.new(@conn) }
10
+ let!(:result2x2) { @conn.exec( "VALUES(1, 'a'), (2, 'b')" ) }
11
+ let!(:result2x3cast) { @conn.exec( "SELECT * FROM (VALUES(1, TRUE, '3'), (2, FALSE, '4')) AS m (a, b, b)" ).map_types!(typemap) }
12
+ let!(:tuple0) { result2x2.tuple(0) }
13
+ let!(:tuple1) { result2x2.tuple(1) }
14
+ let!(:tuple2) { result2x3cast.tuple(0) }
15
+ let!(:tuple3) { str = Marshal.dump(result2x3cast.tuple(1)); Marshal.load(str) }
16
+ let!(:tuple_empty) { PG::Tuple.new }
17
+
18
+ describe "[]" do
19
+ it "returns nil for invalid keys" do
20
+ expect( tuple0["x"] ).to be_nil
21
+ expect( tuple0[0.5] ).to be_nil
22
+ expect( tuple0[2] ).to be_nil
23
+ expect( tuple0[-3] ).to be_nil
24
+ expect( tuple2[-4] ).to be_nil
25
+ expect{ tuple_empty[0] }.to raise_error(TypeError)
26
+ end
27
+
28
+ it "supports array like access" do
29
+ expect( tuple0[0] ).to eq( "1" )
30
+ expect( tuple0[1] ).to eq( "a" )
31
+ expect( tuple1[0] ).to eq( "2" )
32
+ expect( tuple1[1] ).to eq( "b" )
33
+ expect( tuple2[0] ).to eq( 1 )
34
+ expect( tuple2[1] ).to eq( true )
35
+ expect( tuple2[2] ).to eq( "3" )
36
+ expect( tuple3[0] ).to eq( 2 )
37
+ expect( tuple3[1] ).to eq( false )
38
+ expect( tuple3[2] ).to eq( "4" )
39
+ end
40
+
41
+ it "supports negative indices" do
42
+ expect( tuple0[-2] ).to eq( "1" )
43
+ expect( tuple0[-1] ).to eq( "a" )
44
+ expect( tuple2[-3] ).to eq( 1 )
45
+ expect( tuple2[-2] ).to eq( true )
46
+ expect( tuple2[-1] ).to eq( "3" )
47
+ end
48
+
49
+ it "supports hash like access" do
50
+ expect( tuple0["column1"] ).to eq( "1" )
51
+ expect( tuple0["column2"] ).to eq( "a" )
52
+ expect( tuple2["a"] ).to eq( 1 )
53
+ expect( tuple2["b"] ).to eq( "3" )
54
+ expect( tuple0["x"] ).to be_nil
55
+ end
56
+
57
+ it "casts lazy and caches result" do
58
+ a = []
59
+ deco = Class.new(PG::SimpleDecoder) do
60
+ define_method(:decode) do |*args|
61
+ a << args
62
+ args.last
63
+ end
64
+ end.new
65
+
66
+ result2x2.map_types!(PG::TypeMapByColumn.new([deco, deco]))
67
+ t = result2x2.tuple(1)
68
+
69
+ # cast and cache at first call to [0]
70
+ a.clear
71
+ expect( t[0] ).to eq( 0 )
72
+ expect( a ).to eq([["2", 1, 0]])
73
+
74
+ # use cache at second call to [0]
75
+ a.clear
76
+ expect( t[0] ).to eq( 0 )
77
+ expect( a ).to eq([])
78
+
79
+ # cast and cache at first call to [1]
80
+ a.clear
81
+ expect( t[1] ).to eq( 1 )
82
+ expect( a ).to eq([["b", 1, 1]])
83
+ end
84
+ end
85
+
86
+ describe "fetch" do
87
+ it "raises proper errors for invalid keys" do
88
+ expect{ tuple0.fetch("x") }.to raise_error(KeyError)
89
+ expect{ tuple0.fetch(0.5) }.to raise_error(KeyError)
90
+ expect{ tuple0.fetch(2) }.to raise_error(IndexError)
91
+ expect{ tuple0.fetch(-3) }.to raise_error(IndexError)
92
+ expect{ tuple0.fetch(-3) }.to raise_error(IndexError)
93
+ expect{ tuple2.fetch(-4) }.to raise_error(IndexError)
94
+ expect{ tuple_empty[0] }.to raise_error(TypeError)
95
+ end
96
+
97
+ it "supports array like access" do
98
+ expect( tuple0.fetch(0) ).to eq( "1" )
99
+ expect( tuple0.fetch(1) ).to eq( "a" )
100
+ expect( tuple2.fetch(0) ).to eq( 1 )
101
+ expect( tuple2.fetch(1) ).to eq( true )
102
+ expect( tuple2.fetch(2) ).to eq( "3" )
103
+ end
104
+
105
+ it "supports default value for indices" do
106
+ expect( tuple0.fetch(2, 42) ).to eq( 42 )
107
+ expect( tuple0.fetch(2){43} ).to eq( 43 )
108
+ end
109
+
110
+ it "supports negative indices" do
111
+ expect( tuple0.fetch(-2) ).to eq( "1" )
112
+ expect( tuple0.fetch(-1) ).to eq( "a" )
113
+ expect( tuple2.fetch(-3) ).to eq( 1 )
114
+ expect( tuple2.fetch(-2) ).to eq( true )
115
+ expect( tuple2.fetch(-1) ).to eq( "3" )
116
+ end
117
+
118
+ it "supports hash like access" do
119
+ expect( tuple0.fetch("column1") ).to eq( "1" )
120
+ expect( tuple0.fetch("column2") ).to eq( "a" )
121
+ expect( tuple2.fetch("a") ).to eq( 1 )
122
+ expect( tuple2.fetch("b") ).to eq( "3" )
123
+ end
124
+
125
+ it "supports default value for name keys" do
126
+ expect( tuple0.fetch("x", "defa") ).to eq("defa")
127
+ expect( tuple0.fetch("x"){"defa"} ).to eq("defa")
128
+ end
129
+ end
130
+
131
+ describe "each" do
132
+ it "can be used as an enumerator" do
133
+ expect( tuple0.each ).to be_kind_of(Enumerator)
134
+ expect( tuple0.each.to_a ).to eq( [["column1", "1"], ["column2", "a"]] )
135
+ expect( tuple1.each.to_a ).to eq( [["column1", "2"], ["column2", "b"]] )
136
+ expect( tuple2.each.to_a ).to eq( [["a", 1], ["b", true], ["b", "3"]] )
137
+ expect( tuple3.each.to_a ).to eq( [["a", 2], ["b", false], ["b", "4"]] )
138
+ expect{ tuple_empty.each }.to raise_error(TypeError)
139
+ end
140
+
141
+ it "can be used with block" do
142
+ a = []
143
+ tuple0.each do |*v|
144
+ a << v
145
+ end
146
+ expect( a ).to eq( [["column1", "1"], ["column2", "a"]] )
147
+ end
148
+ end
149
+
150
+ describe "each_value" do
151
+ it "can be used as an enumerator" do
152
+ expect( tuple0.each_value ).to be_kind_of(Enumerator)
153
+ expect( tuple0.each_value.to_a ).to eq( ["1", "a"] )
154
+ expect( tuple1.each_value.to_a ).to eq( ["2", "b"] )
155
+ expect( tuple2.each_value.to_a ).to eq( [1, true, "3"] )
156
+ expect( tuple3.each_value.to_a ).to eq( [2, false, "4"] )
157
+ expect{ tuple_empty.each_value }.to raise_error(TypeError)
158
+ end
159
+
160
+ it "can be used with block" do
161
+ a = []
162
+ tuple0.each_value do |v|
163
+ a << v
164
+ end
165
+ expect( a ).to eq( ["1", "a"] )
166
+ end
167
+ end
168
+
169
+ it "responds to values" do
170
+ expect( tuple0.values ).to eq( ["1", "a"] )
171
+ expect( tuple3.values ).to eq( [2, false, "4"] )
172
+ expect{ tuple_empty.values }.to raise_error(TypeError)
173
+ end
174
+
175
+ it "responds to key?" do
176
+ expect( tuple1.key?("column1") ).to eq( true )
177
+ expect( tuple1.key?("other") ).to eq( false )
178
+ expect( tuple1.has_key?("column1") ).to eq( true )
179
+ expect( tuple1.has_key?("other") ).to eq( false )
180
+ end
181
+
182
+ it "responds to keys" do
183
+ expect( tuple0.keys ).to eq( ["column1", "column2"] )
184
+ expect( tuple2.keys ).to eq( ["a", "b", "b"] )
185
+ end
186
+
187
+ describe "each_key" do
188
+ it "can be used as an enumerator" do
189
+ expect( tuple0.each_key ).to be_kind_of(Enumerator)
190
+ expect( tuple0.each_key.to_a ).to eq( ["column1", "column2"] )
191
+ expect( tuple2.each_key.to_a ).to eq( ["a", "b", "b"] )
192
+ end
193
+
194
+ it "can be used with block" do
195
+ a = []
196
+ tuple0.each_key do |v|
197
+ a << v
198
+ end
199
+ expect( a ).to eq( ["column1", "column2"] )
200
+ end
201
+ end
202
+
203
+ it "responds to length" do
204
+ expect( tuple0.length ).to eq( 2 )
205
+ expect( tuple0.size ).to eq( 2 )
206
+ expect( tuple2.size ).to eq( 3 )
207
+ end
208
+
209
+ it "responds to index" do
210
+ expect( tuple0.index("column1") ).to eq( 0 )
211
+ expect( tuple0.index("column2") ).to eq( 1 )
212
+ expect( tuple0.index("x") ).to eq( nil )
213
+ expect( tuple2.index("a") ).to eq( 0 )
214
+ expect( tuple2.index("b") ).to eq( 2 )
215
+ end
216
+
217
+ it "can be used as Enumerable" do
218
+ expect( tuple0.to_a ).to eq( [["column1", "1"], ["column2", "a"]] )
219
+ expect( tuple1.to_a ).to eq( [["column1", "2"], ["column2", "b"]] )
220
+ expect( tuple2.to_a ).to eq( [["a", 1], ["b", true], ["b", "3"]] )
221
+ expect( tuple3.to_a ).to eq( [["a", 2], ["b", false], ["b", "4"]] )
222
+ end
223
+
224
+ it "can be marshaled" do
225
+ [tuple0, tuple1, tuple2, tuple3].each do |t1|
226
+ str = Marshal.dump(t1)
227
+ t2 = Marshal.load(str)
228
+
229
+ expect( t2 ).to be_kind_of(t1.class)
230
+ expect( t2 ).not_to equal(t1)
231
+ expect( t2.to_a ).to eq(t1.to_a)
232
+ end
233
+ end
234
+
235
+ it "passes instance variables when marshaled" do
236
+ t1 = tuple0
237
+ t1.instance_variable_set("@a", 4711)
238
+ str = Marshal.dump(t1)
239
+ t2 = Marshal.load(str)
240
+
241
+ expect( t2.instance_variable_get("@a") ).to eq( 4711 )
242
+ end
243
+
244
+ it "can't be marshaled when empty" do
245
+ expect{ Marshal.dump(tuple_empty) }.to raise_error(TypeError)
246
+ end
247
+
248
+ it "should give account about memory usage" do
249
+ expect( ObjectSpace.memsize_of(tuple0) ).to be > 40
250
+ expect( ObjectSpace.memsize_of(tuple_empty) ).to be > 0
251
+ end
252
+
253
+ it "should override #inspect" do
254
+ expect( tuple1.inspect ).to eq('#<PG::Tuple column1: "2", column2: "b">')
255
+ expect( tuple2.inspect ).to eq('#<PG::Tuple a: 1, b: true, b: "3">')
256
+ expect{ tuple_empty.inspect }.to raise_error(TypeError)
257
+ end
258
+
259
+ context "with cleared result" do
260
+ it "should raise an error when non-materialized fields are used" do
261
+ r = result2x2
262
+ t = r.tuple(0)
263
+ t[0] # materialize first field only
264
+ r.clear
265
+
266
+ # second column should fail
267
+ expect{ t[1] }.to raise_error(PG::Error)
268
+ expect{ t.fetch(1) }.to raise_error(PG::Error)
269
+ expect{ t.fetch("column2") }.to raise_error(PG::Error)
270
+
271
+ # first column should succeed
272
+ expect( t[0] ).to eq( "1" )
273
+ expect( t.fetch(0) ).to eq( "1" )
274
+ expect( t.fetch("column1") ).to eq( "1" )
275
+
276
+ # should fail due to the second column
277
+ expect{ t.values }.to raise_error(PG::Error)
278
+ end
279
+ end
280
+ end
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -59,7 +59,7 @@ describe PG::TypeMapByClass do
59
59
  it "should retrieve particular conversions" do
60
60
  expect( tm[Integer] ).to eq(binaryenc_int)
61
61
  expect( tm[Float] ).to eq(textenc_float)
62
- expect( tm[Bignum] ).to be_nil
62
+ expect( tm[Range] ).to be_nil
63
63
  expect( derived_tm[raise_class] ).to be_kind_of(Proc)
64
64
  expect( derived_tm[Array] ).to eq(:array_type_map_for)
65
65
  end
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'