pytrilogy 0.0.2.4__tar.gz → 0.0.2.6__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 (105) hide show
  1. {pytrilogy-0.0.2.4/pytrilogy.egg-info → pytrilogy-0.0.2.6}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_parsing.py +45 -1
  4. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_where_clause.py +31 -2
  5. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/__init__.py +1 -1
  6. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/enums.py +1 -0
  7. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/models.py +21 -6
  8. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimizations/inline_datasource.py +6 -2
  9. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/base.py +8 -5
  10. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/common.py +2 -1
  11. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/duckdb.py +0 -1
  12. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/presto.py +2 -1
  13. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/parse_engine.py +6 -3
  14. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/trilogy.lark +3 -2
  15. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/LICENSE.md +0 -0
  16. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/README.md +0 -0
  17. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pyproject.toml +0 -0
  18. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pytrilogy.egg-info/SOURCES.txt +0 -0
  19. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/setup.cfg +0 -0
  24. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/setup.py +0 -0
  25. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_datatypes.py +0 -0
  26. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_declarations.py +0 -0
  27. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_derived_concepts.py +0 -0
  28. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_discovery_nodes.py +0 -0
  29. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_environment.py +0 -0
  30. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_functions.py +0 -0
  31. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_imports.py +0 -0
  32. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_metadata.py +0 -0
  33. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_models.py +0 -0
  34. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_multi_join_assignments.py +0 -0
  35. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_partial_handling.py +0 -0
  36. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_query_processing.py +0 -0
  37. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_select.py +0 -0
  38. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_statements.py +0 -0
  39. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/tests/test_undefined_concept.py +0 -0
  40. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/compiler.py +0 -0
  41. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/constants.py +0 -0
  42. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/__init__.py +0 -0
  43. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/constants.py +0 -0
  44. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/env_processor.py +0 -0
  45. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/environment_helpers.py +0 -0
  46. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/ergonomics.py +0 -0
  47. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/exceptions.py +0 -0
  48. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/functions.py +0 -0
  49. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/graph_models.py +0 -0
  50. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/internal.py +0 -0
  51. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimization.py +0 -0
  52. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimizations/__init__.py +0 -0
  53. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimizations/base_optimization.py +0 -0
  54. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimizations/inline_constant.py +0 -0
  55. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  56. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/__init__.py +0 -0
  57. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  58. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/graph_utils.py +0 -0
  59. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/__init__.py +0 -0
  60. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  61. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/common.py +0 -0
  62. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  63. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/group_node.py +0 -0
  64. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  65. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  66. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  67. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  68. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/select_node.py +0 -0
  69. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  70. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/node_generators/window_node.py +0 -0
  71. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/__init__.py +0 -0
  72. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/base_node.py +0 -0
  73. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/filter_node.py +0 -0
  74. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/group_node.py +0 -0
  75. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/merge_node.py +0 -0
  76. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  77. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  78. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/nodes/window_node.py +0 -0
  79. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/processing/utility.py +0 -0
  80. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/core/query_processor.py +0 -0
  81. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/__init__.py +0 -0
  82. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/bigquery.py +0 -0
  83. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/config.py +0 -0
  84. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/enums.py +0 -0
  85. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/postgres.py +0 -0
  86. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/snowflake.py +0 -0
  87. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/dialect/sql_server.py +0 -0
  88. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/engine.py +0 -0
  89. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/executor.py +0 -0
  90. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/hooks/__init__.py +0 -0
  91. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/hooks/base_hook.py +0 -0
  92. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/hooks/graph_hook.py +0 -0
  93. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/hooks/query_debugger.py +0 -0
  94. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/metadata/__init__.py +0 -0
  95. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parser.py +0 -0
  96. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/__init__.py +0 -0
  97. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/common.py +0 -0
  98. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/config.py +0 -0
  99. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/exceptions.py +0 -0
  100. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/helpers.py +0 -0
  101. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/parsing/render.py +0 -0
  102. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/py.typed +0 -0
  103. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/scripts/__init__.py +0 -0
  104. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/scripts/trilogy.py +0 -0
  105. {pytrilogy-0.0.2.4 → pytrilogy-0.0.2.6}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.4
