Implement custom where clause in SQL.

Allow categories to specify a WHERE clause that applies in addition to the
pattern filter. This allows the url completion model to filter out redirect
entries.

This also fixed the usage of ESCAPE so it applies to all the LIKE statements.
This commit is contained in:
Ryan Roden-Corrent 2017-02-14 22:10:49 -05:00
parent 9f27a9a5d7
commit fe80878788
4 changed files with 28 additions and 7 deletions

View File

@ -29,7 +29,7 @@ from qutebrowser.misc import sql
class SqlCompletionCategory(QSqlQueryModel):
def __init__(self, name, sort_by, sort_order, limit, select,
def __init__(self, name, sort_by, sort_order, limit, select, where,
columns_to_filter, parent=None):
super().__init__(parent=parent)
self.tablename = name
@ -37,9 +37,12 @@ class SqlCompletionCategory(QSqlQueryModel):
query = sql.run_query('select * from {} limit 1'.format(name))
self._fields = [query.record().fieldName(i) for i in columns_to_filter]
querystr = 'select {} from {} where '.format(select, name)
querystr += ' or '.join('{} like ?'.format(f) for f in self._fields)
querystr += " escape '\\'"
querystr = 'select {} from {} where ('.format(select, name)
querystr += ' or '.join("{} like ? escape '\\'".format(f)
for f in self._fields)
querystr += ')'
if where:
querystr += ' and ' + where
if sort_by:
sortstr = 'asc' if sort_order == Qt.AscendingOrder else 'desc'
@ -87,13 +90,14 @@ class SqlCompletionModel(QAbstractItemModel):
self.srcmodel = self # TODO: dummy for compat with old API
self.pattern = ''
def new_category(self, name, select='*', sort_by=None,
def new_category(self, name, select='*', where=None, sort_by=None,
sort_order=None, limit=None):
"""Create a new completion category and add it to this model.
Args:
name: Name of category, and the table in the database.
select: A custom result column expression for the select statement.
where: An optional clause to filter out some rows.
sort_by: The name of the field to sort by, or None for no sorting.
sort_order: Sorting order, if sort_by is non-None.
limit: Maximum row count to return on a query.
@ -102,7 +106,7 @@ class SqlCompletionModel(QAbstractItemModel):
"""
cat = SqlCompletionCategory(name, parent=self, sort_by=sort_by,
sort_order=sort_order, limit=limit,
select=select,
select=select, where=where,
columns_to_filter=self.columns_to_filter)
self._categories.append(cat)

View File

@ -38,7 +38,8 @@ def url():
select_time = "strftime('{}', atime, 'unixepoch')".format(timefmt)
model.new_category('History',
limit=limit,
select='url, title, {}'.format(select_time))
select='url, title, {}'.format(select_time),
where='not redirect')
model.new_category('Quickmarks', select='url, name')
model.new_category('Bookmarks')
return model

View File

@ -154,6 +154,8 @@ def web_history(stubs, init_sql):
"""Pre-populate the web-history database."""
table = sql.SqlTable("History", ['url', 'title', 'atime', 'redirect'],
primary_key='url')
table.insert(['http://some-redirect.example.com', 'redirect',
datetime(2016, 9, 5).timestamp(), True])
table.insert(['http://qutebrowser.org', 'qutebrowser',
datetime(2015, 9, 5).timestamp(), False])
table.insert(['https://python.org', 'Welcome to Python.org',
@ -266,6 +268,7 @@ def test_url_completion(qtmodeltester, config_stub, web_history, quickmarks,
- quickmarks, bookmarks, and urls are included
- no more than 'web-history-max-items' items are included (TODO)
- the most recent entries are included
- redirect entries are not included
"""
# TODO: time formatting and item limiting
config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d',

View File

@ -156,6 +156,10 @@ def test_sorting(sort_by, sort_order, data, expected):
[('A', [('a_b', '', ''), ('__a', '', ''), ('abc', '', '')])],
[('A', [('a_b', '', ''), ('__a', '', '')])]),
('%', [0, 1],
[('A', [('\\foo', '\\bar', '')])],
[('A', [])]),
("can't", [0],
[('A', [("can't touch this", '', ''), ('a', '', '')])],
[('A', [("can't touch this", '', '')])]),
@ -218,3 +222,12 @@ def test_select():
model = sqlmodel.SqlCompletionModel()
model.new_category('test_select', select='b, c, a')
_check_model(model, [('test_select', [('bar', 'baz', 'foo')])])
def test_where():
table = sql.SqlTable('test_where', ['a', 'b', 'c'], primary_key='a')
table.insert(['foo', 'bar', False])
table.insert(['baz', 'biz', True])
model = sqlmodel.SqlCompletionModel()
model.new_category('test_where', where='not c')
_check_model(model, [('test_where', [('foo', 'bar', False)])])