pytrilogy 0.0.3.111__py3-none-any.whl → 0.0.3.113__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.111.dist-info → pytrilogy-0.0.3.113.dist-info}/METADATA +14 -34
- {pytrilogy-0.0.3.111.dist-info → pytrilogy-0.0.3.113.dist-info}/RECORD +20 -20
- trilogy/__init__.py +1 -1
- trilogy/core/enums.py +2 -0
- trilogy/core/functions.py +26 -0
- trilogy/core/models/author.py +7 -0
- trilogy/core/models/environment.py +1 -0
- trilogy/core/processing/discovery_utility.py +4 -2
- trilogy/core/processing/node_generators/basic_node.py +3 -1
- trilogy/core/statements/author.py +18 -4
- trilogy/dialect/base.py +2 -0
- trilogy/dialect/duckdb.py +12 -1
- trilogy/executor.py +0 -3
- trilogy/parsing/common.py +44 -14
- trilogy/parsing/parse_engine.py +19 -3
- trilogy/parsing/trilogy.lark +5 -1
- {pytrilogy-0.0.3.111.dist-info → pytrilogy-0.0.3.113.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.111.dist-info → pytrilogy-0.0.3.113.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.111.dist-info → pytrilogy-0.0.3.113.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.111.dist-info → pytrilogy-0.0.3.113.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.113
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Classifier: Programming Language :: Python
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -51,61 +51,41 @@ The Trilogy language is an experiment in better SQL for analytics - a streamline
|
|
|
51
51
|
|
|
52
52
|
Trilogy is especially powerful for data consumption, providing a rich metadata layer that makes creating, interpreting, and visualizing queries easy and expressive.
|
|
53
53
|
|
|
54
|
+
|
|
55
|
+
We recommend starting with the studio to explore Trilogy. For integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
56
|
+
|
|
57
|
+
|
|
54
58
|
## Quick Start
|
|
55
59
|
|
|
56
60
|
> [!TIP]
|
|
57
61
|
> **Try it now:** [Open-source studio](https://trilogydata.dev/trilogy-studio-core/) | [Interactive demo](https://trilogydata.dev/demo/) | [Documentation](https://trilogydata.dev/)
|
|
58
62
|
|
|
59
|
-
**Install
|
|
63
|
+
**Install**
|
|
60
64
|
```bash
|
|
61
65
|
pip install pytrilogy
|
|
62
66
|
```
|
|
63
67
|
|
|
64
|
-
**
|
|
68
|
+
**Save in hello.preql**
|
|
65
69
|
```sql
|
|
66
|
-
|
|
67
|
-
import names;
|
|
68
|
-
|
|
69
|
-
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
70
|
+
const prime <- unnest([2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
|
|
70
71
|
|
|
71
|
-
def
|
|
72
|
+
def cube_plus_one(x) -> (x * x * x + 1);
|
|
72
73
|
|
|
73
74
|
WHERE
|
|
74
|
-
|
|
75
|
+
prime_cubed_plus_one % 7 = 0
|
|
75
76
|
SELECT
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
prime,
|
|
78
|
+
@cube_plus_one(prime) as prime_cubed_plus_one
|
|
78
79
|
ORDER BY
|
|
79
|
-
|
|
80
|
+
prime asc
|
|
80
81
|
LIMIT 10;
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
**Run it
|
|
84
|
+
**Run it in DuckDB**
|
|
84
85
|
```bash
|
|
85
86
|
trilogy run hello.preql duckdb
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
We recommend starting with the studio to explore Trilogy. For integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
89
|
-
|
|
90
|
-
## Trilogy Looks Like SQL
|
|
91
|
-
|
|
92
|
-
```sql
|
|
93
|
-
import names;
|
|
94
|
-
|
|
95
|
-
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
96
|
-
|
|
97
|
-
def initcap(word) -> upper(substring(word, 1, 1)) || substring(word, 2, len(word));
|
|
98
|
-
|
|
99
|
-
WHERE
|
|
100
|
-
@initcap(name) in top_names
|
|
101
|
-
SELECT
|
|
102
|
-
name,
|
|
103
|
-
sum(births) as name_count
|
|
104
|
-
ORDER BY
|
|
105
|
-
name_count desc
|
|
106
|
-
LIMIT 10;
|
|
107
|
-
```
|
|
108
|
-
|
|
109
89
|
## Trilogy is Easy to Write
|
|
110
90
|
For humans *and* AI. Enjoy flexible, one-shot query generation without any DB access or security risks.
|
|
111
91
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.113.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=lAuWMqhwvK53mopDwiFjvqXcgCmF97vD3MVcY8Npwq4,304
|
|
3
3
|
trilogy/constants.py,sha256=g_zkVCNjGop6coZ1kM8eXXAzCnUN22ldx3TYFz0E9sc,1747
|
|
4
4
|
trilogy/engine.py,sha256=v4TpNktM4zZ9OX7jZH2nde4dpX5uAH2U23ELfULTCSg,2280
|
|
5
|
-
trilogy/executor.py,sha256=
|
|
5
|
+
trilogy/executor.py,sha256=q3EsAjzgxNxPn-yTHD_FTFzm7bJ2mlf9CrJEjyt6-pE,17884
|
|
6
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
7
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
|
|
@@ -23,24 +23,24 @@ trilogy/ai/providers/utils.py,sha256=yttP6y2E_XzdytBCwhaKekfXfxM6gE6MRce4AtyLL60
|
|
|
23
23
|
trilogy/authoring/__init__.py,sha256=TABMOETSMERrWuyDLR0nK4ISlqR0yaqeXrmuOdrSvAY,3060
|
|
24
24
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
26
|
-
trilogy/core/enums.py,sha256=
|
|
26
|
+
trilogy/core/enums.py,sha256=B0EbyjIBFl5SaP0J3lHIqudgy16fXCf9rlr16yxk6kk,8933
|
|
27
27
|
trilogy/core/env_processor.py,sha256=H-rr2ALj31l5oh3FqeI47Qju6OOfiXBacXNJGNZ92zQ,4521
|
|
28
28
|
trilogy/core/environment_helpers.py,sha256=TRlqVctqIRBxzfjRBmpQsAVoiCcsEKBhG1B6PUE0l1M,12743
|
|
29
29
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
30
30
|
trilogy/core/exceptions.py,sha256=axkVXYJYQXCCwMHwlyDA232g4tCOwdCZUt7eHeUMDMg,2829
|
|
31
|
-
trilogy/core/functions.py,sha256=
|
|
31
|
+
trilogy/core/functions.py,sha256=966xC6ypgzWwCs4BvORFwzWEMJoVlqvH2biipkIYl4E,34005
|
|
32
32
|
trilogy/core/graph_models.py,sha256=4EWFTHGfYd72zvS2HYoV6hm7nMC_VEd7vWr6txY-ig0,3400
|
|
33
33
|
trilogy/core/internal.py,sha256=r9QagDB2GvpqlyD_I7VrsfbVfIk5mnok2znEbv72Aa4,2681
|
|
34
34
|
trilogy/core/optimization.py,sha256=Km0ITEx9n6Iv5ReX6tm4uXO5uniSv_ooahycNNiET3g,9212
|
|
35
35
|
trilogy/core/query_processor.py,sha256=rMrtLSQxVm7yeyh0nWjDNI9nnu4Xi0NgHvBJ14gvu4I,20384
|
|
36
36
|
trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
|
|
37
37
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
trilogy/core/models/author.py,sha256=
|
|
38
|
+
trilogy/core/models/author.py,sha256=YXom_XC-wdcZmJ2PexWCrJ2Slr3FnrxA__X4ayc_A8w,81792
|
|
39
39
|
trilogy/core/models/build.py,sha256=iqk_-3plxX1BdxvUCTebqE9F3x62f40neKGf6Ld4VVU,70858
|
|
40
40
|
trilogy/core/models/build_environment.py,sha256=mpx7MKGc60fnZLVdeLi2YSREy7eQbQYycCrP4zF-rHU,5258
|
|
41
41
|
trilogy/core/models/core.py,sha256=iT9WdZoiXeglmUHWn6bZyXCTBpkApTGPKtNm_Mhbu_g,12987
|
|
42
42
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
43
|
-
trilogy/core/models/environment.py,sha256=
|
|
43
|
+
trilogy/core/models/environment.py,sha256=do1Xvr9oyjDj0knAxgIqexSbt0HMrHbLJNyl9utkSvs,28760
|
|
44
44
|
trilogy/core/models/execute.py,sha256=3fgEdho2e7S0outq91cCzb9jFwz6L1hTbsTrJwGvIFs,42311
|
|
45
45
|
trilogy/core/optimizations/__init__.py,sha256=yspWc25M5SgAuvXYoSt5J8atyPbDlOfsKjIo5yGD9s4,368
|
|
46
46
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
@@ -50,12 +50,12 @@ trilogy/core/optimizations/predicate_pushdown.py,sha256=5ubatgq1IwWQ4L2FDt4--y16
|
|
|
50
50
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
trilogy/core/processing/concept_strategies_v3.py,sha256=-iC2CLALmSrOglMKZM4TslVncyOrJbUH0V_COmbqHIw,22681
|
|
52
52
|
trilogy/core/processing/discovery_node_factory.py,sha256=p23jiiHyhrW-Q8ndbnRlqMHJKT8ZqPOA89SzE4xaFFo,15445
|
|
53
|
-
trilogy/core/processing/discovery_utility.py,sha256=
|
|
53
|
+
trilogy/core/processing/discovery_utility.py,sha256=BfzeliD0CQ3x3MRLpw3msibGc7rOPh8aH_gbfuchAIs,13352
|
|
54
54
|
trilogy/core/processing/discovery_validation.py,sha256=eZ4HfHMpqZLI8MGG2jez8arS8THs6ceuVrQFIY6gXrU,5364
|
|
55
55
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
56
56
|
trilogy/core/processing/utility.py,sha256=ESs6pKqVP2c9eMdfB2JNjw7D7YnoezVwbLFx1D6OUYA,26088
|
|
57
57
|
trilogy/core/processing/node_generators/__init__.py,sha256=iVJ-crowPxYeut-hFjyEjfibKIDq7PfB4LEuDAUCjGY,943
|
|
58
|
-
trilogy/core/processing/node_generators/basic_node.py,sha256=
|
|
58
|
+
trilogy/core/processing/node_generators/basic_node.py,sha256=rv53il-W633by0plvbN5qaqznJfl60yPinLZGBAC_MI,5707
|
|
59
59
|
trilogy/core/processing/node_generators/common.py,sha256=xF32Kf6B08dZgKs2SOow1HomptSiSC057GCUCHFlS5s,9464
|
|
60
60
|
trilogy/core/processing/node_generators/constant_node.py,sha256=LfpDq2WrBRZ3tGsLxw77LuigKfhbteWWh9L8BGdMGwk,1146
|
|
61
61
|
trilogy/core/processing/node_generators/filter_node.py,sha256=cJ5od1fAfvalaUDO2O4Y6Yrr2RukOCqey7f3zrKSBbI,10808
|
|
@@ -84,7 +84,7 @@ trilogy/core/processing/nodes/union_node.py,sha256=hLAXXVWqEgMWi7dlgSHfCF59fon64
|
|
|
84
84
|
trilogy/core/processing/nodes/unnest_node.py,sha256=oLKMMNMx6PLDPlt2V5neFMFrFWxET8r6XZElAhSNkO0,2181
|
|
85
85
|
trilogy/core/processing/nodes/window_node.py,sha256=JXJ0iVRlSEM2IBr1TANym2RaUf_p5E_l2sNykRzXWDo,1710
|
|
86
86
|
trilogy/core/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
87
|
-
trilogy/core/statements/author.py,sha256=
|
|
87
|
+
trilogy/core/statements/author.py,sha256=w_d55It4zs8qBMyEGxRyUjgMX0U3AQojG_GJ587UgrM,16414
|
|
88
88
|
trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
trilogy/core/statements/common.py,sha256=VnVLULQg1TJLNUFzJaROT1tsf2ewk3IpuhvZaP36R6A,535
|
|
90
90
|
trilogy/core/statements/execute.py,sha256=kiwJcVeMa4wZR-xLfM2oYOJ9DeyJkP8An38WFyJxktM,2413
|
|
@@ -95,12 +95,12 @@ trilogy/core/validation/datasource.py,sha256=nJeEFyb6iMBwlEVdYVy1vLzAbdRZwOsUjGx
|
|
|
95
95
|
trilogy/core/validation/environment.py,sha256=ymvhQyt7jLK641JAAIQkqjQaAmr9C5022ILzYvDgPP0,2835
|
|
96
96
|
trilogy/core/validation/fix.py,sha256=Z818UFNLxndMTLiyhB3doLxIfnOZ-16QGvVFWuD7UsA,3750
|
|
97
97
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
-
trilogy/dialect/base.py,sha256=
|
|
98
|
+
trilogy/dialect/base.py,sha256=B_zjQ6HyOQEW0iRGgGBjhqJ1Xr-KODk1fUBZDAWfE54,50798
|
|
99
99
|
trilogy/dialect/bigquery.py,sha256=XS3hpybeowgfrOrkycAigAF3NX2YUzTzfgE6f__2fT4,4316
|
|
100
100
|
trilogy/dialect/common.py,sha256=I5Ku_TR5MwJTB3ZhcQenrtvXhH2RvTQ8wQe9w5lfkfA,5708
|
|
101
101
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
102
102
|
trilogy/dialect/dataframe.py,sha256=nDTHMSd7GiGjEhjAobrZND0w4zjr-vgOalM2Cdxjets,1596
|
|
103
|
-
trilogy/dialect/duckdb.py,sha256=
|
|
103
|
+
trilogy/dialect/duckdb.py,sha256=Z_mxrfQXS3KP8PEbkKsCLolyjJgPEM-aBkVCdWiLph0,6312
|
|
104
104
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
105
105
|
trilogy/dialect/metadata.py,sha256=p_V-MYPQ2iR6fcTjagnptCIWtsZe4fTfoS_iXpavPzY,7098
|
|
106
106
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
@@ -113,13 +113,13 @@ trilogy/hooks/graph_hook.py,sha256=5BfR7Dt0bgEsCLgwjowgCsVkboGYfVJGOz8g9mqpnos,4
|
|
|
113
113
|
trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
|
|
114
114
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
115
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
|
-
trilogy/parsing/common.py,sha256=
|
|
116
|
+
trilogy/parsing/common.py,sha256=alayr5yy6MZOLTnw8CKLtxU2drtzbq4bKteQWfTw9QU,32281
|
|
117
117
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
118
118
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
119
119
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
120
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
120
|
+
trilogy/parsing/parse_engine.py,sha256=p758ukVifI_ygWp-1vJIy3X5NZVmwpNbbxVDfbkkTbU,82253
|
|
121
121
|
trilogy/parsing/render.py,sha256=FhSU3-bMA0YM3oEn6nfpfjbM74nvH2r1TtFgbWNzOsM,24204
|
|
122
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
122
|
+
trilogy/parsing/trilogy.lark,sha256=EN0Nrwz8cagzt69O85VSteW-k30lj8U5bRtXetM0JiU,16671
|
|
123
123
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
124
124
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
125
125
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -132,8 +132,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
132
132
|
trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
|
|
133
133
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
134
134
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
135
|
-
pytrilogy-0.0.3.
|
|
136
|
-
pytrilogy-0.0.3.
|
|
137
|
-
pytrilogy-0.0.3.
|
|
138
|
-
pytrilogy-0.0.3.
|
|
139
|
-
pytrilogy-0.0.3.
|
|
135
|
+
pytrilogy-0.0.3.113.dist-info/METADATA,sha256=4Ix5np1ZL_PCT0B6i4e8mazXdFNvRkpHznssca1beeI,12911
|
|
136
|
+
pytrilogy-0.0.3.113.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
137
|
+
pytrilogy-0.0.3.113.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
138
|
+
pytrilogy-0.0.3.113.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
139
|
+
pytrilogy-0.0.3.113.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/enums.py
CHANGED
trilogy/core/functions.py
CHANGED
|
@@ -175,6 +175,10 @@ def get_date_trunc_output(
|
|
|
175
175
|
return DataType.DATETIME
|
|
176
176
|
elif target == DatePart.SECOND:
|
|
177
177
|
return DataType.DATETIME
|
|
178
|
+
elif target == DatePart.WEEK:
|
|
179
|
+
return DataType.DATE
|
|
180
|
+
elif target == DatePart.QUARTER:
|
|
181
|
+
return DataType.DATE
|
|
178
182
|
else:
|
|
179
183
|
raise InvalidSyntaxException(f"Date truncation not supported for {target}")
|
|
180
184
|
|
|
@@ -640,6 +644,17 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
640
644
|
output_type=TraitDataType(type=DataType.INTEGER, traits=["day"]),
|
|
641
645
|
arg_count=1,
|
|
642
646
|
),
|
|
647
|
+
FunctionType.DAY_NAME: FunctionConfig(
|
|
648
|
+
valid_inputs={
|
|
649
|
+
DataType.DATE,
|
|
650
|
+
DataType.TIMESTAMP,
|
|
651
|
+
DataType.DATETIME,
|
|
652
|
+
# DataType.STRING,
|
|
653
|
+
},
|
|
654
|
+
output_purpose=Purpose.PROPERTY,
|
|
655
|
+
output_type=TraitDataType(type=DataType.STRING, traits=["day_name"]),
|
|
656
|
+
arg_count=1,
|
|
657
|
+
),
|
|
643
658
|
FunctionType.WEEK: FunctionConfig(
|
|
644
659
|
valid_inputs={
|
|
645
660
|
DataType.DATE,
|
|
@@ -662,6 +677,17 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
662
677
|
output_type=TraitDataType(type=DataType.INTEGER, traits=["month"]),
|
|
663
678
|
arg_count=1,
|
|
664
679
|
),
|
|
680
|
+
FunctionType.MONTH_NAME: FunctionConfig(
|
|
681
|
+
valid_inputs={
|
|
682
|
+
DataType.DATE,
|
|
683
|
+
DataType.TIMESTAMP,
|
|
684
|
+
DataType.DATETIME,
|
|
685
|
+
# DataType.STRING,
|
|
686
|
+
},
|
|
687
|
+
output_purpose=Purpose.PROPERTY,
|
|
688
|
+
output_type=TraitDataType(type=DataType.STRING, traits=["month_name"]),
|
|
689
|
+
arg_count=1,
|
|
690
|
+
),
|
|
665
691
|
FunctionType.QUARTER: FunctionConfig(
|
|
666
692
|
valid_inputs={
|
|
667
693
|
DataType.DATE,
|
trilogy/core/models/author.py
CHANGED
|
@@ -461,6 +461,11 @@ class WhereClause(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
461
461
|
conditional=self.conditional.with_namespace(namespace)
|
|
462
462
|
)
|
|
463
463
|
|
|
464
|
+
def with_reference_replacement(self, source, target):
|
|
465
|
+
return self.__class__.model_construct(
|
|
466
|
+
conditional=self.conditional.with_reference_replacement(source, target)
|
|
467
|
+
)
|
|
468
|
+
|
|
464
469
|
|
|
465
470
|
class HavingClause(WhereClause):
|
|
466
471
|
pass
|
|
@@ -1212,6 +1217,8 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1212
1217
|
return Derivation.FILTER
|
|
1213
1218
|
elif lineage and isinstance(lineage, (BuildAggregateWrapper, AggregateWrapper)):
|
|
1214
1219
|
return Derivation.AGGREGATE
|
|
1220
|
+
# elif lineage and isinstance(lineage, (BuildParenthetical, Parenthetical)):
|
|
1221
|
+
# return Derivation.PARENTHETICAL
|
|
1215
1222
|
elif lineage and isinstance(lineage, (BuildRowsetItem, RowsetItem)):
|
|
1216
1223
|
return Derivation.ROWSET
|
|
1217
1224
|
elif lineage and isinstance(
|
|
@@ -563,6 +563,7 @@ class Environment(BaseModel):
|
|
|
563
563
|
existing = self.validate_concept(concept, meta=meta)
|
|
564
564
|
if existing:
|
|
565
565
|
concept = existing
|
|
566
|
+
|
|
566
567
|
self.concepts[concept.address] = concept
|
|
567
568
|
|
|
568
569
|
from trilogy.core.environment_helpers import generate_related_concepts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import logger
|
|
4
|
-
from trilogy.core.enums import Derivation, Purpose
|
|
4
|
+
from trilogy.core.enums import Derivation, Purpose, SourceType
|
|
5
5
|
from trilogy.core.models.build import (
|
|
6
6
|
BuildConcept,
|
|
7
7
|
BuildDatasource,
|
|
@@ -200,8 +200,10 @@ def group_if_required_v2(
|
|
|
200
200
|
root.set_output_concepts(targets, rebuild=False, change_visibility=False)
|
|
201
201
|
root.rebuild_cache()
|
|
202
202
|
return root
|
|
203
|
+
elif isinstance(root, GroupNode) and root.source_type == SourceType.BASIC:
|
|
204
|
+
# we need to group this one more time
|
|
205
|
+
pass
|
|
203
206
|
elif isinstance(root, GroupNode):
|
|
204
|
-
|
|
205
207
|
if set(x.address for x in final) != set(
|
|
206
208
|
x.address for x in root.output_concepts
|
|
207
209
|
):
|
|
@@ -138,7 +138,9 @@ def gen_basic_node(
|
|
|
138
138
|
for s in parent_node.output_concepts
|
|
139
139
|
if any(s.address in y.pseudonyms for y in targets)
|
|
140
140
|
] + targets
|
|
141
|
-
parent_node.
|
|
141
|
+
hidden = [x for x in parent_node.output_concepts if x.address not in targets]
|
|
142
|
+
parent_node.hide_output_concepts(hidden)
|
|
143
|
+
parent_node.source_type = SourceType.BASIC
|
|
142
144
|
|
|
143
145
|
logger.info(
|
|
144
146
|
f"{depth_prefix}{LOGGER_PREFIX} Returning basic select for {concept}: input: {[x.address for x in parent_node.input_concepts]} output {[x.address for x in parent_node.output_concepts]} hidden {[x for x in parent_node.hidden_concepts]}"
|
|
@@ -31,6 +31,7 @@ from trilogy.core.models.author import (
|
|
|
31
31
|
Metadata,
|
|
32
32
|
MultiSelectLineage,
|
|
33
33
|
OrderBy,
|
|
34
|
+
Parenthetical,
|
|
34
35
|
SelectLineage,
|
|
35
36
|
UndefinedConcept,
|
|
36
37
|
WhereClause,
|
|
@@ -48,7 +49,12 @@ from trilogy.utility import unique
|
|
|
48
49
|
|
|
49
50
|
class ConceptTransform(BaseModel):
|
|
50
51
|
function: (
|
|
51
|
-
Function
|
|
52
|
+
Function
|
|
53
|
+
| FilterItem
|
|
54
|
+
| WindowItem
|
|
55
|
+
| AggregateWrapper
|
|
56
|
+
| FunctionCallWrapper
|
|
57
|
+
| Parenthetical
|
|
52
58
|
)
|
|
53
59
|
output: Concept # this has to be a full concept, as it may not exist in environment
|
|
54
60
|
modifiers: List[Modifier] = Field(default_factory=list)
|
|
@@ -190,9 +196,17 @@ class SelectStatement(HasUUID, SelectTypeMixin, BaseModel):
|
|
|
190
196
|
if self.where_clause:
|
|
191
197
|
for x in self.where_clause.concept_arguments:
|
|
192
198
|
if isinstance(x, UndefinedConcept):
|
|
193
|
-
environment.concepts.
|
|
194
|
-
|
|
195
|
-
|
|
199
|
+
validate = environment.concepts.get(x.address)
|
|
200
|
+
if validate:
|
|
201
|
+
self.where_clause = (
|
|
202
|
+
self.where_clause.with_reference_replacement(
|
|
203
|
+
x.address, validate.reference
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
else:
|
|
207
|
+
environment.concepts.raise_undefined(
|
|
208
|
+
x.address, x.metadata.line_number if x.metadata else None
|
|
209
|
+
)
|
|
196
210
|
all_in_output = [x for x in self.output_components]
|
|
197
211
|
if self.where_clause:
|
|
198
212
|
for cref in self.where_clause.concept_arguments:
|
trilogy/dialect/base.py
CHANGED
|
@@ -263,9 +263,11 @@ FUNCTION_MAP = {
|
|
|
263
263
|
FunctionType.MINUTE: lambda x: f"minute({x[0]})",
|
|
264
264
|
FunctionType.HOUR: lambda x: f"hour({x[0]})",
|
|
265
265
|
FunctionType.DAY: lambda x: f"day({x[0]})",
|
|
266
|
+
FunctionType.DAY_NAME: lambda x: f"dayname({x[0]})",
|
|
266
267
|
FunctionType.DAY_OF_WEEK: lambda x: f"day_of_week({x[0]})",
|
|
267
268
|
FunctionType.WEEK: lambda x: f"week({x[0]})",
|
|
268
269
|
FunctionType.MONTH: lambda x: f"month({x[0]})",
|
|
270
|
+
FunctionType.MONTH_NAME: lambda x: f"monthname({x[0]})",
|
|
269
271
|
FunctionType.QUARTER: lambda x: f"quarter({x[0]})",
|
|
270
272
|
FunctionType.YEAR: lambda x: f"year({x[0]})",
|
|
271
273
|
# string types
|
trilogy/dialect/duckdb.py
CHANGED
|
@@ -57,6 +57,15 @@ def render_log(args):
|
|
|
57
57
|
raise ValueError("log function requires 1 or 2 arguments")
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def map_date_part_specifier(specifier: str) -> str:
|
|
61
|
+
"""Map date part specifiers to DuckDB-compatible names"""
|
|
62
|
+
mapping = {
|
|
63
|
+
"day_of_week": "dow",
|
|
64
|
+
# Add other mappings if needed
|
|
65
|
+
}
|
|
66
|
+
return mapping.get(specifier, specifier)
|
|
67
|
+
|
|
68
|
+
|
|
60
69
|
FUNCTION_MAP = {
|
|
61
70
|
FunctionType.COUNT: lambda args: f"count({args[0]})",
|
|
62
71
|
FunctionType.SUM: lambda args: f"sum({args[0]})",
|
|
@@ -84,11 +93,13 @@ FUNCTION_MAP = {
|
|
|
84
93
|
FunctionType.ARRAY_AGG: lambda args: f"array_agg({args[0]})",
|
|
85
94
|
# datetime is aliased
|
|
86
95
|
FunctionType.CURRENT_DATETIME: lambda x: "cast(get_current_timestamp() as datetime)",
|
|
96
|
+
FunctionType.DATETIME: lambda x: f"cast({x[0]} as datetime)",
|
|
97
|
+
FunctionType.TIMESTAMP: lambda x: f"cast({x[0]} as timestamp)",
|
|
87
98
|
FunctionType.DATE: lambda x: f"cast({x[0]} as date)",
|
|
88
99
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc('{x[1]}', {x[0]})",
|
|
89
100
|
FunctionType.DATE_ADD: lambda x: f"date_add({x[0]}, {x[2]} * INTERVAL 1 {x[1]})",
|
|
90
101
|
FunctionType.DATE_SUB: lambda x: f"date_add({x[0]}, -{x[2]} * INTERVAL 1 {x[1]})",
|
|
91
|
-
FunctionType.DATE_PART: lambda x: f"date_part('{x[1]}', {x[0]})",
|
|
102
|
+
FunctionType.DATE_PART: lambda x: f"date_part('{map_date_part_specifier(x[1])}', {x[0]})",
|
|
92
103
|
FunctionType.DATE_DIFF: lambda x: f"date_diff('{x[2]}', {x[0]}, {x[1]})",
|
|
93
104
|
FunctionType.CONCAT: lambda x: f"({' || '.join(x)})",
|
|
94
105
|
FunctionType.DATE_LITERAL: lambda x: f"date '{x}'",
|
trilogy/executor.py
CHANGED
trilogy/parsing/common.py
CHANGED
|
@@ -55,6 +55,21 @@ from trilogy.core.models.environment import Environment
|
|
|
55
55
|
from trilogy.core.statements.author import RowsetDerivationStatement, SelectStatement
|
|
56
56
|
from trilogy.utility import string_to_hash, unique
|
|
57
57
|
|
|
58
|
+
ARBITRARY_INPUTS = (
|
|
59
|
+
AggregateWrapper
|
|
60
|
+
| FunctionCallWrapper
|
|
61
|
+
| WindowItem
|
|
62
|
+
| FilterItem
|
|
63
|
+
| Function
|
|
64
|
+
| Parenthetical
|
|
65
|
+
| ListWrapper
|
|
66
|
+
| MapWrapper
|
|
67
|
+
| int
|
|
68
|
+
| float
|
|
69
|
+
| str
|
|
70
|
+
| date
|
|
71
|
+
)
|
|
72
|
+
|
|
58
73
|
|
|
59
74
|
def process_function_arg(
|
|
60
75
|
arg,
|
|
@@ -625,7 +640,7 @@ def window_item_to_concept(
|
|
|
625
640
|
fmetadata = metadata or Metadata()
|
|
626
641
|
if not isinstance(parent.content, ConceptRef):
|
|
627
642
|
raise NotImplementedError(
|
|
628
|
-
f"Window function
|
|
643
|
+
f"Window function with non ref content {parent.content} not yet supported"
|
|
629
644
|
)
|
|
630
645
|
bcontent = environment.concepts[parent.content.address]
|
|
631
646
|
if isinstance(bcontent, UndefinedConcept):
|
|
@@ -844,6 +859,7 @@ def generate_concept_name(
|
|
|
844
859
|
| Function
|
|
845
860
|
| ListWrapper
|
|
846
861
|
| MapWrapper
|
|
862
|
+
| Parenthetical
|
|
847
863
|
| int
|
|
848
864
|
| float
|
|
849
865
|
| str
|
|
@@ -865,24 +881,36 @@ def generate_concept_name(
|
|
|
865
881
|
return f"{VIRTUAL_CONCEPT_PREFIX}_group_to_{string_to_hash(str(parent))}"
|
|
866
882
|
else:
|
|
867
883
|
return f"{VIRTUAL_CONCEPT_PREFIX}_func_{parent.operator.value}_{string_to_hash(str(parent))}"
|
|
884
|
+
elif isinstance(parent, Parenthetical):
|
|
885
|
+
return f"{VIRTUAL_CONCEPT_PREFIX}_paren_{string_to_hash(str(parent))}"
|
|
886
|
+
elif isinstance(parent, FunctionCallWrapper):
|
|
887
|
+
return f"{VIRTUAL_CONCEPT_PREFIX}_{parent.name}_{string_to_hash(str(parent))}"
|
|
868
888
|
else: # ListWrapper, MapWrapper, or primitive types
|
|
869
889
|
return f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(parent))}"
|
|
870
890
|
|
|
871
891
|
|
|
892
|
+
def parenthetical_to_concept(
|
|
893
|
+
parent: Parenthetical,
|
|
894
|
+
name: str,
|
|
895
|
+
namespace: str,
|
|
896
|
+
environment: Environment,
|
|
897
|
+
metadata: Metadata | None = None,
|
|
898
|
+
) -> Concept:
|
|
899
|
+
if isinstance(
|
|
900
|
+
parent.content,
|
|
901
|
+
ARBITRARY_INPUTS,
|
|
902
|
+
):
|
|
903
|
+
|
|
904
|
+
return arbitrary_to_concept(
|
|
905
|
+
parent.content, environment, namespace, name, metadata
|
|
906
|
+
)
|
|
907
|
+
raise NotImplementedError(
|
|
908
|
+
f"Parenthetical with non-supported content {parent.content} ({type(parent.content)}) not yet supported"
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
|
|
872
912
|
def arbitrary_to_concept(
|
|
873
|
-
parent:
|
|
874
|
-
AggregateWrapper
|
|
875
|
-
| FunctionCallWrapper
|
|
876
|
-
| WindowItem
|
|
877
|
-
| FilterItem
|
|
878
|
-
| Function
|
|
879
|
-
| ListWrapper
|
|
880
|
-
| MapWrapper
|
|
881
|
-
| int
|
|
882
|
-
| float
|
|
883
|
-
| str
|
|
884
|
-
| date
|
|
885
|
-
),
|
|
913
|
+
parent: ARBITRARY_INPUTS,
|
|
886
914
|
environment: Environment,
|
|
887
915
|
namespace: str | None = None,
|
|
888
916
|
name: str | None = None,
|
|
@@ -938,5 +966,7 @@ def arbitrary_to_concept(
|
|
|
938
966
|
)
|
|
939
967
|
elif isinstance(parent, ListWrapper):
|
|
940
968
|
return constant_to_concept(parent, name, namespace, metadata)
|
|
969
|
+
elif isinstance(parent, Parenthetical):
|
|
970
|
+
return parenthetical_to_concept(parent, name, namespace, environment, metadata)
|
|
941
971
|
else:
|
|
942
972
|
return constant_to_concept(parent, name, namespace, metadata)
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -223,7 +223,14 @@ def expr_to_boolean(
|
|
|
223
223
|
def unwrap_transformation(
|
|
224
224
|
input: Expr,
|
|
225
225
|
environment: Environment,
|
|
226
|
-
) ->
|
|
226
|
+
) -> (
|
|
227
|
+
Function
|
|
228
|
+
| FilterItem
|
|
229
|
+
| WindowItem
|
|
230
|
+
| AggregateWrapper
|
|
231
|
+
| FunctionCallWrapper
|
|
232
|
+
| Parenthetical
|
|
233
|
+
):
|
|
227
234
|
if isinstance(input, Function):
|
|
228
235
|
return input
|
|
229
236
|
elif isinstance(input, AggregateWrapper):
|
|
@@ -243,7 +250,7 @@ def unwrap_transformation(
|
|
|
243
250
|
elif isinstance(input, FunctionCallWrapper):
|
|
244
251
|
return input
|
|
245
252
|
elif isinstance(input, Parenthetical):
|
|
246
|
-
return
|
|
253
|
+
return input
|
|
247
254
|
else:
|
|
248
255
|
return Function.model_construct(
|
|
249
256
|
operator=FunctionType.CONSTANT,
|
|
@@ -779,7 +786,6 @@ class ParseToObjects(Transformer):
|
|
|
779
786
|
lookup, namespace, name, parent = parse_concept_reference(
|
|
780
787
|
name, self.environment
|
|
781
788
|
)
|
|
782
|
-
|
|
783
789
|
concept = Concept(
|
|
784
790
|
name=name,
|
|
785
791
|
datatype=arg_to_datatype(constant),
|
|
@@ -1953,6 +1959,10 @@ class ParseToObjects(Transformer):
|
|
|
1953
1959
|
def fday(self, meta, args):
|
|
1954
1960
|
return self.function_factory.create_function(args, FunctionType.DAY, meta)
|
|
1955
1961
|
|
|
1962
|
+
@v_args(meta=True)
|
|
1963
|
+
def fday_name(self, meta, args):
|
|
1964
|
+
return self.function_factory.create_function(args, FunctionType.DAY_NAME, meta)
|
|
1965
|
+
|
|
1956
1966
|
@v_args(meta=True)
|
|
1957
1967
|
def fday_of_week(self, meta, args):
|
|
1958
1968
|
return self.function_factory.create_function(
|
|
@@ -1967,6 +1977,12 @@ class ParseToObjects(Transformer):
|
|
|
1967
1977
|
def fmonth(self, meta, args):
|
|
1968
1978
|
return self.function_factory.create_function(args, FunctionType.MONTH, meta)
|
|
1969
1979
|
|
|
1980
|
+
@v_args(meta=True)
|
|
1981
|
+
def fmonth_name(self, meta, args):
|
|
1982
|
+
return self.function_factory.create_function(
|
|
1983
|
+
args, FunctionType.MONTH_NAME, meta
|
|
1984
|
+
)
|
|
1985
|
+
|
|
1970
1986
|
@v_args(meta=True)
|
|
1971
1987
|
def fquarter(self, meta, args):
|
|
1972
1988
|
return self.function_factory.create_function(args, FunctionType.QUARTER, meta)
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -379,12 +379,16 @@
|
|
|
379
379
|
fhour: _HOUR expr ")"
|
|
380
380
|
_DAY.1: "day("i
|
|
381
381
|
fday: _DAY expr ")"
|
|
382
|
+
_DAY_NAME.1: "day_name("i
|
|
383
|
+
fday_name: _DAY_NAME expr ")"
|
|
382
384
|
_DAY_OF_WEEK.1: "day_of_week("i
|
|
383
385
|
fday_of_week: _DAY_OF_WEEK expr ")"
|
|
384
386
|
_WEEK.1: "week("i
|
|
385
387
|
fweek: _WEEK expr ")"
|
|
386
388
|
_MONTH.1: "month("i
|
|
387
389
|
fmonth: _MONTH expr ")"
|
|
390
|
+
_MONTH_NAME.1: "month_name("i
|
|
391
|
+
fmonth_name: _MONTH_NAME expr ")"
|
|
388
392
|
_QUARTER.1: "quarter("i
|
|
389
393
|
fquarter: _QUARTER expr ")"
|
|
390
394
|
_YEAR.1: "year("i
|
|
@@ -402,7 +406,7 @@
|
|
|
402
406
|
_DATE_DIFF.1: "date_diff("i
|
|
403
407
|
fdate_diff: _DATE_DIFF expr "," expr "," DATE_PART ")"
|
|
404
408
|
|
|
405
|
-
_date_functions: fdate | fdate_add | fdate_sub | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday | fday_of_week | fweek | fmonth | fquarter | fyear | fdate_part | fdate_trunc
|
|
409
|
+
_date_functions: fdate | fdate_add | fdate_sub | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday |fday_name | fday_of_week | fweek | fmonth | fmonth_name | fquarter | fyear | fdate_part | fdate_trunc
|
|
406
410
|
|
|
407
411
|
_static_functions: _string_functions | _math_functions | _array_functions | _map_functions
|
|
408
412
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|