pg 0.15.0.pre.432 → 0.15.0.pre.454

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.
@@ -74,6 +74,7 @@ have_func 'rb_enc_alias'
74
74
  have_func 'rb_thread_call_without_gvl'
75
75
  have_func 'rb_thread_call_with_gvl'
76
76
  have_func 'rb_thread_fd_select'
77
+ have_func 'rb_w32_wrap_io_handle'
77
78
 
78
79
  have_const 'PGRES_COPY_BOTH', 'libpq-fe.h'
79
80
  have_const 'PGRES_SINGLE_TUPLE', 'libpq-fe.h'
data/ext/pg.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg.c - Toplevel extension
3
- * $Id: pg.c,v d0c5d5f7f1e8 2012/12/26 21:09:28 kanis $
3
+ * $Id$
4
4
  *
5
5
  * Author/s:
6
6
  *
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_connection.c - PG::Connection class extension
3
- * $Id: pg_connection.c,v 7c313c2355b7 2013/02/03 18:37:06 ged $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -63,6 +63,29 @@ pg_get_pgconn( VALUE self )
63
63
  }
64
64
 
65
65
 
66
+ /*
67
+ * Close the associated socket IO object if there is one.
68
+ */
69
+ void
70
+ pgconn_close_socket_io( VALUE self )
71
+ {
72
+ VALUE socket_io = rb_iv_get( self, "@socket_io" );
73
+ int ruby_sd;
74
+
75
+ if ( RTEST(socket_io) ) {
76
+ #if defined(_WIN32) && defined(HAVE_RB_W32_WRAP_IO_HANDLE)
77
+ ruby_sd = NUM2INT(rb_funcall( socket_io, rb_intern("fileno"), 0 ));
78
+ if( rb_w32_unwrap_io_handle(ruby_sd) ){
79
+ rb_raise(rb_ePGerror, "Could not unwrap win32 socket handle");
80
+ }
81
+ #endif
82
+ rb_funcall( socket_io, rb_intern("close"), 0 );
83
+ }
84
+
85
+ rb_iv_set( self, "@socket_io", Qnil );
86
+ }
87
+
88
+
66
89
  /*
67
90
  * Allocation/
68
91
  */
@@ -388,7 +411,7 @@ pgconn_s_encrypt_password(VALUE self, VALUE password, VALUE username)
388
411
  *
389
412
  * Example:
390
413
  * conn = PG::Connection.connect_start("dbname=mydatabase")
391
- * socket = IO.for_fd(conn.socket)
414
+ * socket = conn.socket_io
392
415
  * status = conn.connect_poll
393
416
  * while(status != PG::PGRES_POLLING_OK) do
394
417
  * # do some work while waiting for the connection to complete
@@ -421,10 +444,11 @@ pgconn_connect_poll(VALUE self)
421
444
  * Closes the backend connection.
422
445
  */
423
446
  static VALUE
424
- pgconn_finish(VALUE self)
447
+ pgconn_finish( VALUE self )
425
448
  {
426
- PQfinish(pg_get_pgconn(self));
427
- DATA_PTR(self) = NULL;
449
+ pgconn_close_socket_io( self );
450
+ PQfinish( pg_get_pgconn(self) );
451
+ DATA_PTR( self ) = NULL;
428
452
  return Qnil;
429
453
  }
430
454
 
@@ -451,9 +475,10 @@ pgconn_finished_p( VALUE self )
451
475
  * backend connection and tries to re-connect.
452
476
  */
453
477
  static VALUE
454
- pgconn_reset(VALUE self)
478
+ pgconn_reset( VALUE self )
455
479
  {
456
- PQreset(pg_get_pgconn(self));
480
+ pgconn_close_socket_io( self );
481
+ PQreset( pg_get_pgconn(self) );
457
482
  return self;
458
483
  }
459
484
 
@@ -470,6 +495,7 @@ pgconn_reset(VALUE self)
470
495
  static VALUE
471
496
  pgconn_reset_start(VALUE self)
472
497
  {
498
+ pgconn_close_socket_io( self );
473
499
  if(PQresetStart(pg_get_pgconn(self)) == 0)
474
500
  rb_raise(rb_ePGerror, "reset has failed");
475
501
  return Qnil;
@@ -715,6 +741,55 @@ pgconn_socket(VALUE self)
715
741
  }
716
742
 
717
743
 
