diff --git a/src/bqva/analyzer.py b/src/bqva/analyzer.py index 5c63ed0..9335fe6 100644 --- a/src/bqva/analyzer.py +++ b/src/bqva/analyzer.py @@ -137,6 +137,7 @@ def __init__( view = self._get_table(project_id, dataset_id, view_id) assert view.table_type == "VIEW" self.view = view + self.views_in_cycle: List[TableNode] = [] def __str__(self): return self.view.full_table_id @@ -153,7 +154,8 @@ def _get_table(self, project_id: str, dataset_id: str, table_id: str) -> Table: view_ref = dataset_ref.table(table_id) return self.client.get_table(view_ref) - def _build_tree(self, node: TableNode) -> TableNode: + def _build_tree(self, node: TableNode, ancestors: Optional[List[Table]] = None) -> TableNode: + if ancestors is None: ancestors = [] table = node.table log.info(f"{node.name}") log.info(f"{node.name}: object is of type {table.table_type}") @@ -170,8 +172,13 @@ def _build_tree(self, node: TableNode) -> TableNode: child_node = TableNode( client=self.client, table=child_table, parent=node ) - log.info(f"{node.name}: analyzing '{child_node.name}' ({i}/{count})") - self._build_tree(child_node) + # Check whether this child table is an ancestor + if child_table in ancestors: + log.info(f"{node.name}: found cycle '{child_node.name}' ({i}/{count})") + self.views_in_cycle.append(child_node) + else: + log.info(f"{node.name}: analyzing '{child_node.name}' ({i}/{count})") + self._build_tree(child_node, ancestors + [table]) return node def apply_permissions(self): diff --git a/src/bqva/utils.py b/src/bqva/utils.py index f0d842d..6d58aa3 100644 --- a/src/bqva/utils.py +++ b/src/bqva/utils.py @@ -31,6 +31,11 @@ def format_tree(view: ViewAnalyzer, show_key=False, show_status=False): output.append(format_key()) for pre, _, node in RenderTree(view.tree): output.append(pre + format_node(node, show_status=show_status)) + if len(view.views_in_cycle) > 0: + cycle_view_names = [format_node(x) for x in view.views_in_cycle] + output.append(f"Found a cycle! This cycle(s) include (but are not necessarily limited to):") + for s in cycle_view_names: + output.append("\t" + s) return "\n".join(output)