From bf1494a1018a0bdc50dac4e87fdbf4b6b03083fa Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 8 Apr 2019 17:43:56 +0900 Subject: [PATCH] Fix GROUP BY with calculate longer name field to respect `table_alias_length` Follow up of c9e4c848eeeb8999b778fa1ae52185ca5537fffe. --- .../connection_adapters/abstract/database_limits.rb | 12 ++++++++---- .../connection_adapters/mysql/schema_statements.rb | 4 ++++ .../connection_adapters/postgresql_adapter.rb | 2 -- .../lib/active_record/relation/calculations.rb | 11 ++++++----- activerecord/test/cases/calculations_test.rb | 11 +++++++++++ activerecord/test/schema/schema.rb | 1 + 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb index 1305216be2..75e959045e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -5,20 +5,24 @@ module ActiveRecord module ConnectionAdapters # :nodoc: module DatabaseLimits + def max_identifier_length # :nodoc: + 64 + end + # Returns the maximum length of a table alias. def table_alias_length - 255 + max_identifier_length end # Returns the maximum length of a column name. def column_name_length - 64 + max_identifier_length end deprecate :column_name_length # Returns the maximum length of a table name. def table_name_length - 64 + max_identifier_length end deprecate :table_name_length @@ -33,7 +37,7 @@ def allowed_index_name_length # Returns the maximum length of an index name. def index_name_length - 64 + max_identifier_length end # Returns the maximum number of columns per table. diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index a1f90a0642..ad3a8d1fd9 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -121,6 +121,10 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, size: limit_to_siz sql end + def table_alias_length + 256 # https://dev.mysql.com/doc/refman/8.0/en/identifiers.html + end + private CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"] diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 0ed4e61d18..f8c2e48808 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -400,8 +400,6 @@ def extensions def max_identifier_length @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i end - alias table_alias_length max_identifier_length - alias index_name_length max_identifier_length # Set the authorized user for this session def session_auth=(user) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 910c6b3214..801e312658 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -319,12 +319,11 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc: group_aliases = group_fields.map { |field| field = connection.visitor.compile(field) if Arel.arel_node?(field) - column_alias_for(field) + column_alias_for(field.to_s.downcase) } group_columns = group_aliases.zip(group_fields) - aggregate_alias = "#{operation}_#{column_name.to_s.downcase}" - aggregate_alias = column_alias_for(aggregate_alias) unless aggregate_alias.match?(/\A\w+\z/) + aggregate_alias = column_alias_for("#{operation}_#{column_name.to_s.downcase}") select_values = [ operation_over_aggregate_column( @@ -369,7 +368,7 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc: end] end - # Converts the given keys to the value that the database adapter returns as + # Converts the given field to the value that the database adapter returns as # a usable column name: # # column_alias_for("users.id") # => "users_id" @@ -377,7 +376,9 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc: # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id" # column_alias_for("count(*)") # => "count_all" def column_alias_for(field) - column_alias = field.to_s.downcase + return field if field.match?(/\A\w{,#{connection.table_alias_length}}\z/) + + column_alias = +field column_alias.gsub!(/\*/, "all") column_alias.gsub!(/\W+/, " ") column_alias.strip! diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index c4070fc341..16c2a3661d 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -363,6 +363,17 @@ def test_should_group_by_fields_with_table_alias assert_equal 60, c[2] end + def test_should_calculate_grouped_with_longer_field + field = "a" * Account.connection.max_identifier_length + + Account.update_all("#{field} = credit_limit") + + c = Account.group(:firm_id).sum(field) + assert_equal 50, c[1] + assert_equal 105, c[6] + assert_equal 60, c[2] + end + def test_should_calculate_with_invalid_field assert_equal 6, Account.calculate(:count, "*") assert_equal 6, Account.calculate(:count, :all) diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index ead4de2a13..548671045b 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -19,6 +19,7 @@ t.references :firm, index: false t.string :firm_name t.integer :credit_limit + t.integer "a" * max_identifier_length end create_table :admin_accounts, force: true do |t|