3
+ Version: 0.0.2.6
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.4
3
+ Version: 0.0.2.6
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -160,7 +160,8 @@ address `preqldata.analytics_411641820.events_*`
160
160
  ;"""
161
161
  )
162
162
  query = parsed[-1]
163
- assert query.address.location == "`preqldata.analytics_411641820.events_*`"
163
+ assert query.address.quoted is True
164
+ assert query.address.location == "preqldata.analytics_411641820.events_*"
164
165
 
165
166
 
166
167
  def test_purpose_and_keys():
@@ -448,3 +449,46 @@ select
448
449
  {"a": 1, "b": 2, "c": 3},
449
450
  1,
450
451
  )
452
+
453
+
454
+ def test_datasource_colon():
455
+
456
+ text = """
457
+ key x int;
458
+ key y int;
459
+
460
+ datasource test (
461
+ x:x,
462
+ y:y)
463
+ grain(x)
464
+ address `abc:def`
465
+ ;
466
+
467
+
468
+ select x;
469
+ """
470
+ env, parsed = parse_text(text)
471
+
472
+ results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
473
+
474
+ assert '"abc:def" as test' in results
475
+
476
+ text = """
477
+ key x int;
478
+ key y int;
479
+
480
+ datasource test (
481
+ x:x,
482
+ y:y)
483
+ grain(x)
484
+ address abcdef
485
+ ;
486
+
487
+
488
+ select x;
489
+ """
490
+ env, parsed = parse_text(text)
491
+
492
+ results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
493
+
494
+ assert "abcdef as test" in results, results
@@ -227,5 +227,34 @@ where
227
227
  select: SelectStatement = parsed[-1]
228
228
 
229
229
  assert is_scalar_condition(select.where_clause.conditional) is False
230
- query = BaseDialect().compile_statement(process_query(test_environment, select))
231
- print(query)
230
+ _ = BaseDialect().compile_statement(process_query(test_environment, select))
231
+
232
+
233
+ def test_case_where(test_environment):
234
+ from trilogy.hooks.query_debugger import DebuggingHook
235
+
236
+ declarations = """property order_id_even_name <- CASE
237
+ when order_id %2 = 0 then 'even'
238
+ else 'odd'
239
+ END;
240
+
241
+ const test <- 1;
242
+
243
+ auto order_even_class_filter <- filter category_id where order_id_even_name = 'even' and 1= test;
244
+
245
+ select
246
+ category_id,
247
+ category_name
248
+ where
249
+ category_name like '%abc%' and category_id not in order_even_class_filter
250
+ and category_id = test
251
+ ;"""
252
+ env, parsed = parse(declarations, environment=test_environment)
253
+ select: SelectStatement = parsed[-1]
254
+
255
+ query = BaseDialect().compile_statement(
256
+ process_query(test_environment, select, hooks=[DebuggingHook()])
257
+ )
258
+
259
+ # check to make sure our subselect is well-formed
260
+ assert "`category_id` not in (select" in query, query
@@ -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.2.4"
7
+ __version__ = "0.0.2.6"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -7,6 +7,7 @@ class UnnestMode(Enum):
7
7
  DIRECT = "direct"
8
8
  CROSS_APPLY = "cross_apply"
9
9
  CROSS_JOIN = "cross_join"
10
+ CROSS_JOIN_ALIAS = "cross_join_alias"
10
11
 
11
12
 
12
13
  class ConceptSource(Enum):
@@ -1817,6 +1817,7 @@ class MultiSelectStatement(SelectTypeMixin, Mergeable, Namespaced, BaseModel):
1817
1817
  class Address(BaseModel):
1818
1818
  location: str
1819
1819
  is_query: bool = False
1820
+ quoted: bool = False
1820
1821
 
1821
1822
 
1822
1823
  class Query(BaseModel):
@@ -2433,7 +2434,13 @@ class CTE(BaseModel):
2433
2434
  # if we've entirely removed the need to join to someplace to get the concept
2434
2435
  # drop the join as well.
2435
2436
  for removed_cte in removed:
2436
- still_required = any([removed_cte in x for x in self.source_map.values()])
2437
+ still_required = any(
2438
+ [
2439
+ removed_cte in x
2440
+ for x in self.source_map.values()
2441
+ or self.existence_source_map.values()
2442
+ ]
2443
+ )
2437
2444
  if not still_required:
2438
2445
  self.joins = [
2439
2446
  join
@@ -2451,6 +2458,7 @@ class CTE(BaseModel):
2451
2458
  candidates = [x.name for x in self.parent_ctes]
2452
2459
  self.base_name_override = candidates[0] if candidates else None
2453
2460
  self.base_alias_override = candidates[0] if candidates else None
2461
+ return True
2454
2462
 
2455
2463
  def inline_parent_datasource(self, parent: CTE, force_group: bool = False) -> bool:
2456
2464
  qds_being_inlined = parent.source
@@ -2572,6 +2580,16 @@ class CTE(BaseModel):
2572
2580
  return self.relevant_base_ctes[0].name
2573
2581
  return self.source.name
2574
2582
 
2583
+ @property
2584
+ def quote_address(self) -> bool:
2585
+ if self.is_root_datasource:
2586
+ candidate = self.source.datasources[0]
2587
+ if isinstance(candidate, Datasource) and isinstance(
2588
+ candidate.address, Address
2589
+ ):
2590
+ return candidate.address.quoted
2591
+ return False
2592
+
2575
2593
  @property
2576
2594
  def base_alias(self) -> str:
2577
2595
  if self.base_alias_override:
@@ -3315,7 +3333,7 @@ class Comparison(
3315
3333
  and self.operator == other.operator
3316
3334
  )
3317
3335
 
3318
- def inline_constant(self, constant: Concept) -> "Comparison":
3336
+ def inline_constant(self, constant: Concept):
3319
3337
  assert isinstance(constant.lineage, Function)
3320
3338
  new_val = constant.lineage.arguments[0]
3321
3339
  if isinstance(self.left, ConstantInlineable):
@@ -3332,10 +3350,7 @@ class Comparison(
3332
3350
  else:
3333
3351
  new_right = self.right
3334
3352
 
3335
- if self.right == constant:
3336
- new_right = new_val
3337
-
3338
- return Comparison(
3353
+ return self.__class__(
3339
3354
  left=new_left,
3340
3355
  right=new_right,
3341
3356
  operator=self.operator,
@@ -43,10 +43,14 @@ class InlineDatasource(OptimizationRule):
43
43
  continue
44
44
  root_outputs = {x.address for x in root.output_concepts}
45
45
  cte_outputs = {x.address for x in cte.output_columns}
46
+ inherited = {x for x, v in cte.source_map.items() if v}
46
47
  # cte_inherited_outputs = {x.address for x in parent_cte.output_columns if parent_cte.source_map.get(x.address)}
47
48
  grain_components = {x.address for x in root.grain.components}
48
- if not cte_outputs.issubset(root_outputs):
49
- self.log(f"Not all {parent_cte.name} outputs are found on datasource")
49
+ if not inherited.issubset(root_outputs):
50
+ cte_missing = inherited - root_outputs
51
+ self.log(
52
+ f"Not all {parent_cte.name} require inputs are found on datasource, missing {cte_missing}"
53
+ )
50
54
  continue
51
55
  if not grain_components.issubset(cte_outputs):
52
56
  self.log("Not all datasource components in cte outputs, forcing group")
@@ -188,10 +188,11 @@ FUNCTION_GRAIN_MATCH_MAP = {
188
188
  GENERIC_SQL_TEMPLATE = Template(
189
189
  """{%- if ctes %}
