Coverage for python/lsst/daf/butler/registry/collections/synthIntKey.py : 96%

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
23__all__ = ["SynthIntKeyCollectionManager"]
25from typing import (
26 Any,
27 Dict,
28 Iterable,
29 Optional,
30 Type,
31 TYPE_CHECKING,
32)
34import sqlalchemy
36from ._base import (
37 CollectionTablesTuple,
38 DefaultCollectionManager,
39 makeRunTableSpec,
40 makeCollectionChainTableSpec,
41)
42from ...core import TimespanDatabaseRepresentation, ddl
43from ..interfaces import CollectionRecord, VersionTuple
45if TYPE_CHECKING: 45 ↛ 46line 45 didn't jump to line 46, because the condition on line 45 was never true
46 from ..interfaces import Database, DimensionRecordStorageManager, StaticTablesContext
49_KEY_FIELD_SPEC = ddl.FieldSpec("collection_id", dtype=sqlalchemy.BigInteger, primaryKey=True,
50 autoincrement=True)
53# This has to be updated on every schema change
54_VERSION = VersionTuple(1, 0, 0)
57def _makeTableSpecs(tsRepr: Type[TimespanDatabaseRepresentation]) -> CollectionTablesTuple:
58 return CollectionTablesTuple(
59 collection=ddl.TableSpec(
60 fields=[
61 _KEY_FIELD_SPEC,
62 ddl.FieldSpec("name", dtype=sqlalchemy.String, length=64, nullable=False),
63 ddl.FieldSpec("type", dtype=sqlalchemy.SmallInteger, nullable=False),
64 ],
65 unique=[("name",)],
66 ),
67 run=makeRunTableSpec("collection_id", sqlalchemy.BigInteger, tsRepr),
68 collection_chain=makeCollectionChainTableSpec("collection_id", sqlalchemy.BigInteger),
69 )
72class SynthIntKeyCollectionManager(DefaultCollectionManager):
73 """A `CollectionManager` implementation that uses synthetic primary key
74 (auto-incremented integer) for collections table.
76 Most of the logic, including caching policy, is implemented in the base
77 class, this class only adds customizations specific to this particular
78 table schema.
80 Parameters
81 ----------
82 db : `Database`
83 Interface to the underlying database engine and namespace.
84 tables : `NamedTuple`
85 Named tuple of SQLAlchemy table objects.
86 collectionIdName : `str`
87 Name of the column in collections table that identifies it (PK).
88 dimensions : `DimensionRecordStorageManager`
89 Manager object for the dimensions in this `Registry`.
90 """
91 def __init__(
92 self,
93 db: Database,
94 tables: CollectionTablesTuple,
95 collectionIdName: str,
96 dimensions: DimensionRecordStorageManager,
97 ):
98 super().__init__(db=db, tables=tables, collectionIdName=collectionIdName, dimensions=dimensions)
99 self._nameCache: Dict[str, CollectionRecord] = {} # indexed by collection name
101 @classmethod
102 def initialize(
103 cls,
104 db: Database,
105 context: StaticTablesContext, *,
106 dimensions: DimensionRecordStorageManager,
107 ) -> SynthIntKeyCollectionManager:
108 # Docstring inherited from CollectionManager.
109 return cls(
110 db,
111 tables=context.addTableTuple(_makeTableSpecs(db.getTimespanRepresentation())), # type: ignore
112 collectionIdName="collection_id",
113 dimensions=dimensions,
114 )
116 @classmethod
117 def getCollectionForeignKeyName(cls, prefix: str = "collection") -> str:
118 # Docstring inherited from CollectionManager.
119 return f"{prefix}_id"
121 @classmethod
122 def getRunForeignKeyName(cls, prefix: str = "run") -> str:
123 # Docstring inherited from CollectionManager.
124 return f"{prefix}_id"
126 @classmethod
127 def addCollectionForeignKey(cls, tableSpec: ddl.TableSpec, *, prefix: str = "collection",
128 onDelete: Optional[str] = None,
129 constraint: bool = True,
130 **kwargs: Any) -> ddl.FieldSpec:
131 # Docstring inherited from CollectionManager.
132 original = _KEY_FIELD_SPEC
133 copy = ddl.FieldSpec(cls.getCollectionForeignKeyName(prefix), dtype=original.dtype,
134 autoincrement=False, **kwargs)
135 tableSpec.fields.add(copy)
136 if constraint: 136 ↛ 139line 136 didn't jump to line 139, because the condition on line 136 was never false
137 tableSpec.foreignKeys.append(ddl.ForeignKeySpec("collection", source=(copy.name,),
138 target=(original.name,), onDelete=onDelete))
139 return copy
141 @classmethod
142 def addRunForeignKey(cls, tableSpec: ddl.TableSpec, *, prefix: str = "run",
143 onDelete: Optional[str] = None,
144 constraint: bool = True,
145 **kwargs: Any) -> ddl.FieldSpec:
146 # Docstring inherited from CollectionManager.
147 original = _KEY_FIELD_SPEC
148 copy = ddl.FieldSpec(cls.getRunForeignKeyName(prefix), dtype=original.dtype,
149 autoincrement=False, **kwargs)
150 tableSpec.fields.add(copy)
151 if constraint:
152 tableSpec.foreignKeys.append(ddl.ForeignKeySpec("run", source=(copy.name,),
153 target=(original.name,), onDelete=onDelete))
154 return copy
156 def _setRecordCache(self, records: Iterable[CollectionRecord]) -> None:
157 """Set internal record cache to contain given records,
158 old cached records will be removed.
159 """
160 self._records = {}
161 self._nameCache = {}
162 for record in records:
163 self._records[record.key] = record
164 self._nameCache[record.name] = record
166 def _addCachedRecord(self, record: CollectionRecord) -> None:
167 """Add single record to cache.
168 """
169 self._records[record.key] = record
170 self._nameCache[record.name] = record
172 def _removeCachedRecord(self, record: CollectionRecord) -> None:
173 """Remove single record from cache.
174 """
175 del self._records[record.key]
176 del self._nameCache[record.name]
178 def _getByName(self, name: str) -> Optional[CollectionRecord]:
179 # Docstring inherited from DefaultCollectionManager.
180 return self._nameCache.get(name)
182 @classmethod
183 def currentVersion(cls) -> Optional[VersionTuple]:
184 # Docstring inherited from VersionedExtension.
185 return _VERSION
187 def schemaDigest(self) -> Optional[str]:
188 # Docstring inherited from VersionedExtension.
189 return self._defaultSchemaDigest(self._tables, self._db.dialect)