Remove unnecessary conditions from some queries.

PG9 doesn't like the query from _load_link_comments
in comment_tree.py.

thing.py will automatically filter deleted and spam items
out of thing queries if no rules are specified for those
columns. To get around this, some queries, including the
_load_link_comments one, will specify a rule
like _spam == (True, False). This rule was left in the ruleset,
despite the fact that it's completely unnecessary from the
SQL's standpoint. Its presence then triggered the need of a
join against the thing table when only the data table was
truly necessary. The query planner in PG9 doesn't like this
particular query when it requires a join and will force a
full table scan as a result.

This patch notes the special case of _spam/_deleted ==
(True,False) and turns off the default filtering but removes
the rules from the query. This saves some WHERE clauses
and more importantly removes the need for a JOIN in queries
like this one.
This commit is contained in:
Neil Williams
2011-07-13 21:58:58 -07:00
parent da9736a8b2
commit 165c3d12d6
2 changed files with 14 additions and 5 deletions

View File

@@ -309,6 +309,7 @@ def _load_link_comments(link_id):
q = Comment._query(Comment.c.link_id == link_id,
Comment.c._deleted == (True, False),
Comment.c._spam == (True, False),
optimize_rules=True,
data = True)
comments = list(q)
cids = [c._id for c in comments]

View File

@@ -585,20 +585,28 @@ class Thing(DataThing):
bases.deleted, bases.spam, id)
@classmethod
def _query(cls, *rules, **kw):
def _query(cls, *all_rules, **kw):
need_deleted = True
need_spam = True
#add default spam/deleted
for r in rules:
rules = []
optimize_rules = kw.pop('optimize_rules', False)
for r in all_rules:
if not isinstance(r, operators.op):
continue
if r.lval_name == '_deleted':
need_deleted = False
# if the caller is explicitly unfiltering based on this column,
# we don't need this rule at all. taking this out can save us a
# join that is very expensive on pg9.
if optimize_rules and r.rval == (True, False):
continue
elif r.lval_name == '_spam':
need_spam = False
if need_deleted or need_spam:
rules = list(rules)
# see above for explanation
if optimize_rules and r.rval == (True, False):
continue
rules.append(r)
if need_deleted:
rules.append(cls.c._deleted == False)