pytrilogy 0.0.2.21__tar.gz → 0.0.2.22__tar.gz
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.2.21/pytrilogy.egg-info → pytrilogy-0.0.2.22}/PKG-INFO +17 -11
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/README.md +16 -10
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22/pytrilogy.egg-info}/PKG-INFO +17 -11
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pytrilogy.egg-info/SOURCES.txt +1 -0
- pytrilogy-0.0.2.22/tests/test_show.py +58 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/constants.py +1 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/enums.py +3 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/env_processor.py +4 -2
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/ergonomics.py +11 -4
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/models.py +17 -1
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/concept_strategies_v3.py +0 -1
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/node_merge_node.py +19 -6
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/rowset_node.py +2 -2
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/query_processor.py +5 -4
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/base.py +2 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/executor.py +14 -1
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/common.py +16 -2
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/parse_engine.py +1 -34
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/render.py +2 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/trilogy.lark +35 -18
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pyproject.toml +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/setup.cfg +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/setup.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_models.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_select.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.22
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -145,7 +145,7 @@ Run the following from the directory the file is in.
|
|
|
145
145
|
trilogy run hello.trilogy duckdb
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-

|
|
149
149
|
|
|
150
150
|
## Backends
|
|
151
151
|
|
|
@@ -158,9 +158,9 @@ The current Trilogy implementation supports these backends:
|
|
|
158
158
|
|
|
159
159
|
## Basic Example - Python
|
|
160
160
|
|
|
161
|
-
Trilogy can be run directly in python.
|
|
161
|
+
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
162
162
|
|
|
163
|
-
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console)
|
|
163
|
+
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console).
|
|
164
164
|
|
|
165
165
|
```python
|
|
166
166
|
|
|
@@ -224,7 +224,7 @@ and second the dialect to run.
|
|
|
224
224
|
To pass arguments to a backend, append additional --<option> flags after specifying the dialect.
|
|
225
225
|
|
|
226
226
|
Example:
|
|
227
|
-
`trilogy run key
|
|
227
|
+
`trilogy run "key x int; datasource test_source ( i:x) grain(in) address test; select x;" duckdb --path <path/to/database>`
|
|
228
228
|
|
|
229
229
|
### Bigquery Args
|
|
230
230
|
N/A, only supports default auth. In python you can pass in a custom client.
|
|
@@ -267,7 +267,7 @@ Clone repository and install requirements.txt and requirements-test.txt.
|
|
|
267
267
|
Please open an issue first to discuss what you would like to change, and then create a PR against that issue.
|
|
268
268
|
|
|
269
269
|
## Similar in space
|
|
270
|
-
Trilogy combines two aspects; a semantic layer and a query language.
|
|
270
|
+
Trilogy combines two aspects; a semantic layer and a query language. Examples of both are linked below:
|
|
271
271
|
|
|
272
272
|
Python "semantic layers" are tools for defining data access to a warehouse in a more abstract way.
|
|
273
273
|
|
|
@@ -284,20 +284,26 @@ but all are worth checking out. Please open PRs/comment for anything missed!
|
|
|
284
284
|
|
|
285
285
|
#### IMPORT
|
|
286
286
|
|
|
287
|
-
`import
|
|
287
|
+
`import [path] as [alias];`
|
|
288
288
|
|
|
289
289
|
#### CONCEPT
|
|
290
290
|
|
|
291
|
-
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval
|
|
291
|
+
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval | list<[type]> | map<[type], [type]> | struct<name:[type], name:[type]>`;
|
|
292
292
|
|
|
293
293
|
Key:
|
|
294
|
-
`key
|
|
294
|
+
`key [name] [type];`
|
|
295
295
|
|
|
296
296
|
Property:
|
|
297
|
-
`property
|
|
297
|
+
`property [key>].[name] [type];`
|
|
298
|
+
`property x.y int;`
|
|
299
|
+
or
|
|
300
|
+
`property <[key](,[key])?>.<name> [type];`
|
|
301
|
+
`property <x,y>.z int;`
|
|
302
|
+
|
|
298
303
|
|
|
299
304
|
Transformation:
|
|
300
|
-
`auto
|
|
305
|
+
`auto [name] <- [expression];`
|
|
306
|
+
`auto x <- y + 1;`
|
|
301
307
|
|
|
302
308
|
#### DATASOURCE
|
|
303
309
|
```sql
|
|
@@ -116,7 +116,7 @@ Run the following from the directory the file is in.
|
|
|
116
116
|
trilogy run hello.trilogy duckdb
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-

