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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

# This file is part of daf_butler. 

# 

# Developed for the LSST Data Management System. 

# This product includes software developed by the LSST Project 

# (http://www.lsst.org). 

# See the COPYRIGHT file at the top-level directory of this distribution 

# for details of code ownership. 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the GNU General Public License 

# along with this program. If not, see <http://www.gnu.org/licenses/>. 

from __future__ import annotations 

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

`Registry`. 

""" 

 

__all__ = ["OpaqueTableStorageManager", "OpaqueTableStorage"] 

 

from abc import ABC, abstractmethod 

from typing import ( 

Any, 

Iterator, 

Optional, 

) 

 

from ...core.ddl import TableSpec 

from ._database import Database, StaticTablesContext 

 

 

class OpaqueTableStorage(ABC): 

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

opaque table in a `Registry`. 

 

Parameters 

---------- 

name : `str` 

Name of the opaque table. 

""" 

def __init__(self, name: str): 

self.name = name 

 

@abstractmethod 

def insert(self, *data: dict): 

"""Insert records into the table 

 

Parameters 

---------- 

*data 

Each additional positional argument is a dictionary that represents 

a single row to be added. 

""" 

raise NotImplementedError() 

 

@abstractmethod 

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

"""Retrieve records from an opaque table. 

 

Parameters 

---------- 

**where 

Additional keyword arguments are interpreted as equality 

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

keyword arguments are column names and values are the values they 

must have. 

 

Yields 

------ 

row : `dict` 

A dictionary representing a single result row. 

""" 

raise NotImplementedError() 

 

@abstractmethod 

def delete(self, **where: Any): 

"""Remove records from an opaque table. 

 

Parameters 

---------- 

**where 

Additional keyword arguments are interpreted as equality 

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

keyword arguments are column names and values are the values they 

must have. 

""" 

raise NotImplementedError() 

 

name: str 

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

""" 

 

 

class OpaqueTableStorageManager(ABC): 

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

 

`OpaqueTableStorageManager` primarily serves as a container and factory for 

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

for a different (logical) opaque table. 

 

Notes 

----- 

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

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

limited to this. 

 

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

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

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

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

""" 

 

@classmethod 

@abstractmethod 

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

"""Construct an instance of the manager. 

 

Parameters 

---------- 

db : `Database` 

Interface to the underlying database engine and namespace. 

context : `StaticTablesContext` 

Context object obtained from `Database.declareStaticTables`; used 

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

implemented with this manager. 

 

Returns 

------- 

manager : `OpaqueTableStorageManager` 

An instance of a concrete `OpaqueTableStorageManager` subclass. 

""" 

raise NotImplementedError() 

 

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

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

`None` on failure. 

""" 

r = self.get(name) 

if r is None: 

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

return r 

 

@abstractmethod 

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

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

an opaque logical table. 

 

Parameters 

---------- 

name : `str` 

Name of the logical table. 

 

Returns 

------- 

records : `OpaqueTableStorage` or `None` 

The object representing the records for the given table in this 

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

layer. 

 

Notes 

----- 

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

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

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

obtained from an existing data repository. 

""" 

raise NotImplementedError() 

 

@abstractmethod 

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

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

table, creating new tables as necessary. 

 

Parameters 

---------- 

name : `str` 

Name of the logical table. 

spec : `TableSpec` 

Schema specification for the table to be created. 

 

Returns 

------- 

records : `OpaqueTableStorage` 

The object representing the records for the given element in this 

layer. 

 

Notes 

----- 

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

""" 

raise NotImplementedError()