pg 0.17.1 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +2407 -2
  4. data/History.rdoc +68 -0
  5. data/Manifest.txt +29 -1
  6. data/README-Windows.rdoc +15 -26
  7. data/README.rdoc +52 -2
  8. data/Rakefile +56 -18
  9. data/Rakefile.cross +77 -49
  10. data/ext/extconf.rb +33 -26
  11. data/ext/pg.c +142 -21
  12. data/ext/pg.h +242 -6
  13. data/ext/pg_binary_decoder.c +162 -0
  14. data/ext/pg_binary_encoder.c +162 -0
  15. data/ext/pg_coder.c +479 -0
  16. data/ext/pg_connection.c +858 -553
  17. data/ext/pg_copy_coder.c +561 -0
  18. data/ext/pg_errors.c +6 -0
  19. data/ext/pg_result.c +479 -128
  20. data/ext/pg_text_decoder.c +421 -0
  21. data/ext/pg_text_encoder.c +663 -0
  22. data/ext/pg_type_map.c +159 -0
  23. data/ext/pg_type_map_all_strings.c +116 -0
  24. data/ext/pg_type_map_by_class.c +239 -0
  25. data/ext/pg_type_map_by_column.c +312 -0
  26. data/ext/pg_type_map_by_mri_type.c +284 -0
  27. data/ext/pg_type_map_by_oid.c +355 -0
  28. data/ext/pg_type_map_in_ruby.c +299 -0
  29. data/ext/util.c +149 -0
  30. data/ext/util.h +65 -0
  31. data/lib/pg/basic_type_mapping.rb +399 -0
  32. data/lib/pg/coder.rb +83 -0
  33. data/lib/pg/connection.rb +81 -29
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +44 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/pg.rb +12 -2
  39. data/spec/{lib/helpers.rb → helpers.rb} +101 -39
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +516 -218
  42. data/spec/pg/result_spec.rb +216 -112
  43. data/spec/pg/type_map_by_class_spec.rb +138 -0
  44. data/spec/pg/type_map_by_column_spec.rb +222 -0
  45. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  46. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  47. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  48. data/spec/pg/type_map_spec.rb +22 -0
  49. data/spec/pg/type_spec.rb +697 -0
  50. data/spec/pg_spec.rb +24 -18
  51. data.tar.gz.sig +0 -0
  52. metadata +111 -45
  53. metadata.gz.sig +0 -0
@@ -1,49 +1,14 @@
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 'timeout'
17
7
  require 'socket'
18
8
  require 'pg'
19
9
 
20
10
  describe PG::Connection do
21
11
 
22
- before( :all ) do
23
- @conn = setup_testing_db( described_class.name )
24
- end
25
-
26
- before( :each ) do
27
- @conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
28
- if PG.respond_to?( :library_version )
29
- @conn.exec_params %Q{SET application_name TO '%s'} %
30
- [@conn.escape_string(example.description[0,60])]
31
- end
32
- end
33
-
34
- after( :each ) do
35
- @conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
36
- end
37
-
38
- after( :all ) do
39
- teardown_testing_db( @conn )
40
- end
41
-
42
-
43
- #
44
- # Examples
45
- #
46
-
47
12
  it "can create a connection option string from a Hash of options" do
48
13
  optstring = described_class.parse_connect_args(
49
14
  :host => 'pgsql.example.com',
@@ -51,54 +16,117 @@ describe PG::Connection do
51
16
  'sslmode' => 'require'
52
17
  )
53
18
 
54
- optstring.should be_a( String )
55
- optstring.should =~ /(^|\s)host='pgsql.example.com'/
56
- optstring.should =~ /(^|\s)dbname='db01'/
57
- optstring.should =~ /(^|\s)sslmode='require'/
19
+ expect( optstring ).to be_a( String )
20
+ expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
21
+ expect( optstring ).to match( /(^|\s)dbname='db01'/ )
22
+ expect( optstring ).to match( /(^|\s)sslmode='require'/ )
58
23
  end
59
24
 
60
25
  it "can create a connection option string from positional parameters" do
61
26
  optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
62
27
  'sales' )
63
28
 
64
- optstring.should be_a( String )
65
- optstring.should =~ /(^|\s)host='pgsql.example.com'/
66
- optstring.should =~ /(^|\s)dbname='sales'/
67
- optstring.should =~ /(^|\s)options='-c geqo=off'/
29
+ expect( optstring ).to be_a( String )
30
+ expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
31
+ expect( optstring ).to match( /(^|\s)dbname='sales'/ )
32
+ expect( optstring ).to match( /(^|\s)options='-c geqo=off'/ )
68
33
 
69
- optstring.should_not =~ /port=/
70
- optstring.should_not =~ /tty=/
34
+ expect( optstring ).to_not match( /port=/ )
35
+ expect( optstring ).to_not match( /tty=/ )
71
36
  end
72
37
 
73
38
  it "can create a connection option string from a mix of positional and hash parameters" do
74
39
  optstring = described_class.parse_connect_args( 'pgsql.example.com',
75
40
  :dbname => 'licensing', :user => 'jrandom' )
76
41
 
77
- optstring.should be_a( String )
78
- optstring.should =~ /(^|\s)host='pgsql.example.com'/
79
- optstring.should =~ /(^|\s)dbname='licensing'/
80
- optstring.should =~ /(^|\s)user='jrandom'/
42
+ expect( optstring ).to be_a( String )
43
+ expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
44
+ expect( optstring ).to match( /(^|\s)dbname='licensing'/ )
45
+ expect( optstring ).to match( /(^|\s)user='jrandom'/ )
46
+ end
47
+
48
+ it "can create a connection option string from an option string and a hash" do
49
+ optstring = described_class.parse_connect_args( 'dbname=original', :user => 'jrandom' )
50
+
51
+ expect( optstring ).to be_a( String )
52
+ expect( optstring ).to match( /(^|\s)dbname=original/ )
53
+ expect( optstring ).to match( /(^|\s)user='jrandom'/ )
81
54
  end
82
55
 
83
56
  it "escapes single quotes and backslashes in connection parameters" do
84
- described_class.parse_connect_args( "DB 'browser' \\" ).
85
- should =~ /host='DB \\'browser\\' \\\\'/
57
+ expect(
58
+ described_class.parse_connect_args( "DB 'browser' \\" )
59
+ ).to match( /host='DB \\'browser\\' \\\\'/ )
86
60
 
87
61
  end
88
62
 
