pytrilogy 0.0.2.47__tar.gz → 0.0.2.49__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.

Files changed (112) hide show
  1. {pytrilogy-0.0.2.47/pytrilogy.egg-info → pytrilogy-0.0.2.49}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pyproject.toml +1 -0
  3. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49/pytrilogy.egg-info}/PKG-INFO +1 -1
  4. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pytrilogy.egg-info/SOURCES.txt +2 -0
  5. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_derived_concepts.py +1 -4
  6. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_discovery_nodes.py +2 -2
  7. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_enums.py +1 -1
  8. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_environment.py +19 -3
  9. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_executor.py +2 -1
  10. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_functions.py +6 -5
  11. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_imports.py +2 -1
  12. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_metadata.py +2 -1
  13. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_models.py +16 -15
  14. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_multi_join_assignments.py +1 -1
  15. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_parse_engine.py +6 -4
  16. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_parsing.py +15 -20
  17. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_partial_handling.py +8 -10
  18. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_query_processing.py +4 -8
  19. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_select.py +33 -4
  20. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_statements.py +3 -2
  21. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_undefined_concept.py +1 -1
  22. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_where_clause.py +2 -2
  23. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/__init__.py +2 -2
  24. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/constants.py +4 -2
  25. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/enums.py +7 -1
  26. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/env_processor.py +1 -2
  27. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/environment_helpers.py +5 -5
  28. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/functions.py +11 -10
  29. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/internal.py +2 -3
  30. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/models.py +449 -393
  31. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimization.py +37 -21
  32. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimizations/base_optimization.py +6 -6
  33. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimizations/inline_constant.py +7 -4
  34. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimizations/inline_datasource.py +14 -5
  35. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimizations/predicate_pushdown.py +20 -10
  36. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/concept_strategies_v3.py +43 -24
  37. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/graph_utils.py +2 -3
  38. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/__init__.py +7 -5
  39. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/basic_node.py +4 -4
  40. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/common.py +10 -11
  41. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/filter_node.py +7 -9
  42. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/group_node.py +10 -11
  43. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/multiselect_node.py +10 -12
  44. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/node_merge_node.py +7 -9
  45. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/rowset_node.py +36 -15
  46. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/select_merge_node.py +11 -10
  47. pytrilogy-0.0.2.49/trilogy/core/processing/node_generators/union_node.py +75 -0
  48. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/unnest_node.py +2 -3
  49. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/window_node.py +3 -4
  50. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/__init__.py +9 -5
  51. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/base_node.py +45 -13
  52. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/filter_node.py +3 -4
  53. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/group_node.py +17 -13
  54. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/merge_node.py +14 -12
  55. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/select_node_v2.py +13 -9
  56. pytrilogy-0.0.2.49/trilogy/core/processing/nodes/union_node.py +50 -0
  57. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/unnest_node.py +2 -3
  58. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/nodes/window_node.py +2 -3
  59. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/utility.py +38 -41
  60. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/query_processor.py +71 -51
  61. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/base.py +95 -53
  62. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/bigquery.py +2 -3
  63. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/common.py +5 -4
  64. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/config.py +0 -2
  65. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/duckdb.py +2 -2
  66. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/enums.py +5 -5
  67. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/postgres.py +2 -2
  68. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/presto.py +3 -4
  69. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/snowflake.py +2 -2
  70. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/sql_server.py +3 -4
  71. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/engine.py +2 -1
  72. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/executor.py +43 -30
  73. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/hooks/base_hook.py +5 -4
  74. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/hooks/graph_hook.py +2 -1
  75. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/hooks/query_debugger.py +18 -8
  76. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/common.py +15 -20
  77. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/parse_engine.py +125 -88
  78. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/render.py +32 -35
  79. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/trilogy.lark +8 -1
  80. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/scripts/trilogy.py +6 -4
  81. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/utility.py +1 -1
  82. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/LICENSE.md +0 -0
  83. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/README.md +0 -0
  84. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pytrilogy.egg-info/dependency_links.txt +0 -0
  85. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pytrilogy.egg-info/entry_points.txt +0 -0
  86. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pytrilogy.egg-info/requires.txt +0 -0
  87. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/pytrilogy.egg-info/top_level.txt +0 -0
  88. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/setup.cfg +0 -0
  89. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/setup.py +0 -0
  90. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_datatypes.py +1 -1
  91. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_declarations.py +0 -0
  92. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/tests/test_show.py +1 -1
  93. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/compiler.py +0 -0
  94. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/__init__.py +0 -0
  95. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/constants.py +0 -0
  96. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/ergonomics.py +0 -0
  97. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/exceptions.py +0 -0
  98. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/graph_models.py +0 -0
  99. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/optimizations/__init__.py +1 -1
  100. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/__init__.py +0 -0
  101. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/group_to_node.py +5 -5
  102. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/core/processing/node_generators/select_node.py +5 -5
  103. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/dialect/__init__.py +0 -0
  104. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/hooks/__init__.py +0 -0
  105. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/metadata/__init__.py +0 -0
  106. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parser.py +0 -0
  107. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/__init__.py +0 -0
  108. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/config.py +0 -0
  109. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/exceptions.py +0 -0
  110. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/parsing/helpers.py +0 -0
  111. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/py.typed +0 -0
  112. {pytrilogy-0.0.2.47 → pytrilogy-0.0.2.49}/trilogy/scripts/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.47
