pg 0.14.1-x86-mingw32 → 0.15.0.pre.432-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/ext/pg_result.c CHANGED
@@ -37,7 +37,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
37
37
 
38
38
  /*
39
39
  * call-seq:
40
- * res.result -> nil
40
+ * res.check -> nil
41
41
  *
42
42
  * Raises appropriate exception if PG::Result is in a bad state.
43
43
  */
@@ -728,22 +728,20 @@ pgresult_aref(VALUE self, VALUE index)
728
728
  return tuple;
729
729
  }
730
730
 
731
-
732
731
  /*
733
732
  * call-seq:
734
- * res.values -> Array
733
+ * res.each_row { |row| ... }
735
734
  *
736
- * Returns all tuples as an array of arrays.
735
+ * Yields each row of the result. The row is a list of column values.
737
736
  */
738
737
  static VALUE
739
- pgresult_values(VALUE self)
738
+ pgresult_each_row(VALUE self)
740
739
  {
741
740
  PGresult* result = (PGresult*) pgresult_get(self);
742
741
  int row;
743
742
  int field;
744
743
  int num_rows = PQntuples(result);
745
744
  int num_fields = PQnfields(result);
746
- VALUE ary = rb_ary_new2(num_rows);
747
745
 
748
746
  for ( row = 0; row < num_rows; row++ ) {
749
747
  VALUE new_row = rb_ary_new2(num_fields);
@@ -768,11 +766,10 @@ pgresult_values(VALUE self)
768
766
  rb_ary_store( new_row, field, val );
769
767
  }
770
768
  }
771
-
772
- rb_ary_store( ary, row, new_row );
769
+ rb_yield( new_row );
773
770
  }
774
771
 
775
- return ary;
772
+ return Qnil;
776
773
  }
777
774
 
778
775
 
@@ -839,7 +836,7 @@ static VALUE
839
836
  pgresult_field_values( VALUE self, VALUE field )
840
837
  {
841
838
  PGresult *result = pgresult_get( self );
842
- const char *fieldname = RSTRING_PTR( field );
839
+ const char *fieldname = StringValuePtr( field );
843
840
  int fnum = PQfnumber( result, fieldname );
844
841
 
845
842
  if ( fnum < 0 )
@@ -934,7 +931,7 @@ init_pg_result()
934
931
  rb_define_method(rb_cPGresult, "[]", pgresult_aref, 1);
935
932
  rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
936
933
  rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
937
- rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
934
+ rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
938
935
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
939
936
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
940
937
  }
data/lib/1.8/pg_ext.so CHANGED
Binary file
data/lib/1.9/pg_ext.so CHANGED
Binary file
data/lib/pg.rb CHANGED
@@ -19,7 +19,7 @@ end
19
19
  module PG
20
20
 
21
21
  # Library version
22
- VERSION = '0.14.1'
22
+ VERSION = '0.15.0.pre.432'
23
23
 
24
24
  # VCS revision
25
25
  REVISION = %q$Revision$
data/lib/pg/connection.rb CHANGED
@@ -23,11 +23,19 @@ class PG::Connection
23
23
  # This will be swapped soon for code that makes options like those required for
24
24
  # PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
25
25
  # PQconnectdb()/PQconnectStart().
26
- connopts = []
26
+
27
+ # Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
28
+ # together with PQescapeLiteral().
29
+ if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
30
+ appname = PG::Connection.quote_connstr( $0 )
31
+ connopts = ["fallback_application_name=#{appname}"]
32
+ else
33
+ connopts = []
34
+ end
27
35
 
28
36
  # Handle an options hash first
29
37
  if args.last.is_a?( Hash )
30
- opthash = args.pop
38
+ opthash = args.pop
31
39
  opthash.each do |key, val|
32
40
  connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
33
41
  end
@@ -58,6 +66,18 @@ class PG::Connection
58
66
  end
59
67
 
60
68
 
69
+ ### Fetch a memoized IO object created from the Connection's underlying socket.
70
+ ### Using this avoids the problem of the underlying connection being closed by
71
+ ### Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt> goes out of scope.
72
+ def socket_io
73
+ unless @socket_io
74
+ @socket_io = IO.for_fd( self.socket )
75
+ @socket_io.autoclose = false if @socket_io.respond_to?( :autoclose= )
76
+ end
77
+
78
+ return @socket_io
79
+ end
80
+
61
81
  end # class PG::Connection