63
+ let(:uri) { 'postgresql://user:pass@pgsql.example.com:222/db01?sslmode=require' }
64
+
65
+ it "can connect using a URI" do
66
+ string = described_class.parse_connect_args( uri )
67
+
68
+ expect( string ).to be_a( String )
69
+ expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
70
+ expect( string ).to match( %r{\?.*sslmode=require} )
71
+
72
+ string = described_class.parse_connect_args( URI.parse(uri) )
73
+
74
+ expect( string ).to be_a( String )
75
+ expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
76
+ expect( string ).to match( %r{\?.*sslmode=require} )
77
+ end
78
+
79
+ it "can create a connection URI from a URI and a hash" do
80
+ string = described_class.parse_connect_args( uri, :connect_timeout => 2 )
81
+
82
+ expect( string ).to be_a( String )
83
+ expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
84
+ expect( string ).to match( %r{\?.*sslmode=require} )
85
+ expect( string ).to match( %r{\?.*connect_timeout=2} )
86
+
87
+ string = described_class.parse_connect_args( uri,
88
+ :user => 'a',
89
+ :password => 'b',
90
+ :host => 'localhost',
91
+ :port => 555,
92
+ :dbname => 'x' )
93
+
94
+ expect( string ).to be_a( String )
95
+ expect( string ).to match( %r{^postgresql://\?} )
96
+ expect( string ).to match( %r{\?.*user=a} )
97
+ expect( string ).to match( %r{\?.*password=b} )
98
+ expect( string ).to match( %r{\?.*host=localhost} )
99
+ expect( string ).to match( %r{\?.*port=555} )
100
+ expect( string ).to match( %r{\?.*dbname=x} )
101
+ end
102
+
103
+ it "can create a connection URI with a non-standard domain socket directory" do
104
+ string = described_class.parse_connect_args( 'postgresql://%2Fvar%2Flib%2Fpostgresql/dbname' )
105
+
106
+ expect( string ).to be_a( String )
107
+ expect( string ).to match( %r{^postgresql://%2Fvar%2Flib%2Fpostgresql/dbname} )
108
+
109
+ string = described_class.
110
+ parse_connect_args( 'postgresql:///dbname', :host => '/var/lib/postgresql' )
111
+
112
+ expect( string ).to be_a( String )
113
+ expect( string ).to match( %r{^postgresql:///dbname\?} )
114
+ expect( string ).to match( %r{\?.*host=%2Fvar%2Flib%2Fpostgresql} )
115
+ end
116
+
89
117
  it "connects with defaults if no connection parameters are given" do
90
- described_class.parse_connect_args.should == ''
118
+ expect( described_class.parse_connect_args ).to eq( '' )
91
119
  end
92
120
 
93
121
  it "connects successfully with connection string" do
94
- tmpconn = described_class.connect(@conninfo)
95
- tmpconn.status.should == PG::CONNECTION_OK
122
+ tmpconn = described_class.connect( @conninfo )
123
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
96
124
  tmpconn.finish
97
125
  end
98
126
 
99
127
  it "connects using 7 arguments converted to strings" do
100
- tmpconn = described_class.connect('localhost', @port, nil, nil, :test, nil, nil)
101
- tmpconn.status.should == PG::CONNECTION_OK
128
+ tmpconn = described_class.connect( 'localhost', @port, nil, nil, :test, nil, nil )
129
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
102
130
  tmpconn.finish
103
131
  end
104
132
 
@@ -107,7 +135,7 @@ describe PG::Connection do
107
135
  :host => 'localhost',
108
136
  :port => @port,
109
137
  :dbname => :test)
110
- tmpconn.status.should == PG::CONNECTION_OK
138
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
111
139
  tmpconn.finish
112
140
  end
113
141
 
@@ -117,19 +145,24 @@ describe PG::Connection do
117
145
  :port => @port,
118
146
  :dbname => :test,
119
147
  :keepalives => 1)
120
- tmpconn.status.should == PG::CONNECTION_OK
148
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
121
149
  tmpconn.finish
122
150
  end
123
151
 
124
152
  it "raises an exception when connecting with an invalid number of arguments" do
125
153
  expect {
126
- described_class.connect( 1, 2, 3, 4, 5, 6, 7, 'extra' )
127
- }.to raise_error( ArgumentError, /extra positional parameter/i )
154
+ described_class.connect( 1, 2, 3, 4, 5, 6, 7, 'the-extra-arg' )
155
+ }.to raise_error do |error|
156
+ expect( error ).to be_an( ArgumentError )
157
+ expect( error.message ).to match( /extra positional parameter/i )
158
+ expect( error.message ).to match( /8/ )
159
+ expect( error.message ).to match( /the-extra-arg/ )
160
+ end
128
161
  end
129
162
 
130
163
  it "can connect asynchronously", :socket_io do
131
164
  tmpconn = described_class.connect_start( @conninfo )
132
- tmpconn.should be_a( described_class )
165
+ expect( tmpconn ).to be_a( described_class )
133
166
  socket = tmpconn.socket_io
134
167
  status = tmpconn.connect_poll
135
168
 
@@ -145,7 +178,7 @@ describe PG::Connection do
145
178
  status = tmpconn.connect_poll
146
179
  end
147
180
 
148
- tmpconn.status.should == PG::CONNECTION_OK
181
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
149
182
  tmpconn.finish
150
183
  end
151
184
 
@@ -153,7 +186,7 @@ describe PG::Connection do
153
186
  conn = nil
154
187
 
155
188
  described_class.connect_start(@conninfo) do |tmpconn|
156
- tmpconn.should be_a( described_class )
189
+ expect( tmpconn ).to be_a( described_class )
157
190
  conn = tmpconn
158
191
  socket = tmpconn.socket_io
159
192
  status = tmpconn.connect_poll
@@ -171,10 +204,10 @@ describe PG::Connection do
171
204
  status = tmpconn.connect_poll
172
205
  end
173
206
 
174
- tmpconn.status.should == PG::CONNECTION_OK
207
+ expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
175
208
  end
176
209
 
177
- conn.should be_finished()
210
+ expect( conn ).to be_finished()
178
211
  end
179
212
 
180
213
  it "raises proper error when sending fails" do
@@ -188,9 +221,21 @@ describe PG::Connection do
188
221
  res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
189
222
  WHERE usename IS NOT NULL])
190
223
  # there's still the global @conn, but should be no more
191
- res[0]['n'].should == '1'
224
+ expect( res[0]['n'] ).to eq( '1' )
192
225
  end
193
226
 
227
+ it "can retrieve it's connection parameters for the established connection" do
228
+ expect( @conn.db ).to eq( "test" )
229
+ expect( @conn.user ).to be_a_kind_of( String )
230
+ expect( @conn.pass ).to eq( "" )
231
+ expect( @conn.port ).to eq( 54321 )
232
+ expect( @conn.tty ).to eq( "" )
233
+ expect( @conn.options ).to eq( "" )
234
+ end
235
+ it "can retrieve it's connection parameters for the established connection",
236
+ skip: RUBY_PLATFORM=~/x64-mingw/ ? "host segfaults on Windows-x64" : false do
237
+ expect( @conn.host ).to eq( "localhost" )
238
+ end
194
239
 
195
240
  EXPECTED_TRACE_OUTPUT = %{
196
241
  To backend> Msg Q
@@ -248,7 +293,7 @@ describe PG::Connection do
248
293
  expected_trace_output.sub!( /From backend> "SELECT 1"/, 'From backend> "SELECT"' )
249
294
  end
250
295
 
251
- trace_data.should == expected_trace_output
296
+ expect( trace_data ).to eq( expected_trace_output )
252
297
  end
253
298
 
254
299
  it "allows a query to be cancelled" do
@@ -259,10 +304,12 @@ describe PG::Connection do
259
304
  if(tmpres.result_status != PG::PGRES_TUPLES_OK)
260
305
  error = true
261
306
  end
262
- error.should == true
307
+ expect( error ).to eq( true )
263
308
  end
264
309
 
265
310
  it "can stop a thread that runs a blocking query with async_exec" do
311
+ pending "this does not work on Rubinius" if RUBY_ENGINE=='rbx'
312
+
266
313
  start = Time.now
267
314
  t = Thread.new do
268
315
  @conn.async_exec( 'select pg_sleep(10)' )
@@ -271,10 +318,10 @@ describe PG::Connection do
271
318
 
272
319
  t.kill
273
320
  t.join
274
- (Time.now - start).should < 10
321
+ expect( (Time.now - start) ).to be < 10
275
322
  end
276
323
 
277
- it "should work together with signal handlers" do
324
+ it "should work together with signal handlers", :unix do
278
325
  signal_received = false
279
326
  trap 'USR1' do
280
327
  signal_received = true
@@ -285,7 +332,7 @@ describe PG::Connection do
285
332
  Process.kill("USR1", Process.pid)
286
333
  end
287
334
  @conn.exec("select pg_sleep(0.3)")
288
- signal_received.should be_true
335
+ expect( signal_received ).to be_truthy
289
336
 
290
337
  signal_received = false
291
338
  Thread.new do
@@ -293,7 +340,7 @@ describe PG::Connection do
293
340
  Process.kill("USR1", Process.pid)
294
341
  end
295
342
  @conn.async_exec("select pg_sleep(0.3)")
296
- signal_received.should be_true
343
+ expect( signal_received ).to be_truthy
297
344
  end
298
345
 
299
346
 
@@ -305,15 +352,19 @@ describe PG::Connection do
305
352
  res = nil
306
353
  @conn.exec( "CREATE TABLE pie ( flavor TEXT )" )
307
354
 
308
- expect {
309
- res = @conn.transaction do
310
- @conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
311
- raise "Oh noes! All pie is gone!"
312
- end
313
- }.to raise_exception( RuntimeError, /all pie is gone/i )
355
+ begin
356
+ expect {
357
+ res = @conn.transaction do
358
+ @conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
359
+ raise "Oh noes! All pie is gone!"
360
+ end
361
+ }.to raise_exception( RuntimeError, /all pie is gone/i )
314
362
 
315
- res = @conn.exec( "SELECT * FROM pie" )
316
- res.ntuples.should == 0
363
+ res = @conn.exec( "SELECT * FROM pie" )
364
+ expect( res.ntuples ).to eq( 0 )
365
+ ensure
366
+ @conn.exec( "DROP TABLE pie" )
367
+ end
317
368
  end
318
369
 
319
370
  it "returns the block result from Connection#transaction" do
@@ -323,7 +374,7 @@ describe PG::Connection do
323
374
  res = @conn.transaction do
324
375
  "transaction result"
325
376
  end
326
- res.should == "transaction result"
377
+ expect( res ).to eq( "transaction result" )
327
378
  end
328
379
 
329
380
  it "not read past the end of a large object" do
@@ -331,9 +382,9 @@ describe PG::Connection do
331
382
  oid = @conn.lo_create( 0 )
332
383
  fd = @conn.lo_open( oid, PG::INV_READ|PG::INV_WRITE )
333
384
  @conn.lo_write( fd, "foobar" )
334
- @conn.lo_read( fd, 10 ).should be_nil()
385
+ expect( @conn.lo_read( fd, 10 ) ).to be_nil()
335
386
  @conn.lo_lseek( fd, 0, PG::SEEK_SET )
336
- @conn.lo_read( fd, 10 ).should == 'foobar'
387
+ expect( @conn.lo_read( fd, 10 ) ).to eq( 'foobar' )
337
388
  end
338
389
  end
339
390
 
@@ -345,7 +396,7 @@ describe PG::Connection do
345
396
  @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
346
397
 
347
398
  res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
348
- res.values.should == [ ['Wally'], ['Sally'] ]
399
+ expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
349
400
  end
350
401
 
351
402
  it "supports explicitly calling #exec_params" do
@@ -355,9 +406,32 @@ describe PG::Connection do
355
406
  @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
356
407
 
357
408
  res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
358
- res.values.should == [ ['Wally'], ['Sally'] ]
409
+ expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
359
410
  end
360
411
 
412
+ it "supports hash form parameters for #exec_params" do
413
+ hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
414
+ hash_param_nil = { value: nil, type: 17, format: 1 }
415
+ res = @conn.exec_params( "SELECT $1, $2",
416
+ [ hash_param_bin, hash_param_nil ] )
417
+ expect( res.values ).to eq( [["\\x00ff", nil]] )
418
+ expect( result_typenames(res) ).to eq( ['bytea', 'bytea'] )
419
+ end
420
+
421
+ it "should work with arbitrary number of params" do
422
+ begin
423
+ 3.step( 12, 0.2 ) do |exp|
424
+ num_params = (2 ** exp).to_i
425
+ sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
426
+ params = num_params.times.to_a
427
+ res = @conn.exec_params( "SELECT #{sql}", params )
428
+ expect( res.nfields ).to eq( num_params )
429
+ expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
430
+ end
431
+ rescue PG::ProgramLimitExceeded
432
+ # Stop silently if the server complains about too many params
433
+ end
434
+ end
361
435
 
362
436
  it "can wait for NOTIFY events" do
363
437
  @conn.exec( 'ROLLBACK' )
@@ -373,7 +447,7 @@ describe PG::Connection do
373
447
  end
374
448
  end
375
449
 
376
- @conn.wait_for_notify( 10 ).should == 'woo'
450
+ expect( @conn.wait_for_notify( 10 ) ).to eq( 'woo' )
377
451
  @conn.exec( 'UNLISTEN woo' )
378
452
 
379
453
  t.join
@@ -395,8 +469,8 @@ describe PG::Connection do
395
469
 
396
470
  eventpid = event = nil
397
471
  @conn.wait_for_notify( 10 ) {|*args| event, eventpid = args }
398
- event.should == 'woo'
399
- eventpid.should be_an( Integer )
472
+ expect( event ).to eq( 'woo' )
473
+ expect( eventpid ).to be_an( Integer )
400
474
 
401
475
  @conn.exec( 'UNLISTEN woo' )
402
476
 
@@ -423,8 +497,8 @@ describe PG::Connection do
423
497
  channels << @conn.wait_for_notify( 2 )
424
498
  end
425
499
 
426
- channels.should have( 3 ).members
427
- channels.should include( 'woo', 'war', 'woz' )
500
+ expect( channels.size ).to eq( 3 )
501
+ expect( channels ).to include( 'woo', 'war', 'woz' )
428
502
 
429
503
  @conn.exec( 'UNLISTEN woz' )
430
504
  @conn.exec( 'UNLISTEN war' )
@@ -446,7 +520,7 @@ describe PG::Connection do
446
520
  # Cause the notification to buffer, but not be read yet
447
521
  @conn.exec( 'SELECT 1' )
448
522
 
449
- @conn.wait_for_notify( 10 ).should == 'woo'
523
+ expect( @conn.wait_for_notify( 10 ) ).to eq( 'woo' )
450
524
  @conn.exec( 'UNLISTEN woo' )
451
525
  end
452
526
 
@@ -457,41 +531,41 @@ describe PG::Connection do
457
531
  end
458
532
  st = Time.now
459
533
  @conn.send_query "SELECT pg_sleep(0.5); do $$ BEGIN RAISE NOTICE 'woohoo'; END; $$ LANGUAGE plpgsql;"
460
- @conn.wait_for_notify( 1 ).should be_nil
461
- notices.first.should_not be_nil
534
+ expect( @conn.wait_for_notify( 1 ) ).to be_nil
535
+ expect( notices.first ).to_not be_nil
462
536
  et = Time.now
463
- (et - notices.first[1]).should >= 0.4
464
- (et - st).should >= 0.9
465
- (et - st).should < 1.4
537
+ expect( (et - notices.first[1]) ).to be >= 0.4
538
+ expect( (et - st) ).to be >= 0.9
539
+ expect( (et - st) ).to be < 1.4
466
540
  end
467
541
 
468
542
  it "yields the result if block is given to exec" do
469
543
  rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
470
544
  values = []
471
- result.should be_kind_of( PG::Result )
472
- result.ntuples.should == 2
545
+ expect( result ).to be_kind_of( PG::Result )
546
+ expect( result.ntuples ).to eq( 2 )
473
547
  result.each do |tuple|
474
548
  values << tuple['a']
475
549
  end
476
550
  values
477
551
  end
478
552
 
479
- rval.should have( 2 ).members
480
- rval.should include( '5678', '1234' )
553
+ expect( rval.size ).to eq( 2 )
554
+ expect( rval ).to include( '5678', '1234' )
481
555
  end
482
556
 
483
557
  it "can process #copy_data output queries" do
484
558
  rows = []
485
559
  res2 = @conn.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
486
- res.result_status.should == PG::PGRES_COPY_OUT
487
- res.nfields.should == 1
560
+ expect( res.result_status ).to eq( PG::PGRES_COPY_OUT )
561
+ expect( res.nfields ).to eq( 1 )
488
562
  while row=@conn.get_copy_data
489
563
  rows << row
490
564
  end
491
565
  end
492
- rows.should == ["1\n", "2\n"]
493
- res2.result_status.should == PG::PGRES_COMMAND_OK
494
- verify_clean_exec_status
566
+ expect( rows ).to eq( ["1\n", "2\n"] )
567
+ expect( res2.result_status ).to eq( PG::PGRES_COMMAND_OK )
568
+ expect( @conn ).to still_be_usable
495
569
  end
496
570
 
497
571
  it "can handle incomplete #copy_data output queries" do
@@ -500,7 +574,7 @@ describe PG::Connection do
500
574
  @conn.get_copy_data
501
575
  end
502
576
  }.to raise_error(PG::NotAllCopyDataRetrieved, /Not all/)
503
- verify_clean_exec_status
577
+ expect( @conn ).to still_be_usable
504
578
  end
505
579
 
506
580
  it "can handle client errors in #copy_data for output" do
@@ -509,10 +583,10 @@ describe PG::Connection do
509
583
  raise "boom"
510
584
  end
511
585
  }.to raise_error(RuntimeError, "boom")
