pytrilogy 0.0.3.47__py3-none-any.whl → 0.0.3.48__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.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/METADATA +12 -15
- {pytrilogy-0.0.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/RECORD +18 -19
- trilogy/__init__.py +1 -1
- trilogy/core/models/build.py +27 -5
- trilogy/core/models/execute.py +2 -1
- trilogy/core/optimization.py +0 -3
- trilogy/core/optimizations/__init__.py +0 -2
- trilogy/core/processing/node_generators/group_node.py +4 -1
- trilogy/core/query_processor.py +27 -12
- trilogy/dialect/base.py +21 -4
- trilogy/dialect/common.py +25 -14
- trilogy/executor.py +3 -0
- trilogy/parsing/common.py +0 -1
- trilogy/parsing/parse_engine.py +1 -1
- trilogy/core/optimizations/inline_constant.py +0 -35
- {pytrilogy-0.0.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.47.dist-info → pytrilogy-0.0.3.48.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.48
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -53,7 +53,7 @@ Installation: `pip install pytrilogy`
|
|
|
53
53
|
|
|
54
54
|
You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
|
|
55
55
|
|
|
56
|
-
Trilogy:
|
|
56
|
+
Trilogy looks like SQL:
|
|
57
57
|
```sql
|
|
58
58
|
WHERE
|
|
59
59
|
name like '%lvis%'
|
|
@@ -65,7 +65,7 @@ ORDER BY
|
|
|
65
65
|
LIMIT 10;
|
|
66
66
|
```
|
|
67
67
|
## Goals
|
|
68
|
-
|
|
68
|
+
And aims to:
|
|
69
69
|
|
|
70
70
|
Preserve:
|
|
71
71
|
- Correctness
|
|
@@ -85,6 +85,7 @@ Maintain:
|
|
|
85
85
|
Save the following code in a file named `hello.preql`
|
|
86
86
|
|
|
87
87
|
```python
|
|
88
|
+
# semantic model is abstract from data
|
|
88
89
|
key sentence_id int;
|
|
89
90
|
property sentence_id.word_one string; # comments after a definition
|
|
90
91
|
property sentence_id.word_two string; # are syntactic sugar for adding
|
|
@@ -92,7 +93,8 @@ property sentence_id.word_three string; # a description to it
|
|
|
92
93
|
|
|
93
94
|
# comments in other places are just comments
|
|
94
95
|
|
|
95
|
-
# define our
|
|
96
|
+
# define our datasource to bind the model to data
|
|
97
|
+
# testing using query fixtures is a common pattern
|
|
96
98
|
datasource word_one(
|
|
97
99
|
sentence: sentence_id,
|
|
98
100
|
word:word_one
|
|
@@ -126,25 +128,20 @@ union all
|
|
|
126
128
|
select 2 as sentence, '!'
|
|
127
129
|
''';
|
|
128
130
|
|
|
131
|
+
def concat_with_space(x,y) -> x || ' ' || y;
|
|
132
|
+
|
|
129
133
|
# an actual select statement
|
|
130
134
|
# joins are automatically resolved between the 3 sources
|
|
131
135
|
with sentences as
|
|
132
|
-
select sentence_id, word_one
|
|
136
|
+
select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
|
|
133
137
|
|
|
134
|
-
SELECT
|
|
135
|
-
--sentences.sentence_id,
|
|
136
|
-
sentences.text
|
|
137
138
|
WHERE
|
|
138
|
-
sentences.sentence_id
|
|
139
|
-
;
|
|
140
|
-
|
|
139
|
+
sentences.sentence_id in (1,2)
|
|
141
140
|
SELECT
|
|
142
|
-
--sentences.sentence_id,
|
|
143
141
|
sentences.text
|
|
144
|
-
WHERE
|
|
145
|
-
sentences.sentence_id = 2
|
|
146
142
|
;
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
|
|
148
145
|
|
|
149
146
|
```
|
|
150
147
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.48.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=X7jtGsMd3bHz73UVPQxZzoipqeTD3gI4UEQZtAq3OUs,303
|
|
3
3
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
trilogy/constants.py,sha256=5eQxk1A0pv-TQk3CCvgZCFA9_K-6nxrOm7E5Lxd7KIY,1652
|
|
5
5
|
trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
|
|
6
|
-
trilogy/executor.py,sha256=
|
|
6
|
+
trilogy/executor.py,sha256=GwNhP9UW4565dxnpHbw-VWNE2lX8uroQJQtSpC_j2pI,16298
|
|
7
7
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
8
8
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
|
|
@@ -19,19 +19,18 @@ trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,67
|
|
|
19
19
|
trilogy/core/functions.py,sha256=4fEOGgXWDvgrJtCg_5m2Y9iWnHfLbvLQ82RkIMl_1K0,27722
|
|
20
20
|
trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
|
|
21
21
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
22
|
-
trilogy/core/optimization.py,sha256=
|
|
23
|
-
trilogy/core/query_processor.py,sha256=
|
|
22
|
+
trilogy/core/optimization.py,sha256=O7ag0IVQlJyWdAXBi_hHeU3Df5DRyd75Vlz6pks2J10,8197
|
|
23
|
+
trilogy/core/query_processor.py,sha256=NNzYPKN5HzivQFXugSbJC_MaupkwOYii7A_vnXuBIK4,20063
|
|
24
24
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
trilogy/core/models/author.py,sha256=NhTKuk1eYAuYBbpvaFUxr-LntIoVarFQlNuNJwZmMmw,76990
|
|
26
|
-
trilogy/core/models/build.py,sha256=
|
|
26
|
+
trilogy/core/models/build.py,sha256=MPiHgyfOumZ8zF3iB61pzrAeDAlGV2F9R0Dw7mTTyqQ,62708
|
|
27
27
|
trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
|
|
28
28
|
trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
|
|
29
29
|
trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
|
|
30
30
|
trilogy/core/models/environment.py,sha256=AVSrvjNcNX535GhCPtYhCRY2Lp_Hj0tdY3VVt_kZb9Q,27260
|
|
31
|
-
trilogy/core/models/execute.py,sha256=
|
|
32
|
-
trilogy/core/optimizations/__init__.py,sha256=
|
|
31
|
+
trilogy/core/models/execute.py,sha256=m_GodtQkhuPo5kyBNlfC9c_jgprV7M64kE6x_12_ExQ,34616
|
|
32
|
+
trilogy/core/optimizations/__init__.py,sha256=YH2-mGXZnVDnBcWVi8vTbrdw7Qs5TivG4h38rH3js_I,290
|
|
33
33
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
34
|
-
trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52doLU9sIg3pmU2_S08,1332
|
|
35
34
|
trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
|
|
36
35
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
|
|
37
36
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -42,7 +41,7 @@ trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW
|
|
|
42
41
|
trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
|
|
43
42
|
trilogy/core/processing/node_generators/common.py,sha256=nVeH_AdO58ygtNSO0wNgMR7_h2D0dFSGM_rh1fJd4Yc,9468
|
|
44
43
|
trilogy/core/processing/node_generators/filter_node.py,sha256=JymSKzA-9oQAZ3ZtJRK9c3w5FXs8MjJBGWU9TYUqx4E,9099
|
|
45
|
-
trilogy/core/processing/node_generators/group_node.py,sha256=
|
|
44
|
+
trilogy/core/processing/node_generators/group_node.py,sha256=ISv2lLnr5m5nMpiXYJbgBqfUPQqeypjCAcaool9Kvnk,6109
|
|
46
45
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
47
46
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
|
|
48
47
|
trilogy/core/processing/node_generators/node_merge_node.py,sha256=sv55oynfqgpHEpo1OEtVDri-5fywzPhDlR85qaWikvY,16195
|
|
@@ -70,9 +69,9 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
70
69
|
trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
|
|
71
70
|
trilogy/core/statements/execute.py,sha256=cSlvpHFOqpiZ89pPZ5GDp9Hu6j6uj-5_h21FWm_L-KM,1248
|
|
72
71
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
-
trilogy/dialect/base.py,sha256=
|
|
72
|
+
trilogy/dialect/base.py,sha256=RkfNoNSo46p-WCafAWC5tXqJ_FMZEXANLyZSqX7_Pxw,42082
|
|
74
73
|
trilogy/dialect/bigquery.py,sha256=7LcgPLDkeNBk6YTfaE-RBBi7SjWFV-jjuvZM1VMIXqk,3350
|
|
75
|
-
trilogy/dialect/common.py,sha256=
|
|
74
|
+
trilogy/dialect/common.py,sha256=JQ8ONloalaWEXsTTWUhZcYyzMRaZ9HdUw7cN6QWtY5c,5295
|
|
76
75
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
77
76
|
trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
|
|
78
77
|
trilogy/dialect/duckdb.py,sha256=XTBK4RhE1_wF2_IA_7c2W5ih0uxZx0wZ1mfJ3YFIuso,3768
|
|
@@ -87,11 +86,11 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
|
|
|
87
86
|
trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
|
|
88
87
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
88
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
-
trilogy/parsing/common.py,sha256=
|
|
89
|
+
trilogy/parsing/common.py,sha256=u7V8uc2mdBtszVujk-hzllfDAqM3j5pKd8B9UEj-uNc,29223
|
|
91
90
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
92
91
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
93
92
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
94
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
93
|
+
trilogy/parsing/parse_engine.py,sha256=K3TwjCiiZtG3UrICF9Alik56_KPusVNWfqE-oUaKfho,68664
|
|
95
94
|
trilogy/parsing/render.py,sha256=hI4y-xjXrEXvHslY2l2TQ8ic0zAOpN41ADH37J2_FZY,19047
|
|
96
95
|
trilogy/parsing/trilogy.lark,sha256=q15J3P71yA_4lsWjC1vb7eDTemkJGLPKYvf5Hn9IBIk,13584
|
|
97
96
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -102,8 +101,8 @@ trilogy/std/display.preql,sha256=2BbhvqR4rcltyAbOXAUo7SZ_yGFYZgFnurglHMbjW2g,40
|
|
|
102
101
|
trilogy/std/geography.preql,sha256=-fqAGnBL6tR-UtT8DbSek3iMFg66ECR_B_41pODxv-k,504
|
|
103
102
|
trilogy/std/money.preql,sha256=ZHW-csTX-kYbOLmKSO-TcGGgQ-_DMrUXy0BjfuJSFxM,80
|
|
104
103
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
105
|
-
pytrilogy-0.0.3.
|
|
106
|
-
pytrilogy-0.0.3.
|
|
107
|
-
pytrilogy-0.0.3.
|
|
108
|
-
pytrilogy-0.0.3.
|
|
109
|
-
pytrilogy-0.0.3.
|
|
104
|
+
pytrilogy-0.0.3.48.dist-info/METADATA,sha256=xuGEKV1ZdJtS-uES50q5bj5x-_60E4Pb6VL5G_SKzNQ,9095
|
|
105
|
+
pytrilogy-0.0.3.48.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
106
|
+
pytrilogy-0.0.3.48.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
107
|
+
pytrilogy-0.0.3.48.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
108
|
+
pytrilogy-0.0.3.48.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/models/build.py
CHANGED
|
@@ -247,6 +247,17 @@ def get_concept_arguments(expr) -> List["BuildConcept"]:
|
|
|
247
247
|
return output
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
class BuildParamaterizedConceptReference(BaseModel):
|
|
251
|
+
concept: BuildConcept
|
|
252
|
+
|
|
253
|
+
def __str__(self):
|
|
254
|
+
return f":{self.concept.address}"
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def safe_address(self) -> str:
|
|
258
|
+
return self.concept.safe_address
|
|
259
|
+
|
|
260
|
+
|
|
250
261
|
class BuildGrain(BaseModel):
|
|
251
262
|
components: set[str] = Field(default_factory=set)
|
|
252
263
|
where_clause: Optional[BuildWhereClause] = None
|
|
@@ -1578,7 +1589,7 @@ class Factory:
|
|
|
1578
1589
|
|
|
1579
1590
|
new = BuildFunction.model_construct(
|
|
1580
1591
|
operator=base.operator,
|
|
1581
|
-
arguments=[self.build(c) for c in raw_args],
|
|
1592
|
+
arguments=[self.handle_constant(self.build(c)) for c in raw_args],
|
|
1582
1593
|
output_datatype=base.output_datatype,
|
|
1583
1594
|
output_purpose=base.output_purpose,
|
|
1584
1595
|
valid_inputs=base.valid_inputs,
|
|
@@ -1615,6 +1626,8 @@ class Factory:
|
|
|
1615
1626
|
|
|
1616
1627
|
@build.register
|
|
1617
1628
|
def _(self, base: Concept) -> BuildConcept:
|
|
1629
|
+
|
|
1630
|
+
# TODO: if we are using parameters, wrap it in a new model and use that in rendering
|
|
1618
1631
|
if base.address in self.local_concepts:
|
|
1619
1632
|
return self.local_concepts[base.address]
|
|
1620
1633
|
new_lineage, final_grain, _ = base.get_select_grain_and_keys(
|
|
@@ -1738,8 +1751,8 @@ class Factory:
|
|
|
1738
1751
|
@build.register
|
|
1739
1752
|
def _(self, base: Conditional) -> BuildConditional:
|
|
1740
1753
|
return BuildConditional.model_construct(
|
|
1741
|
-
left=(self.build(base.left)),
|
|
1742
|
-
right=(self.build(base.right)),
|
|
1754
|
+
left=self.handle_constant(self.build(base.left)),
|
|
1755
|
+
right=self.handle_constant(self.build(base.right)),
|
|
1743
1756
|
operator=base.operator,
|
|
1744
1757
|
)
|
|
1745
1758
|
|
|
@@ -1767,8 +1780,8 @@ class Factory:
|
|
|
1767
1780
|
right_c, _ = self.instantiate_concept(right)
|
|
1768
1781
|
right = right_c # type: ignore
|
|
1769
1782
|
return BuildComparison.model_construct(
|
|
1770
|
-
left=(self.build(left)),
|
|
1771
|
-
right=(self.build(right)),
|
|
1783
|
+
left=self.handle_constant(self.build(left)),
|
|
1784
|
+
right=self.handle_constant(self.build(right)),
|
|
1772
1785
|
operator=base.operator,
|
|
1773
1786
|
)
|
|
1774
1787
|
|
|
@@ -2015,3 +2028,12 @@ class Factory:
|
|
|
2015
2028
|
factory.build(base.non_partial_for) if base.non_partial_for else None
|
|
2016
2029
|
),
|
|
2017
2030
|
)
|
|
2031
|
+
|
|
2032
|
+
def handle_constant(self, base):
|
|
2033
|
+
if (
|
|
2034
|
+
isinstance(base, BuildConcept)
|
|
2035
|
+
and isinstance(base.lineage, BuildFunction)
|
|
2036
|
+
and base.lineage.operator == FunctionType.CONSTANT
|
|
2037
|
+
):
|
|
2038
|
+
return BuildParamaterizedConceptReference(concept=base)
|
|
2039
|
+
return base
|
trilogy/core/models/execute.py
CHANGED
|
@@ -24,6 +24,7 @@ from trilogy.core.models.build import (
|
|
|
24
24
|
BuildFunction,
|
|
25
25
|
BuildGrain,
|
|
26
26
|
BuildOrderBy,
|
|
27
|
+
BuildParamaterizedConceptReference,
|
|
27
28
|
BuildParenthetical,
|
|
28
29
|
BuildRowsetItem,
|
|
29
30
|
LooseBuildConceptList,
|
|
@@ -447,7 +448,7 @@ class CTEConceptPair(ConceptPair):
|
|
|
447
448
|
|
|
448
449
|
|
|
449
450
|
class InstantiatedUnnestJoin(BaseModel):
|
|
450
|
-
|
|
451
|
+
object_to_unnest: BuildConcept | BuildParamaterizedConceptReference | BuildFunction
|
|
451
452
|
alias: str = "unnest"
|
|
452
453
|
|
|
453
454
|
|
trilogy/core/optimization.py
CHANGED
|
@@ -5,7 +5,6 @@ from trilogy.core.models.build import (
|
|
|
5
5
|
)
|
|
6
6
|
from trilogy.core.models.execute import CTE, UnionCTE
|
|
7
7
|
from trilogy.core.optimizations import (
|
|
8
|
-
InlineConstant,
|
|
9
8
|
InlineDatasource,
|
|
10
9
|
OptimizationRule,
|
|
11
10
|
PredicatePushdown,
|
|
@@ -206,8 +205,6 @@ def optimize_ctes(
|
|
|
206
205
|
REGISTERED_RULES.append(PredicatePushdown())
|
|
207
206
|
if CONFIG.optimizations.predicate_pushdown:
|
|
208
207
|
REGISTERED_RULES.append(PredicatePushdownRemove())
|
|
209
|
-
if CONFIG.optimizations.constant_inlining:
|
|
210
|
-
REGISTERED_RULES.append(InlineConstant())
|
|
211
208
|
for rule in REGISTERED_RULES:
|
|
212
209
|
loops = 0
|
|
213
210
|
complete = False
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
from .base_optimization import OptimizationRule
|
|
2
|
-
from .inline_constant import InlineConstant
|
|
3
2
|
from .inline_datasource import InlineDatasource
|
|
4
3
|
from .predicate_pushdown import PredicatePushdown, PredicatePushdownRemove
|
|
5
4
|
|
|
6
5
|
__all__ = [
|
|
7
6
|
"OptimizationRule",
|
|
8
|
-
"InlineConstant",
|
|
9
7
|
"InlineDatasource",
|
|
10
8
|
"PredicatePushdown",
|
|
11
9
|
"PredicatePushdownRemove",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import logger
|
|
4
|
+
from trilogy.core.internal import ALL_ROWS_CONCEPT
|
|
4
5
|
from trilogy.core.models.build import (
|
|
5
6
|
BuildAggregateWrapper,
|
|
6
7
|
BuildConcept,
|
|
@@ -92,7 +93,9 @@ def gen_group_node(
|
|
|
92
93
|
logger.info(
|
|
93
94
|
f"{padding(depth)}{LOGGER_PREFIX} fetching group node parents {LooseBuildConceptList(concepts=parent_concepts)}"
|
|
94
95
|
)
|
|
95
|
-
parent_concepts = unique(
|
|
96
|
+
parent_concepts = unique(
|
|
97
|
+
[x for x in parent_concepts if not x.name == ALL_ROWS_CONCEPT], "address"
|
|
98
|
+
)
|
|
96
99
|
parent = source_concepts(
|
|
97
100
|
mandatory_list=parent_concepts,
|
|
98
101
|
environment=environment,
|
trilogy/core/query_processor.py
CHANGED
|
@@ -12,7 +12,9 @@ from trilogy.core.models.build import (
|
|
|
12
12
|
BuildConcept,
|
|
13
13
|
BuildConditional,
|
|
14
14
|
BuildDatasource,
|
|
15
|
+
BuildFunction,
|
|
15
16
|
BuildMultiSelectLineage,
|
|
17
|
+
BuildParamaterizedConceptReference,
|
|
16
18
|
BuildSelectLineage,
|
|
17
19
|
Factory,
|
|
18
20
|
)
|
|
@@ -55,8 +57,14 @@ def base_join_to_join(
|
|
|
55
57
|
"""This function converts joins at the datasource level
|
|
56
58
|
to joins at the CTE level"""
|
|
57
59
|
if isinstance(base_join, UnnestJoin):
|
|
60
|
+
object_to_unnest = base_join.parent.arguments[0]
|
|
61
|
+
if not isinstance(
|
|
62
|
+
object_to_unnest,
|
|
63
|
+
(BuildConcept | BuildParamaterizedConceptReference | BuildFunction),
|
|
64
|
+
):
|
|
65
|
+
raise ValueError(f"Unnest join must be a concept; got {object_to_unnest}")
|
|
58
66
|
return InstantiatedUnnestJoin(
|
|
59
|
-
|
|
67
|
+
object_to_unnest=object_to_unnest,
|
|
60
68
|
alias=base_join.alias,
|
|
61
69
|
)
|
|
62
70
|
|
|
@@ -220,6 +228,8 @@ def resolve_cte_base_name_and_alias_v2(
|
|
|
220
228
|
source_map: Dict[str, list[str]],
|
|
221
229
|
raw_joins: List[Join | InstantiatedUnnestJoin],
|
|
222
230
|
) -> Tuple[str | None, str | None]:
|
|
231
|
+
if not source.datasources:
|
|
232
|
+
return None, None
|
|
223
233
|
if (
|
|
224
234
|
isinstance(source.datasources[0], BuildDatasource)
|
|
225
235
|
and not source.datasources[0].name == CONSTANT_DATASET
|
|
@@ -301,18 +311,23 @@ def datasource_to_cte(
|
|
|
301
311
|
|
|
302
312
|
else:
|
|
303
313
|
# source is the first datasource of the query datasource
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
314
|
+
if query_datasource.datasources:
|
|
315
|
+
|
|
316
|
+
source = query_datasource.datasources[0]
|
|
317
|
+
# this is required to ensure that constant datasets
|
|
318
|
+
# render properly on initial access; since they have
|
|
319
|
+
# no actual source
|
|
320
|
+
if source.name == CONSTANT_DATASET:
|
|
321
|
+
source_map = {k: [] for k in query_datasource.source_map}
|
|
322
|
+
existence_map = source_map
|
|
323
|
+
else:
|
|
324
|
+
source_map = {
|
|
325
|
+
k: [] if not v else [source.safe_identifier]
|
|
326
|
+
for k, v in query_datasource.source_map.items()
|
|
327
|
+
}
|
|
328
|
+
existence_map = source_map
|
|
311
329
|
else:
|
|
312
|
-
source_map = {
|
|
313
|
-
k: [] if not v else [source.safe_identifier]
|
|
314
|
-
for k, v in query_datasource.source_map.items()
|
|
315
|
-
}
|
|
330
|
+
source_map = {k: [] for k in query_datasource.source_map}
|
|
316
331
|
existence_map = source_map
|
|
317
332
|
|
|
318
333
|
human_id = generate_cte_name(query_datasource.identifier, name_map)
|
trilogy/dialect/base.py
CHANGED
|
@@ -3,7 +3,13 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
|
|
3
3
|
|
|
4
4
|
from jinja2 import Template
|
|
5
5
|
|
|
6
|
-
from trilogy.constants import
|
|
6
|
+
from trilogy.constants import (
|
|
7
|
+
CONFIG,
|
|
8
|
+
DEFAULT_NAMESPACE,
|
|
9
|
+
MagicConstants,
|
|
10
|
+
Rendering,
|
|
11
|
+
logger,
|
|
12
|
+
)
|
|
7
13
|
from trilogy.core.enums import (
|
|
8
14
|
DatePart,
|
|
9
15
|
FunctionType,
|
|
@@ -22,6 +28,7 @@ from trilogy.core.models.build import (
|
|
|
22
28
|
BuildFunction,
|
|
23
29
|
BuildMultiSelectLineage,
|
|
24
30
|
BuildOrderItem,
|
|
31
|
+
BuildParamaterizedConceptReference,
|
|
25
32
|
BuildParenthetical,
|
|
26
33
|
BuildRowsetItem,
|
|
27
34
|
BuildSubselectComparison,
|
|
@@ -513,6 +520,9 @@ class BaseDialect:
|
|
|
513
520
|
BuildWindowItem,
|
|
514
521
|
BuildFilterItem,
|
|
515
522
|
BuildParenthetical,
|
|
523
|
+
BuildParamaterizedConceptReference,
|
|
524
|
+
BuildMultiSelectLineage,
|
|
525
|
+
BuildRowsetItem,
|
|
516
526
|
str,
|
|
517
527
|
int,
|
|
518
528
|
list,
|
|
@@ -693,6 +703,14 @@ class BaseDialect:
|
|
|
693
703
|
return self.render_expr(e.type, cte=cte, cte_map=cte_map)
|
|
694
704
|
elif isinstance(e, ListType):
|
|
695
705
|
return f"{self.COMPLEX_DATATYPE_MAP[DataType.LIST](self.render_expr(e.value_data_type, cte=cte, cte_map=cte_map))}"
|
|
706
|
+
elif isinstance(e, BuildParamaterizedConceptReference):
|
|
707
|
+
if self.rendering.parameters:
|
|
708
|
+
if e.concept.namespace == DEFAULT_NAMESPACE:
|
|
709
|
+
return f":{e.concept.name}"
|
|
710
|
+
return f":{e.concept.address}"
|
|
711
|
+
elif e.concept.lineage:
|
|
712
|
+
return self.render_expr(e.concept.lineage, cte=cte, cte_map=cte_map)
|
|
713
|
+
return f"{self.QUOTE_CHARACTER}{e.concept.address}{self.QUOTE_CHARACTER}"
|
|
696
714
|
else:
|
|
697
715
|
raise ValueError(f"Unable to render type {type(e)} {e}")
|
|
698
716
|
|
|
@@ -744,12 +762,12 @@ class BaseDialect:
|
|
|
744
762
|
UnnestMode.CROSS_APPLY,
|
|
745
763
|
):
|
|
746
764
|
|
|
747
|
-
source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.
|
|
765
|
+
source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_expr, cte)}"
|
|
748
766
|
elif (
|
|
749
767
|
cte.join_derived_concepts
|
|
750
768
|
and self.UNNEST_MODE == UnnestMode.SNOWFLAKE
|
|
751
769
|
):
|
|
752
|
-
source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.
|
|
770
|
+
source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_expr, cte)}"
|
|
753
771
|
# direct - eg DUCK DB - can be directly selected inline
|
|
754
772
|
elif (
|
|
755
773
|
cte.join_derived_concepts and self.UNNEST_MODE == UnnestMode.DIRECT
|
|
@@ -803,7 +821,6 @@ class BaseDialect:
|
|
|
803
821
|
render_join(
|
|
804
822
|
join,
|
|
805
823
|
self.QUOTE_CHARACTER,
|
|
806
|
-
self.render_concept_sql,
|
|
807
824
|
self.render_expr,
|
|
808
825
|
cte,
|
|
809
826
|
self.UNNEST_MODE,
|
trilogy/dialect/common.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from typing import Callable
|
|
2
2
|
|
|
3
3
|
from trilogy.core.enums import Modifier, UnnestMode
|
|
4
|
-
from trilogy.core.models.build import
|
|
4
|
+
from trilogy.core.models.build import (
|
|
5
|
+
BuildConcept,
|
|
6
|
+
BuildFunction,
|
|
7
|
+
BuildParamaterizedConceptReference,
|
|
8
|
+
)
|
|
5
9
|
from trilogy.core.models.datasource import RawColumnExpr
|
|
6
10
|
from trilogy.core.models.execute import (
|
|
7
11
|
CTE,
|
|
@@ -19,21 +23,27 @@ def null_wrapper(lval: str, rval: str, modifiers: list[Modifier]) -> str:
|
|
|
19
23
|
def render_unnest(
|
|
20
24
|
unnest_mode: UnnestMode,
|
|
21
25
|
quote_character: str,
|
|
22
|
-
concept: BuildConcept,
|
|
23
|
-
render_func: Callable[
|
|
26
|
+
concept: BuildConcept | BuildParamaterizedConceptReference | BuildFunction,
|
|
27
|
+
render_func: Callable[
|
|
28
|
+
[BuildConcept | BuildParamaterizedConceptReference | BuildFunction, CTE], str
|
|
29
|
+
],
|
|
24
30
|
cte: CTE,
|
|
25
31
|
):
|
|
32
|
+
if not isinstance(concept, (BuildConcept, BuildParamaterizedConceptReference)):
|
|
33
|
+
address = "anon_function"
|
|
34
|
+
else:
|
|
35
|
+
address = concept.safe_address
|
|
26
36
|
if unnest_mode == UnnestMode.CROSS_JOIN:
|
|
27
|
-
return f"{render_func(concept, cte
|
|
37
|
+
return f"{render_func(concept, cte)} as {quote_character}{address}{quote_character}"
|
|
28
38
|
elif unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
|
|
29
|
-
return f"{render_func(concept, cte
|
|
39
|
+
return f"{render_func(concept, cte)} as unnest_wrapper ({quote_character}{address}{quote_character})"
|
|
30
40
|
elif unnest_mode == UnnestMode.SNOWFLAKE:
|
|
31
41
|
# if we don't actually have a join, we're directly unnesting a concept, and we can skip the flatten
|
|
32
42
|
if not cte.render_from_clause:
|
|
33
|
-
return f"{render_func(concept, cte
|
|
43
|
+
return f"{render_func(concept, cte)} as unnest_wrapper ( unnest1, unnest2, unnest3, unnest4, {quote_character}{cte.join_derived_concepts[0].safe_address}{quote_character})"
|
|
34
44
|
# otherwise, flatten the concept for the join
|
|
35
|
-
return f"flatten({render_func(concept, cte
|
|
36
|
-
return f"{render_func(concept, cte
|
|
45
|
+
return f"flatten({render_func(concept, cte)}) as unnest_wrapper ( unnest1, unnest2, unnest3, unnest4, {quote_character}{cte.join_derived_concepts[0].safe_address}{quote_character})"
|
|
46
|
+
return f"{render_func(concept, cte)} as {quote_character}{address}{quote_character}"
|
|
37
47
|
|
|
38
48
|
|
|
39
49
|
def render_join_concept(
|
|
@@ -60,8 +70,9 @@ def render_join_concept(
|
|
|
60
70
|
def render_join(
|
|
61
71
|
join: Join | InstantiatedUnnestJoin,
|
|
62
72
|
quote_character: str,
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
render_expr_func: Callable[
|
|
74
|
+
[BuildConcept | BuildParamaterizedConceptReference | BuildFunction, CTE], str
|
|
75
|
+
],
|
|
65
76
|
cte: CTE,
|
|
66
77
|
unnest_mode: UnnestMode = UnnestMode.CROSS_APPLY,
|
|
67
78
|
) -> str | None:
|
|
@@ -72,12 +83,12 @@ def render_join(
|
|
|
72
83
|
if not cte:
|
|
73
84
|
raise ValueError("must provide a cte to build an unnest joins")
|
|
74
85
|
if unnest_mode == UnnestMode.CROSS_JOIN:
|
|
75
|
-
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.
|
|
86
|
+
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
|
|
76
87
|
if unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
|
|
77
|
-
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.
|
|
88
|
+
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
|
|
78
89
|
if unnest_mode == UnnestMode.SNOWFLAKE:
|
|
79
|
-
return f"LEFT JOIN LATERAL {render_unnest(unnest_mode, quote_character, join.
|
|
80
|
-
return f"FULL JOIN {render_unnest(unnest_mode, quote_character, join.
|
|
90
|
+
return f"LEFT JOIN LATERAL {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
|
|
91
|
+
return f"FULL JOIN {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
|
|
81
92
|
# left_name = join.left_name
|
|
82
93
|
right_name = join.right_name
|
|
83
94
|
if cte.quote_address.get(join.right_name, False):
|
trilogy/executor.py
CHANGED
|
@@ -394,6 +394,9 @@ class Executor(object):
|
|
|
394
394
|
if isinstance(rval, ListWrapper):
|
|
395
395
|
return [x for x in rval]
|
|
396
396
|
if isinstance(rval, MapWrapper):
|
|
397
|
+
# duckdb expects maps in this format as variables
|
|
398
|
+
if self.dialect == Dialects.DUCK_DB:
|
|
399
|
+
return {"key": [x for x in rval], "value": [rval[x] for x in rval]}
|
|
397
400
|
return {k: v for k, v in rval.items()}
|
|
398
401
|
# if isinstance(rval, ConceptRef):
|
|
399
402
|
# return self._concept_to_value(self.environment.concepts[rval.address], local_concepts=local_concepts)
|
trilogy/parsing/common.py
CHANGED
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -1795,7 +1795,7 @@ class ParseToObjects(Transformer):
|
|
|
1795
1795
|
def fyear(self, meta, args):
|
|
1796
1796
|
return self.function_factory.create_function(args, FunctionType.YEAR, meta)
|
|
1797
1797
|
|
|
1798
|
-
def internal_fcast(self, meta, args):
|
|
1798
|
+
def internal_fcast(self, meta, args) -> Function:
|
|
1799
1799
|
args = process_function_args(args, meta=meta, environment=self.environment)
|
|
1800
1800
|
if isinstance(args[0], str):
|
|
1801
1801
|
processed: date | datetime | int | float | bool | str
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from trilogy.core.enums import Derivation
|
|
2
|
-
from trilogy.core.models.build import BuildConcept
|
|
3
|
-
from trilogy.core.models.execute import (
|
|
4
|
-
CTE,
|
|
5
|
-
UnionCTE,
|
|
6
|
-
)
|
|
7
|
-
from trilogy.core.optimizations.base_optimization import OptimizationRule
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class InlineConstant(OptimizationRule):
|
|
11
|
-
def optimize(
|
|
12
|
-
self, cte: CTE | UnionCTE, inverse_map: dict[str, list[CTE | UnionCTE]]
|
|
13
|
-
) -> bool:
|
|
14
|
-
if isinstance(cte, UnionCTE):
|
|
15
|
-
return any(self.optimize(x, inverse_map) for x in cte.internal_ctes)
|
|
16
|
-
|
|
17
|
-
to_inline: list[BuildConcept] = []
|
|
18
|
-
for x in cte.source.input_concepts:
|
|
19
|
-
if x.address not in cte.source_map:
|
|
20
|
-
continue
|
|
21
|
-
if x.derivation == Derivation.CONSTANT:
|
|
22
|
-
self.log(f"Found constant {x.address} on {cte.name}")
|
|
23
|
-
to_inline.append(x)
|
|
24
|
-
if to_inline:
|
|
25
|
-
inlined = False
|
|
26
|
-
for c in to_inline:
|
|
27
|
-
self.log(f"Attempting to inline constant {c.address} on {cte.name}")
|
|
28
|
-
test = cte.inline_constant(c)
|
|
29
|
-
if test:
|
|
30
|
-
self.log(f"Successfully inlined constant {c.address} to {cte.name}")
|
|
31
|
-
inlined = True
|
|
32
|
-
else:
|
|
33
|
-
self.log(f"Could not inline constant to {cte.name}")
|
|
34
|
-
return inlined
|
|
35
|
-
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|