54 |
54 |
self.root_id = parent.root_id
|
55 |
55 |
self.lft = target_lft
|
56 |
56 |
self.rgt = lft + 1
|
57 |
|
self.class.where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
|
|
57 |
get_update {
|
|
58 |
self.class.reorder('lft desc').where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
|
58 |
59 |
"lft = CASE WHEN lft >= :lft THEN lft + 2 ELSE lft END, " +
|
59 |
60 |
"rgt = CASE WHEN rgt >= :lft THEN rgt + 2 ELSE rgt END",
|
60 |
61 |
{:lft => lft}
|
61 |
62 |
])
|
|
63 |
}
|
62 |
64 |
end
|
63 |
65 |
|
64 |
66 |
def add_as_root
|
... | ... | |
84 |
86 |
if parent
|
85 |
87 |
previous_root_id = root_id
|
86 |
88 |
self.root_id = parent.root_id
|
87 |
|
|
|
89 |
|
88 |
90 |
lft_after_move = target_lft
|
89 |
91 |
self.class.where(:root_id => parent.root_id).update_all([
|
90 |
92 |
"lft = CASE WHEN lft >= :lft THEN lft + :shift ELSE lft END, " +
|
91 |
93 |
"rgt = CASE WHEN rgt >= :lft THEN rgt + :shift ELSE rgt END",
|
92 |
94 |
{:lft => lft_after_move, :shift => (rgt - lft + 1)}
|
93 |
95 |
])
|
94 |
|
|
|
96 |
|
95 |
97 |
self.class.where(:root_id => previous_root_id).update_all([
|
96 |
98 |
"root_id = :root_id, lft = lft + :shift, rgt = rgt + :shift",
|
97 |
99 |
{:root_id => parent.root_id, :shift => lft_after_move - lft}
|
98 |
100 |
])
|
99 |
|
|
|
101 |
|
100 |
102 |
self.lft, self.rgt = lft_after_move, (rgt - lft + lft_after_move)
|
101 |
103 |
parent.send :reload_nested_set_values
|
102 |
104 |
end
|
... | ... | |
105 |
107 |
def remove_from_nested_set
|
106 |
108 |
self.class.where(:root_id => root_id).where("lft >= ? AND rgt <= ?", lft, rgt).
|
107 |
109 |
update_all(["root_id = :id, lft = lft - :shift, rgt = rgt - :shift", {:id => id, :shift => lft - 1}])
|
108 |
|
|
|
110 |
|
109 |
111 |
self.class.where(:root_id => root_id).update_all([
|
110 |
112 |
"lft = CASE WHEN lft >= :lft THEN lft - :shift ELSE lft END, " +
|
111 |
113 |
"rgt = CASE WHEN rgt >= :lft THEN rgt - :shift ELSE rgt END",
|
... | ... | |
123 |
125 |
children.each {|c| c.send :destroy_without_nested_set_update}
|
124 |
126 |
reload
|
125 |
127 |
unless @without_nested_set_update
|
126 |
|
self.class.where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
|
|
128 |
get_update {
|
|
129 |
self.class.reorder('lft asc').where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
|
127 |
130 |
"lft = CASE WHEN lft > :lft THEN lft - :shift ELSE lft END, " +
|
128 |
131 |
"rgt = CASE WHEN rgt > :lft THEN rgt - :shift ELSE rgt END",
|
129 |
132 |
{:lft => lft, :shift => rgt - lft + 1}
|
130 |
133 |
])
|
|
134 |
}
|
131 |
135 |
end
|
132 |
136 |
end
|
133 |
137 |
|
... | ... | |
148 |
152 |
new_record? || !is_or_is_ancestor_of?(issue)
|
149 |
153 |
end
|
150 |
154 |
|
151 |
|
def lock_nested_set
|
|
155 |
def get_update(&block)
|
|
156 |
retry_count = 0
|
|
157 |
begin
|
|
158 |
yield
|
|
159 |
rescue ActiveRecord::StatementInvalid => error
|
|
160 |
raise unless error.message =~ /[Dd]eadlock|Lock wait timeout exceeded|[Dd]uplicate/
|
|
161 |
raise unless retry_count < 20
|
|
162 |
retry_count += 1
|
|
163 |
if error.message =~ /[Dd]uplicate/
|
|
164 |
puts "Duplicate retry ##{retry_count} thread: #{Thread.current.object_id}"
|
|
165 |
else
|
|
166 |
puts "Deadlock detected on update, restarting transaction retry ##{retry_count} thread: #{Thread.current.object_id}"
|
|
167 |
end
|
|
168 |
sleep(rand(retry_count)*0.5)
|
|
169 |
retry
|
|
170 |
end
|
|
171 |
end
|
|
172 |
|
|
173 |
def get_lock_with_retry(&block)
|
|
174 |
retry_count = 0
|
|
175 |
begin
|
|
176 |
yield
|
|
177 |
rescue ActiveRecord::StatementInvalid => error
|
|
178 |
raise unless error.message =~ /[Dd]eadlock|Lock wait timeout exceeded/
|
|
179 |
raise unless retry_count < 20
|
|
180 |
retry_count += 1
|
|
181 |
puts "Deadlock detected on getting lock, restarting transaction retry ##{retry_count} thread: #{Thread.current.object_id}"
|
|
182 |
sleep(rand(retry_count)*0.5)
|
|
183 |
retry
|
|
184 |
end
|
|
185 |
end
|
|
186 |
|
|
187 |
def lock_nested_set
|
|
188 |
get_lock_with_retry { get_lock }
|
|
189 |
end
|
|
190 |
|
|
191 |
def get_lock
|
152 |
192 |
if self.class.connection.adapter_name =~ /sqlserver/i
|
153 |
193 |
lock = "WITH (ROWLOCK HOLDLOCK UPDLOCK)"
|
154 |
194 |
# Custom lock for SQLServer
|
... | ... | |
158 |
198 |
self.class.reorder(:id).where(:root_id => sets_to_lock).lock(lock).ids
|
159 |
199 |
else
|
160 |
200 |
sets_to_lock = [id, parent_id].compact
|
161 |
|
self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock.ids
|
|
201 |
#self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock.ids
|
|
202 |
self.class.connection.execute(self.class.reorder(:id).where(:root_id => self.class.where(:id => sets_to_lock).select(:root_id)).lock.to_sql)
|
162 |
203 |
end
|
163 |
204 |
end
|
164 |
205 |
|