512
- verify_clean_exec_status
586
+ expect( @conn ).to still_be_usable
513
587
  end
514
588
 
515
- it "can handle server errors in #copy_data for output" do
589
+ it "can handle server errors in #copy_data for output", :postgresql_90 do
516
590
  @conn.exec "ROLLBACK"
517
591
  @conn.transaction do
518
592
  @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
@@ -523,23 +597,23 @@ describe PG::Connection do
523
597
  end
524
598
  }.to raise_error(PG::Error, /test-error/)
525
599
  end
526
- verify_clean_exec_status
600
+ expect( @conn ).to still_be_usable
527
601
  end
528
602
 
529
603
  it "can process #copy_data input queries" do
530
604
  @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
531
605
  res2 = @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
532
- res.result_status.should == PG::PGRES_COPY_IN
533
- res.nfields.should == 1
606
+ expect( res.result_status ).to eq( PG::PGRES_COPY_IN )
607
+ expect( res.nfields ).to eq( 1 )
534
608
  @conn.put_copy_data "1\n"
535
609
  @conn.put_copy_data "2\n"
536
610
  end
537
- res2.result_status.should == PG::PGRES_COMMAND_OK
611
+ expect( res2.result_status ).to eq( PG::PGRES_COMMAND_OK )
538
612
 
