pytrilogy 0.0.3.95__tar.gz → 0.0.3.97__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.97}/PKG-INFO +44 -7
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/README.md +43 -6
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97/pytrilogy.egg-info}/PKG-INFO +44 -7
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pytrilogy.egg-info/SOURCES.txt +2 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_executor.py +1 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_parsing.py +49 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/authoring/__init__.py +59 -45
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/constants.py +1 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/enums.py +9 -0
- pytrilogy-0.0.3.97/trilogy/core/exceptions.py +105 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/graph_models.py +4 -4
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/statements/execute.py +2 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/validation/common.py +55 -3
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/validation/concept.py +40 -25
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/validation/datasource.py +38 -34
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/validation/environment.py +4 -3
- pytrilogy-0.0.3.97/trilogy/core/validation/fix.py +106 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/base.py +10 -1
- pytrilogy-0.0.3.97/trilogy/dialect/metadata.py +233 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/executor.py +33 -163
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/parse_engine.py +8 -6
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/render.py +30 -3
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/trilogy.lark +7 -4
- pytrilogy-0.0.3.95/trilogy/compiler.py +0 -0
- pytrilogy-0.0.3.95/trilogy/core/exceptions.py +0 -51
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/setup.cfg +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/setup.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/discovery_loop.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/discovery_node_factory.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.97}/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.97
|
|
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,44 @@ 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
|
+
## MCP/Server
|
|
338
|
+
|
|
339
|
+
Trilogy is straightforward to run as a server/MCP server; the former to generate SQL on demand and integrate into other tools, and MCP
|
|
340
|
+
for full interactive query loops.
|
|
341
|
+
|
|
342
|
+
This makes it easy to integrate Trilogy into existing tools or workflows.
|
|
343
|
+
|
|
344
|
+
You can see examples of both use cases in the trilogy-studio codebase [here](https://github.com/trilogy-data/trilogy-studio-core)
|
|
345
|
+
and install and run an MCP server directly with that codebase.
|
|
346
|
+
|
|
347
|
+
If you're interested in a more fleshed out standalone server or MCP server, please open an issue and we'll prioritize it!
|
|
348
|
+
|
|
349
|
+
## Trilogy Syntax Reference
|
|
350
|
+
|
|
351
|
+
Not exhaustive - see [documentation](https://trilogydata.dev/) for more details.
|
|
315
352
|
|
|
316
353
|
### Import
|
|
317
354
|
```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,44 @@ 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
|
+
## MCP/Server
|
|
300
|
+
|
|
301
|
+
Trilogy is straightforward to run as a server/MCP server; the former to generate SQL on demand and integrate into other tools, and MCP
|
|
302
|
+
for full interactive query loops.
|
|
303
|
+
|
|
304
|
+
This makes it easy to integrate Trilogy into existing tools or workflows.
|
|
305
|
+
|
|
306
|
+
You can see examples of both use cases in the trilogy-studio codebase [here](https://github.com/trilogy-data/trilogy-studio-core)
|
|
307
|
+
and install and run an MCP server directly with that codebase.
|
|
308
|
+
|
|
309
|
+
If you're interested in a more fleshed out standalone server or MCP server, please open an issue and we'll prioritize it!
|
|
310
|
+
|
|
311
|
+
## Trilogy Syntax Reference
|
|
312
|
+
|
|
313
|
+
Not exhaustive - see [documentation](https://trilogydata.dev/) for more details.
|
|
277
314
|
|
|
278
315
|
### Import
|
|
279
316
|
```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.97
|
|
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,44 @@ 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
|
+
## MCP/Server
|
|
338
|
+
|
|
339
|
+
Trilogy is straightforward to run as a server/MCP server; the former to generate SQL on demand and integrate into other tools, and MCP
|
|
340
|
+
for full interactive query loops.
|
|
341
|
+
|
|
342
|
+
This makes it easy to integrate Trilogy into existing tools or workflows.
|
|
343
|
+
|
|
344
|
+
You can see examples of both use cases in the trilogy-studio codebase [here](https://github.com/trilogy-data/trilogy-studio-core)
|
|
345
|
+
and install and run an MCP server directly with that codebase.
|
|
346
|
+
|
|
347
|
+
If you're interested in a more fleshed out standalone server or MCP server, please open an issue and we'll prioritize it!
|
|
348
|
+
|
|
349
|
+
## Trilogy Syntax Reference
|
|
350
|
+
|
|
351
|
+
Not exhaustive - see [documentation](https://trilogydata.dev/) for more details.
|
|
315
352
|
|
|
316
353
|
### Import
|
|
317
354
|
```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
|
|
@@ -117,6 +116,7 @@ trilogy/core/validation/common.py
|
|
|
117
116
|
trilogy/core/validation/concept.py
|
|
118
117
|
trilogy/core/validation/datasource.py
|
|
119
118
|
trilogy/core/validation/environment.py
|
|
119
|
+
trilogy/core/validation/fix.py
|
|
120
120
|
trilogy/dialect/__init__.py
|
|
121
121
|
trilogy/dialect/base.py
|
|
122
122
|
trilogy/dialect/bigquery.py
|
|
@@ -125,6 +125,7 @@ trilogy/dialect/config.py
|
|
|
125
125
|
trilogy/dialect/dataframe.py
|
|
126
126
|
trilogy/dialect/duckdb.py
|
|
127
127
|
trilogy/dialect/enums.py
|
|
128
|
+
trilogy/dialect/metadata.py
|
|
128
129
|
trilogy/dialect/postgres.py
|
|
129
130
|
trilogy/dialect/presto.py
|
|
130
131
|
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():
|
|
@@ -321,6 +321,55 @@ def test_the_comments():
|
|
|
321
321
|
assert rendered == "null"
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
def test_the_comment_multiline():
|
|
325
|
+
env, parsed = parse_text(
|
|
326
|
+
"""const
|
|
327
|
+
# comment here?
|
|
328
|
+
order_id <- 4; # this is the order id
|
|
329
|
+
# order ids are important
|
|
330
|
+
|
|
331
|
+
SELECT
|
|
332
|
+
# TOOD - add in more columns?
|
|
333
|
+
order_id
|
|
334
|
+
WHERE
|
|
335
|
+
# order_id should not be null
|
|
336
|
+
order_id
|
|
337
|
+
# in this comp
|
|
338
|
+
is not
|
|
339
|
+
null; # nulls are the worst
|
|
340
|
+
|
|
341
|
+
"""
|
|
342
|
+
)
|
|
343
|
+
parsed[-1]
|
|
344
|
+
assert env.concepts["order_id"].metadata.description is not None
|
|
345
|
+
assert " this is the order id" in env.concepts["order_id"].metadata.description
|
|
346
|
+
assert " order ids are important" in env.concepts["order_id"].metadata.description
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def test_the_comment_multiline_enter():
|
|
350
|
+
# we should not associate it as a description if there is a newline
|
|
351
|
+
env, parsed = parse_text(
|
|
352
|
+
"""const
|
|
353
|
+
# comment here?
|
|
354
|
+
order_id <- 4;
|
|
355
|
+
# this is the order id
|
|
356
|
+
# order ids are important
|
|
357
|
+
|
|
358
|
+
SELECT
|
|
359
|
+
# TOOD - add in more columns?
|
|
360
|
+
order_id
|
|
361
|
+
WHERE
|
|
362
|
+
# order_id should not be null
|
|
363
|
+
order_id
|
|
364
|
+
# in this comp
|
|
365
|
+
is not
|
|
366
|
+
null; # nulls are the worst
|
|
367
|
+
|
|
368
|
+
"""
|
|
369
|
+
)
|
|
370
|
+
assert env.concepts["order_id"].metadata.description is None
|
|
371
|
+
|
|
372
|
+
|
|
324
373
|
def test_purpose_nesting():
|
|
325
374
|
env, parsed = parse_text(
|
|
326
375
|
"""key year int;
|
|
@@ -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
|
]
|
|
@@ -82,6 +82,15 @@ class Modifier(Enum):
|
|
|
82
82
|
return Modifier.NULLABLE
|
|
83
83
|
return super()._missing_(value=strval.capitalize())
|
|
84
84
|
|
|
85
|
+
def __lt__(self, other):
|
|
86
|
+
order = [
|
|
87
|
+
Modifier.HIDDEN,
|
|
88
|
+
Modifier.PARTIAL,
|
|
89
|
+
Modifier.NULLABLE,
|
|
90
|
+
Modifier.OPTIONAL,
|
|
91
|
+
]
|
|
92
|
+
return order.index(self) < order.index(other)
|
|
93
|
+
|
|
85
94
|
|
|
86
95
|
class JoinType(Enum):
|
|
87
96
|
INNER = "inner"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, List, Sequence
|
|
3
|
+
|
|
4
|
+
from trilogy.core.enums import Modifier
|
|
5
|
+
from trilogy.core.models.core import (
|
|
6
|
+
ArrayType,
|
|
7
|
+
DataType,
|
|
8
|
+
MapType,
|
|
9
|
+
NumericType,
|
|
10
|
+
StructType,
|
|
11
|
+
TraitDataType,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UndefinedConceptException(Exception):
|
|
16
|
+
def __init__(self, message, suggestions: List[str]):
|
|
17
|
+
super().__init__(self, message)
|
|
18
|
+
self.message = message
|
|
19
|
+
self.suggestions = suggestions
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FrozenEnvironmentException(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InvalidSyntaxException(Exception):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class UnresolvableQueryException(Exception):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NoDatasourceException(UnresolvableQueryException):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ModelValidationError(Exception):
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
message,
|
|
42
|
+
children: Sequence["ModelValidationError"] | None = None,
|
|
43
|
+
**kwargs,
|
|
44
|
+
):
|
|
45
|
+
super().__init__(self, message, **kwargs)
|
|
46
|
+
self.message = message
|
|
47
|
+
self.children = children
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DatasourceModelValidationError(ModelValidationError):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DatasourceGrainValidationError(DatasourceModelValidationError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class DatasourceColumnBindingData:
|
|
60
|
+
address: str
|
|
61
|
+
value: Any
|
|
62
|
+
value_type: (
|
|
63
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
64
|
+
)
|
|
65
|
+
value_modifiers: List[Modifier]
|
|
66
|
+
actual_type: (
|
|
67
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
68
|
+
)
|
|
69
|
+
actual_modifiers: List[Modifier]
|
|
70
|
+
|
|
71
|
+
def format_failure(self):
|
|
72
|
+
return f"Concept {self.address} value '{self.value}' with type {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
|
|
73
|
+
|
|
74
|
+
def is_modifier_issue(self) -> bool:
|
|
75
|
+
return len(self.value_modifiers) > 0 and any(
|
|
76
|
+
[x not in self.actual_modifiers for x in self.value_modifiers]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def is_type_issue(self) -> bool:
|
|
80
|
+
return self.value_type != self.actual_type
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DatasourceColumnBindingError(DatasourceModelValidationError):
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
address: str,
|
|
87
|
+
errors: list[DatasourceColumnBindingData],
|
|
88
|
+
message: str | None = None,
|
|
89
|
+
):
|
|
90
|
+
if not message:
|
|
91
|
+
message = f"Datasource {address} failed validation. Found rows that do not conform to types: {[failure.format_failure() for failure in errors]}"
|
|
92
|
+
super().__init__(message)
|
|
93
|
+
self.errors = errors
|
|
94
|
+
self.dataset_address = address
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ConceptModelValidationError(ModelValidationError):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AmbiguousRelationshipResolutionException(UnresolvableQueryException):
|
|
102
|
+
def __init__(self, message, parents: List[set[str]]):
|
|
103
|
+
super().__init__(self, message)
|
|
104
|
+
self.message = message
|
|
105
|
+
self.parents = parents
|
|
@@ -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
|
|