ResultSet.is_empty() raises IndexError with valid query
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Storm |
New
|
Undecided
|
Unassigned |
Bug Description
In Launchpad, our queries that use the LIKE operator are written like this:
>>> query = "englishname ILIKE '%%' || %(text)s || '%%'" % {'text': quote_like(
>>> query
"englishname ILIKE '%' || 'Spanish' || '%'"
When we feed that query to Store.find(), we get a ResultSet object which contains the correct items from the DB and looks like a fully-functional ResultSet.
>>> r = s.find(Language, query)
>>> r.count()
21
>>> r.any()
<Language at 0xb932eec>
However, if we call the .is_empty() method, it'll raise an IndexError.
>>> r.is_empty()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/home/
result = self._store.
File "/home/
return Connection.
File "/home/
raw_cursor = self.raw_
File "/home/
return Connection.
File "/home/
self.
File "/home/
return function(*args, **kwargs)
IndexError: tuple index out of range
tags: | added: tech-debt |
The problem is caused by the way psycopg2 interprets query strings. If you call cursor. execute( query), then psycopg2 passes the query through directly. If you call cursor. execute( query, params), it performs string substitution.
Your query string contains unmatched format characters, so it works if the query Storm produces has no parameters (as the count() query appears to) but fails if it does produce parameters (as is_empty() appears to).
There are two solutions to your problem:
1. Use Storm's expression compiler instead of raw query strings. The storm.sqlobject .CONTAINSSTRING expression object does most of what you want, although it doesn't turn on case insensitivity.
2. This one is a hack: double the percent signs. In pattern matching, "%%" will match any number of characters as well as "%", so will work whether string substitution is performed on your query string or not. I think this one is used in one or two places in Launchpad already.