omnata-plugin-runtime 0.10.12__py3-none-any.whl → 0.10.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnata_plugin_runtime/json_schema.py +100 -32
- {omnata_plugin_runtime-0.10.12.dist-info → omnata_plugin_runtime-0.10.13.dist-info}/METADATA +1 -1
- {omnata_plugin_runtime-0.10.12.dist-info → omnata_plugin_runtime-0.10.13.dist-info}/RECORD +5 -5
- {omnata_plugin_runtime-0.10.12.dist-info → omnata_plugin_runtime-0.10.13.dist-info}/WHEEL +1 -1
- {omnata_plugin_runtime-0.10.12.dist-info → omnata_plugin_runtime-0.10.13.dist-info}/LICENSE +0 -0
@@ -270,7 +270,9 @@ class SnowflakeViewColumn(BaseModel):
|
|
270
270
|
This is allowed in Snowflake, as long as the aliased column is defined before it's used in a later column
|
271
271
|
So we need to sort the columns so that if the name of the column appears (in quotes) in the expression of another column, it is ordered first
|
272
272
|
"""
|
273
|
-
|
273
|
+
logger.debug(
|
274
|
+
f"Ordering columns by reference for stream: {current_stream_name} ({len(columns)} columns)"
|
275
|
+
)
|
274
276
|
# Collect columns to be moved
|
275
277
|
columns_to_move:List[Self] = []
|
276
278
|
# Collect Omnata System columns and keep them at the front
|
@@ -562,10 +564,32 @@ class SnowflakeViewParts(BaseModel):
|
|
562
564
|
# Until this generate function is called with the raw stream names, we don't know which streams the user has actually selected, nor which
|
563
565
|
# fields are actually available (some may be dropped due to something like an unsupported formula).
|
564
566
|
# So now there's a pruning process where we remove columns that reference fields that are not available.
|
565
|
-
#
|
567
|
+
# First, explicitly check for circular references between tables, erroring if they are found.
|
568
|
+
circular_refs = {}
|
569
|
+
for part in [main_stream_view_part] + joined_parts:
|
570
|
+
for column in part.columns:
|
571
|
+
if column.referenced_columns:
|
572
|
+
for ref_stream_name, ref_fields in column.referenced_columns.items():
|
573
|
+
# Record this reference
|
574
|
+
if (part.stream_name, ref_stream_name) not in circular_refs:
|
575
|
+
circular_refs[(part.stream_name, ref_stream_name)] = []
|
576
|
+
circular_refs[(part.stream_name, ref_stream_name)].append((column.original_name, ref_fields))
|
577
|
+
|
578
|
+
# Check for circular references
|
579
|
+
for (stream1, stream2), refs1 in circular_refs.items():
|
580
|
+
if (stream2, stream1) in circular_refs:
|
581
|
+
# Found a potential circular reference between stream1 and stream2
|
582
|
+
refs2 = circular_refs[(stream2, stream1)]
|
583
|
+
raise ValueError(f"""Cyclic dependency detected: Circular reference between {stream1} and {stream2}.
|
584
|
+
{stream1} -> {stream2}: {refs1}
|
585
|
+
{stream2} -> {stream1}: {refs2}""")
|
586
|
+
|
587
|
+
# Now proceed with the actual pruning process
|
588
|
+
# First, removing unavailable columns from other streams
|
566
589
|
# then, we can do a final pass and remove columns that reference fields that are not available in the current stream
|
590
|
+
|
567
591
|
prune_count = 0
|
568
|
-
while prune(main_stream_view_part,joined_parts):
|
592
|
+
while prune(main_stream_view_part, joined_parts):
|
569
593
|
prune_count += 1
|
570
594
|
if prune_count > 10000:
|
571
595
|
raise ValueError("Pruning of columns from the view has entered an infinite loop")
|
@@ -574,37 +598,75 @@ class SnowflakeViewParts(BaseModel):
|
|
574
598
|
|
575
599
|
def prune(view_part: SnowflakeViewPart, joined_parts: List[SnowflakeViewPart]) -> bool:
|
576
600
|
"""
|
577
|
-
|
578
|
-
|
601
|
+
Prunes columns from view parts that reference fields that don't exist in the referenced streams.
|
602
|
+
|
603
|
+
This function handles:
|
604
|
+
1. Direct dependencies - removing columns that directly reference non-existent columns
|
605
|
+
2. Transitive dependencies - removing columns that depend on columns that were removed
|
606
|
+
|
607
|
+
Returns True if any columns were removed, False otherwise.
|
608
|
+
Raises ValueError if a cyclic dependency is detected.
|
579
609
|
"""
|
580
|
-
stack = [(view_part, joined_parts)]
|
581
610
|
columns_removed = False
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
611
|
+
|
612
|
+
# Helper function to find a view part by stream name
|
613
|
+
def find_part(stream_name: str) -> Optional[SnowflakeViewPart]:
|
614
|
+
if stream_name == view_part.stream_name:
|
615
|
+
return view_part
|
616
|
+
return next((p for p in joined_parts if p.stream_name == stream_name), None)
|
617
|
+
|
618
|
+
# Helper function to check if a column should be kept or removed
|
619
|
+
def should_keep_column(column: SnowflakeViewColumn, part: SnowflakeViewPart) -> bool:
|
620
|
+
"""
|
621
|
+
Checks if a column should be kept based on its dependencies.
|
622
|
+
Returns True if the column should be kept, False if it should be removed.
|
623
|
+
"""
|
624
|
+
# If no references, keep the column
|
625
|
+
if not column.referenced_columns:
|
626
|
+
return True
|
627
|
+
|
628
|
+
# Check each referenced stream and its fields
|
629
|
+
for ref_stream_name, ref_fields in column.referenced_columns.items():
|
630
|
+
# Find the referenced part
|
631
|
+
ref_part = find_part(ref_stream_name)
|
632
|
+
|
633
|
+
# If referenced stream doesn't exist, remove the column
|
634
|
+
if ref_part is None:
|
635
|
+
logger.warning(
|
636
|
+
f"Column {column.name} in stream {part.stream_name} references stream "
|
637
|
+
f"{ref_stream_name}, but it was not provided"
|
638
|
+
)
|
639
|
+
return False
|
640
|
+
|
641
|
+
# Check each referenced field
|
642
|
+
for ref_field in ref_fields:
|
643
|
+
# Find the referenced column
|
644
|
+
ref_column = next((c for c in ref_part.columns if c.original_name == ref_field), None)
|
645
|
+
|
646
|
+
# If referenced column doesn't exist, remove the column
|
647
|
+
if ref_column is None:
|
648
|
+
logger.warning(
|
649
|
+
f"Column {column.name} in stream {part.stream_name} references field "
|
650
|
+
f"{ref_field} in stream {ref_stream_name}, but it was not provided"
|
651
|
+
)
|
652
|
+
return False
|
653
|
+
|
654
|
+
# All dependencies are satisfied
|
655
|
+
return True
|
656
|
+
|
657
|
+
# Process columns for removal
|
658
|
+
for column in view_part.columns[:]: # Use a copy to allow safe removal
|
659
|
+
if not should_keep_column(column, view_part):
|
660
|
+
view_part.columns.remove(column)
|
661
|
+
columns_removed = True
|
662
|
+
|
663
|
+
# Process joined parts
|
664
|
+
for joined_part in joined_parts:
|
665
|
+
for column in joined_part.columns[:]: # Use a copy to allow safe removal
|
666
|
+
if not should_keep_column(column, joined_part):
|
667
|
+
joined_part.columns.remove(column)
|
668
|
+
columns_removed = True
|
669
|
+
|
608
670
|
return columns_removed
|
609
671
|
|
610
672
|
class JsonSchemaTopLevel(BaseModel):
|
@@ -711,6 +773,9 @@ def normalized_view_part(
|
|
711
773
|
- A list of SnowflakeViewColumn objects, representing the columns to create in the view
|
712
774
|
- A list of SnowflakeViewJoin objects, representing the joins to create in the view
|
713
775
|
"""
|
776
|
+
logger.debug(
|
777
|
+
f"Building normalized view part for stream: {stream_name}"
|
778
|
+
)
|
714
779
|
snowflake_columns: List[SnowflakeViewColumn] = []
|
715
780
|
if include_default_columns:
|
716
781
|
snowflake_columns.append(
|
@@ -757,6 +822,9 @@ def normalized_view_part(
|
|
757
822
|
joins = []
|
758
823
|
comment = None
|
759
824
|
if stream_schema is not None:
|
825
|
+
logger.debug(
|
826
|
+
f"Building view columns for stream: {stream_name}"
|
827
|
+
)
|
760
828
|
json_schema = JsonSchemaTopLevel.model_validate(stream_schema)
|
761
829
|
view_columns += json_schema.build_view_columns(
|
762
830
|
column_name_environment=column_name_environment,
|
@@ -2,12 +2,12 @@ omnata_plugin_runtime/__init__.py,sha256=MS9d1whnfT_B3-ThqZ7l63QeC_8OEKTuaYV5wTw
|
|
2
2
|
omnata_plugin_runtime/api.py,sha256=baGraSMiD4Yvi3ZWrEv_TKh8Ktd1U8riBdOpe9j0Puw,8202
|
3
3
|
omnata_plugin_runtime/configuration.py,sha256=0rfIGv8rCu8OwL7m-VOXIBjd05iyaBWRdt2h9o6scwo,46754
|
4
4
|
omnata_plugin_runtime/forms.py,sha256=9YHJ_T17lT-rwyDaUg_0yj_YMPda4DRCw_wrvf8hE0E,19964
|
5
|
-
omnata_plugin_runtime/json_schema.py,sha256
|
5
|
+
omnata_plugin_runtime/json_schema.py,sha256=us4T3OgpjAyfsmew4pD777PyY0PR9Qhu3v89YqcYVsw,39125
|
6
6
|
omnata_plugin_runtime/logging.py,sha256=WBuZt8lF9E5oFWM4KYQbE8dDJ_HctJ1pN3BHwU6rcd0,4461
|
7
7
|
omnata_plugin_runtime/omnata_plugin.py,sha256=M0b6f9lKKEoEI0zf-ZwZcIPKPQTmHTIMhvcrBc94Mhg,133278
|
8
8
|
omnata_plugin_runtime/plugin_entrypoints.py,sha256=iqGl8_nEEnPGKg3Aem4YLSQ6d5xS3ju5gq8MJbx6sCA,31968
|
9
9
|
omnata_plugin_runtime/rate_limiting.py,sha256=qpr5esU4Ks8hMzuMpSR3gLFdor2ZUXYWCjmsQH_K6lQ,25882
|
10
|
-
omnata_plugin_runtime-0.10.
|
11
|
-
omnata_plugin_runtime-0.10.
|
12
|
-
omnata_plugin_runtime-0.10.
|
13
|
-
omnata_plugin_runtime-0.10.
|
10
|
+
omnata_plugin_runtime-0.10.13.dist-info/LICENSE,sha256=rGaMQG3R3F5-JGDp_-rlMKpDIkg5n0SI4kctTk8eZSI,56
|
11
|
+
omnata_plugin_runtime-0.10.13.dist-info/METADATA,sha256=mb4aB9k6IgMB0-PWnQKQQCyn6otQuofK8uG8jtBH5tI,2208
|
12
|
+
omnata_plugin_runtime-0.10.13.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
13
|
+
omnata_plugin_runtime-0.10.13.dist-info/RECORD,,
|
File without changes
|