pytrilogy 0.0.3.95__tar.gz → 0.0.3.96__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.3.95/pytrilogy.egg-info → pytrilogy-0.0.3.96}/PKG-INFO +30 -7
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/README.md +29 -6
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96/pytrilogy.egg-info}/PKG-INFO +30 -7
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/SOURCES.txt +1 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_executor.py +1 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/authoring/__init__.py +59 -45
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/graph_models.py +4 -4
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/execute.py +2 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/common.py +2 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/concept.py +24 -21
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/datasource.py +16 -14
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/environment.py +4 -4
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/base.py +9 -1
- pytrilogy-0.0.3.96/trilogy/dialect/metadata.py +233 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/executor.py +33 -163
- pytrilogy-0.0.3.95/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/setup.cfg +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/setup.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_loop.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_node_factory.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/parse_engine.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/trilogy.lark +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/utility.py +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.96
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -136,11 +136,11 @@ Versus SQL, Trilogy aims to:
|
|
|
136
136
|
|
|
137
137
|
| Backend | Status | Notes |
|
|
138
138
|
|---------|--------|-------|
|
|
139
|
-
| **BigQuery** |
|
|
140
|
-
| **DuckDB** |
|
|
141
|
-
| **Snowflake** |
|
|
142
|
-
| **SQL Server** |
|
|
143
|
-
| **Presto** |
|
|
139
|
+
| **BigQuery** | Core | Full support |
|
|
140
|
+
| **DuckDB** | Core | Full support |
|
|
141
|
+
| **Snowflake** | Core | Full support |
|
|
142
|
+
| **SQL Server** | Experimental | Limited testing |
|
|
143
|
+
| **Presto** | Experimental | Limited testing |
|
|
144
144
|
|
|
145
145
|
## Examples
|
|
146
146
|
|
|
@@ -311,7 +311,30 @@ trilogy fmt <path to trilogy file>
|
|
|
311
311
|
- [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
|
|
312
312
|
- [Full documentation](https://trilogydata.dev/)
|
|
313
313
|
|
|
314
|
-
##
|
|
314
|
+
## Python API Integration
|
|
315
|
+
|
|
316
|
+
### Root Imports
|
|
317
|
+
|
|
318
|
+
Are stable and should be sufficient for executing code from Trilogy as text.
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from pytrilogy import Executor, Dialect
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Authoring Imports
|
|
325
|
+
|
|
326
|
+
Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
|
|
327
|
+
or need to process/transform parsed code in more complicated ways.
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from pytrilogy.authoring import Concept, Function, ...
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Other Imports
|
|
334
|
+
|
|
335
|
+
Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
|
|
336
|
+
|
|
337
|
+
## Trilogy Syntax Reference
|
|
315
338
|
|
|
316
339
|
### Import
|
|
317
340
|
```sql
|
|
@@ -98,11 +98,11 @@ Versus SQL, Trilogy aims to:
|
|
|
98
98
|
|
|
99
99
|
| Backend | Status | Notes |
|
|
100
100
|
|---------|--------|-------|
|
|
101
|
-
| **BigQuery** |
|
|
102
|
-
| **DuckDB** |
|
|
103
|
-
| **Snowflake** |
|
|
104
|
-
| **SQL Server** |
|
|
105
|
-
| **Presto** |
|
|
101
|
+
| **BigQuery** | Core | Full support |
|
|
102
|
+
| **DuckDB** | Core | Full support |
|
|
103
|
+
| **Snowflake** | Core | Full support |
|
|
104
|
+
| **SQL Server** | Experimental | Limited testing |
|
|
105
|
+
| **Presto** | Experimental | Limited testing |
|
|
106
106
|
|
|
107
107
|
## Examples
|
|
108
108
|
|
|
@@ -273,7 +273,30 @@ trilogy fmt <path to trilogy file>
|
|
|
273
273
|
- [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
|
|
274
274
|
- [Full documentation](https://trilogydata.dev/)
|
|
275
275
|
|
|
276
|
-
##
|
|
276
|
+
## Python API Integration
|
|
277
|
+
|
|
278
|
+
### Root Imports
|
|
279
|
+
|
|
280
|
+
Are stable and should be sufficient for executing code from Trilogy as text.
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from pytrilogy import Executor, Dialect
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Authoring Imports
|
|
287
|
+
|
|
288
|
+
Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
|
|
289
|
+
or need to process/transform parsed code in more complicated ways.
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from pytrilogy.authoring import Concept, Function, ...
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Other Imports
|
|
296
|
+
|
|
297
|
+
Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
|
|
298
|
+
|
|
299
|
+
## Trilogy Syntax Reference
|
|
277
300
|
|
|
278
301
|
### Import
|
|
279
302
|
```sql
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.96
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -136,11 +136,11 @@ Versus SQL, Trilogy aims to:
|
|
|
136
136
|
|
|
137
137
|
| Backend | Status | Notes |
|
|
138
138
|
|---------|--------|-------|
|
|
139
|
-
| **BigQuery** |
|
|
140
|
-
| **DuckDB** |
|
|
141
|
-
| **Snowflake** |
|
|
142
|
-
| **SQL Server** |
|
|
143
|
-
| **Presto** |
|
|
139
|
+
| **BigQuery** | Core | Full support |
|
|
140
|
+
| **DuckDB** | Core | Full support |
|
|
141
|
+
| **Snowflake** | Core | Full support |
|
|
142
|
+
| **SQL Server** | Experimental | Limited testing |
|
|
143
|
+
| **Presto** | Experimental | Limited testing |
|
|
144
144
|
|
|
145
145
|
## Examples
|
|
146
146
|
|
|
@@ -311,7 +311,30 @@ trilogy fmt <path to trilogy file>
|
|
|
311
311
|
- [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
|
|
312
312
|
- [Full documentation](https://trilogydata.dev/)
|
|
313
313
|
|
|
314
|
-
##
|
|
314
|
+
## Python API Integration
|
|
315
|
+
|
|
316
|
+
### Root Imports
|
|
317
|
+
|
|
318
|
+
Are stable and should be sufficient for executing code from Trilogy as text.
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from pytrilogy import Executor, Dialect
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Authoring Imports
|
|
325
|
+
|
|
326
|
+
Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
|
|
327
|
+
or need to process/transform parsed code in more complicated ways.
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from pytrilogy.authoring import Concept, Function, ...
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Other Imports
|
|
334
|
+
|
|
335
|
+
Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
|
|
336
|
+
|
|
337
|
+
## Trilogy Syntax Reference
|
|
315
338
|
|
|
316
339
|
### Import
|
|
317
340
|
```sql
|
|
@@ -36,7 +36,6 @@ tests/test_undefined_concept.py
|
|
|
36
36
|
tests/test_user_functions.py
|
|
37
37
|
tests/test_where_clause.py
|
|
38
38
|
trilogy/__init__.py
|
|
39
|
-
trilogy/compiler.py
|
|
40
39
|
trilogy/constants.py
|
|
41
40
|
trilogy/engine.py
|
|
42
41
|
trilogy/executor.py
|
|
@@ -125,6 +124,7 @@ trilogy/dialect/config.py
|
|
|
125
124
|
trilogy/dialect/dataframe.py
|
|
126
125
|
trilogy/dialect/duckdb.py
|
|
127
126
|
trilogy/dialect/enums.py
|
|
127
|
+
trilogy/dialect/metadata.py
|
|
128
128
|
trilogy/dialect/postgres.py
|
|
129
129
|
trilogy/dialect/presto.py
|
|
130
130
|
trilogy/dialect/snowflake.py
|
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
|
|
3
3
|
from trilogy import Dialects, parse
|
|
4
4
|
from trilogy.core.models.environment import Environment
|
|
5
|
-
from trilogy.
|
|
5
|
+
from trilogy.dialect.metadata import MockResult, MockResultRow
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def test_file_parsing():
|
|
@@ -60,68 +60,82 @@ from trilogy.core.statements.author import (
|
|
|
60
60
|
RowsetDerivationStatement,
|
|
61
61
|
SelectItem,
|
|
62
62
|
SelectStatement,
|
|
63
|
+
ShowCategory,
|
|
64
|
+
ShowStatement,
|
|
65
|
+
ValidateStatement,
|
|
63
66
|
)
|
|
64
67
|
from trilogy.parsing.common import arbitrary_to_concept, arg_to_datatype
|
|
65
68
|
|
|
66
69
|
__all__ = [
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"CaseElse",
|
|
74
|
-
"AggregateWrapper",
|
|
75
|
-
"WindowItem",
|
|
76
|
-
"WindowOrder",
|
|
77
|
-
"WindowType",
|
|
78
|
-
"WindowItemOrder",
|
|
79
|
-
"WindowItemOver",
|
|
80
|
-
"DataType",
|
|
81
|
-
"StructType",
|
|
82
|
-
"ArrayType",
|
|
83
|
-
"NumericType",
|
|
84
|
-
"Grain",
|
|
85
|
-
"RowsetDerivationStatement",
|
|
86
|
-
"MapType",
|
|
87
|
-
"ListWrapper",
|
|
70
|
+
# trilogy.constants
|
|
71
|
+
"DEFAULT_NAMESPACE",
|
|
72
|
+
# trilogy.core.enums
|
|
73
|
+
"BooleanOperator",
|
|
74
|
+
"ComparisonOperator",
|
|
75
|
+
"FunctionClass",
|
|
88
76
|
"FunctionType",
|
|
77
|
+
"InfiniteFunctionArgs",
|
|
78
|
+
"Ordering",
|
|
79
|
+
"Purpose",
|
|
80
|
+
# trilogy.core.functions
|
|
89
81
|
"FunctionFactory",
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
82
|
+
# trilogy.core.models.author
|
|
83
|
+
"AggregateWrapper",
|
|
84
|
+
"CaseElse",
|
|
85
|
+
"CaseWhen",
|
|
86
|
+
"Comparison",
|
|
87
|
+
"Concept",
|
|
95
88
|
"ConceptRef",
|
|
89
|
+
"Conditional",
|
|
90
|
+
"FilterItem",
|
|
91
|
+
"Function",
|
|
92
|
+
"FunctionCallWrapper",
|
|
96
93
|
"HavingClause",
|
|
97
94
|
"MagicConstants",
|
|
98
95
|
"Metadata",
|
|
96
|
+
"MultiSelectLineage",
|
|
99
97
|
"OrderBy",
|
|
100
98
|
"OrderItem",
|
|
101
99
|
"Parenthetical",
|
|
100
|
+
"RowsetItem",
|
|
102
101
|
"SubselectComparison",
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
102
|
+
"WhereClause",
|
|
103
|
+
"WindowItem",
|
|
104
|
+
"WindowItemOrder",
|
|
105
|
+
"WindowItemOver",
|
|
106
|
+
"WindowOrder",
|
|
107
|
+
"WindowType",
|
|
108
|
+
# trilogy.core.models.core
|
|
109
|
+
"ArrayType",
|
|
110
|
+
"DataType",
|
|
111
|
+
"ListWrapper",
|
|
112
|
+
"MapType",
|
|
113
|
+
"NumericType",
|
|
114
|
+
"StructType",
|
|
115
|
+
"TraitDataType",
|
|
116
|
+
# trilogy.core.models.datasource
|
|
117
|
+
"Address",
|
|
117
118
|
"Datasource",
|
|
118
119
|
"DatasourceMetadata",
|
|
119
|
-
|
|
120
|
-
"
|
|
121
|
-
|
|
120
|
+
# trilogy.core.models.environment
|
|
121
|
+
"Environment",
|
|
122
|
+
# trilogy.core.statements.author
|
|
123
|
+
"ConceptDeclarationStatement",
|
|
124
|
+
"ConceptTransform",
|
|
122
125
|
"CopyStatement",
|
|
126
|
+
"Grain",
|
|
123
127
|
"HasUUID",
|
|
124
128
|
"ImportStatement",
|
|
125
|
-
"
|
|
126
|
-
"
|
|
129
|
+
"MultiSelectStatement",
|
|
130
|
+
"PersistStatement",
|
|
131
|
+
"RawSQLStatement",
|
|
132
|
+
"RowsetDerivationStatement",
|
|
133
|
+
"SelectItem",
|
|
134
|
+
"SelectStatement",
|
|
135
|
+
"ShowCategory",
|
|
136
|
+
"ShowStatement",
|
|
137
|
+
"ValidateStatement",
|
|
138
|
+
# trilogy.parsing.common
|
|
139
|
+
"arbitrary_to_concept",
|
|
140
|
+
"arg_to_datatype",
|
|
127
141
|
]
|
|
@@ -64,13 +64,13 @@ def datasource_to_node(input: BuildDatasource) -> str:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
class ReferenceGraph(nx.DiGraph):
|
|
67
|
-
def __init__(self, *args, **kwargs):
|
|
67
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
68
68
|
super().__init__(*args, **kwargs)
|
|
69
69
|
self.concepts: dict[str, BuildConcept] = {}
|
|
70
70
|
self.datasources: dict[str, BuildDatasource] = {}
|
|
71
71
|
self.pseudonyms: set[tuple[str, str]] = set()
|
|
72
72
|
|
|
73
|
-
def copy(self):
|
|
73
|
+
def copy(self) -> "ReferenceGraph":
|
|
74
74
|
g = ReferenceGraph()
|
|
75
75
|
g.concepts = self.concepts.copy()
|
|
76
76
|
g.datasources = self.datasources.copy()
|
|
@@ -83,7 +83,7 @@ class ReferenceGraph(nx.DiGraph):
|
|
|
83
83
|
# g.add_edges_from(self.edges(data=True))
|
|
84
84
|
return g
|
|
85
85
|
|
|
86
|
-
def remove_node(self, n):
|
|
86
|
+
def remove_node(self, n) -> None:
|
|
87
87
|
if n in self.concepts:
|
|
88
88
|
del self.concepts[n]
|
|
89
89
|
if n in self.datasources:
|
|
@@ -98,7 +98,7 @@ class ReferenceGraph(nx.DiGraph):
|
|
|
98
98
|
self.datasources[node_name] = attr["datasource"]
|
|
99
99
|
super().add_node(node_name, **attr)
|
|
100
100
|
|
|
101
|
-
def add_datasource_node(self, node_name, datasource):
|
|
101
|
+
def add_datasource_node(self, node_name, datasource) -> None:
|
|
102
102
|
self.datasources[node_name] = datasource
|
|
103
103
|
super().add_node(node_name, datasource=datasource)
|
|
104
104
|
|
|
@@ -27,7 +27,8 @@ class ExpectationType(Enum):
|
|
|
27
27
|
@dataclass
|
|
28
28
|
class ValidationTest:
|
|
29
29
|
check_type: ExpectationType
|
|
30
|
-
|
|
30
|
+
raw_query: ProcessedQuery | None = None
|
|
31
|
+
generated_query: str | None = None
|
|
31
32
|
expected: str | None = None
|
|
32
33
|
result: ModelValidationError | None = None
|
|
33
34
|
ran: bool = True
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from trilogy import Executor
|
|
1
|
+
from trilogy import Environment, Executor
|
|
2
2
|
from trilogy.core.enums import Derivation, Purpose
|
|
3
3
|
from trilogy.core.exceptions import (
|
|
4
4
|
ConceptModelValidationError,
|
|
@@ -12,64 +12,68 @@ from trilogy.core.validation.common import ExpectationType, ValidationTest, easy
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def validate_property_concept(
|
|
15
|
-
concept: BuildConcept,
|
|
15
|
+
concept: BuildConcept, exec: Executor | None = None
|
|
16
16
|
) -> list[ValidationTest]:
|
|
17
17
|
return []
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def validate_key_concept(
|
|
21
21
|
concept: BuildConcept,
|
|
22
|
+
env: Environment,
|
|
22
23
|
build_env: BuildEnvironment,
|
|
23
|
-
exec: Executor,
|
|
24
|
-
generate_only: bool = False,
|
|
24
|
+
exec: Executor | None = None,
|
|
25
25
|
):
|
|
26
26
|
results: list[ValidationTest] = []
|
|
27
|
-
seen = {}
|
|
27
|
+
seen: dict[str, int] = {}
|
|
28
28
|
for datasource in build_env.datasources.values():
|
|
29
29
|
if concept.address in [c.address for c in datasource.concepts]:
|
|
30
30
|
assignment = [
|
|
31
31
|
x for x in datasource.columns if x.concept.address == concept.address
|
|
32
32
|
][0]
|
|
33
|
+
# if it's not a partial, skip it
|
|
34
|
+
if not assignment.is_complete:
|
|
35
|
+
continue
|
|
33
36
|
type_query = easy_query(
|
|
34
37
|
concepts=[
|
|
35
38
|
# build_env.concepts[concept.address],
|
|
36
39
|
build_env.concepts[f"grain_check_{concept.safe_address}"],
|
|
37
40
|
],
|
|
38
41
|
datasource=datasource,
|
|
39
|
-
env=
|
|
42
|
+
env=env,
|
|
40
43
|
limit=1,
|
|
41
44
|
)
|
|
42
|
-
|
|
45
|
+
if exec:
|
|
46
|
+
type_sql = exec.generate_sql(type_query)[-1]
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
rows = exec.execute_raw_sql(type_sql).fetchall()
|
|
49
|
+
seen[datasource.name] = rows[0][0] if rows else 0
|
|
50
|
+
else:
|
|
46
51
|
results.append(
|
|
47
52
|
ValidationTest(
|
|
48
|
-
|
|
53
|
+
raw_query=type_query,
|
|
49
54
|
check_type=ExpectationType.ROWCOUNT,
|
|
50
55
|
expected=f"equal_max_{concept.safe_address}",
|
|
51
56
|
result=None,
|
|
52
57
|
ran=False,
|
|
53
58
|
)
|
|
54
59
|
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if generate_only:
|
|
60
|
+
|
|
61
|
+
if not exec:
|
|
58
62
|
return results
|
|
59
|
-
max_seen = max([v for v in seen.values() if v is not None], default=0)
|
|
63
|
+
max_seen: int = max([v for v in seen.values() if v is not None], default=0)
|
|
60
64
|
for datasource in build_env.datasources.values():
|
|
61
65
|
if concept.address in [c.address for c in datasource.concepts]:
|
|
62
66
|
assignment = [
|
|
63
67
|
x for x in datasource.columns if x.concept.address == concept.address
|
|
64
68
|
][0]
|
|
65
69
|
err = None
|
|
66
|
-
|
|
70
|
+
datasource_count: int = seen.get(datasource.name, 0)
|
|
71
|
+
if datasource_count < max_seen and assignment.is_complete:
|
|
67
72
|
err = DatasourceModelValidationError(
|
|
68
73
|
f"Key concept {concept.address} is missing values in datasource {datasource.name} (max cardinality in data {max_seen}, datasource has {seen[datasource.name]} values) but is not marked as partial."
|
|
69
74
|
)
|
|
70
75
|
results.append(
|
|
71
76
|
ValidationTest(
|
|
72
|
-
query=None,
|
|
73
77
|
check_type=ExpectationType.ROWCOUNT,
|
|
74
78
|
expected=str(max_seen),
|
|
75
79
|
result=err,
|
|
@@ -96,7 +100,6 @@ def validate_datasources(
|
|
|
96
100
|
return []
|
|
97
101
|
return [
|
|
98
102
|
ValidationTest(
|
|
99
|
-
query=None,
|
|
100
103
|
check_type=ExpectationType.LOGICAL,
|
|
101
104
|
expected=None,
|
|
102
105
|
result=ConceptModelValidationError(
|
|
@@ -109,14 +112,14 @@ def validate_datasources(
|
|
|
109
112
|
|
|
110
113
|
def validate_concept(
|
|
111
114
|
concept: BuildConcept,
|
|
115
|
+
env: Environment,
|
|
112
116
|
build_env: BuildEnvironment,
|
|
113
|
-
exec: Executor,
|
|
114
|
-
generate_only: bool = False,
|
|
117
|
+
exec: Executor | None = None,
|
|
115
118
|
) -> list[ValidationTest]:
|
|
116
119
|
base: list[ValidationTest] = []
|
|
117
120
|
base += validate_datasources(concept, build_env)
|
|
118
121
|
if concept.purpose == Purpose.PROPERTY:
|
|
119
|
-
base += validate_property_concept(concept
|
|
122
|
+
base += validate_property_concept(concept)
|
|
120
123
|
elif concept.purpose == Purpose.KEY:
|
|
121
|
-
base += validate_key_concept(concept, build_env, exec
|
|
124
|
+
base += validate_key_concept(concept, env, build_env, exec)
|
|
122
125
|
return base
|
|
@@ -2,7 +2,7 @@ from datetime import date, datetime
|
|
|
2
2
|
from decimal import Decimal
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from trilogy import Executor
|
|
5
|
+
from trilogy import Environment, Executor
|
|
6
6
|
from trilogy.authoring import (
|
|
7
7
|
ArrayType,
|
|
8
8
|
DataType,
|
|
@@ -61,12 +61,12 @@ def type_check(
|
|
|
61
61
|
|
|
62
62
|
def validate_datasource(
|
|
63
63
|
datasource: BuildDatasource,
|
|
64
|
+
env: Environment,
|
|
64
65
|
build_env: BuildEnvironment,
|
|
65
|
-
exec: Executor,
|
|
66
|
-
generate_only: bool = False,
|
|
66
|
+
exec: Executor | None = None,
|
|
67
67
|
) -> list[ValidationTest]:
|
|
68
68
|
results: list[ValidationTest] = []
|
|
69
|
-
# we might have merged concepts, where both
|
|
69
|
+
# we might have merged concepts, where both will map out to the same
|
|
70
70
|
unique_outputs = unique(
|
|
71
71
|
[build_env.concepts[col.concept.address] for col in datasource.columns],
|
|
72
72
|
"address",
|
|
@@ -74,18 +74,20 @@ def validate_datasource(
|
|
|
74
74
|
type_query = easy_query(
|
|
75
75
|
concepts=unique_outputs,
|
|
76
76
|
datasource=datasource,
|
|
77
|
-
env=
|
|
77
|
+
env=env,
|
|
78
78
|
limit=100,
|
|
79
79
|
)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
rows = []
|
|
82
|
-
if
|
|
82
|
+
if exec:
|
|
83
|
+
type_sql = exec.generate_sql(type_query)[-1]
|
|
83
84
|
try:
|
|
84
85
|
rows = exec.execute_raw_sql(type_sql).fetchall()
|
|
85
86
|
except Exception as e:
|
|
86
87
|
results.append(
|
|
87
88
|
ValidationTest(
|
|
88
|
-
|
|
89
|
+
raw_query=type_query,
|
|
90
|
+
generated_query=type_sql,
|
|
89
91
|
check_type=ExpectationType.LOGICAL,
|
|
90
92
|
expected="valid_sql",
|
|
91
93
|
result=DatasourceModelValidationError(
|
|
@@ -96,9 +98,10 @@ def validate_datasource(
|
|
|
96
98
|
)
|
|
97
99
|
return results
|
|
98
100
|
else:
|
|
101
|
+
|
|
99
102
|
results.append(
|
|
100
103
|
ValidationTest(
|
|
101
|
-
|
|
104
|
+
raw_query=type_query,
|
|
102
105
|
check_type=ExpectationType.LOGICAL,
|
|
103
106
|
expected="datatype_match",
|
|
104
107
|
result=None,
|
|
@@ -117,7 +120,6 @@ def validate_datasource(
|
|
|
117
120
|
cols_with_error = set()
|
|
118
121
|
for row in rows:
|
|
119
122
|
for col in datasource.columns:
|
|
120
|
-
|
|
121
123
|
actual_address = build_env.concepts[col.concept.address].safe_address
|
|
122
124
|
if actual_address in cols_with_error:
|
|
123
125
|
continue
|
|
@@ -140,7 +142,6 @@ def validate_datasource(
|
|
|
140
142
|
if failures:
|
|
141
143
|
results.append(
|
|
142
144
|
ValidationTest(
|
|
143
|
-
query=None,
|
|
144
145
|
check_type=ExpectationType.LOGICAL,
|
|
145
146
|
expected="datatype_match",
|
|
146
147
|
ran=True,
|
|
@@ -161,10 +162,10 @@ def validate_datasource(
|
|
|
161
162
|
operator=ComparisonOperator.GT,
|
|
162
163
|
),
|
|
163
164
|
)
|
|
164
|
-
if
|
|
165
|
+
if not exec:
|
|
165
166
|
results.append(
|
|
166
167
|
ValidationTest(
|
|
167
|
-
|
|
168
|
+
raw_query=query,
|
|
168
169
|
check_type=ExpectationType.ROWCOUNT,
|
|
169
170
|
expected="0",
|
|
170
171
|
result=None,
|
|
@@ -179,7 +180,8 @@ def validate_datasource(
|
|
|
179
180
|
if rows:
|
|
180
181
|
results.append(
|
|
181
182
|
ValidationTest(
|
|
182
|
-
query
|
|
183
|
+
raw_query=query,
|
|
184
|
+
generated_query=sql,
|
|
183
185
|
check_type=ExpectationType.ROWCOUNT,
|
|
184
186
|
expected="0",
|
|
185
187
|
result=DatasourceModelValidationError(
|
|
@@ -12,12 +12,12 @@ from trilogy.parsing.common import function_to_concept
|
|
|
12
12
|
|
|
13
13
|
def validate_environment(
|
|
14
14
|
env: Environment,
|
|
15
|
-
exec: Executor,
|
|
16
15
|
scope: ValidationScope = ValidationScope.ALL,
|
|
17
16
|
targets: list[str] | None = None,
|
|
18
|
-
|
|
17
|
+
exec: Executor | None = None,
|
|
19
18
|
) -> list[ValidationTest]:
|
|
20
19
|
# avoid mutating the environment for validation
|
|
20
|
+
generate_only = exec is None
|
|
21
21
|
env = env.duplicate()
|
|
22
22
|
grain_check = function_to_concept(
|
|
23
23
|
parent=Function(
|
|
@@ -51,13 +51,13 @@ def validate_environment(
|
|
|
51
51
|
for datasource in build_env.datasources.values():
|
|
52
52
|
if targets and datasource.name not in targets:
|
|
53
53
|
continue
|
|
54
|
-
results += validate_datasource(datasource, build_env, exec
|
|
54
|
+
results += validate_datasource(datasource, env, build_env, exec)
|
|
55
55
|
if scope == ValidationScope.ALL or scope == ValidationScope.CONCEPTS:
|
|
56
56
|
|
|
57
57
|
for bconcept in build_env.concepts.values():
|
|
58
58
|
if targets and bconcept.address not in targets:
|
|
59
59
|
continue
|
|
60
|
-
results += validate_concept(bconcept, build_env, exec
|
|
60
|
+
results += validate_concept(bconcept, env, build_env, exec)
|
|
61
61
|
|
|
62
62
|
# raise a nicely formatted union of all exceptions
|
|
63
63
|
exceptions: list[ModelValidationError] = [e.result for e in results if e.result]
|
|
@@ -76,6 +76,7 @@ from trilogy.core.statements.author import (
|
|
|
76
76
|
)
|
|
77
77
|
from trilogy.core.statements.execute import (
|
|
78
78
|
PROCESSED_STATEMENT_TYPES,
|
|
79
|
+
ProcessedCopyStatement,
|
|
79
80
|
ProcessedQuery,
|
|
80
81
|
ProcessedQueryPersist,
|
|
81
82
|
ProcessedRawSQLStatement,
|
|
@@ -345,6 +346,7 @@ class BaseDialect:
|
|
|
345
346
|
COMPLEX_DATATYPE_MAP = COMPLEX_DATATYPE_MAP
|
|
346
347
|
UNNEST_MODE = UnnestMode.CROSS_APPLY
|
|
347
348
|
GROUP_MODE = GroupMode.AUTO
|
|
349
|
+
EXPLAIN_KEYWORD = "EXPLAIN"
|
|
348
350
|
|
|
349
351
|
def __init__(self, rendering: Rendering | None = None):
|
|
350
352
|
self.rendering = rendering or CONFIG.rendering
|
|
@@ -1135,7 +1137,13 @@ class BaseDialect:
|
|
|
1135
1137
|
query: PROCESSED_STATEMENT_TYPES,
|
|
1136
1138
|
) -> str:
|
|
1137
1139
|
if isinstance(query, ProcessedShowStatement):
|
|
1138
|
-
return ";\n".join(
|
|
1140
|
+
return ";\n".join(
|
|
1141
|
+
[
|
|
1142
|
+
f'{self.EXPLAIN_KEYWORD} {self.compile_statement(x)}'
|
|
1143
|
+
for x in query.output_values
|
|
1144
|
+
if isinstance(x, (ProcessedQuery, ProcessedCopyStatement))
|
|
1145
|
+
]
|
|
1146
|
+
)
|
|
1139
1147
|
elif isinstance(query, ProcessedRawSQLStatement):
|
|
1140
1148
|
return query.text
|
|
1141
1149
|
|