|
|
120
120
|
|
|
121
121
|
## Backends
|
|
122
122
|
|
|
@@ -129,9 +129,9 @@ The current Trilogy implementation supports these backends:
|
|
|
129
129
|
|
|
130
130
|
## Basic Example - Python
|
|
131
131
|
|
|
132
|
-
Trilogy can be run directly in python.
|
|
132
|
+
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
133
133
|
|
|
134
|
-
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console)
|
|
134
|
+
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console).
|
|
135
135
|
|
|
136
136
|
```python
|
|
137
137
|
|
|
@@ -195,7 +195,7 @@ and second the dialect to run.
|
|
|
195
195
|
To pass arguments to a backend, append additional --<option> flags after specifying the dialect.
|
|
196
196
|
|
|
197
197
|
Example:
|
|
198
|
-
`trilogy run key
|
|
198
|
+
`trilogy run "key x int; datasource test_source ( i:x) grain(in) address test; select x;" duckdb --path <path/to/database>`
|
|
199
199
|
|
|
200
200
|
### Bigquery Args
|
|
201
201
|
N/A, only supports default auth. In python you can pass in a custom client.
|
|
@@ -238,7 +238,7 @@ Clone repository and install requirements.txt and requirements-test.txt.
|
|
|
238
238
|
Please open an issue first to discuss what you would like to change, and then create a PR against that issue.
|
|
239
239
|
|
|
240
240
|
## Similar in space
|
|
241
|
-
Trilogy combines two aspects; a semantic layer and a query language.
|
|
241
|
+
Trilogy combines two aspects; a semantic layer and a query language. Examples of both are linked below:
|
|
242
242
|
|
|
243
243
|
Python "semantic layers" are tools for defining data access to a warehouse in a more abstract way.
|
|
244
244
|
|
|
@@ -255,20 +255,26 @@ but all are worth checking out. Please open PRs/comment for anything missed!
|
|
|
255
255
|
|
|
256
256
|
#### IMPORT
|
|
257
257
|
|
|
258
|
-
`import
|
|
258
|
+
`import [path] as [alias];`
|
|
259
259
|
|
|
260
260
|
#### CONCEPT
|
|
261
261
|
|
|
262
|
-
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval
|
|
262
|
+
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval | list<[type]> | map<[type], [type]> | struct<name:[type], name:[type]>`;
|
|
263
263
|
|
|
264
264
|
Key:
|
|
265
|
-
`key
|
|
265
|
+
`key [name] [type];`
|
|
266
266
|
|
|
267
267
|
Property:
|
|
268
|
-
`property
|
|
268
|
+
`property [key>].[name] [type];`
|
|
269
|
+
`property x.y int;`
|
|
270
|
+
or
|
|
271
|
+
`property <[key](,[key])?>.<name> [type];`
|
|
272
|
+
`property <x,y>.z int;`
|
|
273
|
+
|
|
269
274
|
|
|
270
275
|
Transformation:
|
|
271
|
-
`auto
|
|
276
|
+
`auto [name] <- [expression];`
|
|
277
|
+
`auto x <- y + 1;`
|
|
272
278
|
|
|
273
279
|
#### DATASOURCE
|
|
274
280
|
```sql
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.22
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -145,7 +145,7 @@ Run the following from the directory the file is in.
|
|
|
145
145
|
trilogy run hello.trilogy duckdb
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-

