pytrilogy 0.0.1.112__tar.gz → 0.0.1.114__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.1.112/pytrilogy.egg-info → pytrilogy-0.0.1.114}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/setup.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_declarations.py +3 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_parsing.py +24 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_select.py +1 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_where_clause.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/enums.py +7 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/models.py +36 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/base.py +37 -17
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/common.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/parse_engine.py +59 -311
- pytrilogy-0.0.1.114/trilogy/parsing/trilogy.lark +300 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/LICENSE.md +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/README.md +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pyproject.toml +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/setup.cfg +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_models.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/concept_merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/utility.py +0 -0
|
@@ -4,7 +4,7 @@ from trilogy.parser import parse
|
|
|
4
4
|
def test_declarations():
|
|
5
5
|
declarations = """key namespace.user_id int metadata(description="the description");
|
|
6
6
|
metric namespace.count <- count(namespace.user_id);
|
|
7
|
-
metric namespace.distinct_count <- count_distinct(namespace.user_id); #
|
|
7
|
+
metric namespace.distinct_count <- count_distinct(namespace.user_id); #the distinct count of user ids
|
|
8
8
|
"""
|
|
9
9
|
env, _ = parse(declarations)
|
|
10
10
|
|
|
@@ -14,3 +14,5 @@ def test_declarations():
|
|
|
14
14
|
env.concepts["namespace.distinct_count"].metadata.description
|
|
15
15
|
== "the distinct count of user ids"
|
|
16
16
|
)
|
|
17
|
+
|
|
18
|
+
assert env.concepts["namespace.user_id"].metadata.description == "the description"
|
|
@@ -211,7 +211,7 @@ rowset test<- select
|
|
|
211
211
|
;
|
|
212
212
|
|
|
213
213
|
select
|
|
214
|
-
count(test.name) -> test_name_count;
|
|
214
|
+
count( test.name ) -> test_name_count;
|
|
215
215
|
"""
|
|
216
216
|
)
|
|
217
217
|
# assert output_purpose == Purpose.METRIC
|
|
@@ -240,3 +240,26 @@ def test_between():
|
|
|
240
240
|
), type(right)
|
|
241
241
|
assert right.operator == ComparisonOperator.LTE
|
|
242
242
|
assert right.right == 5
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_the_comments():
|
|
246
|
+
_, parsed = parse_text(
|
|
247
|
+
"""const
|
|
248
|
+
# comment here?
|
|
249
|
+
order_id <- 4; SELECT
|
|
250
|
+
# TOOD - add in more columns?
|
|
251
|
+
order_id # this is the order id
|
|
252
|
+
WHERE
|
|
253
|
+
# order_id should not be null
|
|
254
|
+
order_id
|
|
255
|
+
# in this comp
|
|
256
|
+
is not
|
|
257
|
+
null; # nulls are the worst
|
|
258
|
+
|
|
259
|
+
"""
|
|
260
|
+
)
|
|
261
|
+
query = parsed[-1]
|
|
262
|
+
right = query.where_clause.conditional.right
|
|
263
|
+
assert isinstance(right, MagicConstants), type(right)
|
|
264
|
+
rendered = BaseDialect().render_expr(right)
|
|
265
|
+
assert rendered == "null"
|
|
@@ -76,6 +76,12 @@ class JoinType(Enum):
|
|
|
76
76
|
class Ordering(Enum):
|
|
77
77
|
ASCENDING = "asc"
|
|
78
78
|
DESCENDING = "desc"
|
|
79
|
+
ASC_NULLS_AUTO = "asc nulls auto"
|
|
80
|
+
ASC_NULLS_FIRST = "asc nulls first"
|
|
81
|
+
ASC_NULLS_LAST = "asc nulls last"
|
|
82
|
+
DESC_NULLS_FIRST = "desc nulls first"
|
|
83
|
+
DESC_NULLS_LAST = "desc nulls last"
|
|
84
|
+
DESC_NULLS_AUTO = "desc nulls auto"
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
class WindowType(Enum):
|
|
@@ -220,6 +226,7 @@ class ComparisonOperator(Enum):
|
|
|
220
226
|
ILIKE = "ilike"
|
|
221
227
|
CONTAINS = "contains"
|
|
222
228
|
ELSE = "else"
|
|
229
|
+
BETWEEN = "between"
|
|
223
230
|
|
|
224
231
|
@classmethod
|
|
225
232
|
def _missing_(cls, value):
|
|
@@ -812,6 +812,7 @@ class Function(Namespaced, SelectGrain, BaseModel):
|
|
|
812
812
|
"Parenthetical",
|
|
813
813
|
CaseWhen,
|
|
814
814
|
"CaseElse",
|
|
815
|
+
list,
|
|
815
816
|
ListWrapper[int],
|
|
816
817
|
ListWrapper[str],
|
|
817
818
|
ListWrapper[float],
|
|
@@ -996,6 +997,7 @@ class WindowItem(Namespaced, SelectGrain, BaseModel):
|
|
|
996
997
|
content: Concept
|
|
997
998
|
order_by: List["OrderItem"]
|
|
998
999
|
over: List["Concept"] = Field(default_factory=list)
|
|
1000
|
+
index: Optional[int] = None
|
|
999
1001
|
|
|
1000
1002
|
def with_namespace(self, namespace: str) -> "WindowItem":
|
|
1001
1003
|
return WindowItem(
|
|
@@ -2735,6 +2737,14 @@ class Comparison(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2735
2737
|
raise SyntaxError(
|
|
2736
2738
|
f"Cannot compare {self.left} and {self.right} of different types"
|
|
2737
2739
|
)
|
|
2740
|
+
if self.operator == ComparisonOperator.BETWEEN:
|
|
2741
|
+
if (
|
|
2742
|
+
not isinstance(self.right, ComparisonOperator)
|
|
2743
|
+
and self.right.operator == BooleanOperator.AND
|
|
2744
|
+
):
|
|
2745
|
+
raise SyntaxError(
|
|
2746
|
+
f"Between operator must have two operands with and, not {self.right}"
|
|
2747
|
+
)
|
|
2738
2748
|
|
|
2739
2749
|
def __add__(self, other):
|
|
2740
2750
|
if not isinstance(other, (Comparison, Conditional, Parenthetical)):
|
|
@@ -2810,6 +2820,29 @@ class Comparison(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2810
2820
|
output += get_concept_arguments(self.right)
|
|
2811
2821
|
return output
|
|
2812
2822
|
|
|
2823
|
+
@property
|
|
2824
|
+
def row_arguments(self) -> List[Concept]:
|
|
2825
|
+
output = []
|
|
2826
|
+
if isinstance(self.left, ConceptArgs):
|
|
2827
|
+
output += self.left.row_arguments
|
|
2828
|
+
else:
|
|
2829
|
+
output += get_concept_arguments(self.left)
|
|
2830
|
+
if isinstance(self.right, ConceptArgs):
|
|
2831
|
+
output += self.right.row_arguments
|
|
2832
|
+
else:
|
|
2833
|
+
output += get_concept_arguments(self.right)
|
|
2834
|
+
return output
|
|
2835
|
+
|
|
2836
|
+
@property
|
|
2837
|
+
def existence_arguments(self) -> List[Tuple[Concept, ...]]:
|
|
2838
|
+
"""Return concepts directly referenced in where clause"""
|
|
2839
|
+
output: List[Tuple[Concept, ...]] = []
|
|
2840
|
+
if isinstance(self.left, ConceptArgs):
|
|
2841
|
+
output += self.left.existence_arguments
|
|
2842
|
+
if isinstance(self.right, ConceptArgs):
|
|
2843
|
+
output += self.right.existence_arguments
|
|
2844
|
+
return output
|
|
2845
|
+
|
|
2813
2846
|
|
|
2814
2847
|
class SubselectComparison(Comparison):
|
|
2815
2848
|
|
|
@@ -2909,6 +2942,7 @@ class Conditional(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2909
2942
|
float,
|
|
2910
2943
|
list,
|
|
2911
2944
|
bool,
|
|
2945
|
+
MagicConstants,
|
|
2912
2946
|
Concept,
|
|
2913
2947
|
Comparison,
|
|
2914
2948
|
"Conditional",
|
|
@@ -2922,11 +2956,13 @@ class Conditional(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2922
2956
|
float,
|
|
2923
2957
|
list,
|
|
2924
2958
|
bool,
|
|
2959
|
+
MagicConstants,
|
|
2925
2960
|
Concept,
|
|
2926
2961
|
Comparison,
|
|
2927
2962
|
"Conditional",
|
|
2928
2963
|
"Parenthetical",
|
|
2929
2964
|
Function,
|
|
2965
|
+
FilterItem,
|
|
2930
2966
|
]
|
|
2931
2967
|
operator: BooleanOperator
|
|
2932
2968
|
|
|
@@ -10,6 +10,7 @@ from trilogy.core.enums import (
|
|
|
10
10
|
WindowType,
|
|
11
11
|
DatePart,
|
|
12
12
|
PurposeLineage,
|
|
13
|
+
ComparisonOperator,
|
|
13
14
|
)
|
|
14
15
|
from trilogy.core.models import (
|
|
15
16
|
ListType,
|
|
@@ -58,17 +59,23 @@ def INVALID_REFERENCE_STRING(x: Any, callsite: str = ""):
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def window_factory(string: str, include_concept: bool = False) -> Callable:
|
|
61
|
-
def render_window(
|
|
62
|
+
def render_window(
|
|
63
|
+
concept: str, window: str, sort: str, offset: int | None = None
|
|
64
|
+
) -> str:
|
|
62
65
|
if not include_concept:
|
|
63
66
|
concept = ""
|
|
67
|
+
if offset:
|
|
68
|
+
base = f"{string}({concept}, {offset})"
|
|
69
|
+
else:
|
|
70
|
+
base = f"{string}({concept})"
|
|
64
71
|
if window and sort:
|
|
65
|
-
return f"{
|
|
72
|
+
return f"{base} over (partition by {window} order by {sort} )"
|
|
66
73
|
elif window:
|
|
67
|
-
return f"{
|
|
74
|
+
return f"{base} over (partition by {window})"
|
|
68
75
|
elif sort:
|
|
69
|
-
return f"{
|
|
76
|
+
return f"{base} over (order by {sort} )"
|
|
70
77
|
else:
|
|
71
|
-
return f"{
|
|
78
|
+
return f"{base} over ()"
|
|
72
79
|
|
|
73
80
|
return render_window
|
|
74
81
|
|
|
@@ -109,10 +116,10 @@ FUNCTION_MAP = {
|
|
|
109
116
|
FunctionType.INDEX_ACCESS: lambda x: f"{x[0]}[{x[1]}]",
|
|
110
117
|
FunctionType.UNNEST: lambda x: f"unnest({x[0]})",
|
|
111
118
|
# math
|
|
112
|
-
FunctionType.ADD: lambda x: f"
|
|
113
|
-
FunctionType.SUBTRACT: lambda x: f"
|
|
114
|
-
FunctionType.DIVIDE: lambda x: f"
|
|
115
|
-
FunctionType.MULTIPLY: lambda x: f"
|
|
119
|
+
FunctionType.ADD: lambda x: f"{x[0]} + {x[1]}",
|
|
120
|
+
FunctionType.SUBTRACT: lambda x: f"{x[0]} - {x[1]}",
|
|
121
|
+
FunctionType.DIVIDE: lambda x: f"{x[0]} / {x[1]}",
|
|
122
|
+
FunctionType.MULTIPLY: lambda x: f"{x[0]} * {x[1]}",
|
|
116
123
|
FunctionType.ROUND: lambda x: f"round({x[0]},{x[1]})",
|
|
117
124
|
FunctionType.MOD: lambda x: f"({x[0]} % {x[1]})",
|
|
118
125
|
# aggregate types
|
|
@@ -355,17 +362,30 @@ class BaseDialect:
|
|
|
355
362
|
lookup = lookup_cte.existence_source_map[e.right.address]
|
|
356
363
|
|
|
357
364
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} (select {lookup[0]}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} from {lookup[0]})"
|
|
358
|
-
elif isinstance(e.right, (ListWrapper, Parenthetical)):
|
|
365
|
+
elif isinstance(e.right, (ListWrapper, Parenthetical, list)):
|
|
359
366
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
360
|
-
|
|
367
|
+
|
|
368
|
+
elif isinstance(
|
|
369
|
+
e.right,
|
|
370
|
+
(
|
|
371
|
+
str,
|
|
372
|
+
int,
|
|
373
|
+
bool,
|
|
374
|
+
float,
|
|
375
|
+
),
|
|
376
|
+
):
|
|
361
377
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} ({self.render_expr(e.right, cte=cte, cte_map=cte_map)})"
|
|
362
378
|
else:
|
|
363
|
-
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value}
|
|
379
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
364
380
|
elif isinstance(e, Comparison):
|
|
381
|
+
if e.operator == ComparisonOperator.BETWEEN:
|
|
382
|
+
right_comp = e.right
|
|
383
|
+
assert isinstance(right_comp, Conditional)
|
|
384
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(right_comp.left, cte=cte, cte_map=cte_map) and self.render_expr(right_comp.right, cte=cte, cte_map=cte_map)}"
|
|
365
385
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
366
386
|
elif isinstance(e, Conditional):
|
|
367
387
|
# conditions need to be nested in parentheses
|
|
368
|
-
return f"
|
|
388
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
369
389
|
elif isinstance(e, WindowItem):
|
|
370
390
|
rendered_order_components = [
|
|
371
391
|
f"{self.render_expr(x.expr, cte, cte_map=cte_map)} {x.order.value}"
|
|
@@ -375,11 +395,11 @@ class BaseDialect:
|
|
|
375
395
|
self.render_expr(x, cte, cte_map=cte_map) for x in e.over
|
|
376
396
|
]
|
|
377
397
|
return f"{self.WINDOW_FUNCTION_MAP[e.type](concept = self.render_expr(e.content, cte=cte, cte_map=cte_map), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
|
|
378
|
-
elif isinstance(e, FilterItem):
|
|
379
|
-
return f"CASE WHEN {self.render_expr(e.where.conditional, cte=cte, cte_map=cte_map)} THEN {self.render_expr(e.content, cte=cte, cte_map=cte_map)} ELSE 0 END"
|
|
380
398
|
elif isinstance(e, Parenthetical):
|
|
381
399
|
# conditions need to be nested in parentheses
|
|
382
|
-
|
|
400
|
+
if isinstance(e.content, list):
|
|
401
|
+
return f"( {','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e.content])} )"
|
|
402
|
+
return f"( {self.render_expr(e.content, cte=cte, cte_map=cte_map)} )"
|
|
383
403
|
elif isinstance(e, CaseWhen):
|
|
384
404
|
return f"WHEN {self.render_expr(e.comparison, cte=cte, cte_map=cte_map) } THEN {self.render_expr(e.expr, cte=cte, cte_map=cte_map) }"
|
|
385
405
|
elif isinstance(e, CaseElse):
|
|
@@ -412,7 +432,7 @@ class BaseDialect:
|
|
|
412
432
|
elif isinstance(e, ListWrapper):
|
|
413
433
|
return f"[{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}]"
|
|
414
434
|
elif isinstance(e, list):
|
|
415
|
-
return f"{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}"
|
|
435
|
+
return f"[{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}]"
|
|
416
436
|
elif isinstance(e, DataType):
|
|
417
437
|
return str(e.value)
|
|
418
438
|
elif isinstance(e, DatePart):
|
|
@@ -41,7 +41,7 @@ def concept_list_to_keys(concepts: Tuple[Concept, ...]) -> Tuple[Concept, ...]:
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def constant_to_concept(
|
|
44
|
-
parent: ListWrapper | int | float | str,
|
|
44
|
+
parent: ListWrapper | list | int | float | str,
|
|
45
45
|
name: str,
|
|
46
46
|
namespace: str,
|
|
47
47
|
purpose: Purpose | None = None,
|