pg 0.18.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +1221 -4
  5. data/History.rdoc +200 -0
  6. data/Manifest.txt +5 -18
  7. data/README-Windows.rdoc +15 -26
  8. data/README.rdoc +27 -10
  9. data/Rakefile +33 -24
  10. data/Rakefile.cross +57 -39
  11. data/ext/errorcodes.def +37 -0
  12. data/ext/errorcodes.rb +1 -1
  13. data/ext/errorcodes.txt +16 -1
  14. data/ext/extconf.rb +29 -35
  15. data/ext/gvl_wrappers.c +4 -0
  16. data/ext/gvl_wrappers.h +27 -39
  17. data/ext/pg.c +27 -53
  18. data/ext/pg.h +66 -83
  19. data/ext/pg_binary_decoder.c +75 -6
  20. data/ext/pg_binary_encoder.c +14 -12
  21. data/ext/pg_coder.c +83 -13
  22. data/ext/pg_connection.c +627 -351
  23. data/ext/pg_copy_coder.c +44 -9
  24. data/ext/pg_result.c +364 -134
  25. data/ext/pg_text_decoder.c +605 -46
  26. data/ext/pg_text_encoder.c +95 -76
  27. data/ext/pg_tuple.c +541 -0
  28. data/ext/pg_type_map.c +20 -13
  29. data/ext/pg_type_map_by_column.c +7 -7
  30. data/ext/pg_type_map_by_mri_type.c +2 -2
  31. data/ext/pg_type_map_in_ruby.c +4 -7
  32. data/ext/util.c +7 -7
  33. data/ext/util.h +3 -3
  34. data/lib/pg/basic_type_mapping.rb +105 -45
  35. data/lib/pg/binary_decoder.rb +22 -0
  36. data/lib/pg/coder.rb +1 -1
  37. data/lib/pg/connection.rb +109 -39
  38. data/lib/pg/constants.rb +1 -1
  39. data/lib/pg/exceptions.rb +1 -1
  40. data/lib/pg/result.rb +11 -6
  41. data/lib/pg/text_decoder.rb +25 -20
  42. data/lib/pg/text_encoder.rb +43 -1
  43. data/lib/pg/tuple.rb +30 -0
  44. data/lib/pg/type_map_by_column.rb +1 -1
  45. data/lib/pg.rb +21 -11
  46. data/spec/helpers.rb +50 -25
  47. data/spec/pg/basic_type_mapping_spec.rb +287 -30
  48. data/spec/pg/connection_spec.rb +695 -282
  49. data/spec/pg/connection_sync_spec.rb +41 -0
  50. data/spec/pg/result_spec.rb +59 -17
  51. data/spec/pg/tuple_spec.rb +280 -0
  52. data/spec/pg/type_map_by_class_spec.rb +3 -3
  53. data/spec/pg/type_map_by_column_spec.rb +1 -1
  54. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  55. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  56. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  57. data/spec/pg/type_map_spec.rb +1 -1
  58. data/spec/pg/type_spec.rb +319 -35
  59. data/spec/pg_spec.rb +2 -2
  60. data.tar.gz.sig +0 -0
  61. metadata +68 -68
  62. metadata.gz.sig +0 -0
  63. data/sample/array_insert.rb +0 -20
  64. data/sample/async_api.rb +0 -106
  65. data/sample/async_copyto.rb +0 -39
  66. data/sample/async_mixed.rb +0 -56
  67. data/sample/check_conn.rb +0 -21
  68. data/sample/copyfrom.rb +0 -81
  69. data/sample/copyto.rb +0 -19
  70. data/sample/cursor.rb +0 -21
  71. data/sample/disk_usage_report.rb +0 -186
  72. data/sample/issue-119.rb +0 -94
  73. data/sample/losample.rb +0 -69
  74. data/sample/minimal-testcase.rb +0 -17
  75. data/sample/notify_wait.rb +0 -72
  76. data/sample/pg_statistics.rb +0 -294
  77. data/sample/replication_monitor.rb +0 -231
  78. data/sample/test_binary_values.rb +0 -33
  79. data/sample/wal_shipper.rb +0 -434
  80. 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
@@ -102,7 +102,7 @@ describe PG::TypeMapByClass do
102
102
 
103
103
  it "should allow mixed type conversions" do
104
104
  res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
105
- expect( res.values ).to eq([['5', '1.23', '[:TestSymbol]']])
105
+ expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
106
106
  expect( res.ftype(0) ).to eq(20)
107
107
  end
108
108
 
@@ -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'
@@ -116,7 +116,7 @@ describe PG::TypeMapByMriType do
116
116
 
117
117
  it "should allow mixed type conversions" do
118
118
  res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
119
- expect( res.values ).to eq([['5', '1.23', '[:TestSymbol]']])
119
+ expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
120
120
  expect( res.ftype(0) ).to eq(20)
121
121
  end
122
122
 
@@ -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'