pytrilogy 0.0.1.118__py3-none-any.whl → 0.0.2.2__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.2.dist-info}/METADATA +1 -1
- pytrilogy-0.0.2.2.dist-info/RECORD +82 -0
- {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.2.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +1 -1
- trilogy/constants.py +6 -0
- trilogy/core/enums.py +7 -2
- trilogy/core/env_processor.py +43 -19
- trilogy/core/functions.py +1 -0
- trilogy/core/models.py +674 -146
- trilogy/core/optimization.py +31 -28
- trilogy/core/optimizations/inline_constant.py +4 -1
- trilogy/core/optimizations/inline_datasource.py +25 -4
- trilogy/core/optimizations/predicate_pushdown.py +94 -54
- trilogy/core/processing/concept_strategies_v3.py +69 -39
- trilogy/core/processing/graph_utils.py +3 -3
- trilogy/core/processing/node_generators/__init__.py +0 -2
- trilogy/core/processing/node_generators/basic_node.py +30 -17
- trilogy/core/processing/node_generators/filter_node.py +3 -1
- trilogy/core/processing/node_generators/node_merge_node.py +345 -96
- trilogy/core/processing/node_generators/rowset_node.py +18 -16
- trilogy/core/processing/node_generators/select_node.py +45 -85
- trilogy/core/processing/nodes/__init__.py +2 -0
- trilogy/core/processing/nodes/base_node.py +22 -5
- trilogy/core/processing/nodes/filter_node.py +3 -0
- trilogy/core/processing/nodes/group_node.py +20 -2
- trilogy/core/processing/nodes/merge_node.py +32 -18
- trilogy/core/processing/nodes/select_node_v2.py +17 -3
- trilogy/core/processing/utility.py +100 -8
- trilogy/core/query_processor.py +77 -24
- trilogy/dialect/base.py +11 -46
- trilogy/dialect/bigquery.py +1 -1
- trilogy/dialect/common.py +11 -0
- trilogy/dialect/duckdb.py +1 -1
- trilogy/dialect/presto.py +1 -0
- trilogy/hooks/graph_hook.py +50 -5
- trilogy/hooks/query_debugger.py +1 -0
- trilogy/parsing/common.py +8 -5
- trilogy/parsing/parse_engine.py +52 -27
- trilogy/parsing/render.py +20 -9
- trilogy/parsing/trilogy.lark +13 -8
- pytrilogy-0.0.1.118.dist-info/RECORD +0 -83
- trilogy/core/processing/node_generators/concept_merge_node.py +0 -214
- {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.2.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.2.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.1.118.dist-info → pytrilogy-0.0.2.2.dist-info}/top_level.txt +0 -0
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -32,6 +32,7 @@ from trilogy.core.enums import (
|
|
|
32
32
|
WindowType,
|
|
33
33
|
DatePart,
|
|
34
34
|
ShowCategory,
|
|
35
|
+
SelectFiltering,
|
|
35
36
|
)
|
|
36
37
|
from trilogy.core.exceptions import InvalidSyntaxException, UndefinedConceptException
|
|
37
38
|
from trilogy.core.functions import (
|
|
@@ -68,7 +69,7 @@ from trilogy.core.models import (
|
|
|
68
69
|
ConceptTransform,
|
|
69
70
|
Conditional,
|
|
70
71
|
Datasource,
|
|
71
|
-
|
|
72
|
+
MergeStatementV2,
|
|
72
73
|
Environment,
|
|
73
74
|
FilterItem,
|
|
74
75
|
Function,
|
|
@@ -400,7 +401,7 @@ class ParseToObjects(Transformer):
|
|
|
400
401
|
@v_args(meta=True)
|
|
401
402
|
def concept_property_declaration(self, meta: Meta, args) -> Concept:
|
|
402
403
|
|
|
403
|
-
metadata =
|
|
404
|
+
metadata = Metadata()
|
|
404
405
|
modifiers = []
|
|
405
406
|
for arg in args:
|
|
406
407
|
if isinstance(arg, Metadata):
|
|
@@ -439,7 +440,7 @@ class ParseToObjects(Transformer):
|
|
|
439
440
|
|
|
440
441
|
@v_args(meta=True)
|
|
441
442
|
def concept_declaration(self, meta: Meta, args) -> ConceptDeclarationStatement:
|
|
442
|
-
metadata =
|
|
443
|
+
metadata = Metadata()
|
|
443
444
|
modifiers = []
|
|
444
445
|
for arg in args:
|
|
445
446
|
if isinstance(arg, Metadata):
|
|
@@ -447,9 +448,7 @@ class ParseToObjects(Transformer):
|
|
|
447
448
|
if isinstance(arg, Modifier):
|
|
448
449
|
modifiers.append(arg)
|
|
449
450
|
name = args[1]
|
|
450
|
-
|
|
451
|
-
name, self.environment
|
|
452
|
-
)
|
|
451
|
+
_, namespace, name, _ = parse_concept_reference(name, self.environment)
|
|
453
452
|
concept = Concept(
|
|
454
453
|
name=name,
|
|
455
454
|
datatype=args[2],
|
|
@@ -606,6 +605,7 @@ class ParseToObjects(Transformer):
|
|
|
606
605
|
columns: List[ColumnAssignment] = args[1]
|
|
607
606
|
grain: Optional[Grain] = None
|
|
608
607
|
address: Optional[Address] = None
|
|
608
|
+
where: Optional[WhereClause] = None
|
|
609
609
|
for val in args[1:]:
|
|
610
610
|
if isinstance(val, Address):
|
|
611
611
|
address = val
|
|
@@ -613,6 +613,8 @@ class ParseToObjects(Transformer):
|
|
|
613
613
|
grain = val
|
|
614
614
|
elif isinstance(val, Query):
|
|
615
615
|
address = Address(location=f"({val.text})", is_query=True)
|
|
616
|
+
elif isinstance(val, WhereClause):
|
|
617
|
+
where = val
|
|
616
618
|
if not address:
|
|
617
619
|
raise ValueError(
|
|
618
620
|
"Malformed datasource, missing address or query declaration"
|
|
@@ -625,6 +627,7 @@ class ParseToObjects(Transformer):
|
|
|
625
627
|
grain=grain, # type: ignore
|
|
626
628
|
address=address,
|
|
627
629
|
namespace=self.environment.namespace,
|
|
630
|
+
where=where,
|
|
628
631
|
)
|
|
629
632
|
for column in columns:
|
|
630
633
|
column.concept = column.concept.with_grain(datasource.grain)
|
|
@@ -754,19 +757,21 @@ class ParseToObjects(Transformer):
|
|
|
754
757
|
return [x for x in args]
|
|
755
758
|
|
|
756
759
|
@v_args(meta=True)
|
|
757
|
-
def
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
760
|
+
def merge_statement_v2(self, meta: Meta, args) -> MergeStatementV2:
|
|
761
|
+
modifiers = []
|
|
762
|
+
concepts = []
|
|
763
|
+
for arg in args:
|
|
764
|
+
if isinstance(arg, Modifier):
|
|
765
|
+
modifiers.append(arg)
|
|
766
|
+
else:
|
|
767
|
+
concepts.append(self.environment.concepts[arg])
|
|
768
|
+
new = MergeStatementV2(
|
|
769
|
+
source=concepts[0], target=concepts[1], modifiers=modifiers
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
self.environment.merge_concept(new.source, new.target, modifiers)
|
|
773
|
+
|
|
774
|
+
return new
|
|
770
775
|
|
|
771
776
|
@v_args(meta=True)
|
|
772
777
|
def rawsql_statement(self, meta: Meta, args) -> RawSQLStatement:
|
|
@@ -892,7 +897,7 @@ class ParseToObjects(Transformer):
|
|
|
892
897
|
|
|
893
898
|
@v_args(meta=True)
|
|
894
899
|
def select_statement(self, meta: Meta, args) -> SelectStatement:
|
|
895
|
-
select_items = None
|
|
900
|
+
select_items: List[SelectItem] | None = None
|
|
896
901
|
limit = None
|
|
897
902
|
order_by = None
|
|
898
903
|
where = None
|
|
@@ -919,16 +924,24 @@ class ParseToObjects(Transformer):
|
|
|
919
924
|
# so rebuild at this point in the tree
|
|
920
925
|
# TODO: simplify
|
|
921
926
|
if isinstance(item.content, ConceptTransform):
|
|
922
|
-
new_concept = item.content.output.
|
|
927
|
+
new_concept = item.content.output.with_select_context(
|
|
928
|
+
output.grain,
|
|
929
|
+
conditional=(
|
|
930
|
+
output.where_clause.conditional
|
|
931
|
+
if output.where_clause
|
|
932
|
+
and output.where_clause_category == SelectFiltering.IMPLICIT
|
|
933
|
+
else None
|
|
934
|
+
),
|
|
935
|
+
)
|
|
923
936
|
self.environment.add_concept(new_concept, meta=meta)
|
|
924
937
|
item.content.output = new_concept
|
|
925
938
|
if order_by:
|
|
926
|
-
for
|
|
939
|
+
for orderitem in order_by.items:
|
|
927
940
|
if (
|
|
928
|
-
isinstance(
|
|
929
|
-
and
|
|
941
|
+
isinstance(orderitem.expr, Concept)
|
|
942
|
+
and orderitem.expr.purpose == Purpose.METRIC
|
|
930
943
|
):
|
|
931
|
-
|
|
944
|
+
orderitem.expr = orderitem.expr.with_grain(output.grain)
|
|
932
945
|
return output
|
|
933
946
|
|
|
934
947
|
@v_args(meta=True)
|
|
@@ -1045,8 +1058,20 @@ class ParseToObjects(Transformer):
|
|
|
1045
1058
|
def parenthetical(self, args):
|
|
1046
1059
|
return Parenthetical(content=args[0])
|
|
1047
1060
|
|
|
1061
|
+
def condition_parenthetical(self, args):
|
|
1062
|
+
return Parenthetical(content=args[0])
|
|
1063
|
+
|
|
1048
1064
|
def conditional(self, args):
|
|
1049
|
-
|
|
1065
|
+
def munch_args(args):
|
|
1066
|
+
while args:
|
|
1067
|
+
if len(args) == 1:
|
|
1068
|
+
return args[0]
|
|
1069
|
+
else:
|
|
1070
|
+
return Conditional(
|
|
1071
|
+
left=args[0], operator=args[1], right=munch_args(args[2:])
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
return munch_args(args)
|
|
1050
1075
|
|
|
1051
1076
|
def window_order(self, args):
|
|
1052
1077
|
return WindowOrder(args[0])
|
|
@@ -1723,7 +1748,7 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
|
1723
1748
|
| None
|
|
1724
1749
|
],
|
|
1725
1750
|
]:
|
|
1726
|
-
environment = environment or Environment(
|
|
1751
|
+
environment = environment or Environment()
|
|
1727
1752
|
parser = ParseToObjects(visit_tokens=True, text=text, environment=environment)
|
|
1728
1753
|
|
|
1729
1754
|
try:
|
trilogy/parsing/render.py
CHANGED
|
@@ -33,13 +33,13 @@ from trilogy.core.models import (
|
|
|
33
33
|
PersistStatement,
|
|
34
34
|
ListWrapper,
|
|
35
35
|
RowsetDerivationStatement,
|
|
36
|
-
MergeStatement,
|
|
37
36
|
MultiSelectStatement,
|
|
38
37
|
OrderBy,
|
|
39
38
|
AlignClause,
|
|
40
39
|
AlignItem,
|
|
41
40
|
RawSQLStatement,
|
|
42
41
|
NumericType,
|
|
42
|
+
MergeStatementV2,
|
|
43
43
|
)
|
|
44
44
|
from trilogy.core.enums import Modifier
|
|
45
45
|
|
|
@@ -131,22 +131,21 @@ class Renderer:
|
|
|
131
131
|
@to_string.register
|
|
132
132
|
def _(self, arg: Datasource):
|
|
133
133
|
assignments = ",\n\t".join([self.to_string(x) for x in arg.columns])
|
|
134
|
-
|
|
134
|
+
base = f"""datasource {arg.name} (
|
|
135
135
|
{assignments}
|
|
136
136
|
)
|
|
137
137
|
{self.to_string(arg.grain)}
|
|
138
|
-
{self.to_string(arg.address)}
|
|
138
|
+
{self.to_string(arg.address)}"""
|
|
139
|
+
if arg.where:
|
|
140
|
+
base += f"\n{self.to_string(arg.where)}"
|
|
141
|
+
base += ";"
|
|
142
|
+
return base
|
|
139
143
|
|
|
140
144
|
@to_string.register
|
|
141
145
|
def _(self, arg: "Grain"):
|
|
142
146
|
components = ",".join(self.to_string(x) for x in arg.components)
|
|
143
147
|
return f"grain ({components})"
|
|
144
148
|
|
|
145
|
-
@to_string.register
|
|
146
|
-
def _(self, arg: MergeStatement):
|
|
147
|
-
components = ", ".join(self.to_string(x) for x in arg.concepts)
|
|
148
|
-
return f"merge {components};"
|
|
149
|
-
|
|
150
149
|
@to_string.register
|
|
151
150
|
def _(self, arg: "Query"):
|
|
152
151
|
return f"""query {arg.text}"""
|
|
@@ -201,7 +200,7 @@ class Renderer:
|
|
|
201
200
|
|
|
202
201
|
@to_string.register
|
|
203
202
|
def _(self, arg: "ColumnAssignment"):
|
|
204
|
-
return f"{arg.alias}:{self.to_string(arg.concept)}"
|
|
203
|
+
return f"{arg.alias}: {self.to_string(arg.concept)}"
|
|
205
204
|
|
|
206
205
|
@to_string.register
|
|
207
206
|
def _(self, arg: "ConceptDeclarationStatement"):
|
|
@@ -341,6 +340,18 @@ class Renderer:
|
|
|
341
340
|
return f"{self.to_string(arg.function)} by {self.to_string(arg.by)}"
|
|
342
341
|
return f"{self.to_string(arg.function)}"
|
|
343
342
|
|
|
343
|
+
@to_string.register
|
|
344
|
+
def _(self, arg: MergeStatementV2):
|
|
345
|
+
return f"MERGE {self.to_string(arg.source)} into {''.join([self.to_string(modifier) for modifier in arg.modifiers])}{self.to_string(arg.target)};"
|
|
346
|
+
|
|
347
|
+
@to_string.register
|
|
348
|
+
def _(self, arg: Modifier):
|
|
349
|
+
if arg == Modifier.PARTIAL:
|
|
350
|
+
return "~"
|
|
351
|
+
if arg == Modifier.HIDDEN:
|
|
352
|
+
return "--"
|
|
353
|
+
return arg.value
|
|
354
|
+
|
|
344
355
|
@to_string.register
|
|
345
356
|
def _(self, arg: int):
|
|
346
357
|
return f"{arg}"
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
| persist_statement
|
|
9
9
|
| rowset_derivation_statement
|
|
10
10
|
| import_statement
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
| merge_statement_v2
|
|
12
13
|
| rawsql_statement
|
|
13
14
|
|
|
14
15
|
_TERMINATOR: ";"i /\s*/
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
prop_ident: "<" IDENTIFIER ("," IDENTIFIER )* ","? ">" "." IDENTIFIER
|
|
35
36
|
|
|
36
37
|
// datasource concepts
|
|
37
|
-
datasource: "datasource" IDENTIFIER "(" column_assignment_list ")" grain_clause? (address | query)
|
|
38
|
+
datasource: "datasource" IDENTIFIER "(" column_assignment_list ")" grain_clause? (address | query) where?
|
|
38
39
|
|
|
39
40
|
grain_clause: "grain" "(" column_list ")"
|
|
40
41
|
|
|
@@ -69,8 +70,8 @@
|
|
|
69
70
|
|
|
70
71
|
align_clause: align_item ("AND"i align_item)* "AND"i?
|
|
71
72
|
|
|
72
|
-
//
|
|
73
|
-
|
|
73
|
+
// merge_v2
|
|
74
|
+
merge_statement_v2: "merge"i IDENTIFIER "into"i SHORTHAND_MODIFIER? IDENTIFIER
|
|
74
75
|
|
|
75
76
|
// raw sql statement
|
|
76
77
|
rawsql_statement: "raw_sql"i "(" MULTILINE_STRING ")"
|
|
@@ -125,9 +126,13 @@
|
|
|
125
126
|
|
|
126
127
|
!logical_operator: "and"i | "or"i
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
condition_parenthetical: "(" conditional ")"
|
|
130
|
+
|
|
131
|
+
_condition_unit: (expr | condition_parenthetical)
|
|
132
|
+
|
|
133
|
+
conditional: _condition_unit (logical_operator _condition_unit)*
|
|
129
134
|
|
|
130
|
-
where: "WHERE"i
|
|
135
|
+
where: "WHERE"i conditional
|
|
131
136
|
|
|
132
137
|
!array_comparison: ( ("NOT"i "IN"i) | "IN"i)
|
|
133
138
|
|
|
@@ -150,7 +155,7 @@
|
|
|
150
155
|
index_access: expr "[" int_lit "]"
|
|
151
156
|
attr_access: expr "[" _string_lit "]"
|
|
152
157
|
|
|
153
|
-
expr: _constant_functions | window_item | filter_item | subselect_comparison | between_comparison | fgroup | aggregate_functions | unnest | _static_functions | literal | concept_lit | index_access | attr_access | parenthetical | expr_tuple | comparison |
|
|
158
|
+
expr: _constant_functions | window_item | filter_item | subselect_comparison | between_comparison | fgroup | aggregate_functions | unnest | _static_functions | literal | concept_lit | index_access | attr_access | parenthetical | expr_tuple | comparison | alt_like
|
|
154
159
|
|
|
155
160
|
// functions
|
|
156
161
|
|
|
@@ -168,7 +173,7 @@
|
|
|
168
173
|
fcast: "cast"i "(" expr "as"i data_type ")"
|
|
169
174
|
concat: ("concat"i "(" (expr ",")* expr ")") | (expr "||" expr)
|
|
170
175
|
fcoalesce: "coalesce"i "(" (expr ",")* expr ")"
|
|
171
|
-
fcase_when: "WHEN"i
|
|
176
|
+
fcase_when: "WHEN"i conditional "THEN"i expr
|
|
172
177
|
fcase_else: "ELSE"i expr
|
|
173
178
|
fcase: "CASE"i (fcase_when)* (fcase_else)? "END"i
|
|
174
179
|
len: "len"i "(" expr ")"
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=fYN8J2EkHXTX5cKVWpbBMQHilscbkhmZF-pMLoMqHFc,292
|
|
2
|
-
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
trilogy/constants.py,sha256=u2dNxhwy0v-6HrvG1GcpDVvuhzdTH5fuyYNCxDPlr2E,770
|
|
4
|
-
trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
|
|
5
|
-
trilogy/executor.py,sha256=5cRbU4Rj7p1pNV76rfp1pz704Hx_0q8_O8HFURjgXxQ,11016
|
|
6
|
-
trilogy/parser.py,sha256=UtuqSiGiCjpMAYgo1bvNq-b7NSzCA5hzbUW31RXaMII,281
|
|
7
|
-
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
|
|
9
|
-
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
trilogy/core/constants.py,sha256=LL8NLvxb3HRnAjvofyLRXqQJijLcYiXAQYQzGarVD-g,128
|
|
11
|
-
trilogy/core/enums.py,sha256=9IzCv0Hzzr3OTJQWzg9yHV_b46Jvk47RNoZjdBi2Its,5678
|
|
12
|
-
trilogy/core/env_processor.py,sha256=SU-jpaGfoWLe9sGTeQYG1qjVnwGQ7TwctmnJRlfzluc,1459
|
|
13
|
-
trilogy/core/environment_helpers.py,sha256=mzBDHhdF9ssZ_-LY8CcaM_ddfJavkpRYrFImUd3cjXI,5972
|
|
14
|
-
trilogy/core/ergonomics.py,sha256=w3gwXdgrxNHCuaRdyKg73t6F36tj-wIjQf47WZkHmJk,1465
|
|
15
|
-
trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,561
|
|
16
|
-
trilogy/core/functions.py,sha256=fWuuPOeWRAiukTkXvTEu_NCSbu6eUmogtLGpC4ey2FI,9465
|
|
17
|
-
trilogy/core/graph_models.py,sha256=oJUMSpmYhqXlavckHLpR07GJxuQ8dZ1VbB1fB0KaS8c,2036
|
|
18
|
-
trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
|
|
19
|
-
trilogy/core/models.py,sha256=Mp6MB8Gl2CZcPDWkofPX8Mym1ZvT0OUS0dKAUWCj7K8,116419
|
|
20
|
-
trilogy/core/optimization.py,sha256=oM3Ry7UpbpTSm2xNkmWx70OHd2V2vWRjM72sZpsZfb8,4116
|
|
21
|
-
trilogy/core/query_processor.py,sha256=clIRJ6IcsqIVBPKFsxt8bqCLsLyajvAu02MUIcKQhTo,15713
|
|
22
|
-
trilogy/core/optimizations/__init__.py,sha256=pxRzNzd2g8oRMy4f_ub5va6bNS2pd4hnyp9JBzTKc1E,300
|
|
23
|
-
trilogy/core/optimizations/base_optimization.py,sha256=tWWT-xnTbnEU-mNi_isMNbywm8B9WTRsNFwGpeh3rqE,468
|
|
24
|
-
trilogy/core/optimizations/inline_constant.py,sha256=neZOFjX7M2pzQ-8m-f8nApy_MfJuowX6SzcGwGFt5w4,927
|
|
25
|
-
trilogy/core/optimizations/inline_datasource.py,sha256=BSp54fwF4RRwInd-09pggemC7JuXj-uqGzi32ufeqYo,2171
|
|
26
|
-
trilogy/core/optimizations/predicate_pushdown.py,sha256=sIojWvoYp6k_ANCyVqxCpEyLY_GLmzsG-Sghj0cbk3k,4135
|
|
27
|
-
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256=MYrpNMidqvPOg123RekOcqVTjcj03i_538gBo0MzoWE,23432
|
|
29
|
-
trilogy/core/processing/graph_utils.py,sha256=ulCJ4hYAISbUxLD6VM2fah9RBPGIXSEHEPeRBSFl0Rs,1197
|
|
30
|
-
trilogy/core/processing/utility.py,sha256=acxH5448-j8JXqxMRibyAxjz1Wqu7QudbR0PfMuucww,9902
|
|
31
|
-
trilogy/core/processing/node_generators/__init__.py,sha256=LIs6uBEum8LDc-26zjyAwjxa-ay2ok9tKtPjDNvbVkE,757
|
|
32
|
-
trilogy/core/processing/node_generators/basic_node.py,sha256=HJnIhZLgkUdorKYcofe-QnKSM3Lf_3QO91cbSJhsqf4,2242
|
|
33
|
-
trilogy/core/processing/node_generators/common.py,sha256=liZDth7mvhkF_sUFXK7JitJsiaKD132w3ySLbF7l-nE,8956
|
|
34
|
-
trilogy/core/processing/node_generators/concept_merge_node.py,sha256=x4M8VVZZmBcqHDY1uq7M9KGKCBwjU6mcE_x2BOEk2Mg,7328
|
|
35
|
-
trilogy/core/processing/node_generators/filter_node.py,sha256=y_tqYe2So18vWHASMwVPLzDO-PnyQCO-MAlI4B-rY3Y,4526
|
|
36
|
-
trilogy/core/processing/node_generators/group_node.py,sha256=xWI1xNIXEOj6jlRGD9hcv2_vVNvY6lpzJl6pQ8HuFBE,2988
|
|
37
|
-
trilogy/core/processing/node_generators/group_to_node.py,sha256=BzPdYwzoo8gRMH7BDffTTXq4z-mjfCEzvfB5I-P0_nw,2941
|
|
38
|
-
trilogy/core/processing/node_generators/multiselect_node.py,sha256=vP84dnLQy6dtypi6mUbt9sMAcmmrTgQ1Oz4GI6X1IEo,6421
|
|
39
|
-
trilogy/core/processing/node_generators/node_merge_node.py,sha256=wNDHAbRrKSjsns-EROM_G12mRyOMjbcWpYav2uefXOE,6045
|
|
40
|
-
trilogy/core/processing/node_generators/rowset_node.py,sha256=eNG6rfLifUKraoRGxE8pesQMy5cKT6R5XNIaa3Wuiwk,6081
|
|
41
|
-
trilogy/core/processing/node_generators/select_node.py,sha256=Qb00Kizsv-877UMkGfusl5jXKXMZtZTtLks5pxU07SU,20698
|
|
42
|
-
trilogy/core/processing/node_generators/unnest_node.py,sha256=6CH66eGwpadNX7TzUhWZ8aqIisOtQeHINbLV6X3QBUk,1779
|
|
43
|
-
trilogy/core/processing/node_generators/window_node.py,sha256=9nXUXUgQrNczU1gaOqhOZPNzCUxw-lkxt0R7HORI6ss,2582
|
|
44
|
-
trilogy/core/processing/nodes/__init__.py,sha256=baODkJfvUoWEEbu843GEd7snubwLeOG5FQ8l-CwIaC8,3928
|
|
45
|
-
trilogy/core/processing/nodes/base_node.py,sha256=yhjmsAUmhHDqgbQjz_9YdfP-M5pj4xbrPRDF6Y4XVuw,10498
|
|
46
|
-
trilogy/core/processing/nodes/filter_node.py,sha256=rDw4vfE6tqWxuKT0arihVmIOoOWDDCyzRA-2yONX_Ek,1860
|
|
47
|
-
trilogy/core/processing/nodes/group_node.py,sha256=vzeU9J4xMhRrPj4-KPJTgNbH-KFu2ZS8b57SOynsdw0,4448
|
|
48
|
-
trilogy/core/processing/nodes/merge_node.py,sha256=FvSiTWKOzaUsXBkf6wJD8QQqQxp_aphS_I5VzNRw8Yo,13600
|
|
49
|
-
trilogy/core/processing/nodes/select_node_v2.py,sha256=ERCflBFzKpD5SzweMevnJLyQnxmF_-IQ6VRu5yVeiBg,6552
|
|
50
|
-
trilogy/core/processing/nodes/unnest_node.py,sha256=JFtm90IVM-46aCYkTNIaJah6v9ApAfonjVhcVM1HmDE,1903
|
|
51
|
-
trilogy/core/processing/nodes/window_node.py,sha256=X7qxLUKd3tekjUUsmH_4vz5b-U89gMnGd04VBxuu2Ns,1280
|
|
52
|
-
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
trilogy/dialect/base.py,sha256=ycsxbUL68DsodOsHEgEoNLKMn5vgRN3sDIRfiH9fQDs,31719
|
|
54
|
-
trilogy/dialect/bigquery.py,sha256=9vxQn2BMv_oTGQSWQpoN5ho_OgqMWaHH9e-5vQVf44c,2906
|
|
55
|
-
trilogy/dialect/common.py,sha256=zWrYmvevlXznocw9uGHmY5Ws1rp_kICm9zA_ulTe4eg,2165
|
|
56
|
-
trilogy/dialect/config.py,sha256=tLVEMctaTDhUgARKXUNfHUcIolGaALkQ0RavUvXAY4w,2994
|
|
57
|
-
trilogy/dialect/duckdb.py,sha256=Ddyt68sr8IL2HnZMenyytoD65FXwY_O2pz1McyS0bis,3075
|
|
58
|
-
trilogy/dialect/enums.py,sha256=4NdpsydBpDn6jnh0JzFz5VvQEtnShErWtWHVyT6TNpw,3948
|
|
59
|
-
trilogy/dialect/postgres.py,sha256=r47xbCA7nfEYENofiVfLZ-SnReNfDmUmW4OSHVkkP4E,3206
|
|
60
|
-
trilogy/dialect/presto.py,sha256=UxBodRiV3szpFcQlcjoJaGXEwAhZJf_OT7dHczYvO80,3092
|
|
61
|
-
trilogy/dialect/snowflake.py,sha256=N3HknYgN-fjD7BLX1Ucj-ss_ku2Ox8DgLsF3BIHutHo,2941
|
|
62
|
-
trilogy/dialect/sql_server.py,sha256=HX68vNTrcDaTnOxe6Zbx_PBgrO42e2VuThxO6CYQ2cY,3026
|
|
63
|
-
trilogy/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
-
trilogy/hooks/base_hook.py,sha256=Xkb-A2qCHozYjum0A36zOy5PwTVwrP3NLDF0U2GpgHo,1100
|
|
65
|
-
trilogy/hooks/graph_hook.py,sha256=i-Tv9sxZU0sMc-God8bLLz-nAg4-wYafogZtHaU8LXw,801
|
|
66
|
-
trilogy/hooks/query_debugger.py,sha256=D2VJUcyvQrVJ8sT6FCMKR3NKTfVrgZQ7gly91avHHpw,4222
|
|
67
|
-
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
|
-
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
trilogy/parsing/common.py,sha256=iR3fiiZ7w8VJuUGrQ0v06XGDXov81f4z1ZlFnj6y40E,5804
|
|
70
|
-
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
71
|
-
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
72
|
-
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
73
|
-
trilogy/parsing/parse_engine.py,sha256=F1ok96qT6EhKRKV1Q_YzfHxMFtNV8qAXopK8NaePgU4,57080
|
|
74
|
-
trilogy/parsing/render.py,sha256=TnLf5fg4wimpd9EvhLU-FMDwpyW9pesoedBZ0RrmWD4,11810
|
|
75
|
-
trilogy/parsing/trilogy.lark,sha256=1AZbQGpNmpm4KamAXA5IWcuOr2B8Gb8kUJcAOmKf_zY,10862
|
|
76
|
-
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
|
-
trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
|
|
78
|
-
pytrilogy-0.0.1.118.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
79
|
-
pytrilogy-0.0.1.118.dist-info/METADATA,sha256=tZ1kQcOTJ5JTYb5tqlfdo7g-o94TC0tKmcdqLtJ35bI,7878
|
|
80
|
-
pytrilogy-0.0.1.118.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
81
|
-
pytrilogy-0.0.1.118.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
82
|
-
pytrilogy-0.0.1.118.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
83
|
-
pytrilogy-0.0.1.118.dist-info/RECORD,,
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
from trilogy.core.models import (
|
|
2
|
-
Concept,
|
|
3
|
-
Environment,
|
|
4
|
-
MergeStatement,
|
|
5
|
-
)
|
|
6
|
-
from trilogy.core.processing.nodes import MergeNode, NodeJoin, History
|
|
7
|
-
from trilogy.core.processing.nodes.base_node import concept_list_to_grain, StrategyNode
|
|
8
|
-
from typing import List
|
|
9
|
-
|
|
10
|
-
from trilogy.core.enums import JoinType, PurposeLineage
|
|
11
|
-
from trilogy.constants import logger
|
|
12
|
-
from trilogy.core.processing.utility import padding
|
|
13
|
-
from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
|
|
14
|
-
from itertools import combinations
|
|
15
|
-
from trilogy.core.processing.node_generators.common import resolve_join_order
|
|
16
|
-
from trilogy.utility import unique
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
LOGGER_PREFIX = "[GEN_CONCEPT_MERGE_NODE]"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def merge_joins(
|
|
23
|
-
parents: List[StrategyNode], merge_concepts: List[Concept]
|
|
24
|
-
) -> List[NodeJoin]:
|
|
25
|
-
output = []
|
|
26
|
-
for left, right in combinations(parents, 2):
|
|
27
|
-
output.append(
|
|
28
|
-
NodeJoin(
|
|
29
|
-
left_node=left,
|
|
30
|
-
right_node=right,
|
|
31
|
-
concepts=merge_concepts,
|
|
32
|
-
join_type=JoinType.FULL,
|
|
33
|
-
filter_to_mutual=True,
|
|
34
|
-
)
|
|
35
|
-
)
|
|
36
|
-
return resolve_join_order(output)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def gen_concept_merge_node(
|
|
40
|
-
concept: Concept,
|
|
41
|
-
local_optional: List[Concept],
|
|
42
|
-
environment: Environment,
|
|
43
|
-
g,
|
|
44
|
-
depth: int,
|
|
45
|
-
source_concepts,
|
|
46
|
-
history: History | None = None,
|
|
47
|
-
) -> MergeNode | None:
|
|
48
|
-
if not isinstance(concept.lineage, MergeStatement):
|
|
49
|
-
logger.info(
|
|
50
|
-
f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge node for {concept}"
|
|
51
|
-
)
|
|
52
|
-
return None
|
|
53
|
-
lineage: MergeStatement = concept.lineage
|
|
54
|
-
|
|
55
|
-
base_parents: List[StrategyNode] = []
|
|
56
|
-
|
|
57
|
-
# get additional concepts that should be merged across the environments
|
|
58
|
-
additional_merge: List[Concept] = [*lineage.concepts]
|
|
59
|
-
target_namespaces = set(x.namespace for x in [concept] + local_optional)
|
|
60
|
-
for x in local_optional:
|
|
61
|
-
if x.address in environment.merged_concepts:
|
|
62
|
-
ms = environment.merged_concepts[x.address].lineage
|
|
63
|
-
assert isinstance(ms, MergeStatement)
|
|
64
|
-
additional_merge += ms.concepts
|
|
65
|
-
|
|
66
|
-
for select in lineage.concepts:
|
|
67
|
-
# if it's a merge concept, filter it out of the optional
|
|
68
|
-
if select.namespace not in target_namespaces:
|
|
69
|
-
continue
|
|
70
|
-
sub_optional = [
|
|
71
|
-
x
|
|
72
|
-
for x in local_optional
|
|
73
|
-
if x.address not in environment.merged_concepts
|
|
74
|
-
and x.namespace == select.namespace
|
|
75
|
-
]
|
|
76
|
-
|
|
77
|
-
sub_additional_merge = [
|
|
78
|
-
x for x in additional_merge if x.namespace == select.namespace
|
|
79
|
-
]
|
|
80
|
-
sub_optional += sub_additional_merge
|
|
81
|
-
final: List[Concept] = unique([select] + sub_optional, "address")
|
|
82
|
-
logger.info(
|
|
83
|
-
f"{padding(depth)}{LOGGER_PREFIX} generating concept merge parent node with {[x.address for x in final]}"
|
|
84
|
-
)
|
|
85
|
-
snode: StrategyNode = source_concepts(
|
|
86
|
-
mandatory_list=final,
|
|
87
|
-
environment=environment,
|
|
88
|
-
g=g,
|
|
89
|
-
depth=depth + 1,
|
|
90
|
-
history=history,
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
if not snode:
|
|
94
|
-
logger.info(
|
|
95
|
-
f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge node for {concept}"
|
|
96
|
-
)
|
|
97
|
-
return None
|
|
98
|
-
for x in sub_additional_merge:
|
|
99
|
-
snode.add_output_concept(environment.merged_concepts[x.address])
|
|
100
|
-
base_parents.append(snode)
|
|
101
|
-
|
|
102
|
-
node_joins = merge_joins(
|
|
103
|
-
base_parents,
|
|
104
|
-
unique(
|
|
105
|
-
[environment.merged_concepts[x.address] for x in additional_merge],
|
|
106
|
-
"address",
|
|
107
|
-
),
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
enrichment = set([x.address for x in local_optional])
|
|
111
|
-
outputs = [
|
|
112
|
-
x
|
|
113
|
-
for y in base_parents
|
|
114
|
-
for x in y.output_concepts
|
|
115
|
-
if x.derivation != PurposeLineage.MERGE
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
additional_relevant = [x for x in outputs if x.address in enrichment]
|
|
119
|
-
final_outputs = outputs + additional_relevant + [concept]
|
|
120
|
-
virtual_outputs = [x for x in final_outputs if x.derivation == PurposeLineage.MERGE]
|
|
121
|
-
node = MergeNode(
|
|
122
|
-
input_concepts=[x for y in base_parents for x in y.output_concepts],
|
|
123
|
-
output_concepts=[
|
|
124
|
-
x for x in final_outputs if x.derivation != PurposeLineage.MERGE
|
|
125
|
-
],
|
|
126
|
-
environment=environment,
|
|
127
|
-
g=g,
|
|
128
|
-
depth=depth,
|
|
129
|
-
parents=base_parents,
|
|
130
|
-
node_joins=node_joins,
|
|
131
|
-
virtual_output_concepts=virtual_outputs,
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
qds = node.rebuild_cache()
|
|
135
|
-
|
|
136
|
-
# assume grain to be outoput of select
|
|
137
|
-
# but don't include anything aggregate at this point
|
|
138
|
-
qds.grain = concept_list_to_grain(
|
|
139
|
-
node.output_concepts, parent_sources=qds.datasources
|
|
140
|
-
)
|
|
141
|
-
possible_joins = concept_to_relevant_joins(additional_relevant)
|
|
142
|
-
if not local_optional:
|
|
143
|
-
logger.info(
|
|
144
|
-
f"{padding(depth)}{LOGGER_PREFIX} no enriched required for merge concept node; exiting early"
|
|
145
|
-
)
|
|
146
|
-
return node
|
|
147
|
-
if not possible_joins:
|
|
148
|
-
logger.info(
|
|
149
|
-
f"{padding(depth)}{LOGGER_PREFIX} no possible joins for merge concept node; exiting early"
|
|
150
|
-
)
|
|
151
|
-
return node
|
|
152
|
-
if all(
|
|
153
|
-
[x.address in [y.address for y in node.output_concepts] for x in local_optional]
|
|
154
|
-
):
|
|
155
|
-
logger.info(
|
|
156
|
-
f"{padding(depth)}{LOGGER_PREFIX} all enriched concepts returned from base merge concept node; exiting early"
|
|
157
|
-
)
|
|
158
|
-
return node
|
|
159
|
-
missing = [
|
|
160
|
-
x
|
|
161
|
-
for x in local_optional
|
|
162
|
-
if x.address not in [y.address for y in node.output_concepts]
|
|
163
|
-
]
|
|
164
|
-
logger.info(
|
|
165
|
-
f"{padding(depth)}{LOGGER_PREFIX} generating merge concept enrichment node for missing {[x.address for x in missing]}"
|
|
166
|
-
)
|
|
167
|
-
enrich_node: MergeNode = source_concepts( # this fetches the parent + join keys
|
|
168
|
-
# to then connect to the rest of the query
|
|
169
|
-
mandatory_list=additional_relevant + missing,
|
|
170
|
-
environment=environment,
|
|
171
|
-
g=g,
|
|
172
|
-
depth=depth + 1,
|
|
173
|
-
history=history,
|
|
174
|
-
)
|
|
175
|
-
if not enrich_node:
|
|
176
|
-
logger.info(
|
|
177
|
-
f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge concept enrichment node for {concept.address} with optional {[x.address for x in local_optional]}, returning just merge concept"
|
|
178
|
-
)
|
|
179
|
-
return node
|
|
180
|
-
|
|
181
|
-
# we still need the hidden concepts to be returned to the search
|
|
182
|
-
# since they must be on the final node
|
|
183
|
-
# to avoid further recursion
|
|
184
|
-
# TODO: let the downstream search know they were found
|
|
185
|
-
return MergeNode(
|
|
186
|
-
input_concepts=enrich_node.output_concepts + node.output_concepts,
|
|
187
|
-
# also filter out the
|
|
188
|
-
output_concepts=[
|
|
189
|
-
x
|
|
190
|
-
for x in node.output_concepts + local_optional
|
|
191
|
-
if x.derivation != PurposeLineage.MERGE
|
|
192
|
-
],
|
|
193
|
-
hidden_concepts=[],
|
|
194
|
-
environment=environment,
|
|
195
|
-
g=g,
|
|
196
|
-
depth=depth,
|
|
197
|
-
parents=[
|
|
198
|
-
# this node gets the window
|
|
199
|
-
node,
|
|
200
|
-
# this node gets enrichment
|
|
201
|
-
enrich_node,
|
|
202
|
-
],
|
|
203
|
-
node_joins=[
|
|
204
|
-
NodeJoin(
|
|
205
|
-
left_node=enrich_node,
|
|
206
|
-
right_node=node,
|
|
207
|
-
concepts=possible_joins,
|
|
208
|
-
filter_to_mutual=False,
|
|
209
|
-
join_type=JoinType.LEFT_OUTER,
|
|
210
|
-
)
|
|
211
|
-
],
|
|
212
|
-
partial_concepts=node.partial_concepts,
|
|
213
|
-
virtual_output_concepts=virtual_outputs,
|
|
214
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|