Skip to content

Loading…

PostgreSQL: Allow consistent setting of libpq params in database connection specification #4315

Merged
merged 3 commits into from

4 participants

@larskanis

The PostgreSQL connection adapter is based on pg.gem (for two years now), which uses libpq to connect to the server. Several connection parameters can be given to PGconn and so to libpq, but only some parameters can be used in ActiveRecords connection specification. Furthermore libpq inspects several environment variables for setting default values. The list of parameters varies between different PostgreSQL versions.

Current behavior of connection settings for adapter postgresql is inconsistent. Some connection parameters can be set by the connection specification only (:port, :database) but not per environment variable (PGPORT, PGDATABASE). Some parameters can be set by both (:host/PGHOST, :username/PGUSER, :password/PASSWORD) and some parameters can only be set per env (PGOPTIONS, PGSSLKEY, PGSERVICE, ...).

On the other hand, using environment variables is quite difficult, if one needs different connections to several databases at the same time. It conflicts with the connection pooling and on demand connectivity, since ActiveRecords ConnectionSpecitfication doesn't take these settings into account.

The attached patch passes all parameters not recognized by ActiveRecord through to PGconn, so every (possibly future) libpq parameter can be used in the connection specification. Furthermore it updates the documentation and adds a test case for setting a specific libpq parameter. I did not add a test case for using environment variables, because I guess it's out of scope of ActiveRecord.

@tenderlove tenderlove merged commit 351a600 into rails:master
@mokolabs

Thanks for fixing this. I wasted a few hours trying to deploy a simple Sinatra app to Heroku that refused to connect to the shared database.

I kept getting this error but couldn't figure out why:
ActiveRecord::StatementInvalid: PGError: ERROR: invalid value for parameter "client_min_messages": ""

Since I wasn't setting a value for :min_messages, it was being set to "", and hence the error.

:min_messages isn't touched by this commit - only the values that are used for PGconn.connect. But you're right, also for the other parameters, nil values should be handled equal to not defined keys for consistency.

@jeshuaborges

This commit seems to change some previously working behavior where the connection_parameters could have a'socket parameter. This seems to have changed to reuse host. I was unable to find this documentation in the change log. This change might be worth documenting.

The mysql adapter has a socket parameter, but AFAIK the postgresql adapter always used host.

Im sorry I didn't explain the issue I am having well, and the more I think about it my thought about adding documentation for this change doesn't make sense. So feel free to disregard. Thanks for replying quickly, and allowing the passing of additional params to PGconn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
41 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -11,20 +11,20 @@ module ConnectionHandling
# Establishes a connection to the database that's used by all Active Record objects
def postgresql_connection(config) # :nodoc:
config = config.symbolize_keys
- host = config[:host]
- port = config[:port] || 5432
- username = config[:username].to_s if config[:username]
- password = config[:password].to_s if config[:password]
- if config.key?(:database)
- database = config[:database]
- else
- raise ArgumentError, "No database specified. Missing argument: database."
- end
+ # Forward any unused config params to PGconn.connect.
+ conn_params = config.except(:statement_limit, :encoding, :min_messages,
+ :schema_search_path, :schema_order,
+ :adapter, :pool, :wait_timeout)
+ conn_params.delete_if { |k,v| v.nil? }
+
+ # Map ActiveRecords param names to PGs.
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
# The postgres drivers don't allow the creation of an unconnected PGconn object,
# so just pass a nil connection object for the time being.
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
end
end
@@ -226,22 +226,29 @@ def self.extract_value_from_default(default)
end
end
- # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
- # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
#
# Options:
#
- # * <tt>:host</tt> - Defaults to "localhost".
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
+ # the default is to connect to localhost.
# * <tt>:port</tt> - Defaults to 5432.
- # * <tt>:username</tt> - Defaults to nothing.
- # * <tt>:password</tt> - Defaults to nothing.
- # * <tt>:database</tt> - The name of the database. No default, must be provided.
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
+ #
+ # Any further options are used as connection parameters to libpq. See
+ # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
+ # list of parameters.
+ #
+ # In addition, default connection parameters of libpq can be set per environment variables.
+ # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
def xml(*args)
@@ -1200,7 +1207,7 @@ def prepare_statement(sql)
# Connects to a PostgreSQL server and sets up the adapter depending on the
# connected server's characteristics.
def connect
- @connection = PGconn.connect(*@connection_parameters)
+ @connection = PGconn.connect(@connection_parameters)
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
View
15 activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -2,6 +2,9 @@
module ActiveRecord
class PostgresqlConnectionTest < ActiveRecord::TestCase
+ class NonExistentTable < ActiveRecord::Base
+ end
+
def setup
super
@connection = ActiveRecord::Base.connection
@@ -10,5 +13,17 @@ def setup
def test_encoding
assert_not_nil @connection.encoding
end
+
+ # Ensure, we can set connection params using the example of Generic
+ # Query Optimizer (geqo). It is 'on' per default.
+ def test_connection_options
+ params = ActiveRecord::Base.connection_config.dup
+ params[:options] = "-c geqo=off"
+ NonExistentTable.establish_connection(params)
+
+ # Verify the connection param has been applied.
+ expect = NonExistentTable.connection.query('show geqo').first.first
+ assert_equal 'off', expect
+ end
end
end
Something went wrong with that request. Please try again.