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

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.
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