Coverage for python/lsst/daf/butler/registry/simpleQuery.py : 35%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = ("Select", "SimpleQuery")
26from typing import (
27 Any,
28 ClassVar,
29 List,
30 Optional,
31 Union,
32 Type,
33 TypeVar,
34)
36import sqlalchemy
39T = TypeVar("T")
42class Select:
43 """Tag class used to indicate that a field should be returned in
44 a SELECT query.
45 """
47 Or: ClassVar
50Select.Or = Union[T, Type[Select]]
51"""A type annotation for arguments that can take the `Select` type or some
52other value.
53"""
56class SimpleQuery:
57 """A struct that combines SQLAlchemy objects representing SELECT, FROM,
58 and WHERE clauses.
59 """
61 def __init__(self):
62 self.columns = []
63 self.where = []
64 self._from = None
66 def join(self, table: sqlalchemy.sql.FromClause, *,
67 onclause: Optional[sqlalchemy.sql.ColumnElement] = None,
68 isouter: bool = False,
69 full: bool = False,
70 **kwargs: Select.Or[Any]):
71 """Add a table or subquery join to the query, possibly adding
72 SELECT columns or WHERE expressions at the same time.
74 Parameters
75 ----------
76 table : `sqlalchemy.sql.FromClause`
77 Table or subquery to include.
78 onclause : `sqlalchemy.sql.ColumnElement`, optional
79 Expression used to join the new table or subquery to those already
80 present. Passed directly to `sqlalchemy.sql.FromClause.join`, but
81 ignored if this is the first call to `SimpleQuery.join`.
82 isouter : `bool`, optional
83 If `True`, make this an LEFT OUTER JOIN. Passed directly to
84 `sqlalchemy.sql.FromClause.join`.
85 full : `bool`, optional
86 If `True`, make this a FULL OUTER JOIN. Passed directly to
87 `sqlalchemy.sql.FromClause.join`.
88 **kwargs
89 Additional keyword arguments correspond to columns in the joined
90 table or subquery. Values may be:
92 - `Select` (a special tag type) to indicate that this column
93 should be added to the SELECT clause as a query result;
94 - `None` to do nothing (equivalent to no keyword argument);
95 - Any other value to add an equality constraint to the WHERE
96 clause that constrains this column to the given value. Note
97 that this cannot be used to add ``IS NULL`` constraints, because
98 the previous condition for `None` is checked first.
99 """
100 if self._from is None:
101 self._from = table
102 else:
103 self._from = self._from.join(table, onclause=onclause, isouter=isouter, full=full)
104 for name, arg in kwargs.items():
105 if arg is Select:
106 self.columns.append(table.columns[name].label(name))
107 elif arg is not None:
108 self.where.append(table.columns[name] == arg)
110 def combine(self) -> sqlalchemy.sql.Select:
111 """Combine all terms into a single query object.
113 Returns
114 -------
115 sql : `sqlalchemy.sql.Select`
116 A SQLAlchemy object representing the full query.
117 """
118 result = sqlalchemy.sql.select(self.columns)
119 if self._from is not None:
120 result = result.select_from(self._from)
121 if self.where:
122 result = result.where(sqlalchemy.sql.and_(*self.where))
123 return result
125 @property
126 def from_(self) -> sqlalchemy.sql.FromClause:
127 """The FROM clause of the query (`sqlalchemy.sql.FromClause`).
129 This property cannot be set. To add tables to the FROM clause, call
130 `join`.
131 """
132 return self._from
134 columns: List[sqlalchemy.sql.ColumnElement]
135 """The columns in the SELECT clause
136 (`list` [ `sqlalchemy.sql.ColumnElement` ]).
137 """
139 where: List[sqlalchemy.sql.ColumnElement]
140 """Boolean expressions that will be combined with AND to form the WHERE
141 clause (`list` [ `sqlalchemy.sql.ColumnElement` ]).
142 """