Skip to content

Commit 9220e6c

Browse files
committed
Rewrite main CF dashboard query to use SQL
We've long gone past what the django ORM can handle and got into hundreds of queries for large commitfests. And it's not really that hard to rewrite as SQL, even though we have to dynamically build the WHERE clauses. So do that.
1 parent 3fff88c commit 9220e6c

File tree

3 files changed

+49
-23
lines changed

3 files changed

+49
-23
lines changed

pgcommitfest/commitfest/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class PatchOnCommitFest(models.Model):
177177
(STATUS_REJECTED, 'danger'),
178178
(STATUS_RETURNED, 'danger'),
179179
)
180-
OPEN_STATUSES=(STATUS_REVIEW, STATUS_AUTHOR, STATUS_COMMITTER)
180+
OPEN_STATUSES=[STATUS_REVIEW, STATUS_AUTHOR, STATUS_COMMITTER]
181181
OPEN_STATUS_CHOICES=[x for x in _STATUS_CHOICES if x[0] in OPEN_STATUSES]
182182

183183
patch = models.ForeignKey(Patch, blank=False, null=False)

pgcommitfest/commitfest/templates/commitfest.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ <h3>{{p.is_open|yesno:"Active patches,Closed patches"}}</h3>
8282
{%endifchanged%}
8383
{%endif%}
8484
<tr>
85-
<td><a href="{{p.id}}/">{{p}}</a></td>
85+
<td><a href="{{p.id}}/">{{p.name}}</a></td>
8686
<td><span class="label label-{{p.status|patchstatuslabel}}">{{p.status|patchstatusstring}}</span></td>
8787
<td>{{p.author_names|default:''}}</td>
8888
<td>{{p.reviewer_names|default:''}}</td>

pgcommitfest/commitfest/views.py

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,48 +104,55 @@ def commitfest(request, cfid):
104104
cf = get_object_or_404(CommitFest, pk=cfid)
105105

106106
# Build a dynamic filter based on the filtering options entered
107-
q = Q()
107+
whereclauses = []
108+
whereparams = {}
108109
if request.GET.has_key('status') and request.GET['status'] != "-1":
109110
try:
110-
q = q & Q(patchoncommitfest__status=int(request.GET['status']))
111+
whereclauses.append("poc.status=%(status)s")
112+
whereparams['status'] = int(request.GET['status'])
111113
except ValueError:
112114
# int() failed -- so just ignore this filter
113115
pass
114116

115117
if request.GET.has_key('author') and request.GET['author'] != "-1":
116118
if request.GET['author'] == '-2':
117-
q = q & Q(authors=None)
119+
whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id)")
118120
elif request.GET['author'] == '-3':
119121
# Checking for "yourself" requires the user to be logged in!
120122
if not request.user.is_authenticated():
121123
return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
122-
q = q & Q(authors=request.user)
124+
whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(self)s)")
125+
whereparams['self'] = request.user.id
123126
else:
124127
try:
125-
q = q & Q(authors__id=int(request.GET['author']))
128+
whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(author)s)")
129+
whereparams['author'] = int(request.GET['author'])
126130
except ValueError:
127131
# int() failed -- so just ignore this filter
128132
pass
129133

130134
if request.GET.has_key('reviewer') and request.GET['reviewer'] != "-1":
131135
if request.GET['reviewer'] == '-2':
132-
q = q & Q(reviewers=None)
136+
whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id)")
133137
elif request.GET['reviewer'] == '-3':
134138
# Checking for "yourself" requires the user to be logged in!
135139
if not request.user.is_authenticated():
136140
return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
137-
q = q & Q(reviewers=request.user)
141+
whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(self)s)")
142+
whereparams['self'] = request.user.id
138143
else:
139144
try:
140-
q = q & Q(reviewers__id=int(request.GET['reviewer']))
145+
whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(reviewer)s)")
146+
whereparams['reviewer'] = int(request.GET['reviewer'])
141147
except ValueError:
142148
# int() failed -- so just ignore this filter
143149
pass
144150

