11
11
from django .core import exceptions
12
12
from django .db import connection , transaction
13
13
from django .db import models
14
- from django .db .models import Index
15
14
from django .db .models .query import QuerySet
16
15
17
16
from django_pgviews .db import get_fields_by_name
@@ -100,6 +99,55 @@ def _concurrent_index_name(view_name, concurrent_index):
100
99
return view_name + "_" + "_" .join ([s .strip () for s in concurrent_index .split ("," )]) + "_index"
101
100
102
101
102
+ def _create_concurrent_index (cursor , view_name , concurrent_index ):
103
+ cursor .execute (
104
+ "CREATE UNIQUE INDEX {index_name} ON {view_name} ({concurrent_index})" .format (
105
+ view_name = view_name ,
106
+ index_name = _concurrent_index_name (view_name , concurrent_index ),
107
+ concurrent_index = concurrent_index ,
108
+ )
109
+ )
110
+
111
+
112
+ def _ensure_indexes (connection , cursor , view_cls , schema_name_log ):
113
+ """
114
+ This function gets called when a materialized view is deemed not needing a re-create. That is however only a part
115
+ of the story, since that checks just the SQL of the view itself. The second part is the indexes.
116
+ This function gets the current indexes on the materialized view and reconciles them with the indexes that
117
+ should be in the view, dropping extra ones and creating new ones.
118
+ """
119
+ view_name = view_cls ._meta .db_table
120
+ concurrent_index = view_cls ._concurrent_index
121
+ indexes = view_cls ._meta .indexes
122
+ vschema , vname = _schema_and_name (connection , view_name )
123
+
124
+ cursor .execute ("SELECT indexname FROM pg_indexes WHERE tablename = %s AND schemaname = %s" , [vname , vschema ])
125
+
126
+ existing_indexes = set (x [0 ] for x in cursor .fetchall ())
127
+ required_indexes = set (x .name for x in indexes )
128
+
129
+ if view_cls ._concurrent_index is not None :
130
+ concurrent_index_name = _concurrent_index_name (view_name , concurrent_index )
131
+ required_indexes .add (concurrent_index_name )
132
+ else :
133
+ concurrent_index_name = None
134
+
135
+ for index_name in existing_indexes - required_indexes :
136
+ cursor .execute (f"DROP INDEX { index_name } " )
137
+ log .info ("pgview dropped index %s on view %s (%s)" , index_name , view_name , schema_name_log )
138
+
139
+ for index_name in required_indexes - existing_indexes :
140
+ if index_name == concurrent_index_name :
141
+ _create_concurrent_index (cursor , view_name , concurrent_index )
142
+ log .info ("pgview created concurrent index on view %s (%s)" , view_name , schema_name_log )
143
+ else :
144
+ for index in indexes :
145
+ if index .name == index_name :
146
+ connection .schema_editor ().add_index (view_cls , index )
147
+ log .info ("pgview created index %s on view %s (%s)" , index .name , view_name , schema_name_log )
148
+ break
149
+
150
+
103
151
@transaction .atomic ()
104
152
def create_materialized_view (connection , view_cls , check_sql_changed = False ):
105
153
"""
@@ -155,6 +203,7 @@ def create_materialized_view(connection, view_cls, check_sql_changed=False):
155
203
_drop_mat_view (cursor , temp_viewname )
156
204
157
205
if definitions [0 ] == definitions [1 ]:
206
+ _ensure_indexes (connection , cursor , view_cls , schema_name_log )
158
207
return "EXISTS"
159
208
160
209
if view_exists :
@@ -165,13 +214,7 @@ def create_materialized_view(connection, view_cls, check_sql_changed=False):
165
214
log .info ("pgview created materialized view %s (%s)" , view_name , schema_name_log )
166
215
167
216
if concurrent_index is not None :
168
- cursor .execute (
169
- "CREATE UNIQUE INDEX {index_name} ON {view_name} ({concurrent_index})" .format (
170
- view_name = view_name ,
171
- index_name = _concurrent_index_name (view_name , concurrent_index ),
172
- concurrent_index = concurrent_index ,
173
- )
174
- )
217
+ _create_concurrent_index (cursor , view_name , concurrent_index )
175
218
log .info ("pgview created concurrent index on view %s (%s)" , view_name , schema_name_log )
176
219
177
220
if view_cls ._meta .indexes :
0 commit comments