Skip to content

Commit dc4420c

Browse files
committed
Define AbstractAdapter#configure_connection
All the adapters do this, so let's just accept that it's part of the necessary structure. Doing this helps AbstractAdapter have better control of the order of operations immediately after a reconnect.
1 parent 4975e0b commit dc4420c

File tree

4 files changed

+79
-50
lines changed

4 files changed

+79
-50
lines changed

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def initialize(connection, logger = nil, config = {}) # :nodoc:
112112
)
113113

114114
@default_timezone = self.class.validate_default_timezone(config[:default_timezone])
115+
116+
configure_connection
115117
end
116118

117119
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
@@ -550,11 +552,13 @@ def active?
550552
end
551553

552554
# Disconnects from the database if already connected, and establishes a
553-
# new connection with the database. Implementors should call super if they
554-
# override the default implementation.
555+
# new connection with the database. Implementors should call super
556+
# immediately after establishing the new connection (and while still
557+
# holding @lock).
555558
def reconnect!
556559
clear_cache!(new_connection: true)
557560
reset_transaction
561+
configure_connection
558562
end
559563

560564
# Disconnects from the database if already connected. Otherwise, this
@@ -584,10 +588,14 @@ def discard!
584588
# transactions and other connection-related server-side state. Usually a
585589
# database-dependent operation.
586590
#
587-
# The default implementation does nothing; the implementation should be
588-
# overridden by concrete adapters.
591+
# If a database driver or protocol does not support such a feature,
592+
# implementors may alias this to #reconnect!. Otherwise, implementors
593+
# should call super immediately after resetting the connection (and while
594+
# still holding @lock).
589595
def reset!
590-
# this should be overridden by concrete adapters
596+
clear_cache!(new_connection: true)
597+
reset_transaction
598+
configure_connection
591599
end
592600

593601
# Removes the connection from the pool and disconnect it.
@@ -883,6 +891,16 @@ def build_statement_pool
883891
def build_result(columns:, rows:, column_types: {})
884892
ActiveRecord::Result.new(columns, rows, column_types)
885893
end
894+
895+
# Perform any necessary initialization upon the newly-established
896+
# @raw_connection -- this is the place to modify the adapter's
897+
# connection settings, run queries to configure any application-global
898+
# "session" variables, etc.
899+
#
900+
# Implementations may assume this method will only be called while
901+
# holding @lock (or from #initialize).
902+
def configure_connection
903+
end
886904
end
887905
end
888906
end

activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def initialize(connection, logger, connection_options, config)
5959
check_prepared_statements_deprecation(config)
6060
superclass_config = config.reverse_merge(prepared_statements: false)
6161
super(connection, logger, connection_options, superclass_config)
62-
configure_connection
6362
end
6463

6564
def self.database_exists?(config)
@@ -125,9 +124,11 @@ def active?
125124
end
126125

127126
def reconnect!
128-
super
129-
disconnect!
130-
connect
127+
@lock.synchronize do
128+
disconnect!
129+
connect
130+
super
131+
end
131132
end
132133
alias :reset! :reconnect!
133134

@@ -155,7 +156,6 @@ def check_prepared_statements_deprecation(config)
155156

156157
def connect
157158
@raw_connection = self.class.new_client(@config)
158-
configure_connection
159159
end
160160

161161
def configure_connection

activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -283,21 +283,13 @@ def connection_active?
283283

284284
# Initializes and connects a PostgreSQL adapter.
285285
def initialize(connection, logger, connection_parameters, config)
286-
super(connection, logger, config)
287-
288286
@connection_parameters = connection_parameters || {}
289287

290-
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
291-
@local_tz = nil
292288
@max_identifier_length = nil
289+
@type_map = nil
293290

294-
configure_connection
295-
add_pg_encoders
296-
add_pg_decoders
291+
super(connection, logger, config)
297292

298-
@type_map = Type::HashLookupTypeMap.new
299-
initialize_type_map
300-
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
301293
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
302294
end
303295

@@ -318,31 +310,38 @@ def active?
318310
end
319311

