summaryrefslogtreecommitdiffstats
path: root/nova/common
diff options
context:
space:
mode:
authorpengyuwei <pengyuwei@gmail.com>2012-08-14 14:47:36 +0800
committerpengyuwei <pengyuwei@gmail.com>2012-09-07 19:20:51 +0800
commit75ca5dfa4a740c1f73750394722687cbdf3155e5 (patch)
tree11eb8602de501d70ca4fc08f7a64071bb78c5ad7 /nova/common
parent37cc45b8fdaa199b248a7ef5f683d514733b8387 (diff)
downloadnova-75ca5dfa4a740c1f73750394722687cbdf3155e5.tar.gz
nova-75ca5dfa4a740c1f73750394722687cbdf3155e5.tar.xz
nova-75ca5dfa4a740c1f73750394722687cbdf3155e5.zip
Implement paginate query use marker in nova-api
1.add limit and marker param to db.instance_get_all_by_filters() 2.set the filter use marker 3.execute limit before sqlarchmy get_all() 4.add testcase 'test_db_api.test_instance_get_all_by_filters_paginate' 5.related testcase: test_get_servers_with_marker() test_get_servers_with_limit_and_marker() in nova/tests/api/openstack/compute/test_servers.py test_instance_get_all_by_filters_paginate() in nova/tests/test_db_api.py 6.add InvalidSortkey exception Implement bp:efficient-limiting. Change-Id: Iea3eeb7b51194b6017d624506aafc6469d7338e4
Diffstat (limited to 'nova/common')
-rw-r--r--nova/common/sqlalchemyutils.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/nova/common/sqlalchemyutils.py b/nova/common/sqlalchemyutils.py
new file mode 100644
index 000000000..a186948ac
--- /dev/null
+++ b/nova/common/sqlalchemyutils.py
@@ -0,0 +1,128 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2010-2011 OpenStack LLC.
+# Copyright 2012 Justin Santa Barbara
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Implementation of paginate query."""
+
+import sqlalchemy
+
+from nova import exception
+from nova.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+# copy from glance/db/sqlalchemy/api.py
+def paginate_query(query, model, limit, sort_keys, marker=None,
+ sort_dir=None, sort_dirs=None):
+ """Returns a query with sorting / pagination criteria added.
+
+ Pagination works by requiring a unique sort_key, specified by sort_keys.
+ (If sort_keys is not unique, then we risk looping through values.)
+ We use the last row in the previous page as the 'marker' for pagination.
+ So we must return values that follow the passed marker in the order.
+ With a single-valued sort_key, this would be easy: sort_key > X.
+ With a compound-values sort_key, (k1, k2, k3) we must do this to repeat
+ the lexicographical ordering:
+ (k1 > X1) or (k1 == X1 && k2 > X2) or (k1 == X1 && k2 == X2 && k3 > X3)
+
+ We also have to cope with different sort_directions.
+
+ Typically, the id of the last row is used as the client-facing pagination
+ marker, then the actual marker object must be fetched from the db and
+ passed in to us as marker.
+
+ :param query: the query object to which we should add paging/sorting
+ :param model: the ORM model class
+ :param limit: maximum number of items to return
+ :param sort_keys: array of attributes by which results should be sorted
+ :param marker: the last item of the previous page; we returns the next
+ results after this value.
+ :param sort_dir: direction in which results should be sorted (asc, desc)
+ :param sort_dirs: per-column array of sort_dirs, corresponding to sort_keys
+
+ :rtype: sqlalchemy.orm.query.Query
+ :return: The query with sorting/pagination added.
+ """
+
+ if 'id' not in sort_keys:
+ # TODO(justinsb): If this ever gives a false-positive, check
+ # the actual primary key, rather than assuming its id
+ LOG.warn(_('Id not in sort_keys; is sort_keys unique?'))
+
+ assert(not (sort_dir and sort_dirs))
+
+ # Default the sort direction to ascending
+ if sort_dirs is None and sort_dir is None:
+ sort_dir = 'asc'
+
+ # Ensure a per-column sort direction
+ if sort_dirs is None:
+ sort_dirs = [sort_dir for _sort_key in sort_keys]
+
+ assert(len(sort_dirs) == len(sort_keys))
+
+ # Add sorting
+ for current_sort_key, current_sort_dir in zip(sort_keys, sort_dirs):
+ sort_dir_func = {
+ 'asc': sqlalchemy.asc,
+ 'desc': sqlalchemy.desc,
+ }[current_sort_dir]
+
+ try:
+ sort_key_attr = getattr(model, current_sort_key)
+ except AttributeError:
+ raise exception.InvalidSortKey()
+ query = query.order_by(sort_dir_func(sort_key_attr))
+
+ # Add pagination
+ if marker is not None:
+ marker_values = []
+ for sort_key in sort_keys:
+ v = getattr(marker, sort_key)
+ marker_values.append(v)
+
+ # Build up an array of sort criteria as in the docstring
+ criteria_list = []
+ for i in xrange(0, len(sort_keys)):
+ crit_attrs = []
+ for j in xrange(0, i):
+ model_attr = getattr(model, sort_keys[j])
+ crit_attrs.append((model_attr == marker_values[j]))
+
+ model_attr = getattr(model, sort_keys[i])
+ if sort_dirs[i] == 'desc':
+ crit_attrs.append((model_attr < marker_values[i]))
+ elif sort_dirs[i] == 'asc':
+ crit_attrs.append((model_attr > marker_values[i]))
+ else:
+ raise ValueError(_("Unknown sort direction, "
+ "must be 'desc' or 'asc'"))
+
+ criteria = sqlalchemy.sql.and_(*crit_attrs)
+ criteria_list.append(criteria)
+
+ f = sqlalchemy.sql.or_(*criteria_list)
+ query = query.filter(f)
+
+ if limit is not None:
+ query = query.limit(limit)
+
+ return query