62
82
 
63
83
  # Backward-compatible alias
data/lib/pg/result.rb CHANGED
@@ -4,7 +4,12 @@ require 'pg' unless defined?( PG )
4
4
 
5
5
 
6
6
  class PG::Result
7
-
7
+
8
+ ### Returns all tuples as an array of arrays
9
+ def values
10
+ return enum_for(:each_row).to_a
11
+ end
12
+
8
13
  end # class PG::Result
9
14
 
10
15
  # Backward-compatible alias
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ c = PG.connect( dbname: 'test' )
6
+
7
+ # this one works:
8
+ c.exec( "DROP TABLE IF EXISTS foo" )
9
+ c.exec( "CREATE TABLE foo (strings character varying[]);" )
10
+
11
+ # But using a prepared statement works:
12
+ c.set_error_verbosity( PG::PQERRORS_VERBOSE )
13
+ c.prepare( 'stmt', "INSERT INTO foo VALUES ($1);" )
14
+
15
+ # This won't work
16
+ #c.exec_prepared( 'stmt', ["ARRAY['this','that']"] )
17
+
18
+ # but this will:
19
+ c.exec_prepared( 'stmt', ["{'this','that'}"] )
20
+
data/sample/async_api.rb CHANGED
@@ -29,7 +29,7 @@ abort "Connection failed: %s" % [ conn.error_message ] if
29
29
 
30
30
  # Now grab a reference to the underlying socket so we know when the
31
31
  # connection is established
32
- socket = IO.for_fd( conn.socket )
32
+ socket = conn.socket_io
33
33
 
34
34
  # Track the progress of the connection, waiting for the socket to become readable/writable
35
35
  # before polling it
@@ -9,7 +9,7 @@ $stderr.puts "Opening database connection ..."
9
9
  conn = PG.connect( :dbname => 'test' )
10
10
  conn.setnonblocking( true )
11
11
 
12
- socket = IO.for_fd( conn.socket )
12
+ socket = conn.socket_io
13
13
 
14
14
  $stderr.puts "Running COPY command ..."
15
15
  buf = ''
@@ -26,7 +26,7 @@ conn = PG.connect( CONN_OPTS ) or abort "Unable to create a new connection!"
26
26
  abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
27
27
 
28
28
  # Now grab a reference to the underlying socket to select() on while the query is running
29
- socket = IO.for_fd( conn.socket )
29
+ socket = conn.socket_io
30
30
 
31
31
  # Send the (asynchronous) query
32
32
  output_progress "Sending query"
data/spec/lib/helpers.rb CHANGED
@@ -48,7 +48,7 @@ module PG::TestingHelpers
48
48
  attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
49
49
 
50
50
  # $stderr.puts " attr is: %p" % [attributes]
51
- if attributes.empty?
51
+ if attributes.empty?
52
52
  return ''
53
53
  else
54
54
  return "\e[%sm" % attributes
@@ -56,7 +56,7 @@ module PG::TestingHelpers
56
56
  end
57
57
 
58
58
 
59
- ### Colorize the given +string+ with the specified +attributes+ and return it, handling
59
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
60
60
  ### line-endings, color reset, etc.
61
61
  def colorize( *args )
62
62
  string = ''
@@ -110,9 +110,7 @@ module PG::TestingHelpers
110
110
  end
111
111
 
112
112
 
113
- NOFORK_PLATFORMS = %w{java}
114
-
115
- ### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
113
+ ### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
116
114
  ### +logpath+, failing if the execution fails.
117
115
  def log_and_run( logpath, *cmd )
118
116
  cmd.flatten!
@@ -126,11 +124,14 @@ module PG::TestingHelpers
126
124
  # Eliminate the noise of creating/tearing down the database by
127
125
  # redirecting STDERR/STDOUT to a logfile if the Ruby interpreter
128
126
  # supports fork()
129
- if NOFORK_PLATFORMS.include?( RUBY_PLATFORM )
127
+ logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
128
+ begin
129
+ pid = fork
130
+ rescue NotImplementedError
131
+ logfh.close
130
132
  system( *cmd )
