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 

23__all__ = ["NameKeyCollectionManager"] 

24 

25from typing import ( 

26 Any, 

27 Optional, 

28 Type, 

29 TYPE_CHECKING, 

30) 

31 

32import sqlalchemy 

33 

34from ._base import ( 

35 CollectionTablesTuple, 

36 DefaultCollectionManager, 

37 makeRunTableSpec, 

38 makeCollectionChainTableSpec, 

39) 

40 

41from ...core import TimespanDatabaseRepresentation, ddl 

42from ..interfaces import VersionTuple 

43 

44if TYPE_CHECKING: 44 ↛ 45line 44 didn't jump to line 45, because the condition on line 44 was never true

45 from ..interfaces import CollectionRecord, Database, DimensionRecordStorageManager, StaticTablesContext 

46 

47 

48_KEY_FIELD_SPEC = ddl.FieldSpec("name", dtype=sqlalchemy.String, length=64, primaryKey=True) 

49 

50 

51# This has to be updated on every schema change 

52_VERSION = VersionTuple(2, 0, 0) 

53 

54 

55def _makeTableSpecs(TimespanReprClass: Type[TimespanDatabaseRepresentation]) -> CollectionTablesTuple: 

56 return CollectionTablesTuple( 

57 collection=ddl.TableSpec( 

58 fields=[ 

59 _KEY_FIELD_SPEC, 

60 ddl.FieldSpec("type", dtype=sqlalchemy.SmallInteger, nullable=False), 

61 ddl.FieldSpec("doc", dtype=sqlalchemy.Text, nullable=True), 

62 ], 

63 ), 

64 run=makeRunTableSpec("name", sqlalchemy.String, TimespanReprClass), 

65 collection_chain=makeCollectionChainTableSpec("name", sqlalchemy.String), 

66 ) 

67 

68 

69class NameKeyCollectionManager(DefaultCollectionManager): 

70 """A `CollectionManager` implementation that uses collection names for 

71 primary/foreign keys and aggressively loads all collection/run records in 

72 the database into memory. 

73 

74 Most of the logic, including caching policy, is implemented in the base 

75 class, this class only adds customizations specific to this particular 

76 table schema. 

77 """ 

78 

79 @classmethod 

80 def initialize( 

81 cls, 

82 db: Database, 

83 context: StaticTablesContext, *, 

84 dimensions: DimensionRecordStorageManager, 

85 ) -> NameKeyCollectionManager: 

86 # Docstring inherited from CollectionManager. 

87 return cls( 

88 db, 

89 tables=context.addTableTuple(_makeTableSpecs(db.getTimespanRepresentation())), # type: ignore 

90 collectionIdName="name", 

91 dimensions=dimensions, 

92 ) 

93 

94 @classmethod 

95 def getCollectionForeignKeyName(cls, prefix: str = "collection") -> str: 

96 # Docstring inherited from CollectionManager. 

97 return f"{prefix}_name" 

98 

99 @classmethod 

100 def getRunForeignKeyName(cls, prefix: str = "run") -> str: 

101 # Docstring inherited from CollectionManager. 

102 return f"{prefix}_name" 

103 

104 @classmethod 

105 def addCollectionForeignKey(cls, tableSpec: ddl.TableSpec, *, prefix: str = "collection", 

106 onDelete: Optional[str] = None, 

107 constraint: bool = True, 

108 **kwargs: Any) -> ddl.FieldSpec: 

109 # Docstring inherited from CollectionManager. 

110 original = _KEY_FIELD_SPEC 

111 copy = ddl.FieldSpec(cls.getCollectionForeignKeyName(prefix), dtype=original.dtype, 

112 length=original.length, **kwargs) 

113 tableSpec.fields.add(copy) 

114 if constraint: 114 ↛ 117line 114 didn't jump to line 117, because the condition on line 114 was never false

115 tableSpec.foreignKeys.append(ddl.ForeignKeySpec("collection", source=(copy.name,), 

116 target=(original.name,), onDelete=onDelete)) 

117 return copy 

118 

119 @classmethod 

120 def addRunForeignKey(cls, tableSpec: ddl.TableSpec, *, prefix: str = "run", 

121 onDelete: Optional[str] = None, 

122 constraint: bool = True, 

123 **kwargs: Any) -> ddl.FieldSpec: 

124 # Docstring inherited from CollectionManager. 

125 original = _KEY_FIELD_SPEC 

126 copy = ddl.FieldSpec(cls.getRunForeignKeyName(prefix), dtype=original.dtype, 

127 length=original.length, **kwargs) 

128 tableSpec.fields.add(copy) 

129 if constraint: 

130 tableSpec.foreignKeys.append(ddl.ForeignKeySpec("run", source=(copy.name,), 

131 target=(original.name,), onDelete=onDelete)) 

132 return copy 

133 

134 def _getByName(self, name: str) -> Optional[CollectionRecord]: 

135 # Docstring inherited from DefaultCollectionManager. 

136 return self._records.get(name) 

137 

138 @classmethod 

139 def currentVersion(cls) -> Optional[VersionTuple]: 

140 # Docstring inherited from VersionedExtension. 

141 return _VERSION 

142 

143 def schemaDigest(self) -> Optional[str]: 

144 # Docstring inherited from VersionedExtension. 

145 return self._defaultSchemaDigest(self._tables, self._db.dialect)