539
- verify_clean_exec_status
613
+ expect( @conn ).to still_be_usable
540
614
 
541
615
  res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
542
- res.values.should == [["1"], ["2"]]
616
+ expect( res.values ).to eq( [["1"], ["2"]] )
543
617
  end
544
618
 
545
619
  it "can handle client errors in #copy_data for input" do
@@ -552,7 +626,8 @@ describe PG::Connection do
552
626
  end
553
627
  }.to raise_error(RuntimeError, "boom")
554
628
  end
555
- verify_clean_exec_status
629
+
630
+ expect( @conn ).to still_be_usable
556
631
  end
557
632
 
558
633
  it "can handle server errors in #copy_data for input" do
@@ -565,7 +640,7 @@ describe PG::Connection do
565
640
  end
566
641
  }.to raise_error(PG::Error, /invalid input syntax for integer/)
567
642
  end
568
- verify_clean_exec_status
643
+ expect( @conn ).to still_be_usable
569
644
  end
570
645
 
571
646
  it "should raise an error for non copy statements in #copy_data" do
@@ -573,7 +648,7 @@ describe PG::Connection do
573
648
  @conn.copy_data( "SELECT 1" ){}
574
649
  }.to raise_error(ArgumentError, /no COPY/)
575
650
 
576
- verify_clean_exec_status
651
+ expect( @conn ).to still_be_usable
577
652
  end
578
653
 
579
654
  it "correctly finishes COPY queries passed to #async_exec" do
@@ -589,8 +664,8 @@ describe PG::Connection do
589
664
  results << data if data
590
665
  end until data.nil?
591
666
 
592
- results.should have( 2 ).members
593
- results.should include( "1\n", "2\n" )
667
+ expect( results.size ).to eq( 2 )
668
+ expect( results ).to include( "1\n", "2\n" )
594
669
  end
595
670
 
596
671
 
@@ -602,10 +677,10 @@ describe PG::Connection do
602
677
  end
603
678
 
604
679
  sleep 0.5
605
- t.should be_alive()
680
+ expect( t ).to be_alive()
606
681
  @conn.cancel
607
682
  t.join
608
- (Time.now - start).should < 3
683
+ expect( (Time.now - start) ).to be < 3
609
684
  end
610
685
 
611
686
  it "described_class#block should allow a timeout" do
@@ -615,13 +690,49 @@ describe PG::Connection do
615
690
  @conn.block( 0.1 )
616
691
  finish = Time.now
617
692
 
618
- (finish - start).should be_within( 0.05 ).of( 0.1 )
693
+ expect( (finish - start) ).to be_within( 0.05 ).of( 0.1 )
619
694
  end
620
695
 
621
696
 
622
697
  it "can encrypt a string given a password and username" do
623
- described_class.encrypt_password("postgres", "postgres").
624
- should =~ /\S+/
698
+ expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
699
+ end
700
+
701
+ it "can return the default connection options" do
702
+ expect( described_class.conndefaults ).to be_a( Array )
703
+ expect( described_class.conndefaults ).to all( be_a(Hash) )
704
+ expect( described_class.conndefaults[0] ).to include( :keyword, :label, :dispchar, :dispsize )
705
+ expect( @conn.conndefaults ).to eq( described_class.conndefaults )
706
+ end
707
+
708
+ it "can return the default connection options as a Hash" do
709
+ expect( described_class.conndefaults_hash ).to be_a( Hash )
710
+ expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
711
+ expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
712
+ expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
713
+ end
714
+
715
+ it "can return the connection's connection options", :postgresql_93 do
716
+ expect( @conn.conninfo ).to be_a( Array )
717
+ expect( @conn.conninfo ).to all( be_a(Hash) )
718
+ expect( @conn.conninfo[0] ).to include( :keyword, :label, :dispchar, :dispsize )
719
+ end
720
+
721
+
722
+ it "can return the connection's connection options as a Hash", :postgresql_93 do
723
+ expect( @conn.conninfo_hash ).to be_a( Hash )
724
+ expect( @conn.conninfo_hash ).to include( :user, :password, :connect_timeout, :dbname, :host )
725
+ expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
726
+ end
727
+
728
+
729
+ it "honors the connect_timeout connection parameter", :postgresql_93 do
730
+ conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
731
+ begin
732
+ expect( conn.conninfo_hash[:connect_timeout] ).to eq( "11" )
733
+ ensure
734
+ conn.finish
735
+ end
625
736
  end
