pytrilogy 0.0.2.49__tar.gz → 0.0.2.51__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 (114) hide show
  1. {pytrilogy-0.0.2.49/pytrilogy.egg-info → pytrilogy-0.0.2.51}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/SOURCES.txt +2 -0
  4. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_discovery_nodes.py +0 -1
  5. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_environment.py +2 -2
  6. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_functions.py +33 -0
  7. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_models.py +5 -3
  8. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_query_processing.py +28 -9
  9. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/__init__.py +1 -1
  10. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/enums.py +11 -0
  11. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/functions.py +4 -1
  12. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/internal.py +5 -1
  13. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/models.py +135 -263
  14. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/concept_strategies_v3.py +14 -7
  15. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/basic_node.py +7 -3
  16. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/common.py +8 -5
  17. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/filter_node.py +5 -8
  18. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/group_node.py +24 -9
  19. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/group_to_node.py +0 -2
  20. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/multiselect_node.py +4 -5
  21. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/node_merge_node.py +14 -3
  22. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/rowset_node.py +3 -5
  23. pytrilogy-0.0.2.51/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +203 -0
  24. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/select_merge_node.py +153 -66
  25. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/union_node.py +0 -1
  26. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/unnest_node.py +0 -2
  27. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/window_node.py +0 -2
  28. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/base_node.py +2 -36
  29. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/filter_node.py +0 -3
  30. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/group_node.py +19 -13
  31. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/merge_node.py +2 -5
  32. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/select_node_v2.py +0 -4
  33. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/union_node.py +0 -3
  34. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/unnest_node.py +0 -3
  35. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/window_node.py +0 -3
  36. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/utility.py +3 -0
  37. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/query_processor.py +0 -1
  38. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/base.py +14 -2
  39. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/duckdb.py +7 -0
  40. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/graph_hook.py +17 -1
  41. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/common.py +68 -17
  42. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/parse_engine.py +70 -20
  43. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/render.py +8 -1
  44. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/trilogy.lark +3 -1
  45. pytrilogy-0.0.2.51/trilogy/scripts/__init__.py +0 -0
  46. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/LICENSE.md +0 -0
  47. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/README.md +0 -0
  48. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pyproject.toml +0 -0
  49. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/dependency_links.txt +0 -0
  50. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/entry_points.txt +0 -0
  51. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/requires.txt +0 -0
  52. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/top_level.txt +0 -0
  53. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/setup.cfg +0 -0
  54. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/setup.py +0 -0
  55. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_datatypes.py +0 -0
  56. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_declarations.py +0 -0
  57. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_derived_concepts.py +0 -0
  58. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_enums.py +0 -0
  59. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_executor.py +0 -0
  60. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_imports.py +0 -0
  61. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_metadata.py +0 -0
  62. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_multi_join_assignments.py +0 -0
  63. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_parse_engine.py +0 -0
  64. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_parsing.py +0 -0
  65. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_partial_handling.py +0 -0
  66. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_select.py +0 -0
  67. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_show.py +0 -0
  68. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_statements.py +0 -0
  69. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_undefined_concept.py +0 -0
  70. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_where_clause.py +0 -0
  71. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/compiler.py +0 -0
  72. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/constants.py +0 -0
  73. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/__init__.py +0 -0
  74. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/constants.py +0 -0
  75. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/env_processor.py +0 -0
  76. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/environment_helpers.py +0 -0
  77. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/ergonomics.py +0 -0
  78. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/exceptions.py +0 -0
  79. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/graph_models.py +0 -0
  80. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimization.py +0 -0
  81. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/__init__.py +0 -0
  82. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/base_optimization.py +0 -0
  83. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/inline_constant.py +0 -0
  84. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/inline_datasource.py +0 -0
  85. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  86. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/__init__.py +0 -0
  87. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/graph_utils.py +0 -0
  88. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/__init__.py +0 -0
  89. {pytrilogy-0.0.2.49/trilogy/dialect → pytrilogy-0.0.2.51/trilogy/core/processing/node_generators/select_helpers}/__init__.py +0 -0
  90. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/select_node.py +0 -0
  91. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/__init__.py +0 -0
  92. {pytrilogy-0.0.2.49/trilogy/hooks → pytrilogy-0.0.2.51/trilogy/dialect}/__init__.py +0 -0
  93. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/bigquery.py +0 -0
  94. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/common.py +0 -0
  95. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/config.py +0 -0
  96. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/enums.py +0 -0
  97. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/postgres.py +0 -0
  98. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/presto.py +0 -0
  99. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/snowflake.py +0 -0
  100. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/sql_server.py +0 -0
  101. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/engine.py +0 -0
  102. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/executor.py +0 -0
  103. {pytrilogy-0.0.2.49/trilogy/metadata → pytrilogy-0.0.2.51/trilogy/hooks}/__init__.py +0 -0
  104. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/base_hook.py +0 -0
  105. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/query_debugger.py +0 -0
  106. {pytrilogy-0.0.2.49/trilogy/parsing → pytrilogy-0.0.2.51/trilogy/metadata}/__init__.py +0 -0
  107. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parser.py +0 -0
  108. {pytrilogy-0.0.2.49/trilogy/scripts → pytrilogy-0.0.2.51/trilogy/parsing}/__init__.py +0 -0
  109. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/config.py +0 -0
  110. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/exceptions.py +0 -0
  111. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/helpers.py +0 -0
  112. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/py.typed +0 -0
  113. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/scripts/trilogy.py +0 -0
  114. {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.49
3
+ Version: 0.0.2.51
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.49
3
+ Version: 0.0.2.51
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -73,6 +73,8 @@ trilogy/core/processing/node_generators/select_node.py
73
73
  trilogy/core/processing/node_generators/union_node.py
74
74
  trilogy/core/processing/node_generators/unnest_node.py
75
75
  trilogy/core/processing/node_generators/window_node.py
76
+ trilogy/core/processing/node_generators/select_helpers/__init__.py
77
+ trilogy/core/processing/node_generators/select_helpers/datasource_injection.py
76
78
  trilogy/core/processing/nodes/__init__.py
77
79
  trilogy/core/processing/nodes/base_node.py
78
80
  trilogy/core/processing/nodes/filter_node.py
@@ -15,7 +15,6 @@ def test_group_node(test_environment, test_environment_graph):
15
15
  output_concepts=[total_revenue, category],
16
16
  input_concepts=[category, revenue],
17
17
  environment=test_environment,
18
- g=test_environment_graph,
19
18
  parents=[
20
19
  search_concepts(
21
20
  [category, revenue],
@@ -7,9 +7,9 @@ from trilogy.core.models import Environment
7
7
 
8
8
 
9
9
  def test_environment_serialization(test_environment: Environment):
10
- str(test_environment)
11
- path = test_environment.to_cache()
12
10
 
11
+ path = test_environment.to_cache()
12
+ print(path)
13
13
  test_environment2 = Environment.from_cache(path)
14
14
  assert test_environment2
15
15
 
@@ -1,8 +1,10 @@
1
1
  # from trilogy.compiler import compile
2
+ from datetime import date, datetime
2
3
  from logging import INFO
3
4
 
4
5
  from pytest import raises
5
6
 
7
+ from trilogy import Dialects
6
8
  from trilogy.constants import logger
7
9
  from trilogy.core.enums import Purpose, PurposeLineage
8
10
  from trilogy.core.exceptions import InvalidSyntaxException
@@ -154,6 +156,36 @@ def test_explicit_cast(test_environment):
154
156
  dialect.compile_statement(process_query(test_environment, select))
155
157
 
156
158
 
159
+ def test_literal_cast(test_environment):
160
+ declarations = """
161
+ select
162
+ '1'::int -> one,
163
+ '1'::float -> one_float,
164
+ '1'::string -> one_string,
165
+ '2024-01-01'::date -> one_date,
166
+ '2024-01-01 01:01:01'::datetime -> one_datetime,
167
+ 'true'::bool -> one_bool,
168
+ ;"""
169
+ env, parsed = parse(declarations, environment=test_environment)
170
+
171
+ select: SelectStatement = parsed[-1]
172
+ z = (
173
+ Dialects.DUCK_DB.default_executor(environment=test_environment)
174
+ .execute_query(parsed[-1])
175
+ .fetchall()
176
+ )
177
+ assert z[0].one == 1
178
+ assert z[0].one_float == 1.0
179
+ assert z[0].one_string == "1"
180
+ assert z[0].one_date == date(year=2024, month=1, day=1)
181
+ assert z[0].one_datetime == datetime(
182
+ year=2024, month=1, day=1, hour=1, minute=1, second=1
183
+ )
184
+ assert z[0].one_bool
185
+ for dialect in TEST_DIALECTS:
186
+ dialect.compile_statement(process_query(test_environment, select))
187
+
188
+
157
189
  def test_math_functions(test_environment):
158
190
  declarations = """
159
191
 
@@ -210,6 +242,7 @@ def test_case_function(test_environment):
210
242
  test_upper_case
211
243
  ;"""
212
244
  env, parsed = parse(declarations, environment=test_environment)
245
+
213
246
  assert (
214
247
  test_environment.concepts["category_name"]
215
248
  in test_environment.concepts["test_upper_case"].lineage.concept_arguments
@@ -68,7 +68,7 @@ def test_cte_merge(test_environment, test_environment_graph):
68
68
 
69
69
 
70
70
  def test_concept(test_environment, test_environment_graph):
71
- test_concept = list(test_environment.concepts.values())[0]
71
+ test_concept: Concept = list(test_environment.concepts.values())[0]
72
72
  new = test_concept.with_namespace("test")
73
73
  assert (
74
74
  new.namespace == ("test" + "." + test_concept.namespace)
@@ -129,7 +129,7 @@ def test_grain(test_environment):
129
129
  x = Grain(components=[oid, pid])
130
130
  y = Grain(components=[pid, cid])
131
131
  z = Grain(components=[cid])
132
- z2 = Grain(components=[cid, cname])
132
+ z2 = Grain.from_concepts([cid, cname], environment=test_environment)
133
133
 
134
134
  assert x.intersection(y) == Grain(components=[pid])
135
135
  assert x.union(y) == Grain(components=[oid, pid, cid])
@@ -137,7 +137,9 @@ def test_grain(test_environment):
137
137
  assert z.isdisjoint(x)
138
138
  assert z.issubset(y)
139
139
 
140
- assert z2 == z, f"Property should be removed from grain ({z.set}) vs {z2.set}"
140
+ assert (
141
+ z2 == z
142
+ ), f"Property should be removed from grain ({z.components}) vs {z2.components}"
141
143
 
142
144
 
143
145
  def test_select(test_environment: Environment):
@@ -1,4 +1,10 @@
1
- from trilogy.core.models import Environment, Grain, QueryDatasource, SelectStatement
1
+ from trilogy.core.models import (
2
+ Environment,
3
+ Grain,
4
+ QueryDatasource,
5
+ SelectStatement,
6
+ SourceType,
7
+ )
2
8
  from trilogy.core.processing.concept_strategies_v3 import search_concepts
3
9
  from trilogy.core.query_processor import get_query_datasources, process_query
4
10
 
@@ -7,7 +13,7 @@ def test_direct_select(test_environment, test_environment_graph):
7
13
  product = test_environment.concepts["product_id"]
8
14
  # concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
9
15
  datasource = search_concepts(
10
- [product] + product.grain.components_copy,
16
+ [product] + [test_environment.concepts[c] for c in product.grain.components],
11
17
  environment=test_environment,
12
18
  g=test_environment_graph,
13
19
  depth=0,
@@ -27,7 +33,8 @@ def test_get_datasource_from_window_function(
27
33
  # concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
28
34
  # assert product_rank.grain.components[0] == test_environment.concepts['name']
29
35
  node = search_concepts(
30
- [product_rank] + product_rank.grain.components_copy,
36
+ [product_rank]
37
+ + [test_environment.concepts[c] for c in product_rank.grain.components],
31
38
  environment=test_environment,
32
39
  g=test_environment_graph,
33
40
  depth=0,
@@ -38,14 +45,18 @@ def test_get_datasource_from_window_function(
38
45
  assert product_rank in datasource.output_concepts
39
46
  # assert datasource.grain == product_rank.grain
40
47
  assert isinstance(datasource, QueryDatasource)
41
- assert datasource.grain.set == product_rank.grain.set
48
+ assert datasource.grain.components == product_rank.grain.components
42
49
 
43
50
  product_rank_by_category = test_environment.concepts[
44
51
  "product_revenue_rank_by_category"
45
52
  ]
46
53
  # concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
47
54
  datasource = search_concepts(
48
- [product_rank_by_category] + product_rank_by_category.grain.components_copy,
55
+ [product_rank_by_category]
56
+ + [
57
+ test_environment.concepts[c]
58
+ for c in product_rank_by_category.grain.components
59
+ ],
49
60
  environment=test_environment,
50
61
  g=test_environment_graph,
51
62
  depth=0,
@@ -68,7 +79,8 @@ def test_get_datasource_for_filter(
68
79
  "product_id",
69
80
  }
70
81
  datasource = search_concepts(
71
- [hi_rev_product] + hi_rev_product.grain.components_copy,
82
+ [hi_rev_product]
83
+ + [test_environment.concepts[c] for c in hi_rev_product.grain.components],
72
84
  environment=test_environment,
73
85
  g=test_environment_graph,
74
86
  depth=0,
@@ -86,7 +98,7 @@ def test_select_output(test_environment, test_environment_graph):
86
98
  # concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
87
99
 
88
100
  datasource = search_concepts(
89
- [product] + product.grain.components_copy,
101
+ [product] + [test_environment.concepts[c] for c in product.grain.components],
90
102
  environment=test_environment,
91
103
  g=test_environment_graph,
92
104
  depth=0,
@@ -114,18 +126,25 @@ def test_basic_aggregate(test_environment: Environment, test_environment_graph):
114
126
 
115
127
 
116
128
  def test_join_aggregate(test_environment: Environment, test_environment_graph):
129
+ from trilogy.hooks.query_debugger import DebuggingHook
130
+
131
+ DebuggingHook()
117
132
  category_id = test_environment.concepts["category_id"]
118
133
  total_revenue = test_environment.concepts["total_revenue"]
119
134
  # concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
120
135
  datasource = search_concepts(
121
- [total_revenue.with_grain(category_id), category_id],
136
+ [
137
+ total_revenue.with_grain(Grain(components={"local.category_id"})),
138
+ category_id,
139
+ ],
122
140
  environment=test_environment,
123
141
  g=test_environment_graph,
124
142
  depth=0,
125
143
  ).resolve()
126
144
  assert isinstance(datasource, QueryDatasource)
145
+ assert datasource.source_type == SourceType.GROUP
127
146
  assert len(set([datasource.name for datasource in datasource.datasources])) == 1
128
- assert datasource.grain.components == [category_id]
147
+ assert datasource.grain.components == {"local.category_id"}
129
148
 
130
149
 
131
150
  def test_query_aggregation(test_environment, test_environment_graph):
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.2.49"
7
+ __version__ = "0.0.2.51"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -120,6 +120,8 @@ class FunctionType(Enum):
120
120
 
121
121
  ALIAS = "alias"
122
122
 
123
+ PARENTHETICAL = "parenthetical"
124
+
123
125
  # Generic
124
126
  CASE = "case"
125
127
  CAST = "cast"
@@ -135,6 +137,8 @@ class FunctionType(Enum):
135
137
  ATTR_ACCESS = "attr_access"
136
138
  STRUCT = "struct"
137
139
  ARRAY = "array"
140
+ DATE_LITERAL = "date_literal"
141
+ DATETIME_LITERAL = "datetime_literal"
138
142
 
139
143
  # TEXT AND MAYBE MORE
140
144
  SPLIT = "split"
@@ -260,6 +264,13 @@ class ComparisonOperator(Enum):
260
264
  CONTAINS = "contains"
261
265
  ELSE = "else"
262
266
 
267
+ def __eq__(self, other):
268
+ if isinstance(other, str):
269
+ return self.value == other
270
+ if not isinstance(other, ComparisonOperator):
271
+ return False
272
+ return self.value == other.value
273
+
263
274
  @classmethod
264
275
  def _missing_(cls, value):
265
276
  if not isinstance(value, list) and " " in str(value):
@@ -1,3 +1,4 @@
1
+ from datetime import date, datetime
1
2
  from typing import Optional
2
3
 
3
4
  from trilogy.constants import MagicConstants
@@ -17,6 +18,8 @@ from trilogy.core.models import (
17
18
  arg_to_datatype,
18
19
  )
19
20
 
21
+ GENERIC_ARGS = Concept | Function | str | int | float | date | datetime
22
+
20
23
 
21
24
  def create_function_derived_concept(
22
25
  name: str,
@@ -262,7 +265,7 @@ def get_attr_datatype(
262
265
  return arg.datatype
263
266
 
264
267
 
265
- def AttrAccess(args: list[Concept]):
268
+ def AttrAccess(args: list[GENERIC_ARGS]):
266
269
  return Function(
267
270
  operator=FunctionType.ATTR_ACCESS,
268
271
  arguments=args,
@@ -1,6 +1,6 @@
1
1
  from trilogy.core.constants import ALL_ROWS_CONCEPT, INTERNAL_NAMESPACE
2
2
  from trilogy.core.enums import FunctionType, Purpose
3
- from trilogy.core.models import Concept, DataType, Function
3
+ from trilogy.core.models import Concept, DataType, Function, Grain
4
4
 
5
5
  DEFAULT_CONCEPTS = {
6
6
  ALL_ROWS_CONCEPT: Concept(
@@ -8,6 +8,7 @@ DEFAULT_CONCEPTS = {
8
8
  namespace=INTERNAL_NAMESPACE,
9
9
  datatype=DataType.INTEGER,
10
10
  purpose=Purpose.CONSTANT,
11
+ grain=Grain(),
11
12
  lineage=Function(
12
13
  operator=FunctionType.CONSTANT,
13
14
  arguments=[1],
@@ -20,17 +21,20 @@ DEFAULT_CONCEPTS = {
20
21
  namespace=INTERNAL_NAMESPACE,
21
22
  datatype=DataType.STRING,
22
23
  purpose=Purpose.KEY,
24
+ grain=Grain(),
23
25
  ),
24
26
  "datasource": Concept(
25
27
  name="datasource",
26
28
  namespace=INTERNAL_NAMESPACE,
27
29
  datatype=DataType.STRING,
28
30
  purpose=Purpose.KEY,
31
+ grain=Grain(),
29
32
  ),
30
33
  "query_text": Concept(
31
34
  name="query_text",
32
35
  namespace=INTERNAL_NAMESPACE,
33
36
  datatype=DataType.STRING,
34
37
  purpose=Purpose.KEY,
38
+ grain=Grain(),
35
39
  ),
36
40
  }