﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
31615	Do not fail migration if postgresql extension is already installed and user is not superuser	minusf	minusf	"Currently Django facilitates installing postgres extensions using migration operations: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/operations/

But having a superuser is a tall order in certain environments, and the manual helpfully points out:

If the Django database user doesn’t have superuser privileges, you’ll have to create the extension outside of Django migrations with a user that has the appropriate privileges.

This is fairly often the case with Salt and other orchestration systems.

However having a non-superuser will break any migrations using these operations because unfortunately `CREATE EXTENSION` even with `IF NOT EXISTS` still requires superuser privileges... In other words, if the extension is already in place, the migration will fail...

I think a failsafe like this around CREATE/DROP EXTENSION would make the migrations more robust and user friendly.

{{{
diff --git a/django/contrib/postgres/operations.py b/django/contrib/postgres/operations.py
index 0bb131ddf2..46fc11f63f 100644
--- a/django/contrib/postgres/operations.py
+++ b/django/contrib/postgres/operations.py
@@ -21,7 +21,13 @@ class CreateExtension(Operation):
             not router.allow_migrate(schema_editor.connection.alias, app_label)
         ):
             return
-        schema_editor.execute(""CREATE EXTENSION IF NOT EXISTS %s"" % schema_editor.quote_name(self.name))
+
+        cur = schema_editor.connection.cursor()
+        cur.execute(""SELECT * FROM pg_extension WHERE extname = %s"", [self.name])
+        if not cur.fetchone():
+            schema_editor.execute(
+                ""CREATE EXTENSION IF NOT EXISTS %s"" % schema_editor.quote_name(self.name)
+            )
         # Clear cached, stale oids.
         get_hstore_oids.cache_clear()
         get_citext_oids.cache_clear()
@@ -33,7 +39,13 @@ class CreateExtension(Operation):
     def database_backwards(self, app_label, schema_editor, from_state, to_state):
         if not router.allow_migrate(schema_editor.connection.alias, app_label):
             return
-        schema_editor.execute(""DROP EXTENSION %s"" % schema_editor.quote_name(self.name))
+
+        cur = schema_editor.connection.cursor()
+        cur.execute(""SELECT * FROM pg_extension WHERE extname = %s"", [self.name])
+        if cur.fetchone():
+            schema_editor.execute(
+                ""DROP EXTENSION %s"" % schema_editor.quote_name(self.name)
+            )
         # Clear cached, stale oids.
         get_hstore_oids.cache_clear()
         get_citext_oids.cache_clear()
}}}
"	Cleanup/optimization	closed	Migrations	3.0	Normal	fixed	migration extension postgresql		Ready for checkin	1	0	0	0	1	0