626
737
 
627
738
 
@@ -641,15 +752,15 @@ describe PG::Connection do
641
752
 
642
753
  it "allows fetching a column of values from a result by column number" do
643
754
  res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
644
- res.column_values( 0 ).should == %w[1 2 3]
645
- res.column_values( 1 ).should == %w[2 3 4]
755
+ expect( res.column_values( 0 ) ).to eq( %w[1 2 3] )
756
+ expect( res.column_values( 1 ) ).to eq( %w[2 3 4] )
646
757
  end
647
758
 
648
759
 
649
760
  it "allows fetching a column of values from a result by field name" do
650
761
  res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
651
- res.field_values( 'column1' ).should == %w[1 2 3]
652
- res.field_values( 'column2' ).should == %w[2 3 4]
762
+ expect( res.field_values( 'column1' ) ).to eq( %w[1 2 3] )
763
+ expect( res.field_values( 'column2' ) ).to eq( %w[2 3 4] )
653
764
  end
654
765
 
655
766
 
@@ -680,13 +791,13 @@ describe PG::Connection do
680
791
  it "can connect asynchronously", :socket_io do
681
792
  serv = TCPServer.new( '127.0.0.1', 54320 )
682
793
  conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
683
- [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].should include conn.connect_poll
794
+ expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
684
795
  select( nil, [conn.socket_io], nil, 0.2 )
685
796
  serv.close
686
797
  if conn.connect_poll == PG::PGRES_POLLING_READING
687
798
  select( [conn.socket_io], nil, nil, 0.2 )
688
799
  end
689
- conn.connect_poll.should == PG::PGRES_POLLING_FAILED
800
+ expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
690
801
  end
691
802
 
692
803
  it "discards previous results (if any) before waiting on an #async_exec"
@@ -696,7 +807,7 @@ describe PG::Connection do
696
807
  @conn.async_exec( "select 47 as one" ) do |pg_res|
697
808
  result = pg_res[0]
698
809
  end
699
- result.should == { 'one' => '47' }
810
+ expect( result ).to eq( { 'one' => '47' } )
700
811
  end
701
812
 
702
813
  it "raises a rescue-able error if #finish is called twice", :without_transaction do
@@ -710,7 +821,7 @@ describe PG::Connection do
710
821
  conn = PG.connect( @conninfo )
711
822
  io = conn.socket_io
712
823
  conn.finish
713
- io.should be_closed()
824
+ expect( io ).to be_closed()
714
825
  expect { conn.socket_io }.to raise_error( PG::ConnectionBad, /connection is closed/i )
715
826
  end
716
827
 
@@ -718,8 +829,8 @@ describe PG::Connection do
718
829
  conn = PG.connect( @conninfo )
719
830
  io = conn.socket_io
720
831
  conn.reset
721
- io.should be_closed()
722
- conn.socket_io.should_not equal( io )
832
+ expect( io ).to be_closed()
833
+ expect( conn.socket_io ).to_not equal( io )
723
834
  conn.finish
724
835
  end
725
836
 
@@ -742,7 +853,11 @@ describe PG::Connection do
742
853
 
743
854
  it "sets the fallback_application_name on new connections" do
744
855
  conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
745
- connection_string_should_contain_application_name(conn_string, $0)
856
+
857
+ conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
858
+ expect( conn_name ).to include( $0[0..10] )
859
+ expect( conn_name ).to include( $0[-10..-1] )
860
+ expect( conn_name.length ).to be <= 64
746
861
  end
747
862
 
748
863
  it "sets a shortened fallback_application_name on new connections" do
@@ -750,7 +865,10 @@ describe PG::Connection do
750
865
  begin
751
866
  $0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
752
867
  conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
753
- connection_string_should_contain_application_name(conn_string, $0)
868
+ conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
869
+ expect( conn_name ).to include( $0[0..10] )
870
+ expect( conn_name ).to include( $0[-10..-1] )
871
+ expect( conn_name.length ).to be <= 64
754
872
  ensure
755
873
  $0 = old_0
756
874
  end
@@ -772,9 +890,9 @@ describe PG::Connection do
772
890
  end
773
891
  @conn.exec( 'UNLISTEN knees' )
774
892
 
775
- event.should == 'knees'
776
- pid.should be_a_kind_of( Integer )
777
- msg.should == 'skirt and boots'
893
+ expect( event ).to eq( 'knees' )
894
+ expect( pid ).to be_a_kind_of( Integer )
895
+ expect( msg ).to eq( 'skirt and boots' )
778
896
  end
779
897
 
780
898
  it "accepts nil as the timeout in #wait_for_notify " do
@@ -791,8 +909,8 @@ describe PG::Connection do
791
909
  end
792
910
  @conn.exec( 'UNLISTEN knees' )
793
911
 
794
- event.should == 'knees'
795
- pid.should be_a_kind_of( Integer )
912
+ expect( event ).to eq( 'knees' )
913
+ expect( pid ).to be_a_kind_of( Integer )
796
914
  end
797
915
 
798
916
  it "sends nil as the payload if the notification wasn't given one" do
@@ -809,7 +927,7 @@ describe PG::Connection do
809
927
  end
810
928
  @conn.exec( 'UNLISTEN knees' )
811
929
 
812
- payload.should be_nil()
930
+ expect( payload ).to be_nil()
813
931
  end
814
932
 
815
933
  it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
@@ -828,9 +946,9 @@ describe PG::Connection do
828
946
  end
829
947
  @conn.exec( 'UNLISTEN knees' )
830
948
 
831
- event.should == 'knees'
832
- pid.should be_a_kind_of( Integer )
833
- msg.should be_nil()
949
+ expect( event ).to eq( 'knees' )
950
+ expect( pid ).to be_a_kind_of( Integer )
951
+ expect( msg ).to be_nil()
834
952
  end
835
953
 
836
954
  it "calls the block supplied to wait_for_notify with the notify payload if it " +
@@ -849,7 +967,7 @@ describe PG::Connection do
849
967
  end
850
968
  @conn.exec( 'UNLISTEN knees' )
851
969
 
852
- notification_received.should be_true()
970
+ expect( notification_received ).to be_truthy()
853
971
  end
854
972
 
855
973
  it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
@@ -868,9 +986,9 @@ describe PG::Connection do
868
986
  end
869
987
  @conn.exec( 'UNLISTEN knees' )
870
988
 
871
- event.should == 'knees'
872
- pid.should be_a_kind_of( Integer )
873
- msg.should == 'skirt and boots'
989
+ expect( event ).to eq( 'knees' )
990
+ expect( pid ).to be_a_kind_of( Integer )
991
+ expect( msg ).to eq( 'skirt and boots' )
874
992
  end
875
993
 
876
994
  end
@@ -879,12 +997,12 @@ describe PG::Connection do
879
997
 
880
998
  it "pings successfully with connection string" do
881
999
  ping = described_class.ping(@conninfo)
882
- ping.should == PG::PQPING_OK
1000
+ expect( ping ).to eq( PG::PQPING_OK )
883
1001
  end
884
1002
 
885
1003
  it "pings using 7 arguments converted to strings" do
886
1004
  ping = described_class.ping('localhost', @port, nil, nil, :test, nil, nil)
887
- ping.should == PG::PQPING_OK
1005
+ expect( ping ).to eq( PG::PQPING_OK )
888
1006
  end
889
1007
 
890
1008
  it "pings using a hash of connection parameters" do
