Session variables for mysql, mysql2, and postgresql adapters can be set
in the new 'variables:' hash in each database config section in database.yml. The key-value pairs of this hash will be sent in a 'SET key = value, ...' query on new database connections. The configure_connection methods from mysql and mysql2 into are consolidated into the abstract_mysql base class.
This commit is contained in:
parent
0a33fcd68b
commit
97d06e8c4b
@ -1,5 +1,13 @@
|
|||||||
## Rails 4.0.0 (unreleased) ##
|
## Rails 4.0.0 (unreleased) ##
|
||||||
|
|
||||||
|
* Session variables can be set for the `mysql`, `mysql2`, and `postgresql` adapters
|
||||||
|
in the `variables: <hash>` parameter in `database.yml`. The key-value pairs of this
|
||||||
|
hash will be sent in a `SET key = value` query on new database connections. See also:
|
||||||
|
http://dev.mysql.com/doc/refman/5.0/en/set-statement.html
|
||||||
|
http://www.postgresql.org/docs/8.3/static/sql-set.html
|
||||||
|
|
||||||
|
*Aaron Stone*
|
||||||
|
|
||||||
* Allow `Relation#where` with no arguments to be chained with new `not` query method.
|
* Allow `Relation#where` with no arguments to be chained with new `not` query method.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -704,6 +704,45 @@ def column_for(table_name, column_name)
|
|||||||
end
|
end
|
||||||
column
|
column
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def configure_connection
|
||||||
|
variables = @config[:variables] || {}
|
||||||
|
|
||||||
|
# By default, MySQL 'where id is null' selects the last inserted id.
|
||||||
|
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
||||||
|
variables[:sql_auto_is_null] = 0
|
||||||
|
|
||||||
|
# Increase timeout so the server doesn't disconnect us.
|
||||||
|
wait_timeout = @config[:wait_timeout]
|
||||||
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
||||||
|
variables[:wait_timeout] = wait_timeout
|
||||||
|
|
||||||
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
||||||
|
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
||||||
|
# If the user has provided another value for sql_mode, don't replace it.
|
||||||
|
if strict_mode? && !variables.has_key?(:sql_mode)
|
||||||
|
variables[:sql_mode] = 'STRICT_ALL_TABLES'
|
||||||
|
end
|
||||||
|
|
||||||
|
# NAMES does not have an equals sign, see
|
||||||
|
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
||||||
|
# (trailing comma because variable_assignments will always have content)
|
||||||
|
encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
|
||||||
|
|
||||||
|
# Gather up all of the SET variables...
|
||||||
|
variable_assignments = variables.map do |k, v|
|
||||||
|
if v == ':default' || v == :default
|
||||||
|
"@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
|
||||||
|
elsif !v.nil?
|
||||||
|
"@@SESSION.#{k.to_s} = #{quote(v)}"
|
||||||
|
end
|
||||||
|
# or else nil; compact to clear nils out
|
||||||
|
end.compact.join(', ')
|
||||||
|
|
||||||
|
# ...and send them all in one query
|
||||||
|
execute("SET #{encoding} #{variable_assignments}", :skip_logging)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -251,27 +251,7 @@ def connect
|
|||||||
|
|
||||||
def configure_connection
|
def configure_connection
|
||||||
@connection.query_options.merge!(:as => :array)
|
@connection.query_options.merge!(:as => :array)
|
||||||
|
super
|
||||||
# By default, MySQL 'where id is null' selects the last inserted id.
|
|
||||||
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
|
||||||
variable_assignments = ['SQL_AUTO_IS_NULL=0']
|
|
||||||
|
|
||||||
# Make MySQL reject illegal values rather than truncating or
|
|
||||||
# blanking them. See
|
|
||||||
# http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
|
|
||||||
variable_assignments << "SQL_MODE='STRICT_ALL_TABLES'" if strict_mode?
|
|
||||||
|
|
||||||
encoding = @config[:encoding]
|
|
||||||
|
|
||||||
# make sure we set the encoding
|
|
||||||
variable_assignments << "NAMES '#{encoding}'" if encoding
|
|
||||||
|
|
||||||
# increase timeout so mysql server doesn't disconnect us
|
|
||||||
wait_timeout = @config[:wait_timeout]
|
|
||||||
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
|
||||||
variable_assignments << "@@wait_timeout = #{wait_timeout}"
|
|
||||||
|
|
||||||
execute("SET #{variable_assignments.join(', ')}", :skip_logging)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def version
|
def version
|
||||||
|
@ -51,7 +51,8 @@ module ConnectionAdapters
|
|||||||
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
||||||
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
||||||
# * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
|
# * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
|
||||||
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html)
|
# * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html)
|
||||||
|
# * <tt>:variables</tt> - (Optional) A hash session variables to send as `SET @@SESSION.key = value` on each database connection. Use the value `:default` to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
|
||||||
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
||||||
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
||||||
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
||||||
@ -535,18 +536,10 @@ def connect
|
|||||||
configure_connection
|
configure_connection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Many Rails applications monkey-patch a replacement of the configure_connection method
|
||||||
|
# and don't call 'super', so leave this here even though it looks superfluous.
|
||||||
def configure_connection
|
def configure_connection
|
||||||
encoding = @config[:encoding]
|
super
|
||||||
execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
|
|
||||||
|
|
||||||
# By default, MySQL 'where id is null' selects the last inserted id.
|
|
||||||
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
|
||||||
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
|
|
||||||
|
|
||||||
# Make MySQL reject illegal values rather than truncating or
|
|
||||||
# blanking them. See
|
|
||||||
# http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
|
|
||||||
execute("SET SQL_MODE='STRICT_ALL_TABLES'", :skip_logging) if strict_mode?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def select(sql, name = nil, binds = [])
|
def select(sql, name = nil, binds = [])
|
||||||
|
@ -24,7 +24,7 @@ def postgresql_connection(config) # :nodoc:
|
|||||||
# Forward any unused config params to PGconn.connect.
|
# Forward any unused config params to PGconn.connect.
|
||||||
[:statement_limit, :encoding, :min_messages, :schema_search_path,
|
[:statement_limit, :encoding, :min_messages, :schema_search_path,
|
||||||
:schema_order, :adapter, :pool, :checkout_timeout, :template,
|
:schema_order, :adapter, :pool, :checkout_timeout, :template,
|
||||||
:reaping_frequency, :insert_returning].each do |key|
|
:reaping_frequency, :insert_returning, :variables].each do |key|
|
||||||
conn_params.delete key
|
conn_params.delete key
|
||||||
end
|
end
|
||||||
conn_params.delete_if { |k,v| v.nil? }
|
conn_params.delete_if { |k,v| v.nil? }
|
||||||
@ -238,6 +238,8 @@ def simplified_type(field_type)
|
|||||||
# <encoding></tt> call on the connection.
|
# <encoding></tt> call on the connection.
|
||||||
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
|
# * <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.
|
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
|
||||||
|
# * <tt>:variables</tt> - An optional hash of additional parameters that
|
||||||
|
# will be used in <tt>SET SESSION key = val</tt> calls on the connection.
|
||||||
# * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
|
# * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
|
||||||
# defaults to true.
|
# defaults to true.
|
||||||
#
|
#
|
||||||
@ -706,11 +708,24 @@ def configure_connection
|
|||||||
|
|
||||||
# If using Active Record's time zone support configure the connection to return
|
# If using Active Record's time zone support configure the connection to return
|
||||||
# TIMESTAMP WITH ZONE types in UTC.
|
# 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
|
if ActiveRecord::Base.default_timezone == :utc
|
||||||
execute("SET time zone 'UTC'", 'SCHEMA')
|
execute("SET time zone 'UTC'", 'SCHEMA')
|
||||||
elsif @local_tz
|
elsif @local_tz
|
||||||
execute("SET time zone '#{@local_tz}'", 'SCHEMA')
|
execute("SET time zone '#{@local_tz}'", 'SCHEMA')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# SET statements from :variables config hash
|
||||||
|
# http://www.postgresql.org/docs/8.3/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_s} TO DEFAULT", 'SCHEMA')
|
||||||
|
elsif !v.nil?
|
||||||
|
execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the current ID of a table's sequence.
|
# Returns the current ID of a table's sequence.
|
||||||
|
@ -137,6 +137,23 @@ def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_mysql_set_session_variable
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
|
||||||
|
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
|
||||||
|
assert_equal 3, session_mode.rows.first.first.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_mysql_set_session_variable_to_default
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => :default}}))
|
||||||
|
global_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.DEFAULT_WEEK_FORMAT"
|
||||||
|
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
|
||||||
|
assert_equal global_mode.rows, session_mode.rows
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def run_without_connection
|
def run_without_connection
|
||||||
|
@ -53,6 +53,23 @@ def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_mysql_set_session_variable
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
|
||||||
|
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
|
||||||
|
assert_equal 3, session_mode.rows.first.first.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_mysql_set_session_variable_to_default
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => :default}}))
|
||||||
|
global_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.DEFAULT_WEEK_FORMAT"
|
||||||
|
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
|
||||||
|
assert_equal global_mode.rows, session_mode.rows
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_logs_name_structure_dump
|
def test_logs_name_structure_dump
|
||||||
@connection.structure_dump
|
@connection.structure_dump
|
||||||
assert_equal "SCHEMA", @connection.logged[0][1]
|
assert_equal "SCHEMA", @connection.logged[0][1]
|
||||||
|
@ -154,5 +154,46 @@ def test_reconnection_after_actual_disconnection_with_verify
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_set_session_variable_true
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => true}}))
|
||||||
|
set_true = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
|
||||||
|
assert_equal set_true.rows, [["on"]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_set_session_variable_false
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => false}}))
|
||||||
|
set_false = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
|
||||||
|
assert_equal set_false.rows, [["off"]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_set_session_variable_nil
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
# This should be a no-op that does not raise an error
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => nil}}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_set_session_variable_default
|
||||||
|
run_without_connection do |orig_connection|
|
||||||
|
# This should execute a query that does not raise an error
|
||||||
|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => :default}}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def run_without_connection
|
||||||
|
original_connection = ActiveRecord::Base.remove_connection
|
||||||
|
begin
|
||||||
|
yield original_connection
|
||||||
|
ensure
|
||||||
|
ActiveRecord::Base.establish_connection(original_connection)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user