pg 0.17.1 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +2407 -2
  4. data/History.rdoc +68 -0
  5. data/Manifest.txt +29 -1
  6. data/README-Windows.rdoc +15 -26
  7. data/README.rdoc +52 -2
  8. data/Rakefile +56 -18
  9. data/Rakefile.cross +77 -49
  10. data/ext/extconf.rb +33 -26
  11. data/ext/pg.c +142 -21
  12. data/ext/pg.h +242 -6
  13. data/ext/pg_binary_decoder.c +162 -0
  14. data/ext/pg_binary_encoder.c +162 -0
  15. data/ext/pg_coder.c +479 -0
  16. data/ext/pg_connection.c +858 -553
  17. data/ext/pg_copy_coder.c +561 -0
  18. data/ext/pg_errors.c +6 -0
  19. data/ext/pg_result.c +479 -128
  20. data/ext/pg_text_decoder.c +421 -0
  21. data/ext/pg_text_encoder.c +663 -0
  22. data/ext/pg_type_map.c +159 -0
  23. data/ext/pg_type_map_all_strings.c +116 -0
  24. data/ext/pg_type_map_by_class.c +239 -0
  25. data/ext/pg_type_map_by_column.c +312 -0
  26. data/ext/pg_type_map_by_mri_type.c +284 -0
  27. data/ext/pg_type_map_by_oid.c +355 -0
  28. data/ext/pg_type_map_in_ruby.c +299 -0
  29. data/ext/util.c +149 -0
  30. data/ext/util.h +65 -0
  31. data/lib/pg/basic_type_mapping.rb +399 -0
  32. data/lib/pg/coder.rb +83 -0
  33. data/lib/pg/connection.rb +81 -29
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +44 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/pg.rb +12 -2
  39. data/spec/{lib/helpers.rb → helpers.rb} +101 -39
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +516 -218
  42. data/spec/pg/result_spec.rb +216 -112
  43. data/spec/pg/type_map_by_class_spec.rb +138 -0
  44. data/spec/pg/type_map_by_column_spec.rb +222 -0
  45. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  46. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  47. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  48. data/spec/pg/type_map_spec.rb +22 -0
  49. data/spec/pg/type_spec.rb +697 -0
  50. data/spec/pg_spec.rb +24 -18
  51. data.tar.gz.sig +0 -0
  52. metadata +111 -45
  53. metadata.gz.sig +0 -0
@@ -1,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