summaryrefslogtreecommitdiffstats
path: root/openstack/common/db/sqlalchemy/utils.py
diff options
context:
space:
mode:
authorEric Windisch <eric@cloudscaling.com>2012-12-04 10:40:10 -0500
committerMark McLoughlin <markmc@redhat.com>2013-02-05 10:50:12 +0000
commit13a9e6dca7742493253d5b9b4bedb01d07fb242d (patch)
tree08455b67bf0e13593ad34260eb7fd7dd71ff36f9 /openstack/common/db/sqlalchemy/utils.py
parentbd5d9f08ecc3c1fade6dce809ee9edef1c226e54 (diff)
downloadoslo-13a9e6dca7742493253d5b9b4bedb01d07fb242d.tar.gz
oslo-13a9e6dca7742493253d5b9b4bedb01d07fb242d.tar.xz
oslo-13a9e6dca7742493253d5b9b4bedb01d07fb242d.zip
Import sqlalchemy session/models/utils
Bring in session, base model, utilities, and tests for sqlalchemy from Nova. Add sqlalchemy to pip-requires and and python-mysql to test-requires. Partially implements blueprint common-db Change-Id: I3e0065cdac87e10c4e0742d66c293c72bb3acbb2
Diffstat (limited to 'openstack/common/db/sqlalchemy/utils.py')
-rw-r--r--openstack/common/db/sqlalchemy/utils.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/openstack/common/db/sqlalchemy/utils.py b/openstack/common/db/sqlalchemy/utils.py
new file mode 100644
index 0000000..f7bb2f9
--- /dev/null
+++ b/openstack/common/db/sqlalchemy/utils.py
@@ -0,0 +1,129 @@
+# 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 openstack.common.db import common as db_common
+from openstack.common.gettextutils import _
+from 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 db_common.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