3
+ Version: 0.0.2.49
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -8,3 +8,4 @@ log_cli_level="INFO"
8
8
  [tool.ruff]
9
9
  # Allow lines to be as long as 200 characters.
10
10
  line-length = 200
11
+ lint.extend-select = ["I"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.47
3
+ Version: 0.0.2.49
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -70,6 +70,7 @@ trilogy/core/processing/node_generators/node_merge_node.py
70
70
  trilogy/core/processing/node_generators/rowset_node.py
71
71
  trilogy/core/processing/node_generators/select_merge_node.py
72
72
  trilogy/core/processing/node_generators/select_node.py
73
+ trilogy/core/processing/node_generators/union_node.py
73
74
  trilogy/core/processing/node_generators/unnest_node.py
74
75
  trilogy/core/processing/node_generators/window_node.py
75
76
  trilogy/core/processing/nodes/__init__.py
@@ -78,6 +79,7 @@ trilogy/core/processing/nodes/filter_node.py
78
79
  trilogy/core/processing/nodes/group_node.py
79
80
  trilogy/core/processing/nodes/merge_node.py
80
81
  trilogy/core/processing/nodes/select_node_v2.py
82
+ trilogy/core/processing/nodes/union_node.py
81
83
  trilogy/core/processing/nodes/unnest_node.py
82
84
  trilogy/core/processing/nodes/window_node.py
83
85
  trilogy/dialect/__init__.py
@@ -33,9 +33,7 @@ def test_filtering_where_on_derived_aggregate(test_environment):
33
33
  )
34
34
  except Exception as e:
35
35
  exception = True
36
- assert str(e).startswith(
37
- "Cannot reference an aggregate derived in the select (local.filtered_cst) in the same statement where clause"
38
- )
36
+ assert "Undefined concept: local.filtered_cst" in str(e)
39
37
  assert exception, "should have an exception"
40
38
 
41
39
 
@@ -68,7 +66,6 @@ def test_filtering_having_on_unincluded_value(test_environment):
68
66
 
69
67
 
70
68
  def test_filtering_valid(test_environment):
