Commits

Michael Granger committed fc29084

Clean up the #socket_io if it exists on #finish and #reset.

  • Participants
  • Parent commits ac1c999

Comments (0)

Files changed (3)

ext/pg_connection.c

 
 
 /*
+ * Close the associated socket IO object if there is one.
+ */
+void
+pgconn_close_socket_io( VALUE self )
+{
+	VALUE socket_io = rb_iv_get( self, "@socket_io" );
+
+	if ( RTEST(socket_io) ) {
+		rb_funcall( socket_io, rb_intern("close"), 0 );
+	}
+
+	rb_iv_set( self, "@socket_io", Qnil );
+}
+
+
+/*
  * Allocation/
  */
 
  *
  * Example:
  *   conn = PG::Connection.connect_start("dbname=mydatabase")
- *   socket = IO.for_fd(conn.socket)
+ *   socket = conn.socket_io
  *   status = conn.connect_poll
  *   while(status != PG::PGRES_POLLING_OK) do
  *     # do some work while waiting for the connection to complete
  * Closes the backend connection.
  */
 static VALUE
-pgconn_finish(VALUE self)
+pgconn_finish( VALUE self )
 {
-	PQfinish(pg_get_pgconn(self));
-	DATA_PTR(self) = NULL;
+	pgconn_close_socket_io( self );
+	PQfinish( pg_get_pgconn(self) );
+	DATA_PTR( self ) = NULL;
 	return Qnil;
 }
 
  * backend connection and tries to re-connect.
  */
 static VALUE
-pgconn_reset(VALUE self)
+pgconn_reset( VALUE self )
 {
-	PQreset(pg_get_pgconn(self));
+	pgconn_close_socket_io( self );
+	PQreset( pg_get_pgconn(self) );
 	return self;
 }
 
 static VALUE
 pgconn_reset_start(VALUE self)
 {
+	pgconn_close_socket_io( self );
 	if(PQresetStart(pg_get_pgconn(self)) == 0)
 		rb_raise(rb_ePGerror, "reset has failed");
 	return Qnil;

spec/lib/helpers.rb

 		require 'pg'
 		stop_existing_postmasters()
 
-		puts "Setting up test database for #{description} tests"
+		puts "Setting up test database for #{description}"
 		@test_pgdata = TEST_DIRECTORY + 'data'
 		@test_pgdata.mkpath
 
 
 		conn = PG.connect( @conninfo )
 		conn.set_notice_processor do |message|
-			$stderr.puts( message ) if $DEBUG
+			$stderr.puts( description + ':' + message ) if $DEBUG
 		end
 
 		return conn
 
 	def teardown_testing_db( conn )
 		puts "Tearing down test database"
-		conn.finish if conn
+
+		# {"datid"=>"16697", "datname"=>"test", "pid"=>"42581", "usesysid"=>"10", "usename"=>"mgranger", "application_name"=>"/Users/mgranger/.rvm/gems/ruby-2.0.0-p0@pg/bin/rspec", "client_addr"=>"::1", "client_hostname"=>"", "client_port"=>"51962", "backend_start"=>"2013-03-03 06:43:48.03735-08", "xact_start"=>"2013-03-03 06:43:48.08376-08", "query_start"=>"2013-03-03 06:43:48.08376-08", "state_change"=>"2013-03-03 06:43:48.083761-08", "waiting"=>"f", "state"=>"active", "query"=>"SELECT * FROM pg_stat_activity"}
+
+		if conn
+			check_for_lingering_connections( conn )
+			conn.finish
+		end
+
 		log_and_run @logfile, 'pg_ctl', '-D', @test_pgdata.to_s, 'stop'
 	end
+
+
+	def check_for_lingering_connections( conn )
+		conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
+			conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
+			unless conns.empty?
+				puts "Lingering connections remain:"
+				conns.each do |row|
+					puts "  [%d] {%s} %s -- %s" % row.values_at( 'pid', 'state', 'application_name', 'query' )
+				end
+			end
+		end
+	end
 end
 
 
 
 	config.mock_with :rspec
 	config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
-	config.filter_run_excluding :unix if RUBY_PLATFORM =~ /mingw|mswin/
+	if RUBY_PLATFORM =~ /mingw|mswin/
+		config.filter_run_excluding :unix
+	else
+		config.filter_run_excluding :windows
+	end
 
 	config.filter_run_excluding :postgresql_90 unless
 		PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )

spec/pg/connection_spec.rb

 describe PG::Connection do
 
 	before( :all ) do
-		@conn = setup_testing_db( "PG_Connection" )
+		@conn = setup_testing_db( described_class.name )
 	end
 
 	before( :each ) do
 		@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
+		@conn.exec_params %Q{SET application_name TO '%s'} %
+			[@conn.escape_string(example.description[0,60])]
 	end
 
 	after( :each ) do
 		From backend> T
 		}.gsub( /^\t{2}/, '' ).lstrip
 
-	unless RUBY_PLATFORM =~ /mswin|mingw/
-		it "trace and untrace client-server communication" do
+	it "trace and untrace client-server communication", :unix do
 			# be careful to explicitly close files so that the
 			# directory can be removed and we don't have to wait for
 			# the GC to run.
 
 			trace_data.should == expected_trace_output
 		end
-	end
 
 	it "allows a query to be cancelled" do
 		error = false
 		error.should == true
 	end
 
-	it "automatically rolls back a transaction started with described_class#transaction if an exception " +
+	it "automatically rolls back a transaction started with Connection#transaction if an exception " +
 	   "is raised" do
 		# abort the per-example transaction so we can test our own
 		@conn.exec( 'ROLLBACK' )
 		expect { conn.finish }.to raise_error( PG::Error, /connection is closed/i )
 	end
 
+	it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction do
+		conn = PG.connect( @conninfo )
+		io = conn.socket_io
+		conn.finish
+		io.should be_closed()
+		expect { conn.socket_io }.to raise_error( PG::Error, /connection is closed/i )
+	end
+
+	it "closes the IO fetched from #socket_io when the connection is reset", :without_transaction do
+		conn = PG.connect( @conninfo )
+		io = conn.socket_io
+		conn.reset
+		io.should be_closed()
+		conn.socket_io.should_not equal( io )
+		conn.finish
+	end
+
 
 	context "under PostgreSQL 9", :postgresql_90 do