744
+ #if !defined(_WIN32) || defined(HAVE_RB_W32_WRAP_IO_HANDLE)
745
+
746
+ /*
747
+ * call-seq:
748
+ * conn.socket_io() -> IO
749
+ *
750
+ * Fetch a memoized IO object created from the Connection's underlying socket.
751
+ * This object can be used for IO.select to wait for events while running
752
+ * asynchronous API calls.
753
+ *
754
+ * Using this instead of #socket avoids the problem of the underlying connection
755
+ * being closed by Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt>
756
+ * goes out of scope.
757
+ *
758
+ * This method can also be used on Windows but requires Ruby-2.0+.
759
+ */
760
+ static VALUE
761
+ pgconn_socket_io(VALUE self)
762
+ {
763
+ int sd;
764
+ int ruby_sd;
765
+ int id_autoclose = rb_intern("autoclose=");
766
+ VALUE socket_io = rb_iv_get( self, "@socket_io" );
767
+
768
+ if ( !RTEST(socket_io) ) {
769
+ if( (sd = PQsocket(pg_get_pgconn(self))) < 0)
770
+ rb_raise(rb_ePGerror, "Can't get socket descriptor");
771
+
772
+ #ifdef _WIN32
773
+ ruby_sd = rb_w32_wrap_io_handle((HANDLE)(intptr_t)sd, O_RDWR|O_BINARY|O_NOINHERIT);
774
+ #else
775
+ ruby_sd = sd;
776
+ #endif
777
+
778
+ socket_io = rb_funcall( rb_cIO, rb_intern("for_fd"), 1, INT2NUM(ruby_sd) );
779
+
780
+ /* Disable autoclose feature, when supported */
781
+ if( rb_respond_to(socket_io, id_autoclose) ){
782
+ rb_funcall( socket_io, id_autoclose, 1, Qfalse );
783
+ }
784
+
785
+ rb_iv_set( self, "@socket_io", socket_io );
786
+ }
787
+
788
+ return socket_io;
789
+ }
790
+
791
+ #endif
792
+
718
793
  /*
719
794
  * call-seq:
720
795
  * conn.backend_pid() -> Fixnum
@@ -797,7 +872,7 @@ pgconn_exec(int argc, VALUE *argv, VALUE self)
797
872
  }
798
873
  return rb_pgresult;
799
874
  }
800
-
875
+
801
876
  /* Otherwise, just call #exec_params instead for backward-compatibility */
802
877
  else {
803
878
  return pgconn_exec_params( argc, argv, self );
@@ -2174,9 +2249,13 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
2174
2249
  return NULL;
2175
2250
  } else if ( wait_ret == WAIT_OBJECT_0 ) {
2176
2251
  /* The event we were waiting for. */
2252
+ } else if ( wait_ret == WAIT_OBJECT_0 + 1) {
2253
+ /* This indicates interruption from timer thread, GC, exception
2254
+ * from other threads etc... */
2255
+ rb_thread_check_ints();
2177
2256
  } else if ( wait_ret == WAIT_FAILED ) {
2178
2257
  WSACloseEvent( hEvent );
2179
- rb_raise( rb_ePGerror, "Wait on socket error (WaitForMultipleObjects): %d", GetLastError() );
2258
+ rb_raise( rb_ePGerror, "Wait on socket error (WaitForMultipleObjects): %lu", GetLastError() );
2180
2259
  } else {
2181
2260
  WSACloseEvent( hEvent );
2182
2261
  rb_raise( rb_ePGerror, "Wait on socket abandoned (WaitForMultipleObjects)" );
@@ -2884,6 +2963,7 @@ pgconn_get_last_result(VALUE self)
2884
2963
  return rb_pgresult;
2885
2964
  }
2886
2965
 
2966
+ #if !defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
2887
2967
 
2888
2968
  /*
2889
2969
  * call-seq:
@@ -2891,10 +2971,14 @@ pgconn_get_last_result(VALUE self)
2891
2971
  * conn.async_exec(sql [, params, result_format ] ) {|pg_result| block }
2892
2972
  *
2893
2973
  * This function has the same behavior as #exec,
2894
- * except that it's implemented using asynchronous command
2895
- * processing and ruby's +rb_thread_select+ in order to
2896
- * allow other threads to process while waiting for the
2897
- * server to complete the request.
2974
+ * but ensures that other threads can process while
2975
+ * waiting for the server to complete the request.
2976
+ *
2977
+ * On Ruby platforms with native threads (MRI-1.9+ and all others)
2978
+ * this method is an alias to #exec.
2979
+ *
2980
+ * On MRI-1.8 it's implemented using asynchronous command
2981
+ * processing and ruby's +rb_thread_select+ .
2898
2982
  */
2899
2983
  static VALUE
2900
2984
  pgconn_async_exec(int argc, VALUE *argv, VALUE self)
@@ -2915,6 +2999,7 @@ pgconn_async_exec(int argc, VALUE *argv, VALUE self)
2915
2999
  return rb_pgresult;
2916
3000
  }
2917
3001
 
3002
+ #endif
2918
3003
 