145151
if request.GET.has_key('text') and request.GET['text'] != '':
146-
q = q & Q(name__icontains=request.GET['text'])
152+
whereclauses.append("p.name ILIKE '%%' || %(txt)s || '%%'")
153+
whereparams['txt'] = request.GET['text']
147154

148-
has_filter = len(q.children) > 0
155+
has_filter = len(whereclauses) > 0
149156

150157
# Figure out custom ordering
151158
ordering = ['-is_open', 'topic__topic', 'created',]
@@ -156,27 +163,46 @@ def commitfest(request, cfid):
156163
sortkey=0
157164

158165
if sortkey==1:
159-
ordering = ['-is_open', 'modified', 'created',]
166+
orderby_str = 'modified, created'
160167
elif sortkey==2:
161-
ordering = ['-is_open', 'lastmail', 'created',]
168+
orderby_str = 'lastmail, created'
162169
elif sortkey==3:
163-
ordering = ['-is_open', '-num_cfs', 'modified', 'created']
170+
orderby_str = 'num_cfs DESC, modified, created'
164171
else:
172+
orderby_str = 'p.id'
165173
sortkey=0
166174
else:
175+
orderby_str = 'p.id'
167176
sortkey = 0
168177

169178
if not has_filter and sortkey==0 and request.GET:
170179
# Redirect to get rid of the ugly url
171180
return HttpResponseRedirect('/%s/' % cf.id)
172181

173-
patches = list(cf.patch_set.filter(q).select_related().extra(select={
174-
'status':'commitfest_patchoncommitfest.status',
175-
'author_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_authors cpa ON cpa.user_id=auth_user.id WHERE cpa.patch_id=commitfest_patch.id",
176-
'reviewer_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_reviewers cpr ON cpr.user_id=auth_user.id WHERE cpr.patch_id=commitfest_patch.id",
177-
'is_open':'commitfest_patchoncommitfest.status IN (%s)' % ','.join([str(x) for x in PatchOnCommitFest.OPEN_STATUSES]),
178-
'num_cfs': 'SELECT count(1) FROM commitfest_patchoncommitfest pcf WHERE pcf.patch_id=commitfest_patch.id',
179-
}).order_by(*ordering))
182+
if whereclauses:
183+
where_str = 'AND ({0})'.format(' AND '.join(whereclauses))
184+
else:
185+
where_str = ''
186+
params = {
187+
'cid': cf.id,
188+
'openstatuses': PatchOnCommitFest.OPEN_STATUSES,
189+
}
190+
params.update(whereparams)
191+
192+
# Let's not overload the poor django ORM
193+
curs = connection.cursor()
194+
curs.execute("""SELECT p.id, p.name, poc.status, p.created, p.modified, p.lastmail, committer.username AS committer,
195+
(poc.status=ANY(%(openstatuses)s)) AS is_open,
196+
(SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_authors cpa ON cpa.user_id=auth_user.id WHERE cpa.patch_id=p.id) AS author_names,
197+
(SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_reviewers cpr ON cpr.user_id=auth_user.id WHERE cpr.patch_id=p.id) AS reviewer_names,
198+
(SELECT count(1) FROM commitfest_patchoncommitfest pcf WHERE pcf.patch_id=p.id) AS num_cfs
199+
FROM commitfest_patch p
200+
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id
201+
LEFT JOIN auth_user committer ON committer.id=p.committer_id
202+
WHERE poc.commitfest_id=%(cid)s {0}
203+
GROUP BY p.id, poc.id, committer.id
204+
ORDER BY is_open DESC, {1}""".format(where_str, orderby_str), params)
205+
patches = [dict(zip([col[0] for col in curs.description], row)) for row in curs.fetchall()]
180206

181207
# Generate patch status summary.
182208
curs = connection.cursor()
@@ -199,7 +225,7 @@ def commitfest(request, cfid):
199225
'title': cf.title,
200226
'grouping': sortkey==0,
201227
'sortkey': sortkey,
202-
'openpatchids': [p.id for p in patches if p.is_open],
228+
'openpatchids': [p['id'] for p in patches if p['is_open']],
203229
'header_activity': 'Activity log',
204230
'header_activity_link': 'activity/',
205231
})

0 commit comments

Comments
 (0)