Hide keyboard shortcuts

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/>. 

21from __future__ import annotations 

22"""Interfaces for the objects that manage opaque (logical) tables within a 

23`Registry`. 

24""" 

25 

26__all__ = ["OpaqueTableStorageManager", "OpaqueTableStorage"] 

27 

28from abc import ABC, abstractmethod 

29from typing import ( 

30 Any, 

31 Iterator, 

32 Optional, 

33) 

34 

35from ...core.ddl import TableSpec 

36from ._database import Database, StaticTablesContext 

37from ._versioning import VersionedExtension 

38 

39 

40class OpaqueTableStorage(ABC): 

41 """An interface that manages the records associated with a particular 

42 opaque table in a `Registry`. 

43 

44 Parameters 

45 ---------- 

46 name : `str` 

47 Name of the opaque table. 

48 """ 

49 def __init__(self, name: str): 

50 self.name = name 

51 

52 @abstractmethod 

53 def insert(self, *data: dict) -> None: 

54 """Insert records into the table 

55 

56 Parameters 

57 ---------- 

58 *data 

59 Each additional positional argument is a dictionary that represents 

60 a single row to be added. 

61 """ 

62 raise NotImplementedError() 

63 

64 @abstractmethod 

65 def fetch(self, **where: Any) -> Iterator[dict]: 

66 """Retrieve records from an opaque table. 

67 

68 Parameters 

69 ---------- 

70 **where 

71 Additional keyword arguments are interpreted as equality 

72 constraints that restrict the returned rows (combined with AND); 

73 keyword arguments are column names and values are the values they 

74 must have. 

75 

76 Yields 

77 ------ 

78 row : `dict` 

79 A dictionary representing a single result row. 

80 """ 

81 raise NotImplementedError() 

82 

83 @abstractmethod 

84 def delete(self, **where: Any) -> None: 

85 """Remove records from an opaque table. 

86 

87 Parameters 

88 ---------- 

89 **where 

90 Additional keyword arguments are interpreted as equality 

91 constraints that restrict the deleted rows (combined with AND); 

92 keyword arguments are column names and values are the values they 

93 must have. 

94 """ 

95 raise NotImplementedError() 

96 

97 name: str 

98 """The name of the logical table this instance manages (`str`). 

99 """ 

100 

101 

102class OpaqueTableStorageManager(VersionedExtension): 

103 """An interface that manages the opaque tables in a `Registry`. 

104 

105 `OpaqueTableStorageManager` primarily serves as a container and factory for 

106 `OpaqueTableStorage` instances, which each provide access to the records 

107 for a different (logical) opaque table. 

108 

109 Notes 

110 ----- 

111 Opaque tables are primarily used by `Datastore` instances to manage their 

112 internal data in the same database that hold the `Registry`, but are not 

113 limited to this. 

114 

115 While an opaque table in a multi-layer `Registry` may in fact be the union 

116 of multiple tables in different layers, we expect this to be rare, as 

117 `Registry` layers will typically correspond to different leaf `Datastore` 

118 instances (each with their own opaque table) in a `ChainedDatastore`. 

119 """ 

120 

121 @classmethod 

122 @abstractmethod 

123 def initialize(cls, db: Database, context: StaticTablesContext) -> OpaqueTableStorageManager: 

124 """Construct an instance of the manager. 

125 

126 Parameters 

127 ---------- 

128 db : `Database` 

129 Interface to the underlying database engine and namespace. 

130 context : `StaticTablesContext` 

131 Context object obtained from `Database.declareStaticTables`; used 

132 to declare any tables that should always be present in a layer 

133 implemented with this manager. 

134 

135 Returns 

136 ------- 

137 manager : `OpaqueTableStorageManager` 

138 An instance of a concrete `OpaqueTableStorageManager` subclass. 

139 """ 

140 raise NotImplementedError() 

141 

142 def __getitem__(self, name: str) -> OpaqueTableStorage: 

143 """Interface to `get` that raises `LookupError` instead of returning 

144 `None` on failure. 

145 """ 

146 r = self.get(name) 

147 if r is None: 

148 raise LookupError(f"No logical table with name '{name}' found.") 

149 return r 

150 

151 @abstractmethod 

152 def get(self, name: str) -> Optional[OpaqueTableStorage]: 

153 """Return an object that provides access to the records associated with 

154 an opaque logical table. 

155 

156 Parameters 

157 ---------- 

158 name : `str` 

159 Name of the logical table. 

160 

161 Returns 

162 ------- 

163 records : `OpaqueTableStorage` or `None` 

164 The object representing the records for the given table in this 

165 layer, or `None` if there are no records for that table in this 

166 layer. 

167 

168 Notes 

169 ----- 

170 Opaque tables must be registered with the layer (see `register`) by 

171 the same client before they can safely be retrieved with `get`. 

172 Unlike most other manager classes, the set of opaque tables cannot be 

173 obtained from an existing data repository. 

174 """ 

175 raise NotImplementedError() 

176 

177 @abstractmethod 

178 def register(self, name: str, spec: TableSpec) -> OpaqueTableStorage: 

179 """Ensure that this layer can hold records for the given opaque logical 

180 table, creating new tables as necessary. 

181 

182 Parameters 

183 ---------- 

184 name : `str` 

185 Name of the logical table. 

186 spec : `TableSpec` 

187 Schema specification for the table to be created. 

188 

189 Returns 

190 ------- 

191 records : `OpaqueTableStorage` 

192 The object representing the records for the given element in this 

193 layer. 

194 

195 Notes 

196 ----- 

197 This operation may not be invoked within a transaction context block. 

198 """ 

199 raise NotImplementedError()