131
133
  else
132
- logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
133
- if pid = fork
134
+ if pid
134
135
  logfh.close
135
136
  else
136
137
  $stdout.reopen( logfh )
@@ -240,10 +241,15 @@ RSpec.configure do |config|
240
241
 
241
242
  config.mock_with :rspec
242
243
  config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
244
+ config.filter_run_excluding :unix if RUBY_PLATFORM =~ /mingw|mswin/
243
245
 
244
246
  config.filter_run_excluding :postgresql_90 unless
245
247
  PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
246
- config.filter_run_excluding :postgresql_91 unless
247
- PG.respond_to?( :library_version )
248
+
249
+ if !PG.respond_to?( :library_version )
250
+ config.filter_run_excluding( :postgresql_91, :postgresql_92 )
251
+ elsif PG.library_version < 90200
252
+ config.filter_run_excluding( :postgresql_92 )
253
+ end
248
254
  end
249
255
 
@@ -41,7 +41,7 @@ describe PG::Connection do
41
41
  #
42
42
 
43
43
  it "can create a connection option string from a Hash of options" do
44
- optstring = described_class.parse_connect_args(
44
+ optstring = described_class.parse_connect_args(
45
45
  :host => 'pgsql.example.com',
46
46
  :dbname => 'db01',
47
47
  'sslmode' => 'require'
@@ -54,14 +54,14 @@ describe PG::Connection do
54
54
  end
55
55
 
56
56
  it "can create a connection option string from positional parameters" do
57
- optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
57
+ optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
58
58
  'sales' )
59
59
 
60
60
  optstring.should be_a( String )
61
61
  optstring.should =~ /(^|\s)host='pgsql.example.com'/
62
62
  optstring.should =~ /(^|\s)dbname='sales'/
63
63
  optstring.should =~ /(^|\s)options='-c geqo=off'/
64
-
64
+
65
65
  optstring.should_not =~ /port=/
66
66
  optstring.should_not =~ /tty=/
67
67
  end
@@ -77,7 +77,8 @@ describe PG::Connection do
77
77
  end
78
78
 
79
79
  it "escapes single quotes and backslashes in connection parameters" do
80
- described_class.parse_connect_args( "DB 'browser' \\" ).should == "host='DB \\'browser\\' \\\\'"
80
+ described_class.parse_connect_args( "DB 'browser' \\" ).
81
+ should =~ /host='DB \\'browser\\' \\\\'/
81
82
 
82
83
  end
83
84
 
@@ -106,7 +107,7 @@ describe PG::Connection do
106
107
  tmpconn.finish
107
108
  end
108
109
 
109
- it "connects using a hash of optional connection parameters" do
110
+ it "connects using a hash of optional connection parameters", :postgresql_90 do
110
111
  tmpconn = described_class.connect(
111
112
  :host => 'localhost',
112
113
  :port => @port,
@@ -122,10 +123,10 @@ describe PG::Connection do
122
123
  }.to raise_error( ArgumentError, /extra positional parameter/i )
123
124
  end
124
125
 
125
- it "can connect asynchronously" do
126
+ it "can connect asynchronously", :unix do
126
127
  tmpconn = described_class.connect_start( @conninfo )
127
128
  tmpconn.should be_a( described_class )
128
- socket = IO.for_fd( tmpconn.socket )
129
+ socket = tmpconn.socket_io
129
130
  status = tmpconn.connect_poll
130
131
 
131
132
  while status != PG::PGRES_POLLING_OK
@@ -144,13 +145,13 @@ describe PG::Connection do
144
145
  tmpconn.finish
145
146
  end
146
147
 
147
- it "can connect asynchronously for the duration of a block" do
148
+ it "can connect asynchronously for the duration of a block", :unix do
148
149
  conn = nil
149
150
 
150
151
  described_class.connect_start(@conninfo) do |tmpconn|
151
152
  tmpconn.should be_a( described_class )
152
153
  conn = tmpconn
153
- socket = IO.for_fd(tmpconn.socket)
154
+ socket = tmpconn.socket_io
154
155
  status = tmpconn.connect_poll
155
156
 
156
157
  while status != PG::PGRES_POLLING_OK
@@ -285,39 +286,58 @@ describe PG::Connection do
285
286
  end
286
287
 
287
288
 
289
+ it "supports parameters passed to #exec (backward compatibility)" do
290
+ @conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
291
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
292
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
293
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
294
+
295
+ res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
296
+ res.values.should == [ ['Wally'], ['Sally'] ]
297
+ end
298
+
299
+ it "supports explicitly calling #exec_params" do
300
+ @conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
301
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
302
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
303
+ @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
304
+
305
+ res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
306
+ res.values.should == [ ['Wally'], ['Sally'] ]
307
+ end
308
+
309
+
288
310
  it "can wait for NOTIFY events" do
289
311
  @conn.exec( 'ROLLBACK' )
290
312
  @conn.exec( 'LISTEN woo' )
291
313
 
292
- pid = fork do
314
+ t = Thread.new do
293
315
  begin
294
316
  conn = described_class.connect( @conninfo )
295
317
  sleep 1
296
- conn.exec( 'NOTIFY woo' )
318
+ conn.async_exec( 'NOTIFY woo' )
297
319
  ensure
298
320
  conn.finish
299
- exit!
300
321
  end
301
322
  end
302
323
 
303
324
  @conn.wait_for_notify( 10 ).should == 'woo'
304
325
  @conn.exec( 'UNLISTEN woo' )
305
326
 
306
- Process.wait( pid )
327
+ t.join
307
328
  end
308
329
 
309
330
  it "calls a block for NOTIFY events if one is given" do
310
331
  @conn.exec( 'ROLLBACK' )
311
332
  @conn.exec( 'LISTEN woo' )
312
333
 
313
- pid = fork do
334
+ t = Thread.new do
314
335
  begin
315
336
  conn = described_class.connect( @conninfo )
316
337
  sleep 1
317
- conn.exec( 'NOTIFY woo' )
338
+ conn.async_exec( 'NOTIFY woo' )
318
339
  ensure
319
340
  conn.finish
320
- exit!
321
341
  end
322
342
  end
323
343
 
@@ -328,7 +348,7 @@ describe PG::Connection do
328
348
 
329
349
  @conn.exec( 'UNLISTEN woo' )
330
350
 
331
- Process.wait( pid )
351
+ t.join
332
352
  end
333
353
 
334
354
  it "doesn't collapse sequential notifications" do
@@ -337,20 +357,15 @@ describe PG::Connection do
337
357
  @conn.exec( 'LISTEN war' )
338
358
  @conn.exec( 'LISTEN woz' )
339
359
 
340
- pid = fork do
341
- begin
342
- conn = described_class.connect( @conninfo )
343
- conn.exec( 'NOTIFY woo' )
344
- conn.exec( 'NOTIFY war' )
345
- conn.exec( 'NOTIFY woz' )
346
- ensure
347
- conn.finish
348
- exit!
349
- end
360
+ begin
361
+ conn = described_class.connect( @conninfo )
362
+ conn.exec( 'NOTIFY woo' )
363
+ conn.exec( 'NOTIFY war' )
364
+ conn.exec( 'NOTIFY woz' )
365
+ ensure
366
+ conn.finish
350
367
  end
351
368
 
352
- Process.wait( pid )
353
-
354
369
  channels = []
355
370
  3.times do
356
371
  channels << @conn.wait_for_notify( 2 )
@@ -369,19 +384,13 @@ describe PG::Connection do
369
384
  @conn.exec( 'ROLLBACK' )
370
385
  @conn.exec( 'LISTEN woo' )
371
386
 
372
- pid = fork do
373
- begin
374
- conn = described_class.connect( @conninfo )
375
- conn.exec( 'NOTIFY woo' )
376
- ensure
377
- conn.finish
378
- exit!
379
- end
387
+ begin
388
+ conn = described_class.connect( @conninfo )
389
+ conn.exec( 'NOTIFY woo' )
390
+ ensure
391
+ conn.finish
380
392
  end
381
393
 
382
- # Wait for the forked child to send the notification
383
- Process.wait( pid )
384
-
385
394
  # Cause the notification to buffer, but not be read yet
386
395
  @conn.exec( 'SELECT 1' )
387
396
 
@@ -424,16 +433,17 @@ describe PG::Connection do
424
433
 
425
434
 
426
435
  it "described_class#block shouldn't block a second thread" do
436
+ start = Time.now
427
437
  t = Thread.new do
428
438
  @conn.send_query( "select pg_sleep(3)" )
429
439
  @conn.block
430
440
  end
431
441
 
432
- # :FIXME: There's a race here, but hopefully it's pretty small.
442
+ sleep 0.5
433
443
  t.should be_alive()
434
-
435
444
  @conn.cancel
436
445
  t.join
446
+ (Time.now - start).should < 3
437
447
  end
438
448
 
439
449
  it "described_class#block should allow a timeout" do
@@ -505,14 +515,14 @@ describe PG::Connection do
505
515
  end
506
516
 
507
517
 
508
- it "can connect asynchronously" do
518
+ it "can connect asynchronously", :unix do
509
519
  serv = TCPServer.new( '127.0.0.1', 54320 )
510
520
  conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
511
- conn.connect_poll.should == PG::PGRES_POLLING_WRITING
512
- select( nil, [IO.for_fd(conn.socket)], nil, 0.2 )
521
+ [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].should include conn.connect_poll
522
+ select( nil, [conn.socket_io], nil, 0.2 )
513
523
  serv.close
514
524
  if conn.connect_poll == PG::PGRES_POLLING_READING
515
- select( [IO.for_fd(conn.socket)], nil, nil, 0.2 )
525
+ select( [conn.socket_io], nil, nil, 0.2 )
516
526
  end
517
527
  conn.connect_poll.should == PG::PGRES_POLLING_FAILED
518
528
  end
@@ -541,20 +551,19 @@ describe PG::Connection do
541
551
  pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
542
552
  end
543
553
 
554
+ it "sets the fallback_application_name on new connections" do
555
+ PG::Connection.parse_connect_args( 'dbname=test' ).should include( $0 )
556
+ end
557
+
544
558
  it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
545
559
  "any number of arguments" do
546
560
 
547
561
  @conn.exec( 'ROLLBACK' )
548
562
  @conn.exec( 'LISTEN knees' )
549
563
 
550
- pid = fork do
551
- conn = described_class.connect( @conninfo )
552
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
553
- conn.finish
554
- exit!
555
- end
556
-
557
- Process.wait( pid )
564
+ conn = described_class.connect( @conninfo )
565
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
566
+ conn.finish
558
567
 
559
568
  event, pid, msg = nil
560
569
  @conn.wait_for_notify( 10 ) do |*args|
@@ -571,14 +580,9 @@ describe PG::Connection do
571
580
  @conn.exec( 'ROLLBACK' )
572
581
  @conn.exec( 'LISTEN knees' )
573
582
 
574
- pid = fork do
575
- conn = described_class.connect( @conninfo )
576
- conn.exec( %Q{NOTIFY knees} )
577
- conn.finish
578
- exit!
579
- end
580
-
581
- Process.wait( pid )
583
+ conn = described_class.connect( @conninfo )
584
+ conn.exec( %Q{NOTIFY knees} )
585
+ conn.finish
582
586
 
583
587
  event, pid = nil
584
588
  @conn.wait_for_notify( nil ) do |*args|
@@ -594,14 +598,9 @@ describe PG::Connection do
594
598
  @conn.exec( 'ROLLBACK' )
595
599
  @conn.exec( 'LISTEN knees' )
596
600
 
597
- pid = fork do
598
- conn = described_class.connect( @conninfo )
599
- conn.exec( %Q{NOTIFY knees} )
600
- conn.finish
601
- exit!
602
- end
603
-
604
- Process.wait( pid )
601
+ conn = described_class.connect( @conninfo )
602
+ conn.exec( %Q{NOTIFY knees} )
603
+ conn.finish
605
604
 
606
605
  payload = :notnil
607
606
  @conn.wait_for_notify( nil ) do |*args|
@@ -618,14 +617,9 @@ describe PG::Connection do
618
617
  @conn.exec( 'ROLLBACK' )
619
618
  @conn.exec( 'LISTEN knees' )
620
619
 
621
- pid = fork do
622
- conn = described_class.connect( @conninfo )
623
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
624
- conn.finish
625
- exit!
626
- end
627
-
628
- Process.wait( pid )
620
+ conn = described_class.connect( @conninfo )
621
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
622
+ conn.finish
629
623
 
630
624
  event, pid, msg = nil
631
625
  @conn.wait_for_notify( 10 ) do |arg1, arg2|
@@ -644,14 +638,9 @@ describe PG::Connection do
644
638
  @conn.exec( 'ROLLBACK' )
645
639
  @conn.exec( 'LISTEN knees' )
646
640
 
647
- pid = fork do
648
- conn = described_class.connect( @conninfo )
649
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
650
- conn.finish
651
- exit!
652
- end
653
-
654
- Process.wait( pid )
641
+ conn = described_class.connect( @conninfo )
642
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
643
+ conn.finish
655
644
 
656
645
  notification_received = false
657
646
  @conn.wait_for_notify( 10 ) do
@@ -668,14 +657,9 @@ describe PG::Connection do
668
657
  @conn.exec( 'ROLLBACK' )
669
658
  @conn.exec( 'LISTEN knees' )
670
659
 
671
- pid = fork do
672
- conn = described_class.connect( @conninfo )
673
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
674
- conn.finish
675
- exit!
676
- end
677
-
678
- Process.wait( pid )
660
+ conn = described_class.connect( @conninfo )
661
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
662
+ conn.finish
679
663
 
680
664
  event, pid, msg = nil
681
665
  @conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
@@ -726,22 +710,71 @@ describe PG::Connection do
726
710
 
727
711
  end
728
712
 
729
- context "multinationalization support", :ruby_19 do
713
+ context "under PostgreSQL 9.2 client library", :postgresql_92 do
714
+ describe "set_single_row_mode" do
715
+
716
+ it "raises an error when called at the wrong time" do
717
+ expect {
718
+ @conn.set_single_row_mode
719
+ }.to raise_error(PG::Error)
720
+ end
721
+
722
+ it "should work in single row mode" do
723
+ @conn.send_query( "SELECT generate_series(1,10)" )
724
+ @conn.set_single_row_mode
725
+
726
+ results = []
727
+ loop do
728
+ @conn.block
729
+ res = @conn.get_result or break
730
+ results << res
731
+ end
732
+ results.length.should == 11
733
+ results[0..-2].each do |res|
734
+ res.result_status.should == PG::PGRES_SINGLE_TUPLE
735
+ values = res.field_values('generate_series')
736
+ values.length.should == 1
737
+ values.first.to_i.should > 0
738
+ end
739
+ results.last.result_status.should == PG::PGRES_TUPLES_OK
740
+ results.last.ntuples.should == 0
741
+ end
730
742
 
731
- it "should return the same bytes in text format that are sent as inline text" do
732
- binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
733
- in_bytes = File.open(binary_file, 'r:ASCII-8BIT').read
734
- escaped_bytes = described_class.escape_bytea( in_bytes )
735
- out_bytes = nil
743
+ it "should receive rows before entire query is finished" do
744
+ @conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
745
+ @conn.set_single_row_mode
736
746
 
737
- @conn.transaction do |conn|
738
- conn.exec("SET standard_conforming_strings=on")
739
- res = conn.exec("VALUES ('#{escaped_bytes}'::bytea)", [], 0)
740
- out_bytes = described_class.unescape_bytea( res[0]['column1'] )
747
+ start_time = Time.now
748
+ first_row_time = nil
749
+ loop do
750
+ res = @conn.get_result or break
751
+ res.check
752
+ first_row_time = Time.now unless first_row_time
753
+ end
754
+ (Time.now - start_time).should >= 1.0
755
+ (first_row_time - start_time).should < 1.0
741
756
  end
742
757
 
743
- out_bytes.should == in_bytes
758
+ it "should receive rows before entire query fails" do
759
+ @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
760
+ @conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
761
+ @conn.set_single_row_mode
762
+
763
+ first_result = nil
764
+ expect do
765
+ loop do
766
+ res = @conn.get_result or break
767
+ res.check
768
+ first_result ||= res
769
+ end
770
+ end.to raise_error(PG::Error)
771
+ first_result.kind_of?(PG::Result).should be_true
772
+ first_result.result_status.should == PG::PGRES_SINGLE_TUPLE
773
+ end
744
774
  end
775
+ end
776
+
777
+ context "multinationalization support", :ruby_19 do
745
778
 
746
779
  describe "rubyforge #22925: m17n support" do
747
780
  it "should return results in the same encoding as the client (iso-8859-1)" do
@@ -818,7 +851,7 @@ describe PG::Connection do
818
851
  escaped.encoding.should == Encoding::EUC_JP
819
852
  end
820
853
 
821
- it "escapes string as literal" do
854
+ it "escapes string as literal", :postgresql_90 do
822
855
  original = "string to\0 escape"
823
856
  escaped = @conn.escape_literal( original )
824
857
  escaped.should == "'string to'"
@@ -852,11 +885,11 @@ describe PG::Connection do
852
885
  it "allows users of the async interface to set the client_encoding to the default_internal" do
853
886
  begin
854
887
  prev_encoding = Encoding.default_internal
855
- Encoding.default_internal = Encoding::KOI8_U
888
+ Encoding.default_internal = Encoding::KOI8_R
856
889
 
857
890
  @conn.set_default_encoding
858
891
 
859
- @conn.internal_encoding.should == Encoding::KOI8_U
892
+ @conn.internal_encoding.should == Encoding::KOI8_R
860
893
  ensure
861
894
  Encoding.default_internal = prev_encoding
862
895
  end
@@ -886,5 +919,76 @@ describe PG::Connection do
886
919
  conn.finish if conn
887
920
  end
888
921
 
922
+ it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
923
+ [:receiver, :processor].each do |kind|
924
+ notices = []
925
+ @conn.internal_encoding = 'utf-8'
926
+ if kind == :processor
927
+ @conn.set_notice_processor do |msg|
928
+ notices << msg
929
+ end
930
+ else
931
+ @conn.set_notice_receiver do |result|
932
+ notices << result.error_message
933
+ end
934
+ end
935
+
936
+ 3.times do
937
+ @conn.exec "do $$ BEGIN RAISE NOTICE '世界線航跡蔵'; END; $$ LANGUAGE plpgsql;"
938
+ end
939
+
940
+ notices.length.should == 3
941
+ notices.each do |notice|
942
+ notice.should =~ /^NOTICE:.*世界線航跡蔵/
943
+ notice.encoding.should == Encoding::UTF_8
944
+ end
945
+ @conn.set_notice_receiver
946
+ @conn.set_notice_processor
947
+ end
948
+ end
949
+
950
+ it "receives properly encoded text from wait_for_notify", :postgresql_90 do
951
+ @conn.internal_encoding = 'utf-8'
952
+ @conn.exec( 'ROLLBACK' )
953
+ @conn.exec( 'LISTEN "Möhre"' )
954
+ @conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
955
+ event, pid, msg = nil
956
+ @conn.wait_for_notify( 10 ) do |*args|
957
+ event, pid, msg = *args
958
+ end
959
+ @conn.exec( 'UNLISTEN "Möhre"' )
960
+
961
+ event.should == "Möhre"
962
+ event.encoding.should == Encoding::UTF_8
963
+ msg.should == '世界線航跡蔵'
964
+ msg.encoding.should == Encoding::UTF_8
965
+ end
966
+
967
+ it "returns properly encoded text from notifies", :postgresql_90 do
968
+ @conn.internal_encoding = 'utf-8'
969
+ @conn.exec( 'ROLLBACK' )
970
+ @conn.exec( 'LISTEN "Möhre"' )
971
+ @conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
972
+ @conn.exec( 'UNLISTEN "Möhre"' )
973
+
974
+ notification = @conn.notifies
975
+ notification[:relname].should == "Möhre"
976
+ notification[:relname].encoding.should == Encoding::UTF_8
977
+ notification[:extra].should == '世界線航跡蔵'
978
+ notification[:extra].encoding.should == Encoding::UTF_8
979
+ notification[:be_pid].should > 0
980
+ end
981
+ end
982
+
983
+ context "OS thread support", :ruby_19 do
984
+ it "described_class#exec shouldn't block a second thread" do
985
+ t = Thread.new do
986
+ @conn.exec( "select pg_sleep(1)" )
987
+ end
988
+
989
+ sleep 0.5
990
+ t.should be_alive()
991
+ t.join
992
+ end
889
993
  end
890
994
  end