diff options
Diffstat (limited to 'activerecord')
6 files changed, 92 insertions, 86 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 42c794c828..431fe25501 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -40,8 +40,9 @@ module ActiveRecord # Returns a single value from a record def select_value(arel, name = nil, binds = []) - if result = select_one(arel, name, binds) - result.values.first + arel, binds = binds_from_relation arel, binds + if result = select_rows(to_sql(arel, binds), name, binds).first + result.first end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 09aee1f467..8c2b4ccac4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -837,19 +837,6 @@ module ActiveRecord MysqlTypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?) end - # MySQL is too stupid to create a temporary table for use subquery, so we have - # to give it some prompting in the form of a subsubquery. Ugh! - def subquery_for(key, select) - subsubselect = select.clone - subsubselect.projections = [key] - - subselect = Arel::SelectManager.new(select.engine) - subselect.project Arel.sql(key.name) - # Materialized subquery by adding distinct - # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on' - subselect.from subsubselect.distinct.as('__active_record_temp') - end - def add_index_length(option_strings, column_names, options = {}) if options.is_a?(Hash) && length = options[:length] case length @@ -951,6 +938,19 @@ module ActiveRecord private + # MySQL is too stupid to create a temporary table for use subquery, so we have + # to give it some prompting in the form of a subsubquery. Ugh! + def subquery_for(key, select) + subsubselect = select.clone + subsubselect.projections = [key] + + subselect = Arel::SelectManager.new(select.engine) + subselect.project Arel.sql(key.name) + # Materialized subquery by adding distinct + # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on' + subselect.from subsubselect.distinct.as('__active_record_temp') + end + def version @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map(&:to_i) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 662c6b4d38..686671b007 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -70,7 +70,7 @@ module ActiveRecord # Returns the list of all tables in the schema search path or a specified schema. def tables(name = nil) - query(<<-SQL, 'SCHEMA').map { |row| row[0] } + select_values(<<-SQL, 'SCHEMA') SELECT tablename FROM pg_tables WHERE schemaname = ANY (current_schemas(false)) @@ -84,7 +84,7 @@ module ActiveRecord name = Utils.extract_schema_qualified_name(name.to_s) return false unless name.identifier - exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0 + select_value(<<-SQL, 'SCHEMA').to_i > 0 SELECT COUNT(*) FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace @@ -100,7 +100,7 @@ module ActiveRecord # Returns true if schema exists. def schema_exists?(name) - exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0 + select_value(<<-SQL, 'SCHEMA').to_i > 0 SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}' @@ -109,7 +109,7 @@ module ActiveRecord # Verifies existence of an index with a given name. def index_name_exists?(table_name, index_name, default) - exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0 + select_value(<<-SQL, 'SCHEMA').to_i > 0 SELECT COUNT(*) FROM pg_class t INNER JOIN pg_index d ON t.oid = d.indrelid @@ -182,17 +182,17 @@ module ActiveRecord # Returns the current database name. def current_database - query('select current_database()', 'SCHEMA')[0][0] + select_value('select current_database()', 'SCHEMA') end # Returns the current schema name. def current_schema - query('SELECT current_schema', 'SCHEMA')[0][0] + select_value('SELECT current_schema', 'SCHEMA') end # Returns the current database encoding format. def encoding - query(<<-end_sql, 'SCHEMA')[0][0] + select_value(<<-end_sql, 'SCHEMA') SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database WHERE pg_database.datname LIKE '#{current_database}' end_sql @@ -200,21 +200,21 @@ module ActiveRecord # Returns the current database collation. def collation - query(<<-end_sql, 'SCHEMA')[0][0] + select_value(<<-end_sql, 'SCHEMA') SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}' end_sql end # Returns the current database ctype. def ctype - query(<<-end_sql, 'SCHEMA')[0][0] + select_value(<<-end_sql, 'SCHEMA') SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}' end_sql end # Returns an array of schema names. def schema_names - query(<<-SQL, 'SCHEMA').flatten + select_values(<<-SQL, 'SCHEMA') SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*' @@ -247,12 +247,12 @@ module ActiveRecord # Returns the active schema search path. def schema_search_path - @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0] + @schema_search_path ||= select_value('SHOW search_path', 'SCHEMA') end # Returns the current client message level. def client_min_messages - query('SHOW client_min_messages', 'SCHEMA')[0][0] + select_value('SHOW client_min_messages', 'SCHEMA') end # Set the client message level. @@ -270,10 +270,7 @@ module ActiveRecord end def serial_sequence(table, column) - result = exec_query(<<-eosql, 'SCHEMA') - SELECT pg_get_serial_sequence('#{table}', '#{column}') - eosql - result.rows.first.first + select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA') end # Sets the sequence of a table's primary key to the specified value. @@ -284,9 +281,7 @@ module ActiveRecord if sequence quoted_sequence = quote_table_name(sequence) - select_value <<-end_sql, 'SCHEMA' - SELECT setval('#{quoted_sequence}', #{value}) - end_sql + select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA') else @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger end @@ -309,7 +304,7 @@ module ActiveRecord if pk && sequence quoted_sequence = quote_table_name(sequence) - select_value <<-end_sql, 'SCHEMA' + select_value(<<-end_sql, 'SCHEMA') SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false) end_sql end @@ -371,7 +366,7 @@ module ActiveRecord # Returns just a table's primary key def primary_key(table) - pks = exec_query(<<-end_sql, 'SCHEMA').rows + pks = query(<<-end_sql, 'SCHEMA') SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 7e15c2ab26..2b33a5b9cb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -770,7 +770,7 @@ module ActiveRecord # - format_type includes the column size constraint, e.g. varchar(50) # - ::regclass is a function that gives the id for a table name def column_definitions(table_name) # :nodoc: - exec_query(<<-end_sql, 'SCHEMA').rows + query(<<-end_sql, 'SCHEMA') SELECT a.attname, format_type(a.atttypid, a.atttypmod), pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod, (SELECT c.collname FROM pg_collation c, pg_type t diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 1ad910c4bc..8a014e682e 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -88,9 +88,10 @@ module ActiveRecord ## # :singleton-method: # Specifies which database schemas to dump when calling db:structure:dump. - # If :schema_search_path (the default), it will dumps any schemas listed in schema_search_path. - # Use :all to always dumps all schemas regardless of the schema_search_path. - # A string of comma separated schemas can also be used to pass a custom list of schemas. + # If the value is :schema_search_path (the default), any schemas listed in + # schema_search_path are dumped. Use :all to dump all schemas regardless + # of schema_search_path, or a string of comma separated schemas for a + # custom list. mattr_accessor :dump_schemas, instance_writer: false self.dump_schemas = :schema_search_path diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 3268555cb8..278fa63e04 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -7,15 +7,32 @@ require 'models/subscriber' require 'models/vegetables' require 'models/shop' +module InheritanceTestHelper + def with_store_full_sti_class(&block) + assign_store_full_sti_class true, &block + end + + def without_store_full_sti_class(&block) + assign_store_full_sti_class false, &block + end + + def assign_store_full_sti_class(flag) + old_store_full_sti_class = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = flag + yield + ensure + ActiveRecord::Base.store_full_sti_class = old_store_full_sti_class + end +end + class InheritanceTest < ActiveRecord::TestCase + include InheritanceTestHelper fixtures :companies, :projects, :subscribers, :accounts, :vegetables def test_class_with_store_full_sti_class_returns_full_name - old = ActiveRecord::Base.store_full_sti_class - ActiveRecord::Base.store_full_sti_class = true - assert_equal 'Namespaced::Company', Namespaced::Company.sti_name - ensure - ActiveRecord::Base.store_full_sti_class = old + with_store_full_sti_class do + assert_equal 'Namespaced::Company', Namespaced::Company.sti_name + end end def test_class_with_blank_sti_name @@ -33,39 +50,31 @@ class InheritanceTest < ActiveRecord::TestCase end def test_class_without_store_full_sti_class_returns_demodulized_name - old = ActiveRecord::Base.store_full_sti_class - ActiveRecord::Base.store_full_sti_class = false - assert_equal 'Company', Namespaced::Company.sti_name - ensure - ActiveRecord::Base.store_full_sti_class = old + without_store_full_sti_class do + assert_equal 'Company', Namespaced::Company.sti_name + end end def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled - old = ActiveRecord::Base.store_full_sti_class - ActiveRecord::Base.store_full_sti_class = false - item = Namespaced::Company.new - assert_equal 'Company', item[:type] - ensure - ActiveRecord::Base.store_full_sti_class = old + without_store_full_sti_class do + item = Namespaced::Company.new + assert_equal 'Company', item[:type] + end end def test_should_store_full_class_name_with_store_full_sti_class_option_enabled - old = ActiveRecord::Base.store_full_sti_class - ActiveRecord::Base.store_full_sti_class = true - item = Namespaced::Company.new - assert_equal 'Namespaced::Company', item[:type] - ensure - ActiveRecord::Base.store_full_sti_class = old + with_store_full_sti_class do + item = Namespaced::Company.new + assert_equal 'Namespaced::Company', item[:type] + end end def test_different_namespace_subclass_should_load_correctly_with_store_full_sti_class_option - old = ActiveRecord::Base.store_full_sti_class - ActiveRecord::Base.store_full_sti_class = true - item = Namespaced::Company.create :name => "Wolverine 2" - assert_not_nil Company.find(item.id) - assert_not_nil Namespaced::Company.find(item.id) - ensure - ActiveRecord::Base.store_full_sti_class = old + with_store_full_sti_class do + item = Namespaced::Company.create name: "Wolverine 2" + assert_not_nil Company.find(item.id) + assert_not_nil Namespaced::Company.find(item.id) + end end def test_company_descends_from_active_record @@ -331,6 +340,7 @@ class InheritanceTest < ActiveRecord::TestCase end class InheritanceComputeTypeTest < ActiveRecord::TestCase + include InheritanceTestHelper fixtures :companies def setup @@ -344,27 +354,26 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase end def test_instantiation_doesnt_try_to_require_corresponding_file - ActiveRecord::Base.store_full_sti_class = false - foo = Firm.first.clone - foo.type = 'FirmOnTheFly' - foo.save! + without_store_full_sti_class do + foo = Firm.first.clone + foo.type = 'FirmOnTheFly' + foo.save! - # Should fail without FirmOnTheFly in the type condition. - assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) } + # Should fail without FirmOnTheFly in the type condition. + assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) } - # Nest FirmOnTheFly in the test case where Dependencies won't see it. - self.class.const_set :FirmOnTheFly, Class.new(Firm) - assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) } + # Nest FirmOnTheFly in the test case where Dependencies won't see it. + self.class.const_set :FirmOnTheFly, Class.new(Firm) + assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) } - # Nest FirmOnTheFly in Firm where Dependencies will see it. - # This is analogous to nesting models in a migration. - Firm.const_set :FirmOnTheFly, Class.new(Firm) + # Nest FirmOnTheFly in Firm where Dependencies will see it. + # This is analogous to nesting models in a migration. + Firm.const_set :FirmOnTheFly, Class.new(Firm) - # And instantiate will find the existing constant rather than trying - # to require firm_on_the_fly. - assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) } - ensure - ActiveRecord::Base.store_full_sti_class = true + # And instantiate will find the existing constant rather than trying + # to require firm_on_the_fly. + assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) } + end end def test_sti_type_from_attributes_disabled_in_non_sti_class |