added Query object and friends (from_string is next)

--HG--
extra : convert_revision : svn%3A41726ec3-264d-0410-9c23-a9f1637257cc/trunk%405
This commit is contained in:
adrian.sampson 2008-05-15 05:08:31 +00:00
parent 2651b1c2b5
commit 932f839bca

View file

@ -31,6 +31,10 @@ item_keys = map(operator.itemgetter(0), item_fields)
class LibraryError(Exception): class LibraryError(Exception):
pass pass
class InvalidFieldError(Exception):
pass
@ -179,6 +183,110 @@ class Item(object):
class QueryElement(object):
"""A building block for library queries."""
def clause(self):
"""Returns (clause, subvals) where clause is a valid sqlite WHERE
clause implementing the query element and subvals is a list of items
to be substituted for ?s in the clause."""
raise NotImplementedError
class SubstringQueryElement(QueryElement):
"""A query element that matches a substring in a specific item field."""
def __init__(self, field, value):
if field not in item_keys:
raise InvalidFieldError(field + ' is not an item key')
self.field = field
self.value = value
def clause(self):
search = '%' + (self.value.replace('\\','\\\\').replace('%','\\%')
.replace('_','\\_')) + '%'
clause = self.field + " like ? escape '\\'"
subvals = [search]
return (clause, subvals)
class AndQueryElement(QueryElement):
"""A conjunction of a list of other query elements. Can be indexed like a
list to access the sub-elements."""
def __init__(self, elements = None):
if elements is None:
self.elements = []
else:
self.elements = elements
# is there a better way to do this?
def __len__(self): return len(self.elements)
def __getitem__(self, key): return self.elements[key]
def __setitem__(self, key, value): self.elements[key] = value
def __delitem__(self, key): del self.elements[key]
def __iter__(self): iter(self.elements)
def __contains__(self, item): item in self.elements
def clause(self):
clause_parts = []
subvals = []
for el in self.elements:
el_clause, el_subvals = el.clause()
clause_parts.append('(' + el_clause + ')')
subvals += el_subvals
clause = ' and '.join(clause_parts)
return (clause, subvals)
@classmethod
def from_dict(cls, matches):
"""Construct a query element from a dictionary, matches, whose keys
are item field names and whose values are substring patterns."""
elements = []
for key, pattern in matches.iteritems():
elements.append(SubstringQueryElement(key, pattern))
return cls(elements)
class Query(AndQueryElement):
"""A query into the item database."""
def statement(self, columns='*'):
"""Returns (query, subvals) where clause is a sqlite SELECT statement
to enact this query and subvals is a list of values to substitute in
for ?s in the query."""
clause, subvals = self.clause()
return ('select ' + columns + ' from items where ' + clause, subvals)
def execute(self, library):
"""Runs the query in the specified library, returning an
ItemResultIterator."""
cursor = library.conn.cursor()
cursor.execute(*self.statement())
return ItemResultIterator(cursor)
class ItemResultIterator(object):
"""An iterator into an item result set."""
def __init__(self, cursor):
self.cursor = cursor
def __iter__(self): return self
def next(self):
try:
row = self.cursor.next()
except StopIteration:
self.cursor.close()
raise StopIteration
return Item(row)
class Library(object): class Library(object):
def __init__(self, path='library.blb'): def __init__(self, path='library.blb'):
self.path = path self.path = path