summaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/common/sql/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/common/sql/core.py')
-rw-r--r--keystone-moon/keystone/common/sql/core.py117
1 files changed, 76 insertions, 41 deletions
diff --git a/keystone-moon/keystone/common/sql/core.py b/keystone-moon/keystone/common/sql/core.py
index bf168701..ebd61bb7 100644
--- a/keystone-moon/keystone/common/sql/core.py
+++ b/keystone-moon/keystone/common/sql/core.py
@@ -239,6 +239,39 @@ def truncated(f):
return wrapper
+class _WontMatch(Exception):
+ """Raised to indicate that the filter won't match.
+
+ This is raised to short-circuit the computation of the filter as soon as
+ it's discovered that the filter requested isn't going to match anything.
+
+ A filter isn't going to match anything if the value is too long for the
+ field, for example.
+
+ """
+
+ @classmethod
+ def check(cls, value, col_attr):
+ """Check if the value can match given the column attributes.
+
+ Raises this class if the value provided can't match any value in the
+ column in the table given the column's attributes. For example, if the
+ column is a string and the value is longer than the column then it
+ won't match any value in the column in the table.
+
+ """
+ col = col_attr.property.columns[0]
+ if isinstance(col.type, sql.types.Boolean):
+ # The column is a Boolean, we should have already validated input.
+ return
+ if not col.type.length:
+ # The column doesn't have a length so can't validate anymore.
+ return
+ if len(value) > col.type.length:
+ raise cls()
+ # Otherwise the value could match a value in the column.
+
+
def _filter(model, query, hints):
"""Applies filtering to a query.
@@ -251,16 +284,14 @@ def _filter(model, query, hints):
:returns query: query, updated with any filters satisfied
"""
- def inexact_filter(model, query, filter_, satisfied_filters, hints):
+ def inexact_filter(model, query, filter_, satisfied_filters):
"""Applies an inexact filter to a query.
:param model: the table model in question
:param query: query to apply filters to
- :param filter_: the dict that describes this filter
- :param satisfied_filters: a cumulative list of satisfied filters, to
- which filter_ will be added if it is
- satisfied.
- :param hints: contains the list of filters yet to be satisfied.
+ :param dict filter_: describes this filter
+ :param list satisfied_filters: filter_ will be added if it is
+ satisfied.
:returns query: query updated to add any inexact filters we could
satisfy
@@ -278,10 +309,13 @@ def _filter(model, query, hints):
return query
if filter_['comparator'] == 'contains':
+ _WontMatch.check(filter_['value'], column_attr)
query_term = column_attr.ilike('%%%s%%' % filter_['value'])
elif filter_['comparator'] == 'startswith':
+ _WontMatch.check(filter_['value'], column_attr)
query_term = column_attr.ilike('%s%%' % filter_['value'])
elif filter_['comparator'] == 'endswith':
+ _WontMatch.check(filter_['value'], column_attr)
query_term = column_attr.ilike('%%%s' % filter_['value'])
else:
# It's a filter we don't understand, so let the caller
@@ -291,53 +325,50 @@ def _filter(model, query, hints):
satisfied_filters.append(filter_)
return query.filter(query_term)
- def exact_filter(
- model, filter_, satisfied_filters, cumulative_filter_dict, hints):
+ def exact_filter(model, filter_, cumulative_filter_dict):
"""Applies an exact filter to a query.
:param model: the table model in question
- :param filter_: the dict that describes this filter
- :param satisfied_filters: a cumulative list of satisfied filters, to
- which filter_ will be added if it is
- satisfied.
- :param cumulative_filter_dict: a dict that describes the set of
- exact filters built up so far
- :param hints: contains the list of filters yet to be satisfied.
-
- :returns: updated cumulative dict
+ :param dict filter_: describes this filter
+ :param dict cumulative_filter_dict: describes the set of exact filters
+ built up so far
"""
key = filter_['name']
- if isinstance(getattr(model, key).property.columns[0].type,
- sql.types.Boolean):
+
+ col = getattr(model, key)
+ if isinstance(col.property.columns[0].type, sql.types.Boolean):
cumulative_filter_dict[key] = (
utils.attr_as_boolean(filter_['value']))
else:
+ _WontMatch.check(filter_['value'], col)
cumulative_filter_dict[key] = filter_['value']
- satisfied_filters.append(filter_)
- return cumulative_filter_dict
-
- filter_dict = {}
- satisfied_filters = []
- for filter_ in hints.filters:
- if filter_['name'] not in model.attributes:
- continue
- if filter_['comparator'] == 'equals':
- filter_dict = exact_filter(
- model, filter_, satisfied_filters, filter_dict, hints)
- else:
- query = inexact_filter(
- model, query, filter_, satisfied_filters, hints)
- # Apply any exact filters we built up
- if filter_dict:
- query = query.filter_by(**filter_dict)
+ try:
+ filter_dict = {}
+ satisfied_filters = []
+ for filter_ in hints.filters:
+ if filter_['name'] not in model.attributes:
+ continue
+ if filter_['comparator'] == 'equals':
+ exact_filter(model, filter_, filter_dict)
+ satisfied_filters.append(filter_)
+ else:
+ query = inexact_filter(model, query, filter_,
+ satisfied_filters)
+
+ # Apply any exact filters we built up
+ if filter_dict:
+ query = query.filter_by(**filter_dict)
+
+ # Remove satisfied filters, then the caller will know remaining filters
+ for filter_ in satisfied_filters:
+ hints.filters.remove(filter_)
- # Remove satisfied filters, then the caller will know remaining filters
- for filter_ in satisfied_filters:
- hints.filters.remove(filter_)
-
- return query
+ return query
+ except _WontMatch:
+ hints.cannot_match = True
+ return
def _limit(query, hints):
@@ -378,6 +409,10 @@ def filter_limit_query(model, query, hints):
# First try and satisfy any filters
query = _filter(model, query, hints)
+ if hints.cannot_match:
+ # Nothing's going to match, so don't bother with the query.
+ return []
+
# NOTE(henry-nash): Any unsatisfied filters will have been left in
# the hints list for the controller to handle. We can only try and
# limit here if all the filters are already satisfied since, if not,