Commit d369d692 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'upgrade-to-rails-5-1' into 'master'

Upgrade Rails to 5.1

See merge request gitlab-org/gitlab-ce!27480
parents dcb23df2 97cd3e8c
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '5.0.7.2' gem 'rails', '5.1.7'
# Improves copy-on-write performance for MRI # Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4' gem 'nakayoshi_fork', '~> 0.0.4'
...@@ -18,7 +18,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql ...@@ -18,7 +18,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 1.1', group: :postgres gem 'pg', '~> 1.1', group: :postgres
gem 'rugged', '~> 0.28' gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.0' gem 'grape-path-helpers', '~> 1.1'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
......
...@@ -4,41 +4,41 @@ GEM ...@@ -4,41 +4,41 @@ GEM
RedCloth (4.3.2) RedCloth (4.3.2)
abstract_type (0.0.7) abstract_type (0.0.7)
ace-rails-ap (4.1.2) ace-rails-ap (4.1.2)
actioncable (5.0.7.2) actioncable (5.1.7)
actionpack (= 5.0.7.2) actionpack (= 5.1.7)
nio4r (>= 1.2, < 3.0) nio4r (~> 2.0)
websocket-driver (~> 0.6.1) websocket-driver (~> 0.6.1)
actionmailer (5.0.7.2) actionmailer (5.1.7)
actionpack (= 5.0.7.2) actionpack (= 5.1.7)
actionview (= 5.0.7.2) actionview (= 5.1.7)
activejob (= 5.0.7.2) activejob (= 5.1.7)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.0.7.2) actionpack (5.1.7)
actionview (= 5.0.7.2) actionview (= 5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
rack (~> 2.0) rack (~> 2.0)
rack-test (~> 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.7.2) actionview (5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.0.7.2) activejob (5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.0.7.2) activemodel (5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
activerecord (5.0.7.2) activerecord (5.1.7)
activemodel (= 5.0.7.2) activemodel (= 5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
arel (~> 7.0) arel (~> 8.0)
activerecord_sane_schema_dumper (1.0) activerecord_sane_schema_dumper (1.0)
rails (>= 5, < 6) rails (>= 5, < 6)
activesupport (5.0.7.2) activesupport (5.1.7)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -52,7 +52,7 @@ GEM ...@@ -52,7 +52,7 @@ GEM
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
arel (7.1.4) arel (8.0.0)
asana (0.8.1) asana (0.8.1)
faraday (~> 0.9) faraday (~> 0.9)
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
...@@ -185,8 +185,7 @@ GEM ...@@ -185,8 +185,7 @@ GEM
mail (~> 2.7) mail (~> 2.7)
encryptor (3.0.0) encryptor (3.0.0)
equalizer (0.0.11) equalizer (0.0.11)
erubi (1.7.1) erubi (1.8.0)
erubis (2.7.0)
escape_utils (1.2.1) escape_utils (1.2.1)
et-orbi (1.1.7) et-orbi (1.1.7)
tzinfo tzinfo
...@@ -257,8 +256,8 @@ GEM ...@@ -257,8 +256,8 @@ GEM
fog-xml (0.1.3) fog-xml (0.1.3)
fog-core fog-core
nokogiri (>= 1.5.11, < 2.0.0) nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-rails (4.7.0.1) font-awesome-rails (4.7.0.4)
railties (>= 3.2, < 5.1) railties (>= 3.2, < 6.0)
foreman (0.84.0) foreman (0.84.0)
thor (~> 0.19.1) thor (~> 0.19.1)
formatador (0.2.5) formatador (0.2.5)
...@@ -339,8 +338,8 @@ GEM ...@@ -339,8 +338,8 @@ GEM
grape-entity (0.7.1) grape-entity (0.7.1)
activesupport (>= 4.0) activesupport (>= 4.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-path-helpers (1.0.6) grape-path-helpers (1.1.0)
activesupport (>= 4, < 5.1) activesupport
grape (~> 1.0) grape (~> 1.0)
rake (~> 12) rake (~> 12)
grape_logging (1.7.0) grape_logging (1.7.0)
...@@ -658,19 +657,19 @@ GEM ...@@ -658,19 +657,19 @@ GEM
rack rack
rack-proxy (0.6.0) rack-proxy (0.6.0)
rack rack
rack-test (0.6.3) rack-test (1.1.0)
rack (>= 1.0) rack (>= 1.0, < 3)
rails (5.0.7.2) rails (5.1.7)
actioncable (= 5.0.7.2) actioncable (= 5.1.7)
actionmailer (= 5.0.7.2) actionmailer (= 5.1.7)
actionpack (= 5.0.7.2) actionpack (= 5.1.7)
actionview (= 5.0.7.2) actionview (= 5.1.7)
activejob (= 5.0.7.2) activejob (= 5.1.7)
activemodel (= 5.0.7.2) activemodel (= 5.1.7)
activerecord (= 5.0.7.2) activerecord (= 5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
bundler (>= 1.3.0) bundler (>= 1.3.0)
railties (= 5.0.7.2) railties (= 5.1.7)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2) rails-controller-testing (1.0.2)
actionpack (~> 5.x, >= 5.0.1) actionpack (~> 5.x, >= 5.0.1)
...@@ -684,9 +683,9 @@ GEM ...@@ -684,9 +683,9 @@ GEM
rails-i18n (5.1.1) rails-i18n (5.1.1)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 5.0, < 6) railties (>= 5.0, < 6)
railties (5.0.7.2) railties (5.1.7)
actionpack (= 5.0.7.2) actionpack (= 5.1.7)
activesupport (= 5.0.7.2) activesupport (= 5.1.7)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
...@@ -1067,7 +1066,7 @@ DEPENDENCIES ...@@ -1067,7 +1066,7 @@ DEPENDENCIES
gpgme (~> 2.0.18) gpgme (~> 2.0.18)
grape (~> 1.1.0) grape (~> 1.1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0) grape-path-helpers (~> 1.1)
grape_logging (~> 1.7) grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10) graphiql-rails (~> 1.4.10)
graphql (~> 1.8.0) graphql (~> 1.8.0)
...@@ -1142,7 +1141,7 @@ DEPENDENCIES ...@@ -1142,7 +1141,7 @@ DEPENDENCIES
rack-cors (~> 1.0.0) rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.9.3) rack-oauth2 (~> 1.9.3)
rack-proxy (~> 0.6.0) rack-proxy (~> 0.6.0)
rails (= 5.0.7.2) rails (= 5.1.7)
rails-controller-testing rails-controller-testing
rails-i18n (~> 5.1) rails-i18n (~> 5.1)
rainbow (~> 3.0) rainbow (~> 3.0)
......
...@@ -56,7 +56,7 @@ module Ci ...@@ -56,7 +56,7 @@ module Ci
update_project_statistics stat: :build_artifacts_size update_project_statistics stat: :build_artifacts_size
after_save :update_file_store, if: :file_changed? after_save :update_file_store, if: :saved_change_to_file?
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
......
...@@ -230,7 +230,7 @@ module Clusters ...@@ -230,7 +230,7 @@ module Clusters
end end
def update_kubernetes_namespace def update_kubernetes_namespace
return unless namespace_changed? return unless saved_change_to_namespace?
run_after_commit do run_after_commit do
ClusterConfigureWorker.perform_async(cluster_id) ClusterConfigureWorker.perform_async(cluster_id)
......
...@@ -13,8 +13,8 @@ module Storage ...@@ -13,8 +13,8 @@ module Storage
raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry") raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry")
end end
parent_was = if parent_changed? && parent_id_was.present? parent_was = if parent_changed? && parent_id_before_last_save.present?
Namespace.find(parent_id_was) # raise NotFound early if needed Namespace.find(parent_id_before_last_save) # raise NotFound early if needed
end end
move_repositories move_repositories
......
...@@ -39,12 +39,12 @@ module UpdateProjectStatistics ...@@ -39,12 +39,12 @@ module UpdateProjectStatistics
end end
def update_project_statistics_attribute_changed? def update_project_statistics_attribute_changed?
attribute_changed?(self.class.statistic_attribute) saved_change_to_attribute?(self.class.statistic_attribute)
end end
def update_project_statistics_after_save def update_project_statistics_after_save
attr = self.class.statistic_attribute attr = self.class.statistic_attribute
delta = read_attribute(attr).to_i - attribute_was(attr).to_i delta = read_attribute(attr).to_i - attribute_before_last_save(attr).to_i
update_project_statistics(delta) update_project_statistics(delta)
end end
......
...@@ -61,7 +61,7 @@ class Group < Namespace ...@@ -61,7 +61,7 @@ class Group < Namespace
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_save :update_two_factor_requirement after_save :update_two_factor_requirement
after_update :path_changed_hook, if: :path_changed? after_update :path_changed_hook, if: :saved_change_to_path?
class << self class << self
def sort_by_attribute(method) def sort_by_attribute(method)
...@@ -411,7 +411,7 @@ class Group < Namespace ...@@ -411,7 +411,7 @@ class Group < Namespace
private private
def update_two_factor_requirement def update_two_factor_requirement
return unless require_two_factor_authentication_changed? || two_factor_grace_period_changed? return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
users.find_each(&:update_two_factor_requirement) users.find_each(&:update_two_factor_requirement)
end end
......
...@@ -13,7 +13,7 @@ class LfsObject < ApplicationRecord ...@@ -13,7 +13,7 @@ class LfsObject < ApplicationRecord
mount_uploader :file, LfsObjectUploader mount_uploader :file, LfsObjectUploader
after_save :update_file_store, if: :file_changed? after_save :update_file_store, if: :saved_change_to_file?
def update_file_store def update_file_store
# The file.object_store is set during `uploader.store!` # The file.object_store is set during `uploader.store!`
......
...@@ -55,7 +55,7 @@ class GroupMember < Member ...@@ -55,7 +55,7 @@ class GroupMember < Member
end end
def post_update_hook def post_update_hook
if access_level_changed? if saved_change_to_access_level?
run_after_commit { notification_service.update_group_member(self) } run_after_commit { notification_service.update_group_member(self) }
end end
......
...@@ -111,7 +111,7 @@ class ProjectMember < Member ...@@ -111,7 +111,7 @@ class ProjectMember < Member
end end
def post_update_hook def post_update_hook
if access_level_changed? if saved_change_to_access_level?
run_after_commit { notification_service.update_project_member(self) } run_after_commit { notification_service.update_project_member(self) }
end end
......
...@@ -698,7 +698,7 @@ class MergeRequest < ApplicationRecord ...@@ -698,7 +698,7 @@ class MergeRequest < ApplicationRecord
end end
def reload_diff_if_branch_changed def reload_diff_if_branch_changed
if (source_branch_changed? || target_branch_changed?) && if (saved_change_to_source_branch? || saved_change_to_target_branch?) &&
(source_branch_head && target_branch_head) (source_branch_head && target_branch_head)
reload_diff reload_diff
end end
......
...@@ -50,6 +50,8 @@ class Namespace < ApplicationRecord ...@@ -50,6 +50,8 @@ class Namespace < ApplicationRecord
validate :nesting_level_allowed validate :nesting_level_allowed
validates_associated :runners
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :avatar_url, to: :owner, allow_nil: true delegate :avatar_url, to: :owner, allow_nil: true
...@@ -57,7 +59,7 @@ class Namespace < ApplicationRecord ...@@ -57,7 +59,7 @@ class Namespace < ApplicationRecord
before_create :sync_share_with_group_lock_with_parent before_create :sync_share_with_group_lock_with_parent
before_update :sync_share_with_group_lock_with_parent, if: :parent_changed? before_update :sync_share_with_group_lock_with_parent, if: :parent_changed?
after_update :force_share_with_group_lock_on_descendants, if: -> { share_with_group_lock_changed? && share_with_group_lock? } after_update :force_share_with_group_lock_on_descendants, if: -> { saved_change_to_share_with_group_lock? && share_with_group_lock? }
# Legacy Storage specific hooks # Legacy Storage specific hooks
...@@ -292,7 +294,7 @@ class Namespace < ApplicationRecord ...@@ -292,7 +294,7 @@ class Namespace < ApplicationRecord
private private
def path_or_parent_changed? def path_or_parent_changed?
path_changed? || parent_changed? saved_change_to_path? || saved_change_to_parent_id?
end end
def refresh_access_of_projects_invited_groups def refresh_access_of_projects_invited_groups
......
...@@ -147,20 +147,20 @@ class PagesDomain < ApplicationRecord ...@@ -147,20 +147,20 @@ class PagesDomain < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
def pages_config_changed? def pages_config_changed?
project_id_changed? || saved_change_to_project_id? ||
domain_changed? || saved_change_to_domain? ||
certificate_changed? || saved_change_to_certificate? ||
key_changed? || saved_change_to_key? ||
became_enabled? || became_enabled? ||
became_disabled? became_disabled?
end end
def became_enabled? def became_enabled?
enabled_until.present? && !enabled_until_was.present? enabled_until.present? && !enabled_until_before_last_save.present?
end end
def became_disabled? def became_disabled?
!enabled_until.present? && enabled_until_was.present? !enabled_until.present? && enabled_until_before_last_save.present?
end end
def validate_matching_key def validate_matching_key
......
...@@ -90,7 +90,7 @@ class Project < ApplicationRecord ...@@ -90,7 +90,7 @@ class Project < ApplicationRecord
before_save :ensure_runners_token before_save :ensure_runners_token
after_save :update_project_statistics, if: :namespace_id_changed? after_save :update_project_statistics, if: :saved_change_to_namespace_id?
after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? } after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
...@@ -116,7 +116,7 @@ class Project < ApplicationRecord ...@@ -116,7 +116,7 @@ class Project < ApplicationRecord
after_initialize :use_hashed_storage after_initialize :use_hashed_storage
after_create :check_repository_absence! after_create :check_repository_absence!
after_create :ensure_storage_path_exists after_create :ensure_storage_path_exists
after_save :ensure_storage_path_exists, if: :namespace_id_changed? after_save :ensure_storage_path_exists, if: :saved_change_to_namespace_id?
acts_as_ordered_taggable acts_as_ordered_taggable
...@@ -1430,7 +1430,7 @@ class Project < ApplicationRecord ...@@ -1430,7 +1430,7 @@ class Project < ApplicationRecord
# update visibility_level of forks # update visibility_level of forks
def update_forks_visibility_level def update_forks_visibility_level
return unless visibility_level < visibility_level_was return unless visibility_level < visibility_level_before_last_save
forks.each do |forked_project| forks.each do |forked_project|
if forked_project.visibility_level > visibility_level if forked_project.visibility_level > visibility_level
......
...@@ -248,7 +248,7 @@ class RemoteMirror < ApplicationRecord ...@@ -248,7 +248,7 @@ class RemoteMirror < ApplicationRecord
# Before adding a new remote we have to delete the data from # Before adding a new remote we have to delete the data from
# the previous remote name # the previous remote name
prev_remote_name = remote_name_was || fallback_remote_name prev_remote_name = remote_name_before_last_save || fallback_remote_name
run_after_commit do run_after_commit do
project.repository.async_remove_remote(prev_remote_name) project.repository.async_remove_remote(prev_remote_name)
end end
......
...@@ -14,26 +14,26 @@ class Route < ApplicationRecord ...@@ -14,26 +14,26 @@ class Route < ApplicationRecord
before_validation :delete_conflicting_orphaned_routes before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed? after_update :delete_conflicting_redirects, if: :saved_change_to_path?
after_update :create_redirect_for_old_path after_update :create_redirect_for_old_path
after_update :rename_descendants after_update :rename_descendants
scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") } scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") }
def rename_descendants def rename_descendants
return unless path_changed? || name_changed? return unless saved_change_to_path? || saved_change_to_name?
descendant_routes = self.class.inside_path(path_was) descendant_routes = self.class.inside_path(path_before_last_save)
descendant_routes.each do |route| descendant_routes.each do |route|
attributes = {} attributes = {}
if path_changed? && route.path.present? if saved_change_to_path? && route.path.present?
attributes[:path] = route.path.sub(path_was, path) attributes[:path] = route.path.sub(path_before_last_save, path)
end end
if name_changed? && name_was.present? && route.name.present? if saved_change_to_name? && name_before_last_save.present? && route.name.present?
attributes[:name] = route.name.sub(name_was, name) attributes[:name] = route.name.sub(name_before_last_save, name)
end end
if attributes.present? if attributes.present?
...@@ -65,7 +65,7 @@ class Route < ApplicationRecord ...@@ -65,7 +65,7 @@ class Route < ApplicationRecord
private private
def create_redirect_for_old_path def create_redirect_for_old_path
create_redirect(path_was) if path_changed? create_redirect(path_before_last_save) if saved_change_to_path?
end end
def delete_conflicting_orphaned_routes def delete_conflicting_orphaned_routes
......
...@@ -194,7 +194,7 @@ class User < ApplicationRecord ...@@ -194,7 +194,7 @@ class User < ApplicationRecord
before_validation :ensure_namespace_correct before_validation :ensure_namespace_correct
before_save :ensure_namespace_correct # in case validation is skipped before_save :ensure_namespace_correct # in case validation is skipped
after_validation :set_username_errors after_validation :set_username_errors
after_update :username_changed_hook, if: :username_changed? after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_destroy :remove_key_cache after_destroy :remove_key_cache
after_commit(on: :update) do after_commit(on: :update) do
......
...@@ -80,7 +80,7 @@ module Projects ...@@ -80,7 +80,7 @@ module Projects
value = value.is_a?(Hash) ? value.to_json : value value = value.is_a?(Hash) ? value.to_json : value
service_hash[ActiveRecord::Base.connection.quote_column_name(key)] = service_hash[ActiveRecord::Base.connection.quote_column_name(key)] =
ActiveRecord::Base.sanitize(value) ActiveRecord::Base.connection.quote(value)
end end
end end
end end
......
---
title: Upgrade to Rails 5.1
merge_request: 27480
author: Jasper Maes
type: other
...@@ -164,9 +164,6 @@ module Gitlab ...@@ -164,9 +164,6 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
# Can be removed once upgraded to Rails 5.1 or higher
config.action_controller.raise_on_unfiltered_parameters = true
# Nokogiri is significantly faster and uses less memory than REXML # Nokogiri is significantly faster and uses less memory than REXML
ActiveSupport::XmlMini.backend = 'Nokogiri' ActiveSupport::XmlMini.backend = 'Nokogiri'
......
# This is a monkey patch which must be removed when migrating to Rails 5.1 from 5.0.
#
# In Rails 5.0 there was introduced a bug which casts types in the uniqueness validator.
# https://github.com/rails/rails/pull/23523/commits/811a4fa8eb6ceea841e61e8ac05747ffb69595ae
#
# That causes to bugs like this:
#
# 1) API::Users POST /user/:id/gpg_keys/:key_id/revoke when authenticated revokes existing key
# Failure/Error: let(:gpg_key) { create(:gpg_key, user: user) }
#
# TypeError:
# can't cast Hash
# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>'
# # ./spec/requests/api/users_spec.rb:908:in `block (4 levels) in <top (required)>'
# # ------------------
# # --- Caused by: ---
# # TypeError:
# # TypeError
# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>'
#
# This bug was fixed in Rails 5.1 by https://github.com/rails/rails/pull/24745/commits/aa062318c451512035c10898a1af95943b1a3803
if Rails.gem_version >= Gem::Version.new("5.1")
raise "Remove this monkey patch: #{__FILE__}"
end
# Copy-paste from https://github.com/kamipo/rails/blob/aa062318c451512035c10898a1af95943b1a3803/activerecord/lib/active_record/validations/uniqueness.rb
# including local fixes to make Rubocop happy again.
module ActiveRecord
module Validations
class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
table = finder_class.arel_table
value = map_enum_attribute(finder_class, attribute, value)
relation = build_relation(finder_class, table, attribute, value)
if record.persisted?
if finder_class.primary_key
relation = relation.where.not(finder_class.primary_key => record.id_was || record.id)
else
raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
end
end
relation = scope_relation(record, table, relation)
relation = relation.merge(options[:conditions]) if options[:conditions]
if relation.exists?
error_options = options.except(:case_sensitive, :scope, :conditions)
error_options[:value] = value
record.errors.add(attribute, :taken, error_options)
end
rescue RangeError
end
protected
def build_relation(klass, table, attribute, value) #:nodoc:
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.klass.primary_key] unless value.nil?
end
# the attribute may be an aliased attribute
if klass.attribute_alias?(attribute)
attribute = klass.attribute_alias(attribute)
end
attribute_name = attribute.to_s
column = klass.columns_hash[attribute_name]
cast_type = klass.type_for_attribute(attribute_name)
comparison =
if !options[:case_sensitive] && !value.nil?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
klass.connection.case_sensitive_comparison(table, attribute, column, value)
end
if value.nil?
klass.unscoped.where(comparison)
else
bind = Relation::QueryAttribute.new(attribute_name, value, cast_type)
klass.unscoped.where(comparison, bind)
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Gitlab/ModuleWithInstanceVariables
# build_select only selects the required fields if the model has ignored_columns.
# This is incompatible with some migrations or background migration specs because
# rails keeps a statement cache in memory. So if a model with ignored_columns in a
# migration is used, the query with select table.col1, table.col2 is stored in the
# statement cache. If a different migration is then run and one of these columns is
# removed in the meantime, the query is invalid.
module ActiveRecord
module QueryMethods
private
def build_select(arel)
if select_values.any?
arel.project(*arel_columns(select_values.uniq))
else
arel.project(@klass.arel_table[Arel.star])
end
end
end
end
# rubocop:disable Lint/RescueException
# Remove this monkey patch when we move to Rails 5.1, because the bug has been fixed in https://github.com/rails/rails/pull/26050.
if Rails.gem_version >= Gem::Version.new("5.1")
raise "Remove this monkey patch: #{__FILE__}"
end
module ActiveRecord
module Locking
module Optimistic
# We overwrite this method because we don't want to have default value
# for newly created records
def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
super
end
def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i
increment_lock
attribute_names += [lock_col]
attribute_names.uniq!
begin
relation = self.class.unscoped
affected_rows = relation.where(
self.class.primary_key => id,
# Patched because when `lock_version` is read as `0`, it may actually be `NULL` in the DB.
lock_col => previous_lock_value == 0 ? [nil, 0] : previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
end.to_h
)
unless affected_rows == 1
raise ActiveRecord::StaleObjectError.new(self, "update")
end
affected_rows
# If something went wrong, revert the version.
rescue Exception
send(lock_col + '=', previous_lock_value)
raise
end
end
# This is patched because we need it to query `lock_version IS NULL`
# rather than `lock_version = 0` whenever lock_version is NULL.
def relation_for_destroy
return super unless locking_enabled?
column_name = self.class.locking_column
super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
end
# This is patched because we want `lock_version` default to `NULL`
# rather than `0`
class LockingType
def deserialize(value)
super
end
def serialize(value)
super
end
end
end
end
...@@ -4,7 +4,8 @@ module ActiveRecord ...@@ -4,7 +4,8 @@ module ActiveRecord
module ConnectionAdapters module ConnectionAdapters
class AbstractMysqlAdapter class AbstractMysqlAdapter
NATIVE_DATABASE_TYPES.merge!( NATIVE_DATABASE_TYPES.merge!(
bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' } bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' },
serial: { name: 'int auto_increment PRIMARY KEY' }
) )
end end
end end
......
...@@ -78,7 +78,7 @@ module ActiveRecord ...@@ -78,7 +78,7 @@ module ActiveRecord
if index_name.length > max_index_length if index_name.length > max_index_length
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters" raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
end end
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false) if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists" raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end end
index_columns = quoted_columns_for_index(column_names, options).join(", ") index_columns = quoted_columns_for_index(column_names, options).join(", ")
......
...@@ -9,11 +9,11 @@ class AddUniqueIndexToSubscriptions < ActiveRecord::Migration[4.2] ...@@ -9,11 +9,11 @@ class AddUniqueIndexToSubscriptions < ActiveRecord::Migration[4.2]
def up def up
add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id, :project_id], { unique: true, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' } add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id, :project_id], { unique: true, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' }
remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields', false) remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields')
end end
def down def down
add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id], { unique: true, name: 'subscriptions_user_id_and_ref_fields' } add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id], { unique: true, name: 'subscriptions_user_id_and_ref_fields' }
remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id', false) remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id')
end end
end end
This diff is collapsed.
...@@ -26,11 +26,7 @@ module Gitlab ...@@ -26,11 +26,7 @@ module Gitlab
subscribe('sql.active_record') do |_, start, finish, _, data| subscribe('sql.active_record') do |_, start, finish, _, data|
if Gitlab::SafeRequestStore.store[:peek_enabled] if Gitlab::SafeRequestStore.store[:peek_enabled]
# data[:cached] is only available starting from Rails 5.1.0 unless data[:cached]
# https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113
# Before that, data[:name] was set to 'CACHE'
# https://github.com/rails/rails/blob/v4.2.9/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L80
unless data.fetch(:cached, data[:name] == 'CACHE')
track_query(data[:sql].strip, data[:binds], start, finish) track_query(data[:sql].strip, data[:binds], start, finish)
end end
end end
......
...@@ -113,7 +113,7 @@ module Gitlab ...@@ -113,7 +113,7 @@ module Gitlab
issues.full_search(query) issues.full_search(query)
end end
issues.reorder('updated_at DESC') issues.reorder('issues.updated_at DESC')
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -121,7 +121,7 @@ module Gitlab ...@@ -121,7 +121,7 @@ module Gitlab
def milestones def milestones
milestones = Milestone.where(project_id: project_ids_relation) milestones = Milestone.where(project_id: project_ids_relation)
milestones = milestones.search(query) milestones = milestones.search(query)
milestones.reorder('updated_at DESC') milestones.reorder('milestones.updated_at DESC')
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -139,7 +139,7 @@ module Gitlab ...@@ -139,7 +139,7 @@ module Gitlab
merge_requests.full_search(query) merge_requests.full_search(query)
end end
merge_requests.reorder('updated_at DESC') merge_requests.reorder('merge_requests.updated_at DESC')
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
"iid": { "type": "integer" }, "iid": { "type": "integer" },
"author_id": { "type": "integer" }, "author_id": { "type": "integer" },
"description": { "type": ["string", "null"] }, "description": { "type": ["string", "null"] },
"lock_version": { "type": ["string", "null"] }, "lock_version": { "type": ["integer", "null"] },
"milestone_id": { "type": ["string", "null"] }, "milestone_id": { "type": ["string", "null"] },
"title": { "type": "string" }, "title": { "type": "string" },
"moved_to_id": { "type": ["integer", "null"] }, "moved_to_id": { "type": ["integer", "null"] },
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
}, },
"task_status": { "type": "string" }, "task_status": { "type": "string" },
"task_status_short": { "type": "string" }, "task_status_short": { "type": "string" },
"lock_version": { "type": ["string", "null"] } "lock_version": { "type": ["integer", "null"] }
}, },
"additionalProperties": false "additionalProperties": false
} }
...@@ -58,7 +58,7 @@ describe('MergeRequest', function() { ...@@ -58,7 +58,7 @@ describe('MergeRequest', function() {
{ {
merge_request: { merge_request: {
description: '- [ ] Task List Item', description: '- [ ] Task List Item',
lock_version: undefined, lock_version: 0,
update_task: { line_number: lineNumber, line_source: lineSource, index, checked }, update_task: { line_number: lineNumber, line_source: lineSource, index, checked },
}, },
}, },
......
...@@ -177,7 +177,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m ...@@ -177,7 +177,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
end end
before do before do
allow_any_instance_of(described_class::MergeRequestDiff::ActiveRecord_Relation) allow_any_instance_of(ActiveRecord::Relation)
.to receive(:update_all).and_raise(exception) .to receive(:update_all).and_raise(exception)
end end
......
...@@ -29,7 +29,7 @@ describe 'Import/Export attribute configuration' do ...@@ -29,7 +29,7 @@ describe 'Import/Export attribute configuration' do
it 'has no new columns' do it 'has no new columns' do
relation_names.each do |relation_name| relation_names.each do |relation_name|
relation_class = relation_class_for_name(relation_name) relation_class = relation_class_for_name(relation_name)
relation_attributes = relation_class.new.attributes.keys relation_attributes = relation_class.new.attributes.keys - relation_class.encrypted_attributes.keys.map(&:to_s)
current_attributes = parsed_attributes(relation_name, relation_attributes) current_attributes = parsed_attributes(relation_name, relation_attributes)
safe_attributes = safe_model_attributes[relation_class.to_s].dup || [] safe_attributes = safe_model_attributes[relation_class.to_s].dup || []
......
...@@ -73,9 +73,8 @@ describe Ci::Runner do ...@@ -73,9 +73,8 @@ describe Ci::Runner do
end end
it 'fails to save a group assigned to a project runner even if the runner is already saved' do it 'fails to save a group assigned to a project runner even if the runner is already saved' do
group_runner group.runners << project_runner
expect { group.save! }
expect { create(:group, runners: [project_runner]) }
.to raise_error(ActiveRecord::RecordInvalid) .to raise_error(ActiveRecord::RecordInvalid)
end end
end end
......
...@@ -68,7 +68,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do ...@@ -68,7 +68,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
post api('/runners'), params: { token: group.runners_token } post api('/runners'), params: { token: group.runners_token }
expect(response).to have_http_status 201 expect(response).to have_http_status 201
expect(group.runners.size).to eq(1) expect(group.runners.reload.size).to eq(1)
runner = Ci::Runner.first runner = Ci::Runner.first
expect(runner.token).not_to eq(registration_token) expect(runner.token).not_to eq(registration_token)
expect(runner.token).not_to eq(group.runners_token) expect(runner.token).not_to eq(group.runners_token)
......
...@@ -251,8 +251,8 @@ describe 'Rack Attack global throttles' do ...@@ -251,8 +251,8 @@ describe 'Rack Attack global throttles' do
let(:throttle_setting_prefix) { 'throttle_authenticated_web' } let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
context 'with the token in the query string' do context 'with the token in the query string' do
let(:get_args) { [rss_url(user), nil] } let(:get_args) { [rss_url(user), params: nil] }
let(:other_user_get_args) { [rss_url(other_user), nil] } let(:other_user_get_args) { [rss_url(other_user), params: nil] }
it_behaves_like 'rate-limited token-authenticated requests' it_behaves_like 'rate-limited token-authenticated requests'
end end
......
...@@ -440,6 +440,8 @@ describe Projects::UpdateService do ...@@ -440,6 +440,8 @@ describe Projects::UpdateService do
context 'when auto devops is set to instance setting' do context 'when auto devops is set to instance setting' do
before do before do
project.create_auto_devops!(enabled: nil) project.create_auto_devops!(enabled: nil)
project.reload
allow(project.auto_devops).to receive(:previous_changes).and_return('enabled' => true) allow(project.auto_devops).to receive(:previous_changes).and_return('enabled' => true)
end end
......
...@@ -210,6 +210,8 @@ describe Users::DestroyService do ...@@ -210,6 +210,8 @@ describe Users::DestroyService do
describe "calls the before/after callbacks" do describe "calls the before/after callbacks" do
it 'of project_members' do it 'of project_members' do
expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:find).once
expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:initialize).once
expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once
service.execute(user) service.execute(user)
...@@ -219,6 +221,8 @@ describe Users::DestroyService do ...@@ -219,6 +221,8 @@ describe Users::DestroyService do
group_member = create(:group_member) group_member = create(:group_member)
group_member.group.group_members.create(user: user, access_level: 40) group_member.group.group_members.create(user: user, access_level: 40)
expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:find).once
expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:initialize).once
expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once
service.execute(user) service.execute(user)
......
...@@ -80,7 +80,7 @@ describe Users::MigrateToGhostUserService do ...@@ -80,7 +80,7 @@ describe Users::MigrateToGhostUserService do
context "when record migration fails with a rollback exception" do context "when record migration fails with a rollback exception" do
before do before do
expect_any_instance_of(MergeRequest::ActiveRecord_Associations_CollectionProxy) expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ActiveRecord::Rollback) .to receive(:update_all).and_raise(ActiveRecord::Rollback)
end end
......
...@@ -17,7 +17,7 @@ module ActiveRecord ...@@ -17,7 +17,7 @@ module ActiveRecord
def callback(name, start, finish, message_id, values) def callback(name, start, finish, message_id, values)
show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG'] show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG']
if values[:name]&.include?("CACHE") && skip_cached if values[:cached] && skip_cached
@cached << values[:sql] @cached << values[:sql]
elsif !values[:name]&.include?("SCHEMA") elsif !values[:name]&.include?("SCHEMA")
@log << values[:sql] @log << values[:sql]
......
# frozen_string_literal: true # frozen_string_literal: true
module TestRequestHelpers module TestRequestHelpers
def test_request(remote_ip: '127.0.0.1') def test_request(remote_ip: '127.0.0.1', controller: nil)
ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new) ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new, controller)
end end
end end
...@@ -45,7 +45,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user ...@@ -45,7 +45,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
context "race conditions" do context "race conditions" do
context "when #{record_class_name} migration fails and is rolled back" do context "when #{record_class_name} migration fails and is rolled back" do
before do before do
expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy) expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ActiveRecord::Rollback) .to receive(:update_all).and_raise(ActiveRecord::Rollback)
end end
...@@ -66,7 +66,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user ...@@ -66,7 +66,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
context "when #{record_class_name} migration fails with a non-rollback exception" do context "when #{record_class_name} migration fails with a non-rollback exception" do
before do before do
expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy) expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ArgumentError) .to receive(:update_all).and_raise(ArgumentError)
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment