def add_link(self, referenced, dependent): """Add a link between two relations to the database. Both the old and new entries must already exist in the database. The dependent model refers _to_ the referenced model. So, given arguments of (jake_test, bar, jake_test, foo): both values are in the schema jake_test and foo is a view that refers to bar, so "drop bar cascade" will drop foo and all of foo's dependents. :param DefaultRelation referenced: The referenced model. :param DefaultRelation dependent: The dependent model. :raises InternalError: If either entry does not exist. """ referenced = _make_key(schema=referenced.schema, identifier=referenced.name) if referenced.schema not in self: # if we have not cached the referenced schema at all, we must be # referring to a table outside our control. There's no need to make # a link - we will never drop the referenced relation during a run. logger.debug( '{dep!s} references {ref!s} but {ref.schema} is not in the ' 'cache, skipping assumed external relation'.format( dep=dependent, ref=referenced)) return dependent = _make_key(schema=dependent.schema, identifier=dependent.name) logger.debug('adding link, {!s} references {!s}'.format( dependent, referenced)) with self.lock: self._add_link(referenced, dependent)
def _check_rename_constraints(self, old_key, new_key): """Check the rename constraints, and return whether or not the rename can proceed. If the new key is already present, that is an error. If the old key is absent, we debug log and return False, assuming it's a temp table being renamed. :param _ReferenceKey old_key: The existing key, to rename from. :param _ReferenceKey new_key: The new key, to rename to. :return bool: If the old relation exists for renaming. :raises InternalError: If the new key is already present. """ if new_key in self.relations: dbt.exceptions.raise_cache_inconsistent( 'in rename, new key {} already in cache: {}' .format(new_key, list(self.relations.keys())) ) if old_key not in self.relations: logger.debug( 'old key {} not found in self.relations, assuming temporary' .format(old_key) ) return False return True
def _rename_relation(self, old_key, new_relation): """Rename a relation named old_key to new_key, updating references. Return whether or not there was a key to rename. :param _ReferenceKey old_key: The existing key, to rename from. :param _CachedRelation new_key: The new relation, to rename to. """ # On the database level, a rename updates all values that were # previously referenced by old_name to be referenced by new_name. # basically, the name changes but some underlying ID moves. Kind of # like an object reference! relation = self.relations.pop(old_key) new_key = new_relation.key() # relaton has to rename its innards, so it needs the _CachedRelation. relation.rename(new_relation) # update all the relations that refer to it for cached in self.relations.values(): if cached.is_referenced_by(old_key): logger.debug( 'updated reference from {0} -> {2} to {1} -> {2}'.format( old_key, new_key, cached.key())) cached.rename_key(old_key, new_key) self.relations[new_key] = relation # also fixup the schemas! self.add_schema(new_key.database, new_key.schema) return True
def _rename_relation(self, old_key, new_relation): """Rename a relation named old_key to new_key, updating references. Return whether or not there was a key to rename. :param _ReferenceKey old_key: The existing key, to rename from. :param _CachedRelation new_key: The new relation, to rename to. """ # On the database level, a rename updates all values that were # previously referenced by old_name to be referenced by new_name. # basically, the name changes but some underlying ID moves. Kind of # like an object reference! relation = self.relations.pop(old_key) new_key = new_relation.key() # relaton has to rename its innards, so it needs the _CachedRelation. relation.rename(new_relation) # update all the relations that refer to it for cached in self.relations.values(): if cached.is_referenced_by(old_key): logger.debug( 'updated reference from {0} -> {2} to {1} -> {2}' .format(old_key, new_key, cached.key()) ) cached.rename_key(old_key, new_key) self.relations[new_key] = relation # also fixup the schemas! self.remove_schema(old_key.database, old_key.schema) self.add_schema(new_key.database, new_key.schema) return True
def add_link(self, referenced, dependent): """Add a link between two relations to the database. Both the old and new entries must already exist in the database. The dependent model refers _to_ the referenced model. So, given arguments of (jake_test, bar, jake_test, foo): both values are in the schema jake_test and foo is a view that refers to bar, so "drop bar cascade" will drop foo and all of foo's dependents. :param BaseRelation referenced: The referenced model. :param BaseRelation dependent: The dependent model. :raises InternalError: If either entry does not exist. """ referenced = _make_key(referenced) if (referenced.database, referenced.schema) not in self: # if we have not cached the referenced schema at all, we must be # referring to a table outside our control. There's no need to make # a link - we will never drop the referenced relation during a run. logger.debug( '{dep!s} references {ref!s} but {ref.database}.{ref.schema} ' 'is not in the cache, skipping assumed external relation' .format(dep=dependent, ref=referenced) ) return dependent = _make_key(dependent) logger.debug( 'adding link, {!s} references {!s}'.format(dependent, referenced) ) with self.lock: self._add_link(referenced, dependent)
def rename(self, old, new): """Rename the old schema/identifier to the new schema/identifier and update references. If the new schema/identifier is already present, that is an error. If the schema/identifier key is absent, we only debug log and return, assuming it's a temp table being renamed. :param BaseRelation old: The existing relation name information. :param BaseRelation new: The new relation name information. :raises InternalError: If the new key is already present. """ old_key = _make_key(old) new_key = _make_key(new) logger.debug('Renaming relation {!s} to {!s}'.format(old_key, new_key)) lazy_log('before rename: {!s}', self.dump_graph) with self.lock: if self._check_rename_constraints(old_key, new_key): self._rename_relation(old_key, _CachedRelation(new)) else: self._setdefault(_CachedRelation(new)) lazy_log('after rename: {!s}', self.dump_graph)
def _drop_cascade_relation(self, dropped): """Drop the given relation and cascade it appropriately to all dependent relations. :param _CachedRelation dropped: An existing _CachedRelation to drop. """ if dropped not in self.relations: logger.debug( 'dropped a nonexistent relationship: {!s}'.format(dropped)) return consequences = self.relations[dropped].collect_consequences() logger.debug('drop {} is cascading to {}'.format( dropped, consequences)) self._remove_refs(consequences)
def drop(self, relation): """Drop the named relation and cascade it appropriately to all dependent relations. Because dbt proactively does many `drop relation if exist ... cascade` that are noops, nonexistent relation drops cause a debug log and no other actions. :param str schema: The schema of the relation to drop. :param str identifier: The identifier of the relation to drop. """ dropped = _make_key(relation) logger.debug('Dropping relation: {!s}'.format(dropped)) with self.lock: self._drop_cascade_relation(dropped)
def drop(self, relation): """Drop the named relation and cascade it appropriately to all dependent relations. Because dbt proactively does many `drop relation if exist ... cascade` that are noops, nonexistent relation drops cause a debug log and no other actions. :param str schema: The schema of the relation to drop. :param str identifier: The identifier of the relation to drop. """ dropped = _make_key(relation) logger.debug('Dropping relation: {!s}'.format(dropped)) with self.lock: self._drop_cascade_relation(dropped)
def _drop_cascade_relation(self, dropped): """Drop the given relation and cascade it appropriately to all dependent relations. :param _CachedRelation dropped: An existing _CachedRelation to drop. """ if dropped not in self.relations: logger.debug('dropped a nonexistent relationship: {!s}' .format(dropped)) return consequences = self.relations[dropped].collect_consequences() logger.debug( 'drop {} is cascading to {}'.format(dropped, consequences) ) self._remove_refs(consequences)
def add(self, relation): """Add the relation inner to the cache, under the schema schema and identifier identifier :param BaseRelation relation: The underlying relation. """ cached = _CachedRelation(relation) logger.debug('Adding relation: {!s}'.format(cached)) lazy_log('before adding: {!s}', self.dump_graph) with self.lock: self._setdefault(cached) lazy_log('after adding: {!s}', self.dump_graph)
def add_link(self, referenced, dependent): """Add a link between two relations to the database. If either relation does not exist, it will be added as an "external" relation. The dependent model refers _to_ the referenced model. So, given arguments of (jake_test, bar, jake_test, foo): both values are in the schema jake_test and foo is a view that refers to bar, so "drop bar cascade" will drop foo and all of foo's dependents. :param BaseRelation referenced: The referenced model. :param BaseRelation dependent: The dependent model. :raises InternalError: If either entry does not exist. """ ref_key = _make_key(referenced) if (ref_key.database, ref_key.schema) not in self: # if we have not cached the referenced schema at all, we must be # referring to a table outside our control. There's no need to make # a link - we will never drop the referenced relation during a run. logger.debug( '{dep!s} references {ref!s} but {ref.database}.{ref.schema} ' 'is not in the cache, skipping assumed external relation'. format(dep=dependent, ref=ref_key)) return if ref_key not in self.relations: # Insert a dummy "external" relation. referenced = referenced.replace( type=referenced.RelationType.External) self.add(referenced) dep_key = _make_key(dependent) if dep_key not in self.relations: # Insert a dummy "external" relation. dependent = dependent.replace( type=referenced.RelationType.External) self.add(dependent) logger.debug('adding link, {!s} references {!s}'.format( dep_key, ref_key)) with self.lock: self._add_link(ref_key, dep_key)
def rename(self, old, new): """Rename the old schema/identifier to the new schema/identifier and update references. If the new schema/identifier is already present, that is an error. If the schema/identifier key is absent, we only debug log and return, assuming it's a temp table being renamed. :param BaseRelation old: The existing relation name information. :param BaseRelation new: The new relation name information. :raises InternalError: If the new key is already present. """ old_key = _make_key(old) new_key = _make_key(new) logger.debug('Renaming relation {!s} to {!s}'.format( old_key, new_key) ) logger.debug('before rename: {}'.format( pprint.pformat(self.dump_graph())) ) with self.lock: if self._check_rename_constraints(old_key, new_key): self._rename_relation(old_key, _CachedRelation(new)) else: self._setdefault(_CachedRelation(new)) logger.debug('after rename: {}'.format( pprint.pformat(self.dump_graph())) )
def _rename_relation(self, old_key, new_key): """Rename a relation named old_key to new_key, updating references. If the new key is already present, that is an error. If the old key is absent, we only debug log and return, assuming it's a temp table being renamed. :param _ReferenceKey old_key: The existing key, to rename from. :param _ReferenceKey new_key: The new key, to rename to. :raises InternalError: If the new key is already present. """ if old_key not in self.relations: logger.debug( 'old key {} not found in self.relations, assuming temporary'. format(old_key)) return if new_key in self.relations: dbt.exceptions.raise_cache_inconsistent( 'in rename, new key {} already in cache: {}'.format( new_key, list(self.relations.keys()))) # On the database level, a rename updates all values that were # previously referenced by old_name to be referenced by new_name. # basically, the name changes but some underlying ID moves. Kind of # like an object reference! relation = self.relations.pop(old_key) relation.rename(new_key) # update all the relations that refer to it for cached in self.relations.values(): if cached.is_referenced_by(old_key): logger.debug( 'updated reference from {0} -> {2} to {1} -> {2}'.format( old_key, new_key, cached.key())) cached.rename_key(old_key, new_key) self.relations[new_key] = relation
def add(self, relation): """Add the relation inner to the cache, under the schema schema and identifier identifier :param BaseRelation relation: The underlying relation. """ cached = _CachedRelation(relation) logger.debug('Adding relation: {!s}'.format(cached)) logger.debug('before adding: {}'.format( pprint.pformat(self.dump_graph())) ) with self.lock: self._setdefault(cached) logger.debug('after adding: {}'.format( pprint.pformat(self.dump_graph())) )
def rename(self, old, new): """Rename the old schema/identifier to the new schema/identifier and update references. If the new schema/identifier is already present, that is an error. If the schema/identifier key is absent, we only debug log and return, assuming it's a temp table being renamed. :param DefaultRelation old: The existing relation name information. :param DefaultRelation new: The new relation name information. :raises InternalError: If the new key is already present. """ old_key = _ReferenceKey(schema=old.schema, identifier=old.identifier) new_key = _ReferenceKey(schema=new.schema, identifier=new.identifier) logger.debug('Renaming relation {!s} to {!s}'.format(old_key, new_key)) logger.debug('before rename: {}'.format( pprint.pformat(self.dump_graph()))) with self.lock: self._rename_relation(old_key, new_key) logger.debug('after rename: {}'.format( pprint.pformat(self.dump_graph())))
def lazy_log(msg, func): if logger.disabled: return logger.debug(msg.format(func()))