From aa66dda0f67318beca926ffe80cf8145d0115793 Mon Sep 17 00:00:00 2001
From: Benjamin Dauvergne <bdauvergne@entrouvert.com>
Date: Wed, 14 Oct 2020 21:39:42 +0200
Subject: [PATCH 1/2] misc: do not silence failures of migration
 authentic2.0028 (#47699)

---
 .../migrations/0028_trigram_unaccent_index.py | 84 ++++---------------
 tests/test_migrations.py                      | 12 ---
 2 files changed, 16 insertions(+), 80 deletions(-)

diff --git a/src/authentic2/migrations/0028_trigram_unaccent_index.py b/src/authentic2/migrations/0028_trigram_unaccent_index.py
index 55d462cfe..2b0d4e11a 100644
--- a/src/authentic2/migrations/0028_trigram_unaccent_index.py
+++ b/src/authentic2/migrations/0028_trigram_unaccent_index.py
@@ -1,81 +1,29 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-09-17 15:38
 from __future__ import unicode_literals
 
-from django.db import migrations, transaction
-from django.db.migrations.operations.base import Operation
-from django.db.utils import InternalError, OperationalError, ProgrammingError
-
-
-class SafeExtensionOperation(Operation):
-    reversible = True
-
-    def state_forwards(self, app_label, state):
-        pass
-
-    def database_forwards(self, app_label, schema_editor, from_state, to_state):
-        if schema_editor.connection.vendor != 'postgresql':
-            return
-        try:
-            with transaction.atomic():
-                try:
-                    schema_editor.execute('CREATE EXTENSION IF NOT EXISTS %s SCHEMA public' % self.name)
-                except (OperationalError, ProgrammingError):
-                    # OperationalError if the extension is not available
-                    # ProgrammingError in case of denied permission
-                    RunSQLIfExtension.extensions_installed = False
-        except InternalError:
-            # InternalError (current transaction is aborted, commands ignored
-            # until end of transaction block) would be raised when django-
-            # tenant-schemas set search_path.
-            RunSQLIfExtension.extensions_installed = False
-
-    def database_backwards(self, app_label, schema_editor, from_state, to_state):
-        try:
-            with transaction.atomic():
-                schema_editor.execute('DROP EXTENSION IF EXISTS %s' % self.name)
-        except InternalError:
-            # Raised when other objects depend on the extension. This happens in a multitenant
-            # context, where extension in installed in schema "public" but referenced in others (via
-            # public.gist_trgm_ops). In this case, do nothing, as the query should be successful
-            # when last tenant is processed.
-            pass
-
-
-class RunSQLIfExtension(migrations.RunSQL):
-    extensions_installed = True
-
-    def __getattribute__(self, name):
-        if name == 'sql' and not self.extensions_installed:
-            return migrations.RunSQL.noop
-        return object.__getattribute__(self, name)
-
-
-class UnaccentExtension(SafeExtensionOperation):
-    name = 'unaccent'
-
-
-class TrigramExtension(SafeExtensionOperation):
-    name = 'pg_trgm'
+from django.db import migrations
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ('authentic2', '0027_remove_deleteduser'),
     ]
 
     operations = [
-        TrigramExtension(),
-        UnaccentExtension(),
-        RunSQLIfExtension(
-            sql=["CREATE OR REPLACE FUNCTION public.immutable_unaccent(text) RETURNS varchar AS $$ "
-                 "SELECT public.unaccent('public.unaccent',$1::text); $$ LANGUAGE 'sql' IMMUTABLE"],
-            reverse_sql=['DROP FUNCTION IF EXISTS public.immutable_unaccent(text)']
-        ),
-        RunSQLIfExtension(
-            sql=["CREATE INDEX custom_user_name_gist_idx ON custom_user_user USING gist "
-            "(LOWER(public.immutable_unaccent(first_name || ' ' || last_name)) public.gist_trgm_ops)"],
-            reverse_sql=['DROP INDEX IF EXISTS custom_user_name_gist_idx'],
+        migrations.RunSQL(
+            sql=[
+                "CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public",
+                "CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public",
+                "CREATE OR REPLACE FUNCTION public.immutable_unaccent(text) RETURNS varchar AS $$ "
+                "SELECT public.unaccent('public.unaccent',$1::text); $$ LANGUAGE 'sql' IMMUTABLE",
+                "CREATE INDEX custom_user_name_gist_idx ON custom_user_user USING gist "
+                "(LOWER(public.immutable_unaccent(first_name || ' ' || last_name)) public.gist_trgm_ops)"
+            ],
+            reverse_sql=[
+                "DROP INDEX IF EXISTS custom_user_name_gist_idx",
+                "DROP FUNCTION IF EXISTS public.immutable_unaccent(text)",
+                "DROP EXTENSION IF EXISTS pg_trgm",
+                "DROP EXTENSION IF EXISTS unaccent",
+            ],
         ),
     ]
diff --git a/tests/test_migrations.py b/tests/test_migrations.py
index c206ad1b9..d55edec69 100644
--- a/tests/test_migrations.py
+++ b/tests/test_migrations.py
@@ -21,18 +21,6 @@ from django.db.utils import ProgrammingError
 from django.utils.timezone import now
 
 
-def test_migration_0028_trigram_unaccent_index(transactional_db, migration):
-    migration.before([('authentic2', '0027_remove_deleteduser')])
-
-    def programming_error(*args, **kwargs):
-        raise ProgrammingError
-
-    # when an error occurs, ensure migration runs anyway without complaining
-    with mock.patch('django.db.backends.postgresql.schema.DatabaseSchemaEditor.execute') as mocked:
-        mocked.side_effect = programming_error
-        migration.apply([('authentic2', '0028_trigram_unaccent_index')])
-
-
 def test_migration_custom_user_0021_set_unusable_password(transactional_db, migration):
     old_apps = migration.before([('custom_user', '0020_deleteduser')])
 
-- 
2.31.0

