Coverage for python/lsst/daf/butler/registry/interfaces/_opaque.py: 87%
39 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-25 15:14 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-25 15:14 +0000
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/>.
22"""Interfaces for the objects that manage opaque (logical) tables within a
23`Registry`.
24"""
26from __future__ import annotations
28__all__ = ["OpaqueTableStorageManager", "OpaqueTableStorage"]
30from abc import ABC, abstractmethod
31from collections.abc import Iterable, Iterator, Mapping
32from typing import TYPE_CHECKING, Any
34from ...core.ddl import TableSpec
35from ._database import Database, StaticTablesContext
36from ._versioning import VersionedExtension, VersionTuple
38if TYPE_CHECKING:
39 from ...core.datastore import DatastoreTransaction
42class OpaqueTableStorage(ABC):
43 """An interface that manages the records associated with a particular
44 opaque table in a `Registry`.
46 Parameters
47 ----------
48 name : `str`
49 Name of the opaque table.
50 """
52 def __init__(self, name: str):
53 self.name = name
55 @abstractmethod
56 def insert(self, *data: dict, transaction: DatastoreTransaction | None = None) -> None:
57 """Insert records into the table.
59 Parameters
60 ----------
61 *data
62 Each additional positional argument is a dictionary that represents
63 a single row to be added.
64 transaction : `DatastoreTransaction`, optional
65 Transaction object that can be used to enable an explicit rollback
66 of the insert to be registered. Can be ignored if rollback is
67 handled via a different mechanism, such as by a database. Can be
68 `None` if no external transaction is available.
69 """
70 raise NotImplementedError()
72 @abstractmethod
73 def ensure(self, *data: dict, transaction: DatastoreTransaction | None = None) -> None:
74 """Insert records into the table, skipping rows that already exist.
76 Parameters
77 ----------
78 *data
79 Each additional positional argument is a dictionary that represents
80 a single row to be added.
81 transaction : `DatastoreTransaction`, optional
82 Transaction object that can be used to enable an explicit rollback
83 of the insert to be registered. Can be ignored if rollback is
84 handled via a different mechanism, such as by a database. Can be
85 `None` if no external transaction is available.
86 """
87 raise NotImplementedError()
89 @abstractmethod
90 def replace(self, *data: dict, transaction: DatastoreTransaction | None = None) -> None:
91 """Insert records into the table, replacing if previously existing
92 but different.
94 Parameters
95 ----------
96 *data
97 Each additional positional argument is a dictionary that represents
98 a single row to be added.
99 transaction : `DatastoreTransaction`, optional
100 Transaction object that can be used to enable an explicit rollback
101 of the insert to be registered. Can be ignored if rollback is
102 handled via a different mechanism, such as by a database. Can be
103 `None` if no external transaction is available.
104 """
105 raise NotImplementedError()
107 @abstractmethod
108 def fetch(self, **where: Any) -> Iterator[Mapping[Any, Any]]:
109 """Retrieve records from an opaque table.
111 Parameters
112 ----------
113 **where
114 Additional keyword arguments are interpreted as equality
115 constraints that restrict the returned rows (combined with AND);
116 keyword arguments are column names and values are the values they
117 must have.
119 Yields
120 ------
121 row : `dict`
122 A dictionary representing a single result row.
123 """
124 raise NotImplementedError()
126 @abstractmethod
127 def delete(self, columns: Iterable[str], *rows: dict) -> None:
128 """Remove records from an opaque table.
130 Parameters
131 ----------
132 columns: `~collections.abc.Iterable` of `str`
133 The names of columns that will be used to constrain the rows to
134 be deleted; these will be combined via ``AND`` to form the
135 ``WHERE`` clause of the delete query.
136 *rows
137 Positional arguments are the keys of rows to be deleted, as
138 dictionaries mapping column name to value. The keys in all
139 dictionaries must be exactly the names in ``columns``.
140 """
141 raise NotImplementedError()
143 name: str
144 """The name of the logical table this instance manages (`str`).
145 """
148class OpaqueTableStorageManager(VersionedExtension):
149 """An interface that manages the opaque tables in a `Registry`.
151 `OpaqueTableStorageManager` primarily serves as a container and factory for
152 `OpaqueTableStorage` instances, which each provide access to the records
153 for a different (logical) opaque table.
155 Notes
156 -----
157 Opaque tables are primarily used by `Datastore` instances to manage their
158 internal data in the same database that hold the `Registry`, but are not
159 limited to this.
161 While an opaque table in a multi-layer `Registry` may in fact be the union
162 of multiple tables in different layers, we expect this to be rare, as
163 `Registry` layers will typically correspond to different leaf `Datastore`
164 instances (each with their own opaque table) in a `ChainedDatastore`.
165 """
167 def __init__(self, *, registry_schema_version: VersionTuple | None = None):
168 super().__init__(registry_schema_version=registry_schema_version)
170 @classmethod
171 @abstractmethod
172 def initialize(
173 cls, db: Database, context: StaticTablesContext, registry_schema_version: VersionTuple | None = None
174 ) -> OpaqueTableStorageManager:
175 """Construct an instance of the manager.
177 Parameters
178 ----------
179 db : `Database`
180 Interface to the underlying database engine and namespace.
181 context : `StaticTablesContext`
182 Context object obtained from `Database.declareStaticTables`; used
183 to declare any tables that should always be present in a layer
184 implemented with this manager.
185 registry_schema_version : `VersionTuple` or `None`
186 Schema version of this extension as defined in registry.
188 Returns
189 -------
190 manager : `OpaqueTableStorageManager`
191 An instance of a concrete `OpaqueTableStorageManager` subclass.
192 """
193 raise NotImplementedError()
195 def __getitem__(self, name: str) -> OpaqueTableStorage:
196 """Interface to `get` that raises `LookupError` instead of returning
197 `None` on failure.
198 """
199 r = self.get(name)
200 if r is None:
201 raise LookupError(f"No logical table with name '{name}' found.")
202 return r
204 @abstractmethod
205 def get(self, name: str) -> OpaqueTableStorage | None:
206 """Return an object that provides access to the records associated with
207 an opaque logical table.
209 Parameters
210 ----------
211 name : `str`
212 Name of the logical table.
214 Returns
215 -------
216 records : `OpaqueTableStorage` or `None`
217 The object representing the records for the given table in this
218 layer, or `None` if there are no records for that table in this
219 layer.
221 Notes
222 -----
223 Opaque tables must be registered with the layer (see `register`) by
224 the same client before they can safely be retrieved with `get`.
225 Unlike most other manager classes, the set of opaque tables cannot be
226 obtained from an existing data repository.
227 """
228 raise NotImplementedError()
230 @abstractmethod
231 def register(self, name: str, spec: TableSpec) -> OpaqueTableStorage:
232 """Ensure that this layer can hold records for the given opaque logical
233 table, creating new tables as necessary.
235 Parameters
236 ----------
237 name : `str`
238 Name of the logical table.
239 spec : `TableSpec`
240 Schema specification for the table to be created.
242 Returns
243 -------
244 records : `OpaqueTableStorage`
245 The object representing the records for the given element in this
246 layer.
248 Notes
249 -----
250 This operation may not be invoked within a transaction context block.
251 """
252 raise NotImplementedError()