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__ = ["SynthIntKeyCollectionManager"] 

24 

25from collections import namedtuple 

26from typing import ( 

27 Any, 

28 Iterator, 

29 NamedTuple, 

30 Optional, 

31 TYPE_CHECKING, 

32) 

33 

34import sqlalchemy 

35 

36from ._base import makeRunTableSpec, makeCollectionChainTableSpec, DefaultCollectionManager 

37from ...core import ddl 

38from ..interfaces import CollectionRecord 

39 

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

41 from .database import Database, StaticTablesContext 

42 

43 

44_TablesTuple = namedtuple("CollectionTablesTuple", ["collection", "run", "collection_chain"]) 

45 

46_TABLES_SPEC = _TablesTuple( 

47 collection=ddl.TableSpec( 

48 fields=[ 

49 ddl.FieldSpec("collection_id", dtype=sqlalchemy.Integer, primaryKey=True, autoincrement=True), 

50 ddl.FieldSpec("name", dtype=sqlalchemy.String, length=64, nullable=False), 

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

52 ], 

53 unique=[("name",)], 

54 ), 

55 run=makeRunTableSpec("collection_id", sqlalchemy.Integer), 

56 collection_chain=makeCollectionChainTableSpec("collection_id", sqlalchemy.Integer), 

57) 

58 

59 

60class SynthIntKeyCollectionManager(DefaultCollectionManager): 

61 """A `CollectionManager` implementation that uses synthetic primary key 

62 (auto-incremented integer) for collections table. 

63 

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

65 class, this class only adds customisations specific to this particular 

66 table schema. 

67 

68 Parameters 

69 ---------- 

70 db : `Database` 

71 Interface to the underlying database engine and namespace. 

72 tables : `NamedTuple` 

73 Named tuple of SQLAlchemy table objects. 

74 collectionIdName : `str` 

75 Name of the column in collections table that identifies it (PK). 

76 """ 

77 def __init__(self, db: Database, tables: NamedTuple[sqlalchemy.schema.Table, ...], 

78 collectionIdName: str): 

79 super().__init__(db=db, tables=tables, collectionIdName=collectionIdName) 

80 self._nameCache = {} # indexed by collection name 

81 

82 @classmethod 

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

84 # Docstring inherited from CollectionManager. 

85 return cls(db, tables=context.addTableTuple(_TABLES_SPEC), 

86 collectionIdName="collection_id") 

87 

88 @classmethod 

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

90 # Docstring inherited from CollectionManager. 

91 return f"{prefix}_id" 

92 

93 @classmethod 

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

95 # Docstring inherited from CollectionManager. 

96 return f"{prefix}_id" 

97 

98 @classmethod 

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

100 onDelete: Optional[str] = None, **kwds: Any) -> ddl.FieldSpec: 

101 # Docstring inherited from CollectionManager. 

102 if prefix is None: 102 ↛ 103line 102 didn't jump to line 103, because the condition on line 102 was never true

103 prefix = "collection" 

104 original = _TABLES_SPEC.collection.fields["collection_id"] 

105 copy = ddl.FieldSpec(cls.getCollectionForeignKeyName(prefix), dtype=original.dtype, **kwds) 

106 tableSpec.fields.add(copy) 

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

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

109 return copy 

110 

111 @classmethod 

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

113 onDelete: Optional[str] = None, **kwds: Any) -> ddl.FieldSpec: 

114 # Docstring inherited from CollectionManager. 

115 if prefix is None: 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true

116 prefix = "run" 

117 original = _TABLES_SPEC.run.fields["collection_id"] 

118 copy = ddl.FieldSpec(cls.getRunForeignKeyName(prefix), dtype=original.dtype, **kwds) 

119 tableSpec.fields.add(copy) 

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

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

122 return copy 

123 

124 def _setRecordCache(self, records: Iterator[CollectionRecord]): 

125 """Set internal record cache to contain given records, 

126 old cached records will be removed. 

127 """ 

128 self._records = {} 

129 self._nameCache = {} 

130 for record in records: 

131 self._records[record.key] = record 

132 self._nameCache[record.name] = record 

133 

134 def _addCachedRecord(self, record: CollectionRecord): 

135 """Add single record to cache. 

136 """ 

137 self._records[record.key] = record 

138 self._nameCache[record.name] = record 

139 

140 def _removeCachedRecord(self, record: CollectionRecord): 

141 """Remove single record from cache. 

142 """ 

143 del self._records[record.key] 

144 del self._nameCache[record.name] 

145 

146 def _getByName(self, name: str): 

147 # Docstring inherited from DefaultCollectionManager. 

148 return self._nameCache.get(name)