2919
3004
  /**************************************************************************
2920
3005
  * LARGE OBJECT SUPPORT
@@ -3394,6 +3479,9 @@ init_pg_connection()
3394
3479
  rb_define_method(rb_cPGconn, "server_version", pgconn_server_version, 0);
3395
3480
  rb_define_method(rb_cPGconn, "error_message", pgconn_error_message, 0);
3396
3481
  rb_define_method(rb_cPGconn, "socket", pgconn_socket, 0);
3482
+ #if !defined(_WIN32) || defined(HAVE_RB_W32_WRAP_IO_HANDLE)
3483
+ rb_define_method(rb_cPGconn, "socket_io", pgconn_socket_io, 0);
3484
+ #endif
3397
3485
  rb_define_method(rb_cPGconn, "backend_pid", pgconn_backend_pid, 0);
3398
3486
  rb_define_method(rb_cPGconn, "connection_needs_password", pgconn_connection_needs_password, 0);
3399
3487
  rb_define_method(rb_cPGconn, "connection_used_password", pgconn_connection_used_password, 0);
@@ -3465,7 +3553,11 @@ init_pg_connection()
3465
3553
  rb_define_method(rb_cPGconn, "wait_for_notify", pgconn_wait_for_notify, -1);
3466
3554
  rb_define_alias(rb_cPGconn, "notifies_wait", "wait_for_notify");
3467
3555
  rb_define_method(rb_cPGconn, "quote_ident", pgconn_s_quote_ident, 1);
3556
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3557
+ rb_define_alias(rb_cPGconn, "async_exec", "exec");
3558
+ #else
3468
3559
  rb_define_method(rb_cPGconn, "async_exec", pgconn_async_exec, -1);
3560
+ #endif
3469
3561
  rb_define_alias(rb_cPGconn, "async_query", "async_exec");
3470
3562
  rb_define_method(rb_cPGconn, "get_last_result", pgconn_get_last_result, 0);
3471
3563
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v fe820dfcc7a6 2013/01/29 21:55:34 aaron $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
data/lib/pg.rb CHANGED
@@ -19,10 +19,10 @@ end
19
19
  module PG
20
20
 
21
21
  # Library version
22
- VERSION = '0.15.0'
22
+ VERSION = '0.15.0.pre.454'
23
23
 
24
24
  # VCS revision
25
- REVISION = %q$Revision: ef533f731814 $
25
+ REVISION = %q$Revision$
26
26
 
27
27
 
28
28
  ### Get the PG library version. If +include_buildnum+ is +true+, include the build ID.
@@ -64,20 +64,6 @@ class PG::Connection
64
64
  class << self
65
65
  define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
66
66
  end
67
-
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
-
81
67
  end # class PG::Connection
82
68
 
83
69
  # Backward-compatible alias
@@ -24,7 +24,7 @@ rescue LoadError # 1.8 support
24
24
  raise
25
25
  end
26
26
 
27
- SCRIPT_VERSION = %q$Id: disk_usage_report.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
27
+ SCRIPT_VERSION = %q$Id$
28
28
 
29
29
 
30
30
  ### Gather data and output it to $stdout.
@@ -32,7 +32,7 @@ end
32
32
  ### Optionally run in a continuous loop, displaying deltas.
33
33
  ###
34
34
  class Stats
35
- VERSION = %q$Id: pg_statistics.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
35
+ VERSION = %q$Id$
36
36
 
37
37
  def initialize( opts )
38
38
  @opts = opts
@@ -36,7 +36,7 @@ end
36
36
  ###
37
37
  class PGMonitor
38
38
 
39
- VERSION = %q$Id: replication_monitor.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
39
+ VERSION = %q$Id$
40
40
 
41
41
  # When to consider a slave as 'behind', measured in WAL segments.
42
42
  # The default WAL segment size is 16, so we'll alert after
@@ -181,7 +181,7 @@ module PG::TestingHelpers
181
181
  require 'pg'
182
182
  stop_existing_postmasters()
183
183
 
184
- puts "Setting up test database for #{description} tests"
184
+ puts "Setting up test database for #{description}"
185
185
  @test_pgdata = TEST_DIRECTORY + 'data'
186
186
  @test_pgdata.mkpath
187
187
 
@@ -218,7 +218,7 @@ module PG::TestingHelpers
218
218
 
219
219
  conn = PG.connect( @conninfo )
220
220
  conn.set_notice_processor do |message|
221
- $stderr.puts( message ) if $DEBUG
221
+ $stderr.puts( description + ':' + message ) if $DEBUG
222
222
  end
223
223
 
224
224
  return conn
@@ -227,9 +227,27 @@ module PG::TestingHelpers
227
227
 
228
228
  def teardown_testing_db( conn )
229
229
  puts "Tearing down test database"
230
- conn.finish if conn
230
+
231
+ if conn
232
+ check_for_lingering_connections( conn )
233
+ conn.finish
234
+ end
235
+
231
236
  log_and_run @logfile, 'pg_ctl', '-D', @test_pgdata.to_s, 'stop'
232
237
  end
238
+
239
+
240
+ def check_for_lingering_connections( conn )
241
+ conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
242
+ conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
243
+ unless conns.empty?
244
+ puts "Lingering connections remain:"
245
+ conns.each do |row|
246
+ puts " [%d] {%s} %s -- %s" % row.values_at( 'pid', 'state', 'application_name', 'query' )
247
+ end
248
+ end
249
+ end
250
+ end
233
251
  end
234
252
 
235
253
 
@@ -241,7 +259,13 @@ RSpec.configure do |config|
241
259
 
242
260
  config.mock_with :rspec
243
261
  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/
262
+ if RUBY_PLATFORM =~ /mingw|mswin/
263
+ config.filter_run_excluding :unix
264
+ else
265
+ config.filter_run_excluding :windows
266
+ end
267
+ config.filter_run_excluding :socket_io unless
268
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
245
269
 
246
270
  config.filter_run_excluding :postgresql_90 unless
247
271
  PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
@@ -20,11 +20,15 @@ require 'pg'
20
20
  describe PG::Connection do
21
21
 
22
22
  before( :all ) do
23
- @conn = setup_testing_db( "PG_Connection" )
23
+ @conn = setup_testing_db( described_class.name )
24
24
  end
25
25
 
26
26
  before( :each ) do
27
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
28
32
  end
29
33
 
30
34
  after( :each ) do
@@ -123,7 +127,7 @@ describe PG::Connection do
123
127
  }.to raise_error( ArgumentError, /extra positional parameter/i )
124
128
  end
125
129
 
126
- it "can connect asynchronously", :unix do
130
+ it "can connect asynchronously", :socket_io do
127
131
  tmpconn = described_class.connect_start( @conninfo )
128
132
  tmpconn.should be_a( described_class )
129
133
  socket = tmpconn.socket_io
@@ -145,7 +149,7 @@ describe PG::Connection do
145
149
  tmpconn.finish
146
150
  end
147
151
 
148
- it "can connect asynchronously for the duration of a block", :unix do
152
+ it "can connect asynchronously for the duration of a block", :socket_io do
149
153
  conn = nil
150
154
 
151
155
  described_class.connect_start(@conninfo) do |tmpconn|
@@ -212,8 +216,7 @@ describe PG::Connection do
212
216
  From backend> T
213
217
  }.gsub( /^\t{2}/, '' ).lstrip
214
218
 
215
- unless RUBY_PLATFORM =~ /mswin|mingw/
216
- it "trace and untrace client-server communication" do
219
+ it "trace and untrace client-server communication", :unix do
217
220
  # be careful to explicitly close files so that the
218
221
  # directory can be removed and we don't have to wait for
219
222
  # the GC to run.
@@ -242,7 +245,6 @@ describe PG::Connection do
242
245
 
243
246
  trace_data.should == expected_trace_output
244
247
  end
245
- end
246
248
 
247
249
  it "allows a query to be cancelled" do
248
250
  error = false
@@ -255,7 +257,7 @@ describe PG::Connection do
255
257
  error.should == true
256
258
  end
257
259
 
258
- it "automatically rolls back a transaction started with described_class#transaction if an exception " +
260
+ it "automatically rolls back a transaction started with Connection#transaction if an exception " +
259
261
  "is raised" do
260
262
  # abort the per-example transaction so we can test our own
261
263
  @conn.exec( 'ROLLBACK' )
@@ -515,7 +517,7 @@ describe PG::Connection do
515
517
  end
516
518
 
517
519
 
518
- it "can connect asynchronously", :unix do
520
+ it "can connect asynchronously", :socket_io do
519
521
  serv = TCPServer.new( '127.0.0.1', 54320 )
520
522
  conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
521
523
  [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].should include conn.connect_poll
@@ -544,6 +546,23 @@ describe PG::Connection do
544
546
  expect { conn.finish }.to raise_error( PG::Error, /connection is closed/i )
545
547
  end
546
548
 
549
+ it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction, :socket_io do
550
+ conn = PG.connect( @conninfo )
551
+ io = conn.socket_io
552
+ conn.finish
553
+ io.should be_closed()
554
+ expect { conn.socket_io }.to raise_error( PG::Error, /connection is closed/i )
555
+ end
556
+
557
+ it "closes the IO fetched from #socket_io when the connection is reset", :without_transaction, :socket_io do
558
+ conn = PG.connect( @conninfo )
559
+ io = conn.socket_io
560
+ conn.reset
561
+ io.should be_closed()
562
+ conn.socket_io.should_not equal( io )
563
+ conn.finish
564
+ end
565
+
547
566
 
548
567
  context "under PostgreSQL 9", :postgresql_90 do
549
568