190
190
  WITH {% for cte in ctes %}
191
- {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
191
+ {{cte.name}} as (
192
+ {{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
192
193
  {%- if full_select -%}
193
194
  {{full_select}}
194
- {%- else -%}
195
+ {% else -%}
195
196
  SELECT
196
197
  {%- if limit is not none %}
197
198
  TOP {{ limit }}{% endif %}
@@ -498,10 +499,12 @@ class BaseDialect:
498
499
  for c in cte.output_columns
499
500
  if c.address not in [y.address for y in cte.hidden_concepts]
500
501
  ]
501
- if cte.base_name == cte.base_alias:
502
- source = cte.base_name
502
+ if cte.quote_address:
503
+ source = f"{self.QUOTE_CHARACTER}{cte.base_name}{self.QUOTE_CHARACTER}"
503
504
  else:
504
- source = f"{cte.base_name} as {cte.base_alias}"
505
+ source = cte.base_name
506
+ if cte.base_name != cte.base_alias:
507
+ source = f"{source} as {cte.base_alias}"
505
508
  return CompiledCTE(
506
509
  name=cte.name,
507
510
  statement=self.SQL_TEMPLATE.render(
@@ -26,7 +26,8 @@ def render_join(
26
26
  raise ValueError("must provide a cte to build an unnest joins")
27
27
  if unnest_mode == UnnestMode.CROSS_JOIN:
28
28
  return f"CROSS JOIN {render_func(join.concept, cte, False)} as {quote_character}{join.concept.safe_address}{quote_character}"
29
-
29
+ if unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
30
+ return f"CROSS JOIN {render_func(join.concept, cte, False)} as array_unnest ({quote_character}{join.concept.safe_address}{quote_character})"
30
31
  return f"FULL JOIN {render_func(join.concept, cte, False)} as unnest_wrapper({quote_character}{join.concept.safe_address}{quote_character})"
31
32
  left_name = join.left_name
32
33
  right_name = join.right_name
@@ -49,7 +49,6 @@ WITH {% for cte in ctes %}
49
49
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
50
50
  {% if full_select -%}{{full_select}}
51
51
  {% else -%}
52
-
53
52
  SELECT
54
53
  {%- for select in select_columns %}
55
54
  {{ select }}{% if not loop.last %},{% endif %}{% endfor %}
@@ -5,7 +5,7 @@ from jinja2 import Template
5
5
  from trilogy.core.enums import FunctionType, WindowType
6
6
  from trilogy.dialect.base import BaseDialect
7
7
  from trilogy.core.models import DataType
8
-
8
+ from trilogy.core.enums import UnnestMode
9
9
 
10
10
  WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
11
11
 
@@ -86,6 +86,7 @@ class PrestoDialect(BaseDialect):
86
86
  QUOTE_CHARACTER = '"'
87
87
  SQL_TEMPLATE = SQL_TEMPLATE
88
88
  DATATYPE_MAP = {**BaseDialect.DATATYPE_MAP, DataType.NUMERIC: "DECIMAL"}
89
+ UNNEST_MODE = UnnestMode.CROSS_JOIN
89
90
 
90
91
 
91
92
  class TrinoDialect(PrestoDialect):
@@ -297,8 +297,11 @@ class ParseToObjects(Transformer):
297
297
  def concept_lit(self, args) -> Concept:
298
298
  return self.environment.concepts.__getitem__(args[0])
299
299
 
300
- def ADDRESS(self, args) -> str:
301
- return args.value
300
+ def ADDRESS(self, args) -> Address:
301
+ return Address(location=args.value, quoted=False)
302
+
303
+ def QUOTED_ADDRESS(self, args) -> Address:
304
+ return Address(location=args.value[1:-1], quoted=True)
302
305
 
303
306
  def STRING_CHARS(self, args) -> str:
304
307
  return args.value
@@ -1010,7 +1013,7 @@ class ParseToObjects(Transformer):
1010
1013
 
1011
1014
  @v_args(meta=True)
1012
1015
  def address(self, meta: Meta, args):
1013
- return Address(location=args[0])
1016
+ return args[0]
1014
1017
 
1015
1018
  @v_args(meta=True)
1016
1019
  def query(self, meta: Meta, args):
@@ -39,7 +39,7 @@
39
39
 
40
40
  grain_clause: "grain" "(" column_list ")"
41
41
 
42
- address: "address" ADDRESS
42
+ address: "address" (QUOTED_ADDRESS | ADDRESS)
43
43
 
44
44
  query: "query" MULTILINE_STRING
45
45
 
@@ -258,7 +258,8 @@
258
258
  // base language constructs
259
259
  concept_lit: IDENTIFIER
260
260
  IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.\-]*/
261
- ADDRESS: IDENTIFIER | /`[a-zA-Z\_][a-zA-Z0-9\_\-\.\-\*]*`/
261
+ QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_\-\.\-\*\:]*`/
262
+ ADDRESS: IDENTIFIER
262
263
 
263
264
  MULTILINE_STRING: /\'{3}(.*?)\'{3}/s
264
265
 
File without changes
File without changes
File without changes
File without changes
File without changes