class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
The PostgreSQL adapter works with the native C (bitbucket.org/ged/ruby-pg) driver.
Options:
-
:host
- Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets, the default is to connect to localhost. -
:port
- Defaults to 5432. -
:username
- Defaults to be the same as the operating system name of the user running the application. -
:password
- Password to be used if the server demands password authentication. -
:database
- Defaults to be the same as the user name. -
:schema_search_path
- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the:schema_order
option. -
:encoding
- An optional client encoding that is used in aSET client_encoding TO <encoding>
call on the connection. -
:min_messages
- An optional client min messages that is used in aSET client_min_messages TO <min_messages>
call on the connection. -
:variables
- An optional hash of additional parameters that will be used inSET SESSION key = val
calls on the connection. -
:insert_returning
- An optional boolean to control the use ofRETURNING
forINSERT
statements defaults to true.
Any further options are used as connection parameters to libpq. See www.postgresql.org/docs/current/static/libpq-connect.html for the list of parameters.
In addition, default connection parameters of libpq can be set per environment variables. See www.postgresql.org/docs/current/static/libpq-envars.html .
Constants
- ADAPTER_NAME
- CACHED_PLAN_HEURISTIC
Annoyingly, the code for prepared statements whose return value may have changed is FEATURE_NOT_SUPPORTED.
This covers various different error types so we need to do additional work to classify the exception definitively as a ActiveRecord::PreparedStatementCacheExpired
Check here for more details: git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
- FOREIGN_KEY_VIOLATION
- NATIVE_DATABASE_TYPES
- UNIQUE_VIOLATION
- VALUE_LIMIT_VIOLATION
See www.postgresql.org/docs/current/static/errcodes-appendix.html
Public Class Methods
Initializes and connects a PostgreSQL adapter.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 208 def initialize(connection, logger, connection_parameters, config) super(connection, logger, config) @connection_parameters = connection_parameters # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @table_alias_length = nil connect add_pg_encoders @statements = StatementPool.new @connection, self.class.type_cast_config_to_integer(config[:statement_limit]) if postgresql_version < 90100 raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1." end add_pg_decoders @type_map = Type::HashLookupTypeMap.new initialize_type_map(type_map) @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"] @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true end
Public Instance Methods
Is this connection alive and ready for queries?
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 244 def active? @connection.query 'SELECT 1' true rescue PGError false end
Clears the prepared statements cache.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 235 def clear_cache! @statements.clear end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 338 def disable_extension(name) exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap { reload_type_map } end
Disconnects from the database if already connected. Otherwise, this method does nothing.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 270 def disconnect! super @connection.close rescue nil end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 332 def enable_extension(name) exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap { reload_type_map } end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 344 def extension_enabled?(name) if supports_extensions? res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", 'SCHEMA' res.cast_values.first end end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 352 def extensions if supports_extensions? exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values else super end end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 175 def index_algorithms { concurrently: 'CONCURRENTLY' } end
Returns the version of the connected PostgreSQL server.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 399 def postgresql_version @connection.server_version end
Close then reopen the connection.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 252 def reconnect! super @connection.reset configure_connection end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 258 def reset! clear_cache! reset_transaction unless @connection.transaction_status == ::PG::PQTRANS_IDLE @connection.query 'ROLLBACK' end @connection.query 'DISCARD ALL' configure_connection end
Set the authorized user for this session
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 366 def session_auth=(user) clear_cache! exec_query "SET SESSION AUTHORIZATION #{user}" end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 289 def set_standard_conforming_strings execute('SET standard_conforming_strings = on', 'SCHEMA') end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 297 def supports_advisory_locks? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 167 def supports_comments? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 159 def supports_datetime_with_precision? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 293 def supports_ddl_transactions? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 301 def supports_explain? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 143 def supports_expression_index? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 305 def supports_extensions? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 151 def supports_foreign_keys? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 135 def supports_index_sort_order? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 163 def supports_json? postgresql_version >= 90200 end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 314 def supports_materialized_views? postgresql_version >= 90300 end
Returns true, since this connection adapter supports migrations.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 280 def supports_migrations? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 139 def supports_partial_index? true end
Range datatypes weren't introduced until PostgreSQL 9.2
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 310 def supports_ranges? postgresql_version >= 90200 end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 171 def supports_savepoints? true end
Returns true, since this connection adapter supports prepared statement caching.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 131 def supports_statement_cache? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 147 def supports_transaction_isolation? true end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 155 def supports_views? true end
Returns the configured supported identifier length supported by PostgreSQL
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 361 def table_alias_length @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 239 def truncate(table_name, name = nil) exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, [] end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 371 def use_insert_returning? @use_insert_returning end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 375 def valid_type?(type) !native_database_types[type].nil? end
Protected Instance Methods
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 410 def translate_exception(exception, message) return exception unless exception.respond_to?(:result) case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE) when UNIQUE_VIOLATION RecordNotUnique.new(message) when FOREIGN_KEY_VIOLATION InvalidForeignKey.new(message) when VALUE_LIMIT_VIOLATION ValueTooLong.new(message) else super end end
Private Instance Methods
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 793 def add_pg_decoders coders_by_name = { 'int2' => PG::TextDecoder::Integer, 'int4' => PG::TextDecoder::Integer, 'int8' => PG::TextDecoder::Integer, 'oid' => PG::TextDecoder::Integer, 'float4' => PG::TextDecoder::Float, 'float8' => PG::TextDecoder::Float, 'bool' => PG::TextDecoder::Boolean, } known_coder_types = coders_by_name.keys.map { |n| quote(n) } query = " SELECT t.oid, t.typname FROM pg_type as t WHERE t.typname IN (%s) " % known_coder_types.join(", ") coders = execute_and_clear(query, "SCHEMA", []) do |result| result .map { |row| construct_coder(row, coders_by_name[row['typname']]) } .compact end map = PG::TypeMapByOid.new coders.each { |coder| map.add_coder(coder) } @connection.type_map_for_results = map end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 784 def add_pg_encoders map = PG::TypeMapByClass.new map[Integer] = PG::TextEncoder::Integer.new map[TrueClass] = PG::TextEncoder::Boolean.new map[FalseClass] = PG::TextEncoder::Boolean.new map[Float] = PG::TextEncoder::Float.new @connection.type_map_for_queries = map end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 762 def can_perform_case_insensitive_comparison_for?(column) @case_insensitive_cache ||= {} @case_insensitive_cache[column.sql_type] ||= begin sql = <<-end_sql SELECT exists( SELECT * FROM pg_proc WHERE proname = 'lower' AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector ) OR exists( SELECT * FROM pg_proc INNER JOIN pg_cast ON ARRAY[casttarget]::oidvector = proargtypes WHERE proname = 'lower' AND castsource = #{quote column.sql_type}::regtype ) end_sql execute_and_clear(sql, "SCHEMA", []) do |result| result.getvalue(0, 0) end end end
Configures the encoding, verbosity, schema search path, and time zone of the connection. This is called by connect and should not be called manually.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 683 def configure_connection if @config[:encoding] @connection.set_client_encoding(@config[:encoding]) end self.client_min_messages = @config[:min_messages] || 'warning' self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] # Use standard-conforming strings so we don't have to do the E'...' dance. set_standard_conforming_strings # If using Active Record's time zone support configure the connection to return # TIMESTAMP WITH ZONE types in UTC. # (SET TIME ZONE does not use an equals sign like other SET variables) if ActiveRecord::Base.default_timezone == :utc execute("SET time zone 'UTC'", 'SCHEMA') elsif @local_tz execute("SET time zone '#{@local_tz}'", 'SCHEMA') end # SET statements from :variables config hash # http://www.postgresql.org/docs/current/static/sql-set.html variables = @config[:variables] || {} variables.map do |k, v| if v == ':default' || v == :default # Sets the value to the global or compile default execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA') elsif !v.nil? execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA') end end end
Connects to a PostgreSQL server and sets up the adapter depending on the connected server's characteristics.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 670 def connect @connection = PGconn.connect(@connection_parameters) configure_connection rescue ::PG::Error => error if error.message.include?("does not exist") raise ActiveRecord::NoDatabaseError else raise end end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 820 def construct_coder(row, coder_class) return unless coder_class coder_class.new(oid: row['oid'].to_i, name: row['typname']) end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 601 def exec_cache(sql, name, binds) stmt_key = prepare_statement(sql) type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) } log(sql, name, binds, stmt_key) do @connection.exec_prepared(stmt_key, type_casted_binds) end rescue ActiveRecord::StatementInvalid => e raise unless is_cached_plan_failure?(e) # Nothing we can do if we are in a transaction because all commands # will raise InFailedSQLTransaction if in_transaction? raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message) else # outside of transactions we can simply flush this query and retry @statements.delete sql_key(sql) retry end end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 596 def exec_no_cache(sql, name, binds) type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) } log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) } end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 583 def execute_and_clear(sql, name, binds, prepare: false) if without_prepared_statement?(binds) result = exec_no_cache(sql, name, []) elsif !prepare result = exec_no_cache(sql, name, binds) else result = exec_cache(sql, name, binds) end ret = yield result result.clear ret end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 640 def in_transaction? open_transactions > 0 end
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 632 def is_cached_plan_failure?(e) pgerror = e.cause code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC) rescue false end
Prepare the statement if it hasn't been prepared, return the statement key.
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 652 def prepare_statement(sql) sql_key = sql_key(sql) unless @statements.key? sql_key nextkey = @statements.next_key begin @connection.prepare nextkey, sql rescue => e raise translate_exception_class(e, sql) end # Clear the queue @connection.get_last_result @statements[sql_key] = nextkey end @statements[sql_key] end
Returns the statement identifier for the client side cache of statements
# File lib/active_record/connection_adapters/postgresql_adapter.rb, line 646 def sql_key(sql) "#{schema_search_path}-#{sql}" end