2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								class  FixAccountsUniqueIndex  <  ActiveRecord :: Migration [ 5 . 2 ] 
							 
						 
					
						
							
								
									
										
										
										
											2018-08-11 18:00:41 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  class  Account  <  ApplicationRecord 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # Dummy class, to make migration possible across version changes 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    has_one  :user ,  inverse_of :  :account 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  local? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      domain . nil? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
									
										
										
										
											2018-08-15 20:23:12 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  acct 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      local?  ?  username  :  " #{ username } @ #{ domain } " 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
									
										
										
										
											2018-08-11 18:00:41 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2019-07-10 17:09:10 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  class  StreamEntry  <  ApplicationRecord 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # Dummy class, to make migration possible across version changes 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    belongs_to  :account ,  inverse_of :  :stream_entries 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								  disable_ddl_transaction! 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  up 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  '' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'WARNING: This migration may take a *long* time for large instances' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'It will *not* lock tables for any significant time, but it may run' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'for a very long time. We will pause for 10 seconds to allow you to' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'interrupt this migration if you are not ready.' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  '' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'This migration will irreversibly delete user accounts with duplicate' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'usernames. You may use the `rake mastodon:maintenance:find_duplicate_usernames`' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    say  'task to manually deal with such accounts before running this migration.' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    10 . downto ( 1 )  do  | i | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      say  " Continuing in  #{ i }  second #{ i  ==  1  ?  ''  :  's' } ... " ,  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      sleep  1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    duplicates  =  Account . connection . select_all ( 'SELECT string_agg(id::text, \',\') AS ids FROM accounts GROUP BY lower(username), lower(domain) HAVING count(*) > 1' ) . to_hash 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    duplicates . each  do  | row | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      deduplicate_account! ( row [ 'ids' ] . split ( ',' ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    remove_index  :accounts ,  name :  'index_accounts_on_username_and_domain_lower'  if  index_name_exists? ( :accounts ,  'index_accounts_on_username_and_domain_lower' ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    safety_assured  {  execute  'CREATE UNIQUE INDEX CONCURRENTLY index_accounts_on_username_and_domain_lower ON accounts (lower(username), lower(domain))'  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    remove_index  :accounts ,  name :  'index_accounts_on_username_and_domain'  if  index_name_exists? ( :accounts ,  'index_accounts_on_username_and_domain' ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  down 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    raise  ActiveRecord :: IrreversibleMigration 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  private 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  deduplicate_account! ( account_ids ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    accounts           =  Account . where ( id :  account_ids ) . to_a 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 16:39:52 +09:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    accounts           =  accounts . first . local?  ?  accounts . sort_by ( & :created_at )  :  accounts . sort_by ( & :updated_at ) . reverse 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    reference_account  =  accounts . shift 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    say_with_time  " Deduplicating @ #{ reference_account . acct }  ( #{ accounts . size }  duplicates)... "  do 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      accounts . each  do  | other_account | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  other_account . public_key  ==  reference_account . public_key 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # The accounts definitely point to the same resource, so 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # it's safe to re-attribute content and relationships 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          merge_accounts! ( reference_account ,  other_account ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        elsif  other_account . local? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # Since domain is in the GROUP BY clause, both accounts 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # are always either going to be local or not local, so only 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # one check is needed. Since we cannot support two users with 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # the same username locally, one has to go. 😢 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          other_account . user & . destroy 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        other_account . destroy 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  merge_accounts! ( main_account ,  duplicate_account ) 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    [ Status ,  Mention ,  StatusPin ,  StreamEntry ] . each  do  | klass | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      klass . where ( account_id :  duplicate_account . id ) . in_batches . update_all ( account_id :  main_account . id ) 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # Since it's the same remote resource, the remote resource likely 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # already believes we are following/blocking, so it's safe to 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # re-attribute the relationships too. However, during the presence 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # of the index bug users could have *also* followed the reference 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # account already, therefore mass update will not work and we need 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # to check for (and skip past) uniqueness errors 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    [ Favourite ,  Follow ,  FollowRequest ,  Block ,  Mute ] . each  do  | klass | 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      klass . where ( account_id :  duplicate_account . id ) . find_each  do  | record | 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 16:39:52 +09:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        begin 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          record . update_attribute ( :account_id ,  main_account . id ) 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:22:33 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        rescue  ActiveRecord :: RecordNotUnique 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 16:39:52 +09:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          next 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:22:33 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:22:33 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    [ Follow ,  FollowRequest ,  Block ,  Mute ] . each  do  | klass | 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      klass . where ( target_account_id :  duplicate_account . id ) . find_each  do  | record | 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 16:39:52 +09:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        begin 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:09:09 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          record . update_attribute ( :target_account_id ,  main_account . id ) 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-31 17:22:33 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        rescue  ActiveRecord :: RecordNotUnique 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 16:39:52 +09:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          next 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        end 
							 
						 
					
						
							
								
									
										
										
										
											2018-05-30 02:51:26 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								      end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  end 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								end