summaryrefslogtreecommitdiffstats
path: root/nova/db
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-08-13 16:17:08 +0000
committerGerrit Code Review <review@openstack.org>2012-08-13 16:17:08 +0000
commitdafa79ff7880af907b5836d832e6c5dbfd7e0529 (patch)
tree2ed64a97450761726b0fc722f4e9c06b7aa40fb0 /nova/db
parent2db23f6ba4db8fe95d05874dc3c6bef017a412ae (diff)
parent56a6fa2bb68a8738f8d02f41f92983ee3115a19d (diff)
Merge "Move results filtering to db."
Diffstat (limited to 'nova/db')
-rw-r--r--nova/db/sqlalchemy/api.py91
-rw-r--r--nova/db/sqlalchemy/session.py12
2 files changed, 56 insertions, 47 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index ba34a08d8..c54388f07 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -23,7 +23,6 @@ import collections
import copy
import datetime
import functools
-import re
import warnings
from nova import block_device
@@ -44,7 +43,6 @@ from sqlalchemy.orm import joinedload_all
from sqlalchemy.sql.expression import asc
from sqlalchemy.sql.expression import desc
from sqlalchemy.sql.expression import literal_column
-from sqlalchemy.sql.expression import or_
from sqlalchemy.sql import func
FLAGS = flags.FLAGS
@@ -247,7 +245,19 @@ def exact_filter(query, model, filters, legal_keys):
# OK, filtering on this key; what value do we search for?
value = filters.pop(key)
- if isinstance(value, (list, tuple, set, frozenset)):
+ if key == 'metadata':
+ column_attr = getattr(model, key)
+ if isinstance(value, list):
+ for item in value:
+ for k, v in item.iteritems():
+ query = query.filter(column_attr.any(key=k))
+ query = query.filter(column_attr.any(value=v))
+
+ else:
+ for k, v in value.iteritems():
+ query = query.filter(column_attr.any(key=k))
+ query = query.filter(column_attr.any(value=v))
+ elif isinstance(value, (list, tuple, set, frozenset)):
# Looking for values in a list; apply to query directly
column_attr = getattr(model, key)
query = query.filter(column_attr.in_(value))
@@ -1517,28 +1527,6 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir):
will be returned by default, unless there's a filter that says
otherwise"""
- def _regexp_filter_by_metadata(instance, meta):
- inst_metadata = [{node['key']: node['value']}
- for node in instance['metadata']]
- if isinstance(meta, list):
- for node in meta:
- if node not in inst_metadata:
- return False
- elif isinstance(meta, dict):
- for k, v in meta.iteritems():
- if {k: v} not in inst_metadata:
- return False
- return True
-
- def _regexp_filter_by_column(instance, filter_name, filter_re):
- try:
- v = getattr(instance, filter_name)
- except AttributeError:
- return True
- if v and filter_re.match(unicode(v)):
- return True
- return False
-
sort_fn = {'desc': desc, 'asc': asc}
session = get_session()
@@ -1580,37 +1568,46 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir):
# Filters for exact matches that we can do along with the SQL query...
# For other filters that don't match this, we will do regexp matching
exact_match_filter_names = ['project_id', 'user_id', 'image_ref',
- 'vm_state', 'instance_type_id', 'uuid']
+ 'vm_state', 'instance_type_id', 'uuid',
+ 'metadata']
# Filter the query
query_prefix = exact_filter(query_prefix, models.Instance,
filters, exact_match_filter_names)
+ query_prefix = regex_filter(query_prefix, models.Instance, filters)
instances = query_prefix.all()
- if not instances:
- return []
+ return instances
- # Now filter on everything else for regexp matching..
- # For filters not in the list, we'll attempt to use the filter_name
- # as a column name in Instance..
- regexp_filter_funcs = {}
- for filter_name in filters.iterkeys():
- filter_func = regexp_filter_funcs.get(filter_name, None)
- filter_re = re.compile(str(filters[filter_name]))
- if filter_func:
- filter_l = lambda instance: filter_func(instance, filter_re)
- elif filter_name == 'metadata':
- filter_l = lambda instance: _regexp_filter_by_metadata(instance,
- filters[filter_name])
- else:
- filter_l = lambda instance: _regexp_filter_by_column(instance,
- filter_name, filter_re)
- instances = filter(filter_l, instances)
- if not instances:
- break
+def regex_filter(query, model, filters):
+ """Applies regular expression filtering to a query.
- return instances
+ Returns the updated query.
+
+ :param query: query to apply filters to
+ :param model: model object the query applies to
+ :param filters: dictionary of filters with regex values
+ """
+
+ regexp_op_map = {
+ 'postgresql': '~',
+ 'mysql': 'REGEXP',
+ 'oracle': 'REGEXP_LIKE',
+ 'sqlite': 'REGEXP'
+ }
+ db_string = FLAGS.sql_connection.split(':')[0].split('+')[0]
+ db_regexp_op = regexp_op_map.get(db_string, 'LIKE')
+ for filter_name in filters.iterkeys():
+ try:
+ column_attr = getattr(model, filter_name)
+ except AttributeError:
+ continue
+ if 'property' == type(column_attr).__name__:
+ continue
+ query = query.filter(column_attr.op(db_regexp_op)(
+ str(filters[filter_name])))
+ return query
@require_context
diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py
index 6aa5050e4..cada9d79a 100644
--- a/nova/db/sqlalchemy/session.py
+++ b/nova/db/sqlalchemy/session.py
@@ -18,6 +18,7 @@
"""Session Handling for SQLAlchemy backend."""
+import re
import time
from sqlalchemy.exc import DisconnectionError, OperationalError
@@ -85,6 +86,16 @@ def is_db_connection_error(args):
return False
+def regexp(expr, item):
+ reg = re.compile(expr)
+ return reg.search(unicode(item)) is not None
+
+
+class AddRegexFactory(sqlalchemy.interfaces.PoolListener):
+ def connect(delf, dbapi_con, con_record):
+ dbapi_con.create_function('REGEXP', 2, regexp)
+
+
def get_engine():
"""Return a SQLAlchemy engine."""
global _ENGINE
@@ -109,6 +120,7 @@ def get_engine():
if FLAGS.sql_connection == "sqlite://":
engine_args["poolclass"] = StaticPool
engine_args["connect_args"] = {'check_same_thread': False}
+ engine_args['listeners'] = [AddRegexFactory()]
_ENGINE = sqlalchemy.create_engine(FLAGS.sql_connection, **engine_args)