@@ -892,7 +1010,7 @@ describe PG::Connection do
892
1010
  :host => 'localhost',
893
1011
  :port => @port,
894
1012
  :dbname => :test)
895
- ping.should == PG::PQPING_OK
1013
+ expect( ping ).to eq( PG::PQPING_OK )
896
1014
  end
897
1015
 
898
1016
  it "returns correct response when ping connection cannot be established" do
@@ -900,12 +1018,12 @@ describe PG::Connection do
900
1018
  :host => 'localhost',
901
1019
  :port => 9999,
902
1020
  :dbname => :test)
903
- ping.should == PG::PQPING_NO_RESPONSE
1021
+ expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
904
1022
  end
905
1023
 
906
1024
  it "returns correct response when ping connection arguments are wrong" do
907
1025
  ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
908
- ping.should == PG::PQPING_NO_ATTEMPT
1026
+ expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
909
1027
  end
910
1028
 
911
1029
 
@@ -930,15 +1048,15 @@ describe PG::Connection do
930
1048
  res = @conn.get_result or break
931
1049
  results << res
932
1050
  end
933
- results.length.should == 11
1051
+ expect( results.length ).to eq( 11 )
934
1052
  results[0..-2].each do |res|
935
- res.result_status.should == PG::PGRES_SINGLE_TUPLE
1053
+ expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
936
1054
  values = res.field_values('generate_series')
937
- values.length.should == 1
938
- values.first.to_i.should > 0
1055
+ expect( values.length ).to eq( 1 )
1056
+ expect( values.first.to_i ).to be > 0
939
1057
  end
940
- results.last.result_status.should == PG::PGRES_TUPLES_OK
941
- results.last.ntuples.should == 0
1058
+ expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
1059
+ expect( results.last.ntuples ).to eq( 0 )
942
1060
  end
943
1061
 
944
1062
  it "should receive rows before entire query is finished" do
@@ -952,8 +1070,8 @@ describe PG::Connection do
952
1070
  res.check
953
1071
  first_row_time = Time.now unless first_row_time
954
1072
  end
955
- (Time.now - start_time).should >= 1.0
956
- (first_row_time - start_time).should < 1.0
1073
+ expect( (Time.now - start_time) ).to be >= 1.0
1074
+ expect( (first_row_time - start_time) ).to be < 1.0
957
1075
  end
958
1076
 
959
1077
  it "should receive rows before entire query fails" do
@@ -969,8 +1087,8 @@ describe PG::Connection do
969
1087
  first_result ||= res
970
1088
  end
971
1089
  end.to raise_error(PG::Error)
972
- first_result.kind_of?(PG::Result).should be_true
973
- first_result.result_status.should == PG::PGRES_SINGLE_TUPLE
1090
+ expect( first_result.kind_of?(PG::Result) ).to be_truthy
1091
+ expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
974
1092
  end
975
1093
  end
976
1094
  end
@@ -985,8 +1103,8 @@ describe PG::Connection do
985
1103
  res = conn.exec("VALUES ('fantasia')", [], 0)
986
1104
  out_string = res[0]['column1']
987
1105
  end
988
- out_string.should == 'fantasia'
989
- out_string.encoding.should == Encoding::ISO8859_1
1106
+ expect( out_string ).to eq( 'fantasia' )
1107
+ expect( out_string.encoding ).to eq( Encoding::ISO8859_1 )
990
1108
  end
991
1109
 
992
1110
  it "should return results in the same encoding as the client (utf-8)" do
@@ -996,8 +1114,8 @@ describe PG::Connection do
996
1114
  res = conn.exec("VALUES ('世界線航跡蔵')", [], 0)
997
1115
  out_string = res[0]['column1']
998
1116
  end
999
- out_string.should == '世界線航跡蔵'
1000
- out_string.encoding.should == Encoding::UTF_8
1117
+ expect( out_string ).to eq( '世界線航跡蔵' )
1118
+ expect( out_string.encoding ).to eq( Encoding::UTF_8 )
1001
1119
  end
1002
1120
 
1003
1121
  it "should return results in the same encoding as the client (EUC-JP)" do
@@ -1008,8 +1126,8 @@ describe PG::Connection do
1008
1126
  res = conn.exec(stmt, [], 0)
1009
1127
  out_string = res[0]['column1']
1010
1128
  end
1011
- out_string.should == '世界線航跡蔵'.encode('EUC-JP')
1012
- out_string.encoding.should == Encoding::EUC_JP
1129
+ expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
1130
+ expect( out_string.encoding ).to eq( Encoding::EUC_JP )
1013
1131
  end
1014
1132
 
1015
1133
  it "returns the results in the correct encoding even if the client_encoding has " +
@@ -1022,79 +1140,82 @@ describe PG::Connection do
1022
1140
  conn.internal_encoding = 'utf-8'
1023
1141
  out_string = res[0]['column1']
1024
1142
  end
1025
- out_string.should == '世界線航跡蔵'.encode('EUC-JP')
1026
- out_string.encoding.should == Encoding::EUC_JP
1143
+ expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
1144
+ expect( out_string.encoding ).to eq( Encoding::EUC_JP )
1027
1145
  end
1028
1146
 
1029
1147
  it "the connection should return ASCII-8BIT when it's set to SQL_ASCII" do
1030
1148
  @conn.exec "SET client_encoding TO SQL_ASCII"
1031
- @conn.internal_encoding.should == Encoding::ASCII_8BIT
1032
- end
1033
-
1034
- it "works around the unsupported JOHAB encoding by returning stuff in 'ASCII_8BIT'" do
1035
- pending "figuring out how to create a string in the JOHAB encoding" do
1036
- out_string = nil
1037
- @conn.transaction do |conn|
1038
- conn.exec( "set client_encoding = 'JOHAB';" )
1039
- stmt = "VALUES ('foo')".encode('JOHAB')
1040
- res = conn.exec( stmt, [], 0 )
1041
- out_string = res[0]['column1']
1042
- end
1043
- out_string.should == 'foo'.encode( Encoding::ASCII_8BIT )
1044
- out_string.encoding.should == Encoding::ASCII_8BIT
1045
- end
1149
+ expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
1046
1150
  end
1047
1151
 
1048
1152
  it "uses the client encoding for escaped string" do
1049
1153
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1050
1154
  @conn.set_client_encoding( "euc_jp" )
1051
1155
  escaped = @conn.escape( original )
1052
- escaped.encoding.should == Encoding::EUC_JP
1053
- escaped.should == "string to"
1156
+ expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1157
+ expect( escaped ).to eq( "string to" )
1054
1158
  end
1055
1159
 
1056
1160
  it "uses the client encoding for escaped literal", :postgresql_90 do
1057
1161
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1058
1162
  @conn.set_client_encoding( "euc_jp" )
1059
1163
  escaped = @conn.escape_literal( original )
1060
- escaped.encoding.should == Encoding::EUC_JP
1061
- escaped.should == "'string to'"
1164
+ expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1165
+ expect( escaped ).to eq( "'string to'" )
1062
1166
  end
1063
1167
 
1064
1168
  it "uses the client encoding for escaped identifier", :postgresql_90 do
1065
1169
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1066
1170
  @conn.set_client_encoding( "euc_jp" )
1067
1171
  escaped = @conn.escape_identifier( original )
1068
- escaped.encoding.should == Encoding::EUC_JP
1069
- escaped.should == "\"string to\""
1172
+ expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1173
+ expect( escaped ).to eq( "\"string to\"" )
1070
1174
  end
1071
1175
 