|
|
149
149
|
|
|
150
150
|
## Backends
|
|
151
151
|
|
|
@@ -158,9 +158,9 @@ The current Trilogy implementation supports these backends:
|
|
|
158
158
|
|
|
159
159
|
## Basic Example - Python
|
|
160
160
|
|
|
161
|
-
Trilogy can be run directly in python.
|
|
161
|
+
Trilogy can be run directly in python through the core SDK. Trilogy code can be defined and parsed inline or parsed out of files.
|
|
162
162
|
|
|
163
|
-
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console)
|
|
163
|
+
A bigquery example, similar to bigquery [the quickstart](https://cloud.google.com/bigquery/docs/quickstarts/query-public-dataset-console).
|
|
164
164
|
|
|
165
165
|
```python
|
|
166
166
|
|
|
@@ -224,7 +224,7 @@ and second the dialect to run.
|
|
|
224
224
|
To pass arguments to a backend, append additional --<option> flags after specifying the dialect.
|
|
225
225
|
|
|
226
226
|
Example:
|
|
227
|
-
`trilogy run key
|
|
227
|
+
`trilogy run "key x int; datasource test_source ( i:x) grain(in) address test; select x;" duckdb --path <path/to/database>`
|
|
228
228
|
|
|
229
229
|
### Bigquery Args
|
|
230
230
|
N/A, only supports default auth. In python you can pass in a custom client.
|
|
@@ -267,7 +267,7 @@ Clone repository and install requirements.txt and requirements-test.txt.
|
|
|
267
267
|
Please open an issue first to discuss what you would like to change, and then create a PR against that issue.
|
|
268
268
|
|
|
269
269
|
## Similar in space
|
|
270
|
-
Trilogy combines two aspects; a semantic layer and a query language.
|
|
270
|
+
Trilogy combines two aspects; a semantic layer and a query language. Examples of both are linked below:
|
|
271
271
|
|
|
272
272
|
Python "semantic layers" are tools for defining data access to a warehouse in a more abstract way.
|
|
273
273
|
|
|
@@ -284,20 +284,26 @@ but all are worth checking out. Please open PRs/comment for anything missed!
|
|
|
284
284
|
|
|
285
285
|
#### IMPORT
|
|
286
286
|
|
|
287
|
-
`import
|
|
287
|
+
`import [path] as [alias];`
|
|
288
288
|
|
|
289
289
|
#### CONCEPT
|
|
290
290
|
|
|
291
|
-
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval
|
|
291
|
+
Types: `string | int | float | bool | date | datetime | time | numeric(scale, precision) | timestamp | interval | list<[type]> | map<[type], [type]> | struct<name:[type], name:[type]>`;
|
|
292
292
|
|
|
293
293
|
Key:
|
|
294
|
-
`key
|
|
294
|
+
`key [name] [type];`
|
|
295
295
|
|
|
296
296
|
Property:
|
|
297
|
-
`property
|
|
297
|
+
`property [key>].[name] [type];`
|
|
298
|
+
`property x.y int;`
|
|
299
|
+
or
|
|
300
|
+
`property <[key](,[key])?>.<name> [type];`
|
|
301
|
+
`property <x,y>.z int;`
|
|
302
|
+
|
|
298
303
|
|
|
299
304
|
Transformation:
|
|
300
|
-
`auto
|
|
305
|
+
`auto [name] <- [expression];`
|
|
306
|
+
`auto x <- y + 1;`
|
|
301
307
|
|
|
302
308
|
#### DATASOURCE
|
|
303
309
|
```sql
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from trilogy.core.models import ShowStatement
|
|
2
|
+
from trilogy.core.query_processor import process_query
|
|
3
|
+
from trilogy import Dialects
|
|
4
|
+
from trilogy.parser import parse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_show_bigquery():
|
|
8
|
+
declarations = """
|
|
9
|
+
key user_id int metadata(description="the description");
|
|
10
|
+
property user_id.display_name string metadata(description="The display name ");
|
|
11
|
+
property user_id.about_me string metadata(description="User provided description");
|
|
12
|
+
key post_id int;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
datasource posts (
|
|
16
|
+
user_id: user_id,
|
|
17
|
+
id: post_id
|
|
18
|
+
)
|
|
19
|
+
grain (post_id)
|
|
20
|
+
address bigquery-public-data.stackoverflow.post_history
|
|
21
|
+
;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
datasource users (
|
|
25
|
+
id: user_id,
|
|
26
|
+
display_name: display_name,
|
|
27
|
+
about_me: about_me,
|
|
28
|
+
)
|
|
29
|
+
grain (user_id)
|
|
30
|
+
address bigquery-public-data.stackoverflow.users
|
|
31
|
+
;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
env, parsed = parse(declarations)
|
|
36
|
+
|
|
37
|
+
q1 = """
|
|
38
|
+
metric post_count<- count(post_id);
|
|
39
|
+
metric distinct_post_count <- count_distinct(post_id);
|
|
40
|
+
|
|
41
|
+
metric user_count <- count(user_id);
|
|
42
|
+
|
|
43
|
+
show select
|
|
44
|
+
post_count,
|
|
45
|
+
distinct_post_count,
|
|
46
|
+
user_count
|
|
47
|
+
;"""
|
|
48
|
+
env, parsed = parse(q1, environment=env)
|
|
49
|
+
select: ShowStatement = parsed[-1]
|
|
50
|
+
|
|
51
|
+
query = (
|
|
52
|
+
Dialects.DUCK_DB.default_executor(environment=env)
|
|
53
|
+
.execute_query(select)
|
|
54
|
+
.fetchall()
|
|
55
|
+
)
|
|
56
|
+
assert "FULL JOIN wakeful on 1=1" in query[0]["__preql_internal_query_text"], query[
|
|
57
|
+
0
|
|
58
|
+
]["__preql_internal_query_text"]
|
|
@@ -44,6 +44,7 @@ class Comments:
|
|
|
44
44
|
class Config:
|
|
45
45
|
strict_mode: bool = True
|
|
46
46
|
human_identifiers: bool = True
|
|
47
|
+
randomize_cte_names: bool = False
|
|
47
48
|
validate_missing: bool = True
|
|
48
49
|
comments: Comments = field(default_factory=Comments)
|
|
49
50
|
optimizations: Optimizations = field(default_factory=Optimizations)
|
|
@@ -12,6 +12,7 @@ class UnnestMode(Enum):
|
|
|
12
12
|
|
|
13
13
|
class ConceptSource(Enum):
|
|
14
14
|
MANUAL = "manual"
|
|
15
|
+
CTE = "cte"
|
|
15
16
|
PERSIST_STATEMENT = "persist_statement"
|
|
16
17
|
AUTO_DERIVED = "auto_derived"
|
|
17
18
|
|
|
@@ -206,6 +207,8 @@ class FunctionClass(Enum):
|
|
|
206
207
|
FunctionType.CURRENT_DATETIME,
|
|
207
208
|
]
|
|
208
209
|
|
|
210
|
+
ONE_TO_MANY = [FunctionType.UNNEST]
|
|
211
|
+
|
|
209
212
|
|
|
210
213
|
class Boolean(Enum):
|
|
211
214
|
TRUE = "true"
|
|
@@ -10,9 +10,11 @@ def add_concept(concept: Concept, g: ReferenceGraph):
|
|
|
10
10
|
g.add_node(concept)
|
|
11
11
|
# if we have sources, recursively add them
|
|
12
12
|
node_name = concept_to_node(concept)
|
|
13
|
-
if concept.
|
|
14
|
-
for source in concept.
|
|
13
|
+
if concept.concept_arguments:
|
|
14
|
+
for source in concept.concept_arguments:
|
|
15
15
|
generic = source.with_default_grain()
|
|
16
|
+
add_concept(generic, g)
|
|
17
|
+
|
|
16
18
|
g.add_edge(generic, node_name)
|
|
17
19
|
for _, pseudonym in concept.pseudonyms.items():
|
|
18
20
|
pseudonym = pseudonym.with_default_grain()
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from trilogy.constants import CONFIG
|
|
2
|
+
|
|
1
3
|
# source: https://github.com/aaronbassett/Pass-phrase
|
|
2
4
|
CTE_NAMES = """quizzical
|
|
3
5
|
highfalutin
|
|
@@ -103,8 +105,6 @@ mandrill
|
|
|
103
105
|
marlin
|
|
104
106
|
monitor
|
|
105
107
|
ocelot
|
|
106
|
-
osprey
|
|
107
|
-
owl
|
|
108
108
|
petrel
|
|
109
109
|
python
|
|
110
110
|
ray
|
|
@@ -132,7 +132,6 @@ cuckoo
|
|
|
132
132
|
darter
|
|
133
133
|
dove
|
|
134
134
|
duck
|
|
135
|
-
eagle
|
|
136
135
|
falcon
|
|
137
136
|
finch
|
|
138
137
|
flamingo
|
|
@@ -184,4 +183,12 @@ warbler""".split(
|
|
|
184
183
|
"\n"
|
|
185
184
|
)
|
|
186
185
|
|
|
187
|
-
|
|
186
|
+
|
|
187
|
+
def generate_cte_names():
|
|
188
|
+
if CONFIG.randomize_cte_names:
|
|
189
|
+
from random import shuffle
|
|
190
|
+
|
|
191
|
+
new = [*CTE_NAMES]
|
|
192
|
+
shuffle(new)
|
|
193
|
+
return new
|
|
194
|
+
return CTE_NAMES
|
|
@@ -3478,10 +3478,18 @@ class Environment(BaseModel):
|
|
|
3478
3478
|
# to make this a root for discovery purposes
|
|
3479
3479
|
# as it now "exists" in a table
|
|
3480
3480
|
current_concept.lineage = None
|
|
3481
|
+
current_concept = current_concept.with_default_grain()
|
|
3481
3482
|
self.add_concept(
|
|
3482
3483
|
current_concept, meta=meta, force=True, _ignore_cache=True
|
|
3483
3484
|
)
|
|
3484
3485
|
self.merge_concept(new_concept, current_concept, [])
|
|
3486
|
+
else:
|
|
3487
|
+
self.add_concept(current_concept, meta=meta, _ignore_cache=True)
|
|
3488
|
+
|
|
3489
|
+
# else:
|
|
3490
|
+
# self.add_concept(
|
|
3491
|
+
# current_concept, meta=meta, _ignore_cache=True
|
|
3492
|
+
# )
|
|
3485
3493
|
if not _ignore_cache:
|
|
3486
3494
|
self.gen_concept_list_caches()
|
|
3487
3495
|
return datasource
|
|
@@ -4289,6 +4297,9 @@ class RowsetDerivationStatement(Namespaced, BaseModel):
|
|
|
4289
4297
|
def __repr__(self):
|
|
4290
4298
|
return f"RowsetDerivation<{str(self.select)}>"
|
|
4291
4299
|
|
|
4300
|
+
def __str__(self):
|
|
4301
|
+
return self.__repr__()
|
|
4302
|
+
|
|
4292
4303
|
@property
|
|
4293
4304
|
def derived_concepts(self) -> List[Concept]:
|
|
4294
4305
|
output: list[Concept] = []
|
|
@@ -4307,7 +4318,8 @@ class RowsetDerivationStatement(Namespaced, BaseModel):
|
|
|
4307
4318
|
content=orig_concept, where=self.select.where_clause, rowset=self
|
|
4308
4319
|
),
|
|
4309
4320
|
grain=orig_concept.grain,
|
|
4310
|
-
metadata
|
|
4321
|
+
# TODO: add proper metadata
|
|
4322
|
+
metadata=Metadata(concept_source=ConceptSource.CTE),
|
|
4311
4323
|
namespace=(
|
|
4312
4324
|
f"{self.name}.{orig_concept.namespace}"
|
|
4313
4325
|
if orig_concept.namespace != self.namespace
|
|
@@ -4334,6 +4346,7 @@ class RowsetDerivationStatement(Namespaced, BaseModel):
|
|
|
4334
4346
|
components=[orig[c.address] for c in x.grain.components_copy]
|
|
4335
4347
|
)
|
|
4336
4348
|
else:
|
|
4349
|
+
|
|
4337
4350
|
x.grain = default_grain
|
|
4338
4351
|
return output
|
|
4339
4352
|
|
|
@@ -4359,6 +4372,9 @@ class RowsetItem(Mergeable, Namespaced, BaseModel):
|
|
|
4359
4372
|
f"<Rowset<{self.rowset.name}>: {str(self.content)} where {str(self.where)}>"
|
|
4360
4373
|
)
|
|
4361
4374
|
|
|
4375
|
+
def __str__(self):
|
|
4376
|
+
return self.__repr__()
|
|
4377
|
+
|
|
4362
4378
|
def with_merge(self, source: Concept, target: Concept, modifiers: List[Modifier]):
|
|
4363
4379
|
return RowsetItem(
|
|
4364
4380
|
content=self.content.with_merge(source, target, modifiers),
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
@@ -87,12 +87,10 @@ def determine_induced_minimal_nodes(
|
|
|
87
87
|
for node in G.nodes:
|
|
88
88
|
if concepts.get(node):
|
|
89
89
|
lookup: Concept = concepts[node]
|
|
90
|
-
if lookup.derivation
|
|
91
|
-
nodes_to_remove.append(node)
|
|
92
|
-
elif lookup.derivation == PurposeLineage.BASIC and G.out_degree(node) == 0:
|
|
90
|
+
if lookup.derivation in (PurposeLineage.CONSTANT,):
|
|
93
91
|
nodes_to_remove.append(node)
|
|
94
92
|
# purge a node if we're already looking for all it's parents
|
|
95
|
-
|
|
93
|
+
if filter_downstream and lookup.derivation not in (PurposeLineage.ROOT,):
|
|
96
94
|
nodes_to_remove.append(node)
|
|
97
95
|
|
|
98
96
|
H.remove_nodes_from(nodes_to_remove)
|
|
@@ -105,11 +103,12 @@ def determine_induced_minimal_nodes(
|
|
|
105
103
|
zero_out = list(
|
|
106
104
|
x for x in H.nodes if G.out_degree(x) == 0 and x not in nodelist
|
|
107
105
|
)
|
|
106
|
+
|
|
108
107
|
try:
|
|
109
108
|
paths = nx.multi_source_dijkstra_path(H, nodelist)
|
|
110
109
|
except nx.exception.NodeNotFound:
|
|
110
|
+
logger.debug(f"Unable to find paths for {nodelist}")
|
|
111
111
|
return None
|
|
112
|
-
|
|
113
112
|
H.remove_nodes_from(list(x for x in H.nodes if x not in paths))
|
|
114
113
|
sG: nx.Graph = ax.steinertree.steiner_tree(H, nodelist).copy()
|
|
115
114
|
final: nx.DiGraph = nx.subgraph(G, sG.nodes).copy()
|
|
@@ -126,12 +125,24 @@ def determine_induced_minimal_nodes(
|
|
|
126
125
|
# all concept nodes must have a parent
|
|
127
126
|
|
|
128
127
|
if not all(
|
|
129
|
-
[
|
|
128
|
+
[
|
|
129
|
+
final.in_degree(node) > 0
|
|
130
|
+
for node in final.nodes
|
|
131
|
+
if node.startswith("c~") and node in nodelist
|
|
132
|
+
]
|
|
130
133
|
):
|
|
134
|
+
missing = [
|
|
135
|
+
node
|
|
136
|
+
for node in final.nodes
|
|
137
|
+
if node.startswith("c~") and final.in_degree(node) == 0
|
|
138
|
+
]
|
|
139
|
+
logger.debug(f"Skipping graph for {nodelist} as no in_degree {missing}")
|
|
131
140
|
return None
|
|
132
141
|
|
|
133
142
|
if not all([node in final.nodes for node in nodelist]):
|
|
143
|
+
logger.debug(f"Skipping graph for {nodelist} as missing nodes")
|
|
134
144
|
return None
|
|
145
|
+
logger.debug(f"Found final graph {final.nodes}")
|
|
135
146
|
return final
|
|
136
147
|
|
|
137
148
|
|
|
@@ -256,7 +267,9 @@ def resolve_weak_components(
|
|
|
256
267
|
|
|
257
268
|
subgraphs: list[list[Concept]] = []
|
|
258
269
|
# components = nx.strongly_connected_components(g)
|
|
270
|
+
node_list = [x for x in g.nodes if x.startswith("c~")]
|
|
259
271
|
components = extract_ds_components(g, node_list)
|
|
272
|
+
logger.debug(f"Extracted components {components} from {node_list}")
|
|
260
273
|
for component in components:
|
|
261
274
|
# we need to take unique again as different addresses may map to the same concept
|
|
262
275
|
sub_component = unique(
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
@@ -96,7 +96,7 @@ def gen_rowset_node(
|
|
|
96
96
|
# node.set_preexisting_conditions(conditions.conditional if conditions else None)
|
|
97
97
|
return node
|
|
98
98
|
|
|
99
|
-
possible_joins = concept_to_relevant_joins(
|
|
99
|
+
possible_joins = concept_to_relevant_joins(node.output_concepts)
|
|
100
100
|
if not possible_joins:
|
|
101
101
|
logger.info(
|
|
102
102
|
f"{padding(depth)}{LOGGER_PREFIX} no possible joins for rowset node to get {[x.address for x in local_optional]}; have {[x.address for x in node.output_concepts]}"
|
|
@@ -104,7 +104,7 @@ def gen_rowset_node(
|
|
|
104
104
|
return node
|
|
105
105
|
enrich_node: MergeNode = source_concepts( # this fetches the parent + join keys
|
|
106
106
|
# to then connect to the rest of the query
|
|
107
|
-
mandatory_list=
|
|
107
|
+
mandatory_list=possible_joins + local_optional,
|
|
108
108
|
environment=environment,
|
|
109
109
|
g=g,
|
|
110
110
|
depth=depth + 1,
|
|
@@ -32,7 +32,7 @@ from trilogy.utility import unique
|
|
|
32
32
|
|
|
33
33
|
from trilogy.hooks.base_hook import BaseHook
|
|
34
34
|
from trilogy.constants import logger
|
|
35
|
-
from trilogy.core.ergonomics import
|
|
35
|
+
from trilogy.core.ergonomics import generate_cte_names
|
|
36
36
|
from trilogy.core.optimization import optimize_ctes
|
|
37
37
|
from math import ceil
|
|
38
38
|
from collections import defaultdict
|
|
@@ -169,15 +169,16 @@ def datasource_to_query_datasource(datasource: Datasource) -> QueryDatasource:
|
|
|
169
169
|
|
|
170
170
|
|
|
171
171
|
def generate_cte_name(full_name: str, name_map: dict[str, str]) -> str:
|
|
172
|
+
cte_names = generate_cte_names()
|
|
172
173
|
if CONFIG.human_identifiers:
|
|
173
174
|
if full_name in name_map:
|
|
174
175
|
return name_map[full_name]
|
|
175
176
|
suffix = ""
|
|
176
177
|
idx = len(name_map)
|
|
177
|
-
if idx >= len(
|
|
178
|
-
int = ceil(idx / len(
|
|
178
|
+
if idx >= len(cte_names):
|
|
179
|
+
int = ceil(idx / len(cte_names))
|
|
179
180
|
suffix = f"_{int}"
|
|
180
|
-
valid = [x for x in
|
|
181
|
+
valid = [x for x in cte_names if x + suffix not in name_map.values()]
|
|
181
182
|
lookup = valid[0]
|
|
182
183
|
new_name = f"{lookup}{suffix}"
|
|
183
184
|
name_map[full_name] = new_name
|
|
@@ -48,6 +48,7 @@ from trilogy.core.models import (
|
|
|
48
48
|
MapType,
|
|
49
49
|
StructType,
|
|
50
50
|
MergeStatementV2,
|
|
51
|
+
Datasource,
|
|
51
52
|
)
|
|
52
53
|
from trilogy.core.query_processor import process_query, process_persist
|
|
53
54
|
from trilogy.dialect.common import render_join, render_unnest
|
|
@@ -724,6 +725,7 @@ class BaseDialect:
|
|
|
724
725
|
MergeStatementV2,
|
|
725
726
|
ImportStatement,
|
|
726
727
|
RowsetDerivationStatement,
|
|
728
|
+
Datasource,
|
|
727
729
|
),
|
|
728
730
|
):
|
|
729
731
|
continue
|
|
@@ -23,7 +23,7 @@ from trilogy.dialect.base import BaseDialect
|
|
|
23
23
|
from trilogy.dialect.enums import Dialects
|
|
24
24
|
from trilogy.parser import parse_text
|
|
25
25
|
from trilogy.hooks.base_hook import BaseHook
|
|
26
|
-
|
|
26
|
+
from pathlib import Path
|
|
27
27
|
from dataclasses import dataclass
|
|
28
28
|
|
|
29
29
|
|
|
@@ -152,6 +152,13 @@ class Executor(object):
|
|
|
152
152
|
def _(self, query: RawSQLStatement) -> CursorResult:
|
|
153
153
|
return self.execute_raw_sql(query.text)
|
|
154
154
|
|
|
155
|
+
@execute_query.register
|
|
156
|
+
def _(self, query: ShowStatement) -> CursorResult:
|
|
157
|
+
sql = self.generator.generate_queries(
|
|
158
|
+
self.environment, [query], hooks=self.hooks
|
|
159
|
+
)
|
|
160
|
+
return self.execute_query(sql[0])
|
|
161
|
+
|
|
155
162
|
@execute_query.register
|
|
156
163
|
def _(self, query: ProcessedShowStatement) -> CursorResult:
|
|
157
164
|
return generate_result_set(
|
|
@@ -341,3 +348,9 @@ class Executor(object):
|
|
|
341
348
|
if isinstance(statement, ProcessedQueryPersist):
|
|
342
349
|
self.environment.add_datasource(statement.datasource)
|
|
343
350
|
return output
|
|
351
|
+
|
|
352
|
+
def execute_file(self, file: str | Path) -> List[CursorResult]:
|
|
353
|
+
file = Path(file)
|
|
354
|
+
with open(file, "r") as f:
|
|
355
|
+
command = f.read()
|
|
356
|
+
return self.execute_text(command)
|
|
@@ -141,9 +141,12 @@ def function_to_concept(parent: Function, name: str, namespace: str) -> Concept:
|
|
|
141
141
|
for x in parent.concept_arguments
|
|
142
142
|
if not x.derivation == PurposeLineage.CONSTANT
|
|
143
143
|
]
|
|
144
|
-
grain = Grain()
|
|
144
|
+
grain: Grain | None = Grain()
|
|
145
145
|
for x in pkeys:
|
|
146
146
|
grain += x.grain
|
|
147
|
+
if parent.operator in FunctionClass.ONE_TO_MANY.value:
|
|
148
|
+
# if the function will create more rows, we don't know what grain this is at
|
|
149
|
+
grain = None
|
|
147
150
|
modifiers = get_upstream_modifiers(pkeys)
|
|
148
151
|
key_grain = []
|
|
149
152
|
for x in pkeys:
|
|
@@ -156,13 +159,24 @@ def function_to_concept(parent: Function, name: str, namespace: str) -> Concept:
|
|
|
156
159
|
purpose = Purpose.CONSTANT
|
|
157
160
|
else:
|
|
158
161
|
purpose = parent.output_purpose
|
|
162
|
+
if grain is not None:
|
|
163
|
+
return Concept(
|
|
164
|
+
name=name,
|
|
165
|
+
datatype=parent.output_datatype,
|
|
166
|
+
purpose=purpose,
|
|
167
|
+
lineage=parent,
|
|
168
|
+
namespace=namespace,
|
|
169
|
+
keys=keys,
|
|
170
|
+
modifiers=modifiers,
|
|
171
|
+
grain=grain,
|
|
172
|
+
)
|
|
173
|
+
|
|
159
174
|
return Concept(
|
|
160
175
|
name=name,
|
|
161
176
|
datatype=parent.output_datatype,
|
|
162
177
|
purpose=purpose,
|
|
163
178
|
lineage=parent,
|
|
164
179
|
namespace=namespace,
|
|
165
|
-
grain=grain,
|
|
166
180
|
keys=keys,
|
|
167
181
|
modifiers=modifiers,
|
|
168
182
|
)
|
|
@@ -461,40 +461,7 @@ class ParseToObjects(Transformer):
|
|
|
461
461
|
while isinstance(source_value, Parenthetical):
|
|
462
462
|
source_value = source_value.content
|
|
463
463
|
|
|
464
|
-
if (
|
|
465
|
-
isinstance(source_value, Function)
|
|
466
|
-
and source_value.operator == FunctionType.STRUCT
|
|
467
|
-
):
|
|
468
|
-
concept = arbitrary_to_concept(
|
|
469
|
-
source_value,
|
|
470
|
-
name=name,
|
|
471
|
-
namespace=namespace,
|
|
472
|
-
purpose=purpose,
|
|
473
|
-
metadata=metadata,
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
if concept.metadata:
|
|
477
|
-
concept.metadata.line_number = meta.line
|
|
478
|
-
self.environment.add_concept(concept, meta=meta)
|
|
479
|
-
return ConceptDerivation(concept=concept)
|
|
480
|
-
elif (
|
|
481
|
-
isinstance(source_value, Function)
|
|
482
|
-
and source_value.operator == FunctionType.ALIAS
|
|
483
|
-
):
|
|
484
|
-
concept = arbitrary_to_concept(
|
|
485
|
-
source_value,
|
|
486
|
-
name=name,
|
|
487
|
-
namespace=namespace,
|
|
488
|
-
purpose=purpose,
|
|
489
|
-
metadata=metadata,
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
if concept.metadata:
|
|
493
|
-
concept.metadata.line_number = meta.line
|
|
494
|
-
self.environment.add_concept(concept, meta=meta)
|
|
495
|
-
return ConceptDerivation(concept=concept)
|
|
496
|
-
|
|
497
|
-
elif isinstance(
|
|
464
|
+
if isinstance(
|
|
498
465
|
source_value, (FilterItem, WindowItem, AggregateWrapper, Function)
|
|
499
466
|
):
|
|
500
467
|
concept = arbitrary_to_concept(
|
|
@@ -200,29 +200,42 @@
|
|
|
200
200
|
_constant_functions: fcurrent_date | fcurrent_datetime
|
|
201
201
|
|
|
202
202
|
//string
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
_LIKE.1: "like("i
|
|
204
|
+
like: _LIKE expr "," _string_lit ")"
|
|
205
|
+
_ILIKE.1: "ilike("i
|
|
206
|
+
ilike: _ILIKE expr "," _string_lit ")"
|
|
207
|
+
alt_like: expr "like"i expr
|
|
208
|
+
_UPPER.1: "upper("i
|
|
209
|
+
upper: _UPPER expr ")"
|
|
210
|
+
_LOWER.1: "lower("i
|
|
211
|
+
lower: _LOWER expr ")"
|
|
212
|
+
_SPLIT.1: "split("i
|
|
213
|
+
fsplit: _SPLIT expr "," _string_lit ")"
|
|
214
|
+
_STRPOS.1: "strpos("i
|
|
215
|
+
fstrpos: _STRPOS expr "," expr ")"
|
|
210
216
|
_SUBSTRING.1: "substring("i
|
|
211
217
|
fsubstring: _SUBSTRING expr "," expr "," expr ")"
|
|
212
218
|
|
|
213
219
|
_string_functions: like | ilike | upper | lower | fsplit | fstrpos | fsubstring
|
|
214
220
|
|
|
215
221
|
// special aggregate
|
|
216
|
-
|
|
222
|
+
_GROUP.1: "group("i
|
|
223
|
+
fgroup: _GROUP expr ")" aggregate_over?
|
|
217
224
|
//aggregates
|
|
218
225
|
_COUNT.1: "count("i
|
|
219
226
|
count: _COUNT expr ")"
|
|
227
|
+
|
|
220
228
|
count_distinct: "count_distinct"i "(" expr ")"
|
|
221
229
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
// avoid conflicts with the window
|
|
231
|
+
_SUM.1: "sum("i
|
|
232
|
+
sum: _SUM expr ")"
|
|
233
|
+
_AVG.1: "avg("i
|
|
234
|
+
avg: _AVG expr ")"
|
|
235
|
+
_MAX.1: "max("i
|
|
236
|
+
max: _MAX expr ")"
|
|
237
|
+
_MIN.1: "min("i
|
|
238
|
+
min: _MIN expr ")"
|
|
226
239
|
|
|
227
240
|
//aggregates can force a grain
|
|
228
241
|
aggregate_all: "*"
|
|
@@ -255,10 +268,14 @@
|
|
|
255
268
|
fyear: _YEAR expr ")"
|
|
256
269
|
|
|
257
270
|
DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
271
|
+
_DATE_TRUNC.1: "date_trunc("i
|
|
272
|
+
fdate_trunc: _DATE_TRUNC expr "," DATE_PART ")"
|
|
273
|
+
_DATE_PART.1: "date_part("i
|
|
274
|
+
fdate_part: _DATE_PART expr "," DATE_PART ")"
|
|
275
|
+
_DATE_ADD.1: "date_add("i
|
|
276
|
+
fdate_add: _DATE_ADD expr "," DATE_PART "," int_lit ")"
|
|
277
|
+
_DATE_DIFF.1: "date_diff("i
|
|
278
|
+
fdate_diff: _DATE_DIFF expr "," expr "," DATE_PART ")"
|
|
262
279
|
|
|
263
280
|
_date_functions: fdate | fdate_add | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday | fday_of_week | fweek | fmonth | fquarter | fyear | fdate_part | fdate_trunc
|
|
264
281
|
|
|
@@ -288,8 +305,8 @@
|
|
|
288
305
|
|
|
289
306
|
map_lit: "{" (literal ":" literal ",")* literal ":" literal ","? "}"
|
|
290
307
|
|
|
291
|
-
|
|
292
|
-
struct_lit:
|
|
308
|
+
_STRUCT.1: "struct("i
|
|
309
|
+
struct_lit: _STRUCT (IDENTIFIER "=" literal ",")* IDENTIFIER "=" literal ","? ")"
|
|
293
310
|
|
|
294
311
|
!bool_lit: "True"i | "False"i
|
|
295
312
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.21 → pytrilogy-0.0.2.22}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|