Add uniqueness constraints to the database for models that have them in code

This commit is contained in:
Klaus Zanders
2026-01-05 11:14:09 +01:00
parent e5df331114
commit c52abc3bee
13 changed files with 394 additions and 23 deletions
+4 -11
View File
@@ -64,17 +64,10 @@ class CustomField < ApplicationRecord
validates :custom_options,
presence: { message: ->(*) { I18n.t(:"activerecord.errors.models.custom_field.at_least_one_custom_option") } },
if: ->(*) { field_format == "list" }
validates :name, presence: true, length: { maximum: 256 }
validate :uniqueness_of_name_with_scope
def uniqueness_of_name_with_scope
taken_names = CustomField.where(type:)
taken_names = taken_names.where.not(id:) if id
taken_names = taken_names.pluck(:name)
errors.add(:name, :taken) if name.in?(taken_names)
end
validates :name,
presence: true,
length: { maximum: 256 },
uniqueness: { case_sensitive: false, scope: :type }
validate :validate_field_format_inclusion
validate :validate_default_value
+3 -4
View File
@@ -40,11 +40,10 @@ class Enumeration < ApplicationRecord
before_save :ensure_activated, if: -> { self.class.can_have_default_value? && is_default? }
before_destroy :check_integrity
validates :name, presence: true
validates :name,
uniqueness: { scope: %i(type project_id),
case_sensitive: false }
validates :name, length: { maximum: 256 }
presence: true,
length: { maximum: 256 },
uniqueness: { scope: %i(type project_id), case_sensitive: false }
scope :shared, -> { where(project_id: nil) }
scope :active, -> { where(active: true) }
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForCustomFieldNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE custom_fields SET name = custom_fields.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY type, LOWER(name) ORDER BY id) AS rn FROM custom_fields) AS counter
WHERE custom_fields.id = counter.id AND counter.rn > 1;
SQL
add_index :custom_fields, "type, LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_custom_fields_on_type_and_LOWER_name"
end
def down
remove_index :custom_fields, name: "index_custom_fields_on_type_and_LOWER_name", algorithm: :concurrently
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForEnumerationNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE enumerations SET name = enumerations.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY type, project_id, LOWER(name) ORDER BY id) AS rn FROM enumerations) AS counter
WHERE enumerations.id = counter.id AND counter.rn > 1;
SQL
add_index :enumerations, "type, project_id, LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_enumerations_on_type_project_id_and_LOWER_name"
end
def down
remove_index :enumerations, name: "index_enumerations_on_type_project_id_and_LOWER_name", algorithm: :concurrently
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForCategoryNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE categories SET name = categories.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY project_id, LOWER(name) ORDER BY id) AS rn FROM categories) AS counter
WHERE categories.id = counter.id AND counter.rn > 1;
SQL
add_index :categories, "project_id, LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_categories_on_project_id_and_LOWER_name"
end
def down
remove_index :categories, name: "index_categories_on_project_id_and_LOWER_name", algorithm: :concurrently
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForLdapAuthSourceNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE ldap_auth_sources SET name = ldap_auth_sources.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM ldap_auth_sources) AS counter
WHERE ldap_auth_sources.id = counter.id AND counter.rn > 1;
SQL
add_index :ldap_auth_sources, "LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_ldap_auth_sources_on_LOWER_name"
end
def down
remove_index :ldap_auth_sources, name: "index_ldap_auth_sources_on_LOWER_name", algorithm: :concurrently
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForVersionNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE versions SET name = versions.name || ' (' || counter.rn || ')'
FROM (SELECT id, row_number() OVER (PARTITION BY project_id, LOWER(name) ORDER BY id) AS rn FROM versions) AS counter
WHERE versions.id = counter.id AND counter.rn > 1;
SQL
add_index :versions, "project_id, LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_versions_on_project_id_and_LOWER_name"
end
def down
remove_index :versions, name: "index_versions_on_project_id_and_LOWER_name", algorithm: :concurrently
end
end
+2 -2
View File
@@ -31,8 +31,8 @@ class CostType < ApplicationRecord
has_many :cost_entries, dependent: :destroy
has_many :rates, class_name: "CostRate", dependent: :destroy
validates_presence_of :name, :unit, :unit_plural
validates_uniqueness_of :name
validates :unit, :unit_plural, presence: true
validates :name, presence: true, uniqueness: { case_sensitive: false }
after_update :save_rates
@@ -0,0 +1,47 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForCostTypeNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE cost_types SET name = cost_types.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM cost_types) AS counter
WHERE cost_types.id = counter.id AND counter.rn > 1;
SQL
add_index :cost_types, "LOWER(name)", unique: true, algorithm: :concurrently, name: "index_cost_types_on_LOWER_name"
end
def down
remove_index :cost_types, name: "index_cost_types_on_LOWER_name", algorithm: :concurrently
end
end
@@ -54,7 +54,7 @@ module Storages
has_many :remote_identities, as: :integration, dependent: :destroy
validates :host, uniqueness: { allow_nil: true }
validates :name, uniqueness: true
validates :name, uniqueness: { case_sensitive: false }
scope :visible, lambda { |user = User.current|
if user.allowed_in_any_project?(:manage_files_in_project)
@@ -0,0 +1,47 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForStorageNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE storages SET name = storages.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM storages) AS counter
WHERE storages.id = counter.id AND counter.rn > 1;
SQL
add_index :storages, "LOWER(name)", unique: true, algorithm: :concurrently, name: "index_storages_on_LOWER_name"
end
def down
remove_index :storages, name: "index_storages_on_LOWER_name", algorithm: :concurrently
end
end
@@ -2,11 +2,8 @@ module Webhooks
class Webhook < ApplicationRecord
default_scope { order(id: :asc) }
validates_presence_of :name
validates_presence_of :url
validates_uniqueness_of :name
validates :url, url: true
validates :name, presence: true, uniqueness: { case_sensitive: false }
validates :url, presence: true, url: true
has_many :events, foreign_key: :webhooks_webhook_id, class_name: "::Webhooks::Event", dependent: :delete_all
has_many :webhook_projects, foreign_key: :webhooks_webhook_id, class_name: "::Webhooks::Project", dependent: :delete_all
@@ -0,0 +1,48 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class AddUniquenessForWebhookNames < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def up
execute <<~SQL.squish
UPDATE webhooks_webhooks SET name = webhooks_webhooks.name || ' ' || counter.rn
FROM (SELECT id, row_number() OVER (PARTITION BY LOWER(name) ORDER BY id) AS rn FROM webhooks_webhooks) AS counter
WHERE webhooks_webhooks.id = counter.id AND counter.rn > 1;
SQL
add_index :webhooks_webhooks, "LOWER(name)", unique: true, algorithm: :concurrently,
name: "index_webhooks_webhooks_on_LOWER_name"
end
def down
remove_index :webhooks_webhooks, name: "index_webhooks_webhooks_on_LOWER_name", algorithm: :concurrently
end
end