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.

Files changed (100) hide show
  1. {pytrilogy-0.0.1.112/pytrilogy.egg-info → pytrilogy-0.0.1.114}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/SOURCES.txt +1 -0
  4. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/setup.py +1 -1
  5. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_declarations.py +3 -1
  6. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_parsing.py +24 -1
  7. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_select.py +1 -0
  8. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_where_clause.py +1 -1
  9. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/__init__.py +1 -1
  10. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/enums.py +7 -0
  11. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/models.py +36 -0
  12. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/base.py +37 -17
  13. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/common.py +1 -1
  14. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/parse_engine.py +59 -311
  15. pytrilogy-0.0.1.114/trilogy/parsing/trilogy.lark +300 -0
  16. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/LICENSE.md +0 -0
  17. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/README.md +0 -0
  18. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pyproject.toml +0 -0
  19. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/setup.cfg +0 -0
  24. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_derived_concepts.py +0 -0
  25. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_discovery_nodes.py +0 -0
  26. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_environment.py +0 -0
  27. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_functions.py +0 -0
  28. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_imports.py +0 -0
  29. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_metadata.py +0 -0
  30. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_models.py +0 -0
  31. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_multi_join_assignments.py +0 -0
  32. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_partial_handling.py +0 -0
  33. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_query_processing.py +0 -0
  34. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_statements.py +0 -0
  35. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/tests/test_undefined_concept.py +0 -0
  36. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/compiler.py +0 -0
  37. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/constants.py +0 -0
  38. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/__init__.py +0 -0
  39. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/constants.py +0 -0
  40. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/env_processor.py +0 -0
  41. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/environment_helpers.py +0 -0
  42. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/ergonomics.py +0 -0
  43. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/exceptions.py +0 -0
  44. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/functions.py +0 -0
  45. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/graph_models.py +0 -0
  46. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/internal.py +0 -0
  47. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/optimization.py +0 -0
  48. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/__init__.py +0 -0
  49. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  50. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/graph_utils.py +0 -0
  51. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/__init__.py +0 -0
  52. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  53. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/common.py +0 -0
  54. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/concept_merge_node.py +0 -0
  55. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  56. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/group_node.py +0 -0
  57. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  58. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  59. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  60. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  61. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/select_node.py +0 -0
  62. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  63. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/node_generators/window_node.py +0 -0
  64. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/__init__.py +0 -0
  65. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/base_node.py +0 -0
  66. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/filter_node.py +0 -0
  67. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/group_node.py +0 -0
  68. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/merge_node.py +0 -0
  69. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  70. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  71. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/nodes/window_node.py +0 -0
  72. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/processing/utility.py +0 -0
  73. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/core/query_processor.py +0 -0
  74. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/__init__.py +0 -0
  75. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/bigquery.py +0 -0
  76. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/common.py +0 -0
  77. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/config.py +0 -0
  78. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/duckdb.py +0 -0
  79. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/enums.py +0 -0
  80. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/postgres.py +0 -0
  81. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/presto.py +0 -0
  82. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/snowflake.py +0 -0
  83. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/dialect/sql_server.py +0 -0
  84. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/engine.py +0 -0
  85. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/executor.py +0 -0
  86. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/__init__.py +0 -0
  87. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/base_hook.py +0 -0
  88. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/graph_hook.py +0 -0
  89. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/hooks/query_debugger.py +0 -0
  90. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/metadata/__init__.py +0 -0
  91. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parser.py +0 -0
  92. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/__init__.py +0 -0
  93. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/config.py +0 -0
  94. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/exceptions.py +0 -0
  95. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/helpers.py +0 -0
  96. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/parsing/render.py +0 -0
  97. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/py.typed +0 -0
  98. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/scripts/__init__.py +0 -0
  99. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/scripts/trilogy.py +0 -0
  100. {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.114}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.1.112
3
+ Version: 0.0.1.114
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.1.112
3
+ Version: 0.0.1.114
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -93,5 +93,6 @@ trilogy/parsing/exceptions.py
93
93
  trilogy/parsing/helpers.py
94
94
  trilogy/parsing/parse_engine.py
95
95
  trilogy/parsing/render.py
96
+ trilogy/parsing/trilogy.lark
96
97
  trilogy/scripts/__init__.py
97
98
  trilogy/scripts/trilogy.py
@@ -40,7 +40,7 @@ setuptools.setup(
40
40
  ]
41
41
  ),
42
42
  package_data={
43
- "": ["*.tf", "*.jinja", "py.typed"],
43
+ "": ["*.tf", "*.jinja", "py.typed", "*.lark"],
44
44
  },
45
45
  install_requires=install_requires,
46
46
  extras_require={
@@ -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); # the distinct count of user ids
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"
@@ -37,6 +37,7 @@ datasource users (
37
37
  env, parsed = parse(declarations)
38
38
 
39
39
  q1 = """select
40
+ # a comment
40
41
  user_id,
41
42
  about_me,
42
43
  count(post_id)->post_count
@@ -150,7 +150,7 @@ select
150
150
  order_id,
151
151
  total_revenue,
152
152
  where
153
- (order_id =1 or order_id = 2) and total_revenue>30
153
+ (order_id = 1 or order_id = 2) and total_revenue>30
154
154
  ;
155
155
 
156
156
 
@@ -4,6 +4,6 @@ from trilogy.executor import Executor
4
4
  from trilogy.parser import parse
5
5
  from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.1.112"
7
+ __version__ = "0.0.1.114"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -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(concept: str, window: str, sort: str) -> str:
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"{string}({concept}) over (partition by {window} order by {sort} )"
72
+ return f"{base} over (partition by {window} order by {sort} )"
66
73
  elif window:
67
- return f"{string}({concept}) over (partition by {window})"
74
+ return f"{base} over (partition by {window})"
68
75
  elif sort:
69
- return f"{string}({concept}) over (order by {sort} )"
76
+ return f"{base} over (order by {sort} )"
70
77
  else:
71
- return f"{string}({concept}) over ()"
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"({x[0]} + {x[1]})",
113
- FunctionType.SUBTRACT: lambda x: f"({x[0]} - {x[1]})",
114
- FunctionType.DIVIDE: lambda x: f"({x[0]} / {x[1]})",
115
- FunctionType.MULTIPLY: lambda x: f"({x[0]} * {x[1]})",
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
- elif isinstance(e.right, (str, int, bool, float, list)):
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} ({self.render_expr(e.right, cte=cte, cte_map=cte_map)})"
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"( {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)} ) "
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
- return f"( {self.render_expr(e.content, cte=cte, cte_map=cte_map)} ) "
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,