OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/db/migrate/20170829095701_generate_wp_...

262 lines
7.1 KiB

#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# 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-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
class GenerateWpClosure < ActiveRecord::Migration[5.0]
def up
add_relation_type_column
update_relation_column_from_relation_type
invert_from_to_on_follows("relation_type = 'precedes'")
insert_hierarchy_relation_for_parent
insert_reflexive_relations
WorkPackage.rebuild_dag!
relation_types.each do |column|
change_column_null :relations, column, true
end
remove_column :relations, :relation_type
remove_nested_set_columns
end
def down
recreate_nested_set_columns
invert_from_to_on_follows('follows = 1')
truncate_closure_entries
set_parent_id
remove_hierarchy_relations
fill_relation_type_column
remove_relation_type_specific_columns
rebuild_nested_set
end
def relation_types
%i(hierarchy relates duplicates blocks follows includes requires)
end
def add_relation_type_column
change_table :relations do |r|
relation_types.each do |column|
r.column column, :integer, default: 0
end
end
add_index :relations,
%i(hierarchy relates duplicates blocks follows includes requires),
name: 'relations_on_type_columns'
end
def exactly_one_column_eql_1(columns, prefix = '')
columns.map { |column| "#{prefix}#{column} = 1" }.join(' XOR ')
end
def sum_of_columns(columns, prefix = '')
columns.map { |column| "#{prefix}#{column}" }.join(' + ')
end
def remove_nested_set_columns
remove_column :work_packages, :parent_id
remove_column :work_packages, :root_id
remove_column :work_packages, :lft
remove_column :work_packages, :rgt
end
def recreate_nested_set_columns
add_column :work_packages, :parent_id, :integer
add_column :work_packages, :root_id, :integer
add_column :work_packages, :lft, :integer
add_column :work_packages, :rgt, :integer
add_index :work_packages, :parent_id
add_index :work_packages, %i(root_id lft rgt)
end
def truncate_closure_entries
ActiveRecord::Base.connection.execute <<-SQL
DELETE FROM relations
WHERE (#{relation_types.join(' + ')} > 1)
OR (#{relation_types.join(' + ')} = 0)
SQL
end
def remove_hierarchy_relations
ActiveRecord::Base.connection.execute <<-SQL
DELETE FROM relations
WHERE hierarchy > 0
SQL
end
def invert_from_to_on_follows(condition)
if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
ActiveRecord::Base.connection.execute <<-SQL
UPDATE
relations r1,
relations r2
SET
r1.to_id = r1.from_id,
r1.from_id = r2.to_id
WHERE
r1.id = r2.id
AND
r1.#{condition}
SQL
else
ActiveRecord::Base.connection.execute <<-SQL
UPDATE
relations
SET
from_id = to_id,
to_id = from_id
WHERE
#{condition}
SQL
end
end
def update_relation_column_from_relation_type
ActiveRecord::Base.connection.execute <<-SQL
UPDATE
relations
SET
relates = CASE
WHEN relations.relation_type = 'relates'
THEN 1
ELSE 0
END,
duplicates = CASE
WHEN relations.relation_type = 'duplicates'
THEN 1
ELSE 0
END,
blocks = CASE
WHEN relations.relation_type = 'blocks'
THEN 1
ELSE 0
END,
follows = CASE
WHEN relations.relation_type = 'precedes'
THEN 1
ELSE 0
END,
includes = CASE
WHEN relations.relation_type = 'includes'
THEN 1
ELSE 0
END,
requires = CASE
WHEN relations.relation_type = 'requires'
THEN 1
ELSE 0
END
SQL
end
def insert_hierarchy_relation_for_parent
ActiveRecord::Base.connection.execute <<-SQL
INSERT INTO relations
(from_id, to_id, hierarchy)
SELECT w1.id, w2.id, 1
FROM work_packages w1
JOIN work_packages w2
ON w1.id = w2.parent_id
SQL
end
def insert_reflexive_relations
ActiveRecord::Base.connection.execute <<-SQL
INSERT INTO relations
(from_id, to_id)
SELECT id, id
FROM work_packages
SQL
end
def set_parent_id
ActiveRecord::Base.connection.execute <<-SQL
UPDATE
work_packages
SET
parent_id = (SELECT from_id FROM relations WHERE to_id = work_packages.id AND relations.hierarchy = 1)
SQL
end
def fill_relation_type_column
add_column :relations, :relation_type, :string
ActiveRecord::Base.connection.execute <<-SQL
UPDATE
relations
SET
relation_type = CASE
WHEN relations.relates = 1
THEN 'relates'
WHEN relations.duplicates = 1
THEN 'duplicates'
WHEN relations.duplicates = 1
THEN 'blocks'
WHEN relations.follows = 1
THEN 'precedes'
WHEN relations.includes = 1
THEN 'includes'
WHEN relations.requires = 1
THEN 'requires'
END
SQL
end
def remove_relation_type_specific_columns
relation_types.each do |column|
remove_column :relations, column
end
end
def rebuild_nested_set
NestedSetWorkPackage.rebuild_silently!
end
class NestedSetWorkPackage < ActiveRecord::Base
self.table_name = 'work_packages'
acts_as_nested_set scope: 'root_id', dependent: :destroy
include OpenProject::NestedSet::RebuildPatch
end
end