71
-
72
69
  env, _ = parse(
73
70
  """key x int;
74
71
  property x.cost float;
@@ -1,10 +1,10 @@
1
+ from trilogy.core.constants import ALL_ROWS_CONCEPT, INTERNAL_NAMESPACE
2
+ from trilogy.core.models import Environment, Grain
1
3
  from trilogy.core.processing.concept_strategies_v3 import (
2
4
  GroupNode,
3
5
  search_concepts,
4
6
  )
5
7
  from trilogy.core.processing.node_generators import gen_group_node
6
- from trilogy.core.models import Environment, Grain
7
- from trilogy.core.constants import INTERNAL_NAMESPACE, ALL_ROWS_CONCEPT
8
8
 
9
9
 
10
10
  def test_group_node(test_environment, test_environment_graph):
@@ -1,4 +1,4 @@
1
- from trilogy.core.enums import BooleanOperator, Boolean
1
+ from trilogy.core.enums import Boolean, BooleanOperator
2
2
 
3
3
 
4
4
  def test_boolean_operator():
@@ -1,7 +1,9 @@
1
- from trilogy.core.models import Environment
2
1
  from pathlib import Path
2
+
3
+ from trilogy import Dialects
3
4
  from trilogy.core.enums import Modifier
4
5
  from trilogy.core.exceptions import UndefinedConceptException
6
+ from trilogy.core.models import Environment
5
7
 
6
8
 
7
9
  def test_environment_serialization(test_environment: Environment):
@@ -18,14 +20,12 @@ def test_environment_serialization(test_environment: Environment):
18
20
 
19
21
 
20
22
  def test_environment_from_path():
21
-
22
23
  env = Environment.from_file(Path(__file__).parent / "test_env.preql")
23
24
 
24
25
  assert "local.id" in env.concepts
25
26
 
26
27
 
27
28
  def test_environment_invalid():
28
-
29
29
  env = Environment()
30
30
  env.concepts.fail_on_missing = False
31
31
  x = env.concepts["abc"]
@@ -85,3 +85,19 @@ key order_id int;
85
85
  assert x.modifiers == [Modifier.PARTIAL]
86
86
  found = True
87
87
  assert found
88
+
89
+
90
+ def test_environment_select_promotion():
91
+ x = Dialects.DUCK_DB.default_executor()
92
+
93
+ results = x.execute_query(
94
+ """
95
+ const x <- 6;
96
+
97
+ select x+2 as y;
98
+
99
+ select y;
100
+ """
101
+ ).fetchall()
102
+
103
+ assert results[0].y == 8
@@ -1,6 +1,7 @@
1
- from trilogy import Dialects, parse, Environment
2
1
  from pathlib import Path
3
2
 
3
+ from trilogy import Dialects, Environment, parse
4
+
4
5
 
5
6
  def test_file_parsing():
6
7
  target = Path(__file__).parent / "test_env.preql"
@@ -1,18 +1,19 @@
1
1
  # from trilogy.compiler import compile
2
+ from logging import INFO
3
+
2
4
  from pytest import raises
3
5
 
6
+ from trilogy.constants import logger
7
+ from trilogy.core.enums import Purpose, PurposeLineage
4
8
  from trilogy.core.exceptions import InvalidSyntaxException
5
- from trilogy.core.models import DataType, SelectStatement, ListType, Environment
9
+ from trilogy.core.models import DataType, Environment, ListType, SelectStatement
6
10
  from trilogy.core.query_processor import process_query
7
11
  from trilogy.dialect.base import BaseDialect
8
12
  from trilogy.dialect.bigquery import BigqueryDialect
9
13
  from trilogy.dialect.duckdb import DuckDBDialect
10
- from trilogy.dialect.sql_server import SqlServerDialect
11
14
  from trilogy.dialect.snowflake import SnowflakeDialect
15
+ from trilogy.dialect.sql_server import SqlServerDialect
12
16
  from trilogy.parser import parse
13
- from logging import INFO
14
- from trilogy.constants import logger
15
- from trilogy.core.enums import PurposeLineage, Purpose
16
17
 
17
18
  logger.setLevel(INFO)
18
19
 
@@ -1,6 +1,7 @@
1
- from trilogy import Environment
2
1
  from pathlib import Path
3
2
 
3
+ from trilogy import Environment
4
+
4
5
 
5
6
  def test_multi_environment():
6
7
  basic = Environment()
@@ -1,6 +1,7 @@
1
- from trilogy import parse, Environment
2
1
  from pathlib import Path
3
2
 
3
+ from trilogy import Environment, parse
4
+
4
5
 
5
6
  def test_metadata():
6
7
  env, _ = parse(
@@ -1,25 +1,26 @@
1
- from trilogy.core.enums import BooleanOperator, Purpose, JoinType, ComparisonOperator
1
+ from copy import deepcopy
2
+
3
+ from trilogy import parse
4
+ from trilogy.core.enums import BooleanOperator, ComparisonOperator, JoinType, Purpose
2
5
  from trilogy.core.models import (
3
6
  CTE,
4
- Grain,
5
- QueryDatasource,
6
- Conditional,
7
- SelectStatement,
8
- Environment,
9
7
  Address,
10
- UndefinedConcept,
8
+ AggregateWrapper,
11
9
  BaseJoin,
12
10
  Comparison,
13
- Join,
14
- CTEConceptPair,
15
11
  Concept,
16
- AggregateWrapper,
12
+ Conditional,
13
+ CTEConceptPair,
14
+ DataType,
15
+ Environment,
16
+ Grain,
17
+ Join,
18
+ QueryDatasource,
17
19
  RowsetItem,
20
+ SelectStatement,
18
21
  TupleWrapper,
19
- DataType,
22
+ UndefinedConcept,
20
23
  )
21
- from trilogy import parse
22
- from copy import deepcopy
23
24
 
24
25
 
25
26
  def test_cte_merge(test_environment, test_environment_graph):
@@ -162,9 +163,9 @@ def test_undefined(test_environment: Environment):
162
163
  environment=test_environment.concepts,
163
164
  )
164
165
 
165
- y = x.with_select_context(Grain(components=[test_environment.concepts["order_id"]]))
166
+ # y = x.with_select_context({}, Grain(components=[test_environment.concepts["order_id"]]), test_environment)
166
167
 
167
- assert y.grain == Grain(components=[test_environment.concepts["order_id"]])
168
+ # assert y.grain == Grain(components=[test_environment.concepts["order_id"]])
168
169
 
169
170
  z = x.with_default_grain()
170
171
 
@@ -1,5 +1,5 @@
1
1
  # from trilogy.compiler import compile
2
- from trilogy.core.models import SelectStatement, Grain
2
+ from trilogy.core.models import Grain, SelectStatement
3
3
  from trilogy.core.query_processor import process_query
4
4
  from trilogy.parser import parse
5
5
 
@@ -1,7 +1,8 @@
1
- from trilogy.parsing.parse_engine import ParseToObjects, PARSER, unpack_visit_error
1
+ from pytest import raises
2
+
2
3
  from trilogy import Environment
3
4
  from trilogy.core.exceptions import UndefinedConceptException
4
- from pytest import raises
5
+ from trilogy.parsing.parse_engine import PARSER, ParseToObjects, unpack_visit_error
5
6
 
6
7
  TEXT = """
7
8
  const a <- 1;
@@ -18,10 +19,11 @@ def test_parser():
18
19
  x = ParseToObjects(environment=env)
19
20
  x.environment.concepts.fail_on_missing = False
20
21
  x.set_text(TEXT)
21
- tokens = PARSER.parse(TEXT)
22
- x.transform(tokens)
22
+
23
23
  failed = False
24
24
  try:
25
+ tokens = PARSER.parse(TEXT)
26
+ x.transform(tokens)
25
27
  x.hydrate_missing()
26
28
  except Exception as e:
27
29
  failed = True
@@ -1,23 +1,22 @@
1
- from trilogy.core.enums import Purpose, ComparisonOperator
1
+ from trilogy import Dialects
2
+ from trilogy.constants import MagicConstants
3
+ from trilogy.core.enums import BooleanOperator, ComparisonOperator, Purpose
4
+ from trilogy.core.functions import argument_to_purpose, function_args_to_output_purpose
2
5
  from trilogy.core.models import (
6
+ Comparison,
7
+ Datasource,
3
8
  DataType,
9
+ Environment,
4
10
  ProcessedQuery,
5
- ShowStatement,
6
11
  SelectStatement,
7
- Environment,
8
- Comparison,
12
+ ShowStatement,
9
13
  TupleWrapper,
10
- Datasource,
11
14
  )
12
- from trilogy.core.functions import argument_to_purpose, function_args_to_output_purpose
15
+ from trilogy.dialect.base import BaseDialect
13
16
  from trilogy.parsing.parse_engine import (
14
17
  arg_to_datatype,
15
18
  parse_text,
16
19
  )
17
- from trilogy.constants import MagicConstants
18
- from trilogy.dialect.base import BaseDialect
19
- from trilogy.core.enums import BooleanOperator
20
- from trilogy import Dialects
21
20
 
22
21
 
23
22
  def test_in():
@@ -178,7 +177,9 @@ select
178
177
  """
179
178
  )
180
179
 
181
- for name in ["name_alphabetical", "name_alphabetical_2"]:
180
+ for name in [
181
+ "name_alphabetical",
182
+ ]:
182
183
  assert f"local.{name}" in env.concepts
183
184
  assert env.concepts[name].purpose == Purpose.PROPERTY
184
185
  assert env.concepts[name].keys == (env.concepts["id"],)
@@ -206,7 +207,6 @@ select
206
207
 
207
208
 
208
209
  def test_output_purpose():
209
-
210
210
  env, parsed = parse_text(
211
211
  """key id int;
212
212
  property id.name string;
@@ -219,8 +219,10 @@ rowset test<- select
219
219
  row_number id order by name asc -> name_alphabetical_2
220
220
  ;
221
221
 
222
+ auto test_name_count <- count(test.name);
223
+
222
224
  select
223
- count( test.name ) -> test_name_count;
225
+ test_name_count;
224
226
  """
225
227
  )
226
228
  # assert output_purpose == Purpose.METRIC
@@ -274,7 +276,6 @@ def test_the_comments():
274
276
 
275
277
 
276
278
  def test_purpose_nesting():
277
-
278
279
  env, parsed = parse_text(
279
280
  """key year int;
280
281
  """
@@ -434,7 +435,6 @@ const labels <- '';
434
435
 
435
436
 
436
437
  def test_struct_attr_access():
437
-
438
438
  text = """
439
439
  const labels <- struct(a=1, b=2, c=3);
440
440
 
@@ -459,7 +459,6 @@ select
459
459
 
460
460
 
461
461
  def test_datasource_colon():
462
-
463
462
  text = """
464
463
  key x int;
465
464
  key y int;
@@ -502,7 +501,6 @@ select x;
502
501
 
503
502
 
504
503
  def test_datasource_where_equivalent():
505
-
506
504
  text = """
507
505
  key x int;
508
506
  key y int;
@@ -524,7 +522,6 @@ address `abc:def`
524
522
 
525
523
 
526
524
  def test_datasource_quoted():
527
-
528
525
  text = """
529
526
  key x int;
530
527
  key y int;
@@ -546,7 +543,6 @@ address `abc:def`
546
543
 
547
544
 
548
545
  def test_datasource_from_persist():
549
-
550
546
  text = """
551
547
  key x int;
552
548
  key y int;
@@ -575,7 +571,6 @@ where y>10;
575
571
 
576
572
 
577
573
  def test_filter_concise():
578
-
579
574
  text = """
580
575
  key x int;
581
576
  key y int;
@@ -1,23 +1,21 @@
1
- from trilogy import Executor, Dialects
2
-
3
1
  # from trilogy.core.models import Environment
4
2
  from sqlalchemy import create_engine
3
+
4
+ from trilogy import Dialects, Executor
5
+ from trilogy.core.enums import Purpose
5
6
  from trilogy.core.models import (
6
- DataType,
7
- Datasource,
8
- Concept,
9
7
  ColumnAssignment,
8
+ Concept,
9
+ Datasource,
10
+ DataType,
10
11
  Environment,
11
12
  )
12
- from trilogy.core.enums import Purpose
13
-
14
-
15
- from trilogy.core.query_processor import generate_graph
16
- from trilogy.core.processing.nodes import MergeNode
17
13
  from trilogy.core.processing.concept_strategies_v3 import search_concepts
18
14
  from trilogy.core.processing.node_generators import (
19
15
  gen_filter_node,
20
16
  )
17
+ from trilogy.core.processing.nodes import MergeNode
18
+ from trilogy.core.query_processor import generate_graph
21
19
  from trilogy.hooks.query_debugger import DebuggingHook
22
20
 
23
21
 
@@ -1,6 +1,6 @@
1
- from trilogy.core.models import SelectStatement, QueryDatasource, Environment, Grain
1
+ from trilogy.core.models import Environment, Grain, QueryDatasource, SelectStatement
2
2
  from trilogy.core.processing.concept_strategies_v3 import search_concepts
3
- from trilogy.core.query_processor import process_query, get_query_datasources
3
+ from trilogy.core.query_processor import get_query_datasources, process_query
4
4
 
5
5
 
6
6
  def test_direct_select(test_environment, test_environment_graph):
@@ -130,9 +130,7 @@ def test_join_aggregate(test_environment: Environment, test_environment_graph):
130
130
 
131
131
  def test_query_aggregation(test_environment, test_environment_graph):
132
132
  select = SelectStatement(selection=[test_environment.concepts["total_revenue"]])
133
- datasource = get_query_datasources(
134
- environment=test_environment, graph=test_environment_graph, statement=select
135
- )
133
+ datasource = get_query_datasources(environment=test_environment, statement=select)
136
134
 
137
135
  assert {datasource.identifier} == {"revenue_at_local_order_id_at_abstract"}
138
136
  check = datasource
@@ -150,9 +148,7 @@ def test_query_datasources(test_environment, test_environment_graph):
150
148
  test_environment.concepts["total_revenue"],
151
149
  ]
152
150
  )
153
- get_query_datasources(
154
- environment=test_environment, graph=test_environment_graph, statement=select
155
- )
151
+ get_query_datasources(environment=test_environment, statement=select)
156
152
 
157
153
 
158
154
  def test_full_query(test_environment, test_environment_graph):
@@ -1,6 +1,6 @@
1
1
  # from trilogy.compiler import compile
2
- from trilogy.core.models import Grain
3
- from trilogy.core.models import SelectStatement
2
+ from trilogy import Dialects
3
+ from trilogy.core.models import Grain, SelectStatement
4
4
  from trilogy.core.query_processor import process_query
5
5
  from trilogy.dialect.bigquery import BigqueryDialect
6
6
  from trilogy.parser import parse
@@ -32,6 +32,7 @@ datasource users (
32
32
  address `bigquery-public-data.stackoverflow.users`
33
33
  ;
34
34
 
35
+ auto post_count <- count(post_id);
35
36
 
36
37
  """
37
38
  env, parsed = parse(declarations)
@@ -40,7 +41,7 @@ datasource users (
40
41
  # a comment
41
42
  user_id,
42
43
  about_me,
43
- count(post_id)->post_count
44
+ post_count
44
45
  ;"""
45
46
  env, parse_one = parse(q1, environment=env)
46
47
 
@@ -54,7 +55,10 @@ datasource users (
54
55
  env, parse_two = parse(q2, environment=env)
55
56
 
56
57
  select: SelectStatement = parse_two[-1]
57
- assert select.grain == Grain(components=[env.concepts["about_me"]])
58
+ assert (
59
+ select.grain.components
60
+ == Grain(components=[env.concepts["about_me"]]).components
61
+ )
58
62
 
59
63
 
60
64
  def test_double_aggregate():
@@ -147,3 +151,28 @@ def test_having_without_select():
147
151
  assert "that is not in the select projection in the HAVING clause" in str(e)
148
152
  failed = True
149
153
  assert failed
154
+
155
+
156
+ def test_local_select_concepts():
157
+ q1 = """
158
+
159
+ key id int;
160
+ datasource local (
161
+
162
+ id: id
163
+ )
164
+ grain (id)
165
+ query '''
166
+
167
+ SELECT 1 as id
168
+ ''';
169
+
170
+
171
+ select id + 2 as three;
172
+
173
+ """
174
+
175
+ env, parsed = parse(q1)
176
+
177
+ result = Dialects.DUCK_DB.default_executor(environment=env).execute_text(q1)[-1]
178
+ assert result.fetchone().three == 3
@@ -1,7 +1,8 @@
1
- from trilogy.parser import parse
1
+ from pathlib import Path
2
+
2
3
  from trilogy import Dialects
3
4
  from trilogy.core.models import ProcessedCopyStatement
4
- from pathlib import Path
5
+ from trilogy.parser import parse
5
6
 
6
7
  # from trilogy.compiler import compile
7
8
 
@@ -1,6 +1,6 @@
1
1
  from trilogy.core.enums import Purpose
2
2
  from trilogy.core.exceptions import UndefinedConceptException
3
- from trilogy.core.models import DataType, EnvironmentConceptDict, Concept
3
+ from trilogy.core.models import Concept, DataType, EnvironmentConceptDict
4
4
  from trilogy.parser import parse
5
5
 
6
6
 
@@ -1,9 +1,9 @@
1
1
  # from trilogy.compiler import compile
2
- from trilogy.core.models import SelectStatement, Grain, Parenthetical
2
+ from trilogy.core.models import Grain, Parenthetical, SelectStatement
3
+ from trilogy.core.processing.utility import is_scalar_condition
3
4
  from trilogy.core.query_processor import process_query
4
5
  from trilogy.dialect.base import BaseDialect
5
6
  from trilogy.parser import parse
6
- from trilogy.core.processing.utility import is_scalar_condition
7
7
 
8
8
 
9
9
  def test_select_where(test_environment):
@@ -1,9 +1,9 @@
1
+ from trilogy.constants import CONFIG
1
2
  from trilogy.core.models import Environment
2
3
  from trilogy.dialect.enums import Dialects
3
4
  from trilogy.executor import Executor
4
5
  from trilogy.parser import parse
5
- from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.2.47"
7
+ __version__ = "0.0.2.49"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1,7 +1,7 @@
1
- from logging import getLogger
1
+ import random
2
2
  from dataclasses import dataclass, field
3
3
  from enum import Enum
4
- import random
4
+ from logging import getLogger
5
5
 
6
6
  logger = getLogger("trilogy")
7
7
 
@@ -44,6 +44,7 @@ class Rendering:
44
44
  """Control how the SQL is rendered"""
45
45
 
46
46
  parameters: bool = True
47
+ concise: bool = False
47
48
 
48
49
 
49
50
  # TODO: support loading from environments
@@ -56,6 +57,7 @@ class Config:
56
57
  comments: Comments = field(default_factory=Comments)
57
58
  optimizations: Optimizations = field(default_factory=Optimizations)
58
59
  rendering: Rendering = field(default_factory=Rendering)
60
+ select_as_definition: bool = True
59
61
 
60
62
  @property
61
63
  def show_comments(self) -> bool:
@@ -29,6 +29,7 @@ class Purpose(Enum):
29
29
  METRIC = "metric"
30
30
  ROWSET = "rowset"
31
31
  AUTO = "auto"
32
+ UNKNOWN = "unknown"
32
33
 
33
34
  @classmethod
34
35
  def _missing_(cls, value):
@@ -44,6 +45,7 @@ class PurposeLineage(Enum):
44
45
  FILTER = "filter"
45
46
  CONSTANT = "constant"
46
47
  UNNEST = "unnest"
48
+ UNION = "union"
47
49
  ROOT = "root"
48
50
  ROWSET = "rowset"
49
51
  MULTISELECT = "multiselect"
@@ -113,6 +115,9 @@ class FunctionType(Enum):
113
115
 
114
116
  # structural
115
117
  UNNEST = "unnest"
118
+
119
+ UNION = "union"
120
+
116
121
  ALIAS = "alias"
117
122
 
118
123
  # Generic
@@ -133,6 +138,7 @@ class FunctionType(Enum):
133
138
 
134
139
  # TEXT AND MAYBE MORE
135
140
  SPLIT = "split"
141
+ LENGTH = "len"
136
142
 
137
143
  # Math
138
144
  DIVIDE = "divide"
@@ -154,7 +160,6 @@ class FunctionType(Enum):
154
160
  MAX = "max"
155
161
  MIN = "min"
156
162
  AVG = "avg"
157
- LENGTH = "len"
158
163
 
159
164
  # String
160
165
  LIKE = "like"
@@ -299,6 +304,7 @@ class SourceType(Enum):
299
304
  ROWSET = "rowset"
300
305
  MERGE = "merge"
301
306
  BASIC = "basic"
307
+ UNION = "union"
302
308
 
303
309
 
304
310
  class ShowCategory(Enum):
@@ -3,7 +3,7 @@ from trilogy.core.graph_models import (
3
3
  concept_to_node,
4
4
  datasource_to_node,
5
5
  )
6
- from trilogy.core.models import Environment, Concept, Datasource
6
+ from trilogy.core.models import Concept, Datasource, Environment
7
7
 
8
8
 
9
9
  def add_concept(
@@ -68,7 +68,6 @@ def generate_adhoc_graph(
68
68
  def generate_graph(
69
69
  environment: Environment,
70
70
  ) -> ReferenceGraph:
71
-
72
71
  return generate_adhoc_graph(
73
72
  list(environment.concepts.values())
74
73
  + list(environment.alias_origin_lookup.values()),
@@ -1,15 +1,15 @@
1
+ from trilogy.constants import DEFAULT_NAMESPACE
2
+ from trilogy.core.enums import ConceptSource, FunctionType, Purpose
3
+ from trilogy.core.functions import AttrAccess
1
4
  from trilogy.core.models import (
2
- DataType,
3
5
  Concept,
6
+ DataType,
4
7
  Environment,
5
8
  Function,
6
9
  Metadata,
7
10
  StructType,
8
11
  )
9
- from trilogy.core.functions import AttrAccess
10
- from trilogy.core.enums import Purpose, FunctionType, ConceptSource
11
- from trilogy.constants import DEFAULT_NAMESPACE
12
- from trilogy.parsing.common import process_function_args, arg_to_datatype, Meta
12
+ from trilogy.parsing.common import Meta, arg_to_datatype, process_function_args
13
13
 
14
14
 
15
15
  def generate_date_concepts(concept: Concept, environment: Environment):