44from abc import ABC , abstractmethod
55from collections import defaultdict
66from collections .abc import Iterable , Iterator
7- from typing import Any , ClassVar , Generic , NoReturn , Optional , TypeVar
7+ from typing import Any , ClassVar , Generic , MutableMapping , NoReturn , Optional , TypeVar
88from uuid import uuid4
99
1010from typing_extensions import Literal , Protocol , get_type_hints
5959"""A convenience type indicating a mapping from config type to step method."""
6060Stage = Literal ["Pre-filter" , "Filter" , "Post-filter" ]
6161"""The name of a stage within a rule."""
62+ TempTableName = str
63+ """temp tables to cache intermediate results"""
6264
6365
6466class _UnboundStepFunction (Generic [T_contra ], Protocol ): # pylint: disable=too-few-public-methods
@@ -132,6 +134,7 @@ def __init__( # pylint: disable=unused-argument
132134 ):
133135 self .logger = logger or get_logger (type (self ).__name__ )
134136 """The `logging.Logger instance for the data contract config."""
137+ self .entity_cache_tracker : MutableMapping [EntityName , TempTableName ] = {}
135138
136139 @classmethod
137140 @abstractmethod
@@ -312,9 +315,7 @@ def join_header(self, entities: Entities, *, config: HeaderJoin) -> Messages:
312315 raise NotImplementedError
313316
314317 @abstractmethod
315- def identify_orphans (
316- self , entities : Entities , * , config : OrphanIdentification
317- ) -> Iterable :
318+ def identify_orphans (self , entities : Entities , * , config : OrphanIdentification ) -> Iterable :
318319 """Identify records in an entity which don't have at least one corresponding
319320 match in the target. A new boolean column will be added to `entity` ('IsOrphaned')
320321 indicating whether the condition matched.
@@ -413,9 +414,7 @@ def process_node(node: HierarchyNode) -> bool:
413414 entity = node .entity_name ,
414415 record = record , # type: ignore
415416 error_location = location ,
416- error_message = template_object (
417- node .missing_parent_id_error_message , record
418- ),
417+ error_message = template_object (node .missing_parent_id_error_message , record ),
419418 failure_type = "record" ,
420419 error_type = "record" ,
421420 error_code = node .missing_parent_id_error_code ,
@@ -426,6 +425,8 @@ def process_node(node: HierarchyNode) -> bool:
426425 ]
427426 msg_writer .write_queue .put (_messages )
428427
428+ self .cache_entity (node .entity_name , entities )
429+
429430 return len (_messages ) > 0
430431
431432 entity_issues_found : dict [EntityName , bool ] = {}
@@ -434,8 +435,6 @@ def process_node(node: HierarchyNode) -> bool:
434435 for node in tree .iterate_root_down ():
435436 entity_issues_found [node .entity_name ] = process_node (node )
436437
437- entities .update (entities )
438-
439438 return [], entity_issues_found
440439
441440 def identify_and_remove_missing_mandatory_groups (
@@ -493,6 +492,7 @@ def process_node(node: HierarchyNode) -> bool:
493492 for record in missing_children_records
494493 ]
495494 msg_writer .write_queue .put (_messages )
495+ self .cache_entity (node .parent_entity , entities )
496496 return len (_messages ) > 0
497497
498498 entity_issues_found : dict [EntityName , bool ] = {}
@@ -502,8 +502,6 @@ def process_node(node: HierarchyNode) -> bool:
502502 if node .parent_entity and node .mandatory :
503503 entity_issues_found [node .parent_entity ] = process_node (node )
504504
505- # entities.update(entities)
506-
507505 return [], entity_issues_found
508506
509507 # pylint: disable=R0912,R0914
@@ -852,3 +850,20 @@ def filter_data_contract_record_rejections(
852850 def get_entity_count (entity : EntityType ) -> int :
853851 """Method to get count of records in entity"""
854852 raise NotImplementedError ()
853+
854+ def cache_entity (self , entity_name : EntityName , entities : Entities ):
855+ """Store the materialised query in memory and update entity to query directly.
856+ If the entity is already cached, the new cache should be created first, then the old one
857+ removed as part of the function (in case the newer cache depends on the older one)."""
858+ raise NotImplementedError ()
859+
860+ def _remove_cached_artifact (self , entity_name : EntityName ):
861+ """Delete artifact in memory and clear from the entity cache keeping track.
862+ This should not be used directly as removing artifacts from memory, may lead to some
863+ entities being unable to be processed as their execution plans depend on these artifacts."""
864+ raise NotImplementedError ()
865+
866+ def clear_entity_cache (self ):
867+ """Helper method to remove all artifacts and cache trackers at end of processing."""
868+ for entity_name in list (self .entity_cache_tracker ):
869+ self ._remove_cached_artifact (entity_name )
0 commit comments