320312
def reload_type_map # :nodoc:
321-
type_map.clear
322-
initialize_type_map
313+
@lock.synchronize do
314+
if @type_map
315+
type_map.clear
316+
else
317+
@type_map = Type::HashLookupTypeMap.new
318+
end
319+
320+
initialize_type_map
321+
end
323322
end
324323

325324
# Close then reopen the connection.
326325
def reconnect!
327326
@lock.synchronize do
328-
@raw_connection.reset
327+
begin
328+
@raw_connection.reset
329+
rescue PG::ConnectionBad
330+
connect
331+
end
332+
329333
super
330-
configure_connection
331-
reload_type_map
332-
rescue PG::ConnectionBad
333-
connect
334334
end
335335
end
336336

337337
def reset!
338338
@lock.synchronize do
339-
reset_transaction
340339
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
341340
@raw_connection.query "ROLLBACK"
342341
end
343342
@raw_connection.query "DISCARD ALL"
344-
clear_cache!(new_connection: true)
345-
configure_connection
343+
344+
super
346345
end
347346
end
348347

@@ -853,9 +852,6 @@ def prepare_statement(sql, binds)
853852
# connected server's characteristics.
854853
def connect
855854
@raw_connection = self.class.new_client(@connection_parameters)
856-
configure_connection
857-
add_pg_encoders
858-
add_pg_decoders
859855
end
860856

861857
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -872,16 +868,6 @@ def configure_connection
872868

873869
variables = @config.fetch(:variables, {}).stringify_keys
874870

875-
# If using Active Record's time zone support configure the connection to return
876-
# TIMESTAMP WITH ZONE types in UTC.
877-
unless variables["timezone"]
878-
if default_timezone == :utc
879-
variables["timezone"] = "UTC"
880-
elsif @local_tz
881-
variables["timezone"] = @local_tz
882-
end
883-
end
884-
885871
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
886872
execute("SET intervalstyle = iso_8601", "SCHEMA")
887873

@@ -895,6 +881,28 @@ def configure_connection
895881
execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
896882
end
897883
end
884+
885+
add_pg_encoders
886+
add_pg_decoders
887+
888+
reload_type_map
889+
end
890+
891+
def reconfigure_connection_timezone
892+
variables = @config.fetch(:variables, {}).stringify_keys
893+
894+
# If it's been directly configured as a connection variable, we don't
895+
# need to do anything here; it will be set up by configure_connection
896+
# and then never changed.
897+
return if variables["timezone"]
898+
899+
# If using Active Record's time zone support configure the connection
900+
# to return TIMESTAMP WITH ZONE types in UTC.
901+
if default_timezone == :utc
902+
execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
903+
else
904+
execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
905+
end
898906
end
899907

900908
# Returns the list of a table's column names, data types, and default values.
@@ -987,7 +995,7 @@ def update_typemap_for_default_timezone
987995

988996
# if default timezone has changed, we need to reconfigure the connection
989997
# (specifically, the session time zone)
990-
configure_connection
998+
reconfigure_connection_timezone
991999
end
9921000
end
9931001

activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ def dealloc(stmt)
8888
def initialize(connection, logger, connection_options, config)
8989
@memory_database = config[:database] == ":memory:"
9090
super(connection, logger, config)
91-
configure_connection
9291
end
9392

9493
def self.database_exists?(config)
@@ -165,12 +164,17 @@ def active?
165164
end
166165

167166
def reconnect!
168-
unless @raw_connection.closed?
169-
@raw_connection.rollback rescue nil
167+
@lock.synchronize do
168+
if active?
169+
@raw_connection.rollback rescue nil
170+
else
171+
connect
172+
end
173+
174+
super
170175
end
171-
super
172-
connect if @raw_connection.closed?
173176
end
177+
alias :reset! :reconnect!
174178

175179
# Disconnects from the database if already connected. Otherwise, this
176180
# method does nothing.
@@ -608,7 +612,6 @@ def connect
608612
@config[:database].to_s,
609613
@config.merge(results_as_hash: true)
610614
)
611-
configure_connection
612615
end
613616

614617
def configure_connection

0 commit comments

Comments
 (0)