1072
1176
  it "uses the client encoding for quote_ident" do
1073
1177
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1074
1178
  @conn.set_client_encoding( "euc_jp" )
1075
1179
  escaped = @conn.quote_ident( original )
1076
- escaped.encoding.should == Encoding::EUC_JP
1077
- escaped.should == "\"string to\""
1180
+ expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1181
+ expect( escaped ).to eq( "\"string to\"" )
1078
1182
  end
1079
1183
 
1080
1184
  it "uses the previous string encoding for escaped string" do
1081
1185
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1082
1186
  @conn.set_client_encoding( "euc_jp" )
1083
1187
  escaped = described_class.escape( original )
1084
- escaped.encoding.should == Encoding::ISO8859_1
1085
- escaped.should == "string to"
1188
+ expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1189
+ expect( escaped ).to eq( "string to" )
1086
1190
  end
1087
1191
 
1088
1192
  it "uses the previous string encoding for quote_ident" do
1089
1193
  original = "string to\0 escape".force_encoding( "iso8859-1" )
1090
1194
  @conn.set_client_encoding( "euc_jp" )
1091
1195
  escaped = described_class.quote_ident( original )
1092
- escaped.encoding.should == Encoding::ISO8859_1
1093
- escaped.should == "\"string to\""
1196
+ expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1197
+ expect( escaped ).to eq( "\"string to\"" )
1094
1198
  end
1199
+ end
1095
1200
 
1201
+ it "can quote bigger strings with quote_ident" do
1202
+ original = "'01234567\"" * 100
1203
+ escaped = described_class.quote_ident( original + "\0afterzero" )
1204
+ expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
1096
1205
  end
1097
1206
 
1207
+ it "can quote Arrays with quote_ident" do
1208
+ original = "'01234567\""
1209
+ escaped = described_class.quote_ident( [original]*3 )
1210
+ expected = ["\"" + original.gsub("\"", "\"\"") + "\""] * 3
1211
+ expect( escaped ).to eq( expected.join(".") )
1212
+ end
1213
+
1214
+ it "will raise a TypeError for invalid arguments to quote_ident" do
1215
+ expect{ described_class.quote_ident( nil ) }.to raise_error(TypeError)
1216
+ expect{ described_class.quote_ident( [nil] ) }.to raise_error(TypeError)
1217
+ expect{ described_class.quote_ident( [['a']] ) }.to raise_error(TypeError)
1218
+ end
1098
1219
 
1099
1220
  describe "Ruby 1.9.x default_internal encoding" do
1100
1221
 
@@ -1110,10 +1231,11 @@ describe PG::Connection do
1110
1231
  Encoding.default_internal = Encoding::UTF_8
1111
1232
 
1112
1233
  conn = PG.connect( @conninfo )
1113
- conn.internal_encoding.should == Encoding::UTF_8
1234
+ expect( conn.internal_encoding ).to eq( Encoding::UTF_8 )
1114
1235
  res = conn.exec( "SELECT foo FROM defaultinternaltest" )
1115
- res[0]['foo'].encoding.should == Encoding::UTF_8
1236
+ expect( res[0]['foo'].encoding ).to eq( Encoding::UTF_8 )
1116
1237
  ensure
1238
+ conn.exec( "DROP TABLE defaultinternaltest" )
1117
1239
  conn.finish if conn
1118
1240
  Encoding.default_internal = prev_encoding
1119
1241
  end
@@ -1126,7 +1248,7 @@ describe PG::Connection do
1126
1248
 
1127
1249
  @conn.set_default_encoding
1128
1250
 
1129
- @conn.internal_encoding.should == Encoding::KOI8_R
1251
+ expect( @conn.internal_encoding ).to eq( Encoding::KOI8_R )
1130
1252
  ensure
1131
1253
  Encoding.default_internal = prev_encoding
1132
1254
  end
@@ -1147,7 +1269,7 @@ describe PG::Connection do
1147
1269
  query = "INSERT INTO foo VALUES ('Côte d'Ivoire')".encode( 'iso-8859-15' )
1148
1270
  conn.exec( query )
1149
1271
  rescue => err
1150
- err.message.encoding.should == Encoding::ISO8859_15
1272
+ expect( err.message.encoding ).to eq( Encoding::ISO8859_15 )
1151
1273
  else
1152
1274
  fail "No exception raised?!"
1153
1275
  end
@@ -1156,6 +1278,21 @@ describe PG::Connection do
1156
1278
  conn.finish if conn
1157
1279
  end
1158
1280
 
1281
+ it "handles clearing result in or after set_notice_receiver", :postgresql_90 do
1282
+ r = nil
1283
+ @conn.set_notice_receiver do |result|
1284
+ r = result
1285
+ expect( r.cleared? ).to eq(false)
1286
+ end
1287
+ @conn.exec "do $$ BEGIN RAISE NOTICE 'foo'; END; $$ LANGUAGE plpgsql;"
1288
+ sleep 0.2
1289
+ expect( r ).to be_a( PG::Result )
1290
+ expect( r.cleared? ).to eq(true)
1291
+ expect( r.autoclear? ).to eq(true)
1292
+ r.clear
1293
+ @conn.set_notice_receiver
1294
+ end
1295
+
1159
1296
  it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
1160
1297
  [:receiver, :processor].each do |kind|
1161
1298
  notices = []
@@ -1174,10 +1311,10 @@ describe PG::Connection do
1174
1311
  @conn.exec "do $$ BEGIN RAISE NOTICE '世界線航跡蔵'; END; $$ LANGUAGE plpgsql;"
1175
1312
  end
1176
1313
 
1177
- notices.length.should == 3
1314
+ expect( notices.length ).to eq( 3 )
1178
1315
  notices.each do |notice|
1179
- notice.should =~ /^NOTICE:.*世界線航跡蔵/
1180
- notice.encoding.should == Encoding::UTF_8
1316
+ expect( notice ).to match( /^NOTICE:.*世界線航跡蔵/ )
1317
+ expect( notice.encoding ).to eq( Encoding::UTF_8 )
1181
1318
  end
1182
1319
  @conn.set_notice_receiver
1183
1320
  @conn.set_notice_processor
@@ -1195,10 +1332,10 @@ describe PG::Connection do
1195
1332
  end
1196
1333
  @conn.exec( 'UNLISTEN "Möhre"' )
1197
1334
 
1198
- event.should == "Möhre"
1199
- event.encoding.should == Encoding::UTF_8
1200
- msg.should == '世界線航跡蔵'
1201
- msg.encoding.should == Encoding::UTF_8
1335
+ expect( event ).to eq( "Möhre" )
1336
+ expect( event.encoding ).to eq( Encoding::UTF_8 )
1337
+ expect( msg ).to eq( '世界線航跡蔵' )
1338
+ expect( msg.encoding ).to eq( Encoding::UTF_8 )
1202
1339
  end
1203
1340
 
1204
1341
  it "returns properly encoded text from notifies", :postgresql_90 do
@@ -1209,11 +1346,11 @@ describe PG::Connection do
1209
1346
  @conn.exec( 'UNLISTEN "Möhre"' )
1210
1347
 
1211
1348
  notification = @conn.notifies
1212
- notification[:relname].should == "Möhre"
1213
- notification[:relname].encoding.should == Encoding::UTF_8
1214
- notification[:extra].should == '世界線航跡蔵'
1215
- notification[:extra].encoding.should == Encoding::UTF_8
1216
- notification[:be_pid].should > 0
1349
+ expect( notification[:relname] ).to eq( "Möhre" )
1350
+ expect( notification[:relname].encoding ).to eq( Encoding::UTF_8 )
1351
+ expect( notification[:extra] ).to eq( '世界線航跡蔵' )
1352
+ expect( notification[:extra].encoding ).to eq( Encoding::UTF_8 )
1353
+ expect( notification[:be_pid] ).to be > 0
1217
1354
  end
1218
1355
  end
1219
1356
 
@@ -1224,7 +1361,7 @@ describe PG::Connection do
1224
1361
  end
1225
1362
 
1226
1363
  sleep 0.5
1227
- t.should be_alive()
1364
+ expect( t ).to be_alive()
1228
1365
  t.join
1229
1366
  end
1230
1367
 
@@ -1238,9 +1375,170 @@ describe PG::Connection do
1238
1375
  end
1239
1376
 
1240
1377
  sleep 0.5
1241
- t.should be_alive()
1378
+ expect( t ).to be_alive()
1242
1379
  serv.close
1243
1380
  t.join
1244
1381
  end
1245
1382
  end
1383
+
1384
+ describe "type casting" do
1385
+ it "should raise an error on invalid param mapping" do
1386
+ expect{
1387
+ @conn.exec_params( "SELECT 1", [], nil, :invalid )
1388
+ }.to raise_error(TypeError)
1389
+ end
1390
+
1391
+ it "should return nil if no type mapping is set" do
1392
+ expect( @conn.type_map_for_queries ).to be_kind_of(PG::TypeMapAllStrings)
1393
+ expect( @conn.type_map_for_results ).to be_kind_of(PG::TypeMapAllStrings)
1394
+ end
1395
+
1396
+ it "shouldn't type map params unless requested" do
1397
+ expect{
1398
+ @conn.exec_params( "SELECT $1", [5] )
1399
+ }.to raise_error(PG::IndeterminateDatatype)
1400
+ end
1401
+
1402
+ it "should raise an error on invalid encoder to put_copy_data" do
1403
+ expect{
1404
+ @conn.put_copy_data [1], :invalid
1405
+ }.to raise_error(TypeError)
1406
+ end
1407
+
1408
+ it "can type cast parameters to put_copy_data with explicit encoder" do
1409
+ tm = PG::TypeMapByColumn.new [nil]
1410
+ row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
1411
+
1412
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1413
+ res2 = @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1414
+ @conn.put_copy_data [1], row_encoder
1415
+ @conn.put_copy_data ["2"], row_encoder
1416
+ end
1417
+
1418
+ res2 = @conn.copy_data( "COPY copytable FROM STDOUT", row_encoder ) do |res|
1419
+ @conn.put_copy_data [3]
1420
+ @conn.put_copy_data ["4"]
1421
+ end
1422
+
1423
+ res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
1424
+ expect( res.values ).to eq( [["1"], ["2"], ["3"], ["4"]] )
1425
+ end
1426
+
1427
+ context "with default query type map" do
1428
+ before :each do
1429
+ @conn2 = described_class.new(@conninfo)
1430
+ tm = PG::TypeMapByClass.new
1431
+ tm[Integer] = PG::TextEncoder::Integer.new oid: 20
1432
+ @conn2.type_map_for_queries = tm
1433
+
1434
+ row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
1435
+ @conn2.encoder_for_put_copy_data = row_encoder
1436
+ end
1437
+ after :each do
1438
+ @conn2.close
1439
+ end
1440
+
1441
+ it "should respect a type mapping for params and it's OID and format code" do
1442
+ res = @conn2.exec_params( "SELECT $1", [5] )
1443
+ expect( res.values ).to eq( [["5"]] )
1444
+ expect( res.ftype(0) ).to eq( 20 )
1445
+ end
1446
+
1447
+ it "should return the current type mapping" do
1448
+ expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByClass)
1449
+ end
1450
+
1451
+ it "should work with arbitrary number of params in conjunction with type casting" do
1452
+ begin
1453
+ 3.step( 12, 0.2 ) do |exp|
1454
+ num_params = (2 ** exp).to_i
1455
+ sql = num_params.times.map{|n| "$#{n+1}" }.join(",")
1456
+ params = num_params.times.to_a
1457
+ res = @conn2.exec_params( "SELECT #{sql}", params )
1458
+ expect( res.nfields ).to eq( num_params )
1459
+ expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
1460
+ end
1461
+ rescue PG::ProgramLimitExceeded
1462
+ # Stop silently as soon the server complains about too many params
1463
+ end
1464
+ end
1465
+
1466
+ it "can process #copy_data input queries with row encoder" do
1467
+ @conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1468
+ res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1469
+ @conn2.put_copy_data [1]
1470
+ @conn2.put_copy_data ["2"]
1471
+ end
1472
+
1473
+ res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
1474
+ expect( res.values ).to eq( [["1"], ["2"]] )
1475
+ end
1476
+ end
1477
+
1478
+ context "with default result type map" do
1479
+ before :each do
1480
+ @conn2 = described_class.new(@conninfo)
1481
+ tm = PG::TypeMapByOid.new
1482
+ tm.add_coder PG::TextDecoder::Integer.new oid: 23, format: 0
1483
+ @conn2.type_map_for_results = tm
1484
+
1485
+ row_decoder = PG::TextDecoder::CopyRow.new
1486
+ @conn2.decoder_for_get_copy_data = row_decoder
1487
+ end
1488
+ after :each do
1489
+ @conn2.close
1490
+ end
1491
+
1492
+ it "should respect a type mapping for result" do
1493
+ res = @conn2.exec_params( "SELECT $1::INT", ["5"] )
1494
+ expect( res.values ).to eq( [[5]] )
1495
+ end
1496
+
1497
+ it "should return the current type mapping" do
1498
+ expect( @conn2.type_map_for_results ).to be_kind_of(PG::TypeMapByOid)
1499
+ end
1500
+
1501
+ it "should work with arbitrary number of params in conjunction with type casting" do
1502
+ begin
1503
+ 3.step( 12, 0.2 ) do |exp|
1504
+ num_params = (2 ** exp).to_i
1505
+ sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
1506
+ params = num_params.times.to_a
1507
+ res = @conn2.exec_params( "SELECT #{sql}", params )
1508
+ expect( res.nfields ).to eq( num_params )
1509
+ expect( res.values ).to eq( [num_params.times.to_a] )
1510
+ end
1511
+ rescue PG::ProgramLimitExceeded
1512
+ # Stop silently as soon the server complains about too many params
1513
+ end
1514
+ end
1515
+
1516
+ it "can process #copy_data output with row decoder" do
1517
+ rows = []
1518
+ res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
1519
+ while row=@conn2.get_copy_data
1520
+ rows << row
1521
+ end
1522
+ end
1523
+ expect( rows ).to eq( [["1"], ["2"]] )
1524
+ end
1525
+
1526
+ it "can type cast #copy_data output with explicit decoder" do
1527
+ tm = PG::TypeMapByColumn.new [PG::TextDecoder::Integer.new]
1528
+ row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
1529
+ rows = []
1530
+ @conn.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT", row_decoder ) do |res|
1531
+ while row=@conn.get_copy_data
1532
+ rows << row
1533
+ end
1534
+ end
1535
+ @conn.copy_data( "COPY (SELECT 3 UNION ALL SELECT 4) TO STDOUT" ) do |res|
1536
+ while row=@conn.get_copy_data( false, row_decoder )
1537
+ rows << row
1538
+ end
1539
+ end
1540
+ expect( rows ).to eq( [[1], [2], [3], [4]] )
1541
+ end
1542
+ end
1543
+ end
1246
1544
  end