pytrilogy 0.0.2.58__py3-none-any.whl → 0.0.3.1__py3-none-any.whl

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 (76) hide show
  1. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/METADATA +9 -2
  2. pytrilogy-0.0.3.1.dist-info/RECORD +99 -0
  3. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/WHEEL +1 -1
  4. trilogy/__init__.py +2 -2
  5. trilogy/core/enums.py +1 -7
  6. trilogy/core/env_processor.py +17 -5
  7. trilogy/core/environment_helpers.py +11 -25
  8. trilogy/core/exceptions.py +4 -0
  9. trilogy/core/functions.py +695 -261
  10. trilogy/core/graph_models.py +10 -10
  11. trilogy/core/internal.py +11 -2
  12. trilogy/core/models/__init__.py +0 -0
  13. trilogy/core/models/author.py +2110 -0
  14. trilogy/core/models/build.py +1859 -0
  15. trilogy/core/models/build_environment.py +151 -0
  16. trilogy/core/models/core.py +370 -0
  17. trilogy/core/models/datasource.py +297 -0
  18. trilogy/core/models/environment.py +701 -0
  19. trilogy/core/models/execute.py +931 -0
  20. trilogy/core/optimization.py +14 -16
  21. trilogy/core/optimizations/base_optimization.py +1 -1
  22. trilogy/core/optimizations/inline_constant.py +6 -6
  23. trilogy/core/optimizations/inline_datasource.py +17 -11
  24. trilogy/core/optimizations/predicate_pushdown.py +17 -16
  25. trilogy/core/processing/concept_strategies_v3.py +178 -145
  26. trilogy/core/processing/graph_utils.py +1 -1
  27. trilogy/core/processing/node_generators/basic_node.py +19 -18
  28. trilogy/core/processing/node_generators/common.py +50 -44
  29. trilogy/core/processing/node_generators/filter_node.py +26 -13
  30. trilogy/core/processing/node_generators/group_node.py +26 -21
  31. trilogy/core/processing/node_generators/group_to_node.py +11 -8
  32. trilogy/core/processing/node_generators/multiselect_node.py +60 -43
  33. trilogy/core/processing/node_generators/node_merge_node.py +76 -38
  34. trilogy/core/processing/node_generators/rowset_node.py +55 -36
  35. trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +27 -34
  36. trilogy/core/processing/node_generators/select_merge_node.py +161 -64
  37. trilogy/core/processing/node_generators/select_node.py +13 -13
  38. trilogy/core/processing/node_generators/union_node.py +12 -11
  39. trilogy/core/processing/node_generators/unnest_node.py +9 -7
  40. trilogy/core/processing/node_generators/window_node.py +18 -16
  41. trilogy/core/processing/nodes/__init__.py +21 -18
  42. trilogy/core/processing/nodes/base_node.py +82 -66
  43. trilogy/core/processing/nodes/filter_node.py +19 -13
  44. trilogy/core/processing/nodes/group_node.py +50 -35
  45. trilogy/core/processing/nodes/merge_node.py +45 -36
  46. trilogy/core/processing/nodes/select_node_v2.py +53 -39
  47. trilogy/core/processing/nodes/union_node.py +5 -7
  48. trilogy/core/processing/nodes/unnest_node.py +7 -11
  49. trilogy/core/processing/nodes/window_node.py +9 -4
  50. trilogy/core/processing/utility.py +103 -75
  51. trilogy/core/query_processor.py +70 -47
  52. trilogy/core/statements/__init__.py +0 -0
  53. trilogy/core/statements/author.py +413 -0
  54. trilogy/core/statements/build.py +0 -0
  55. trilogy/core/statements/common.py +30 -0
  56. trilogy/core/statements/execute.py +42 -0
  57. trilogy/dialect/base.py +148 -106
  58. trilogy/dialect/common.py +9 -10
  59. trilogy/dialect/duckdb.py +1 -1
  60. trilogy/dialect/enums.py +4 -2
  61. trilogy/dialect/presto.py +1 -1
  62. trilogy/dialect/sql_server.py +1 -1
  63. trilogy/executor.py +44 -32
  64. trilogy/hooks/__init__.py +4 -0
  65. trilogy/hooks/base_hook.py +6 -4
  66. trilogy/hooks/query_debugger.py +113 -97
  67. trilogy/parser.py +1 -1
  68. trilogy/parsing/common.py +307 -64
  69. trilogy/parsing/parse_engine.py +277 -618
  70. trilogy/parsing/render.py +50 -26
  71. trilogy/scripts/trilogy.py +2 -1
  72. pytrilogy-0.0.2.58.dist-info/RECORD +0 -87
  73. trilogy/core/models.py +0 -4960
  74. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/LICENSE.md +0 -0
  75. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/entry_points.txt +0 -0
  76. {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/top_level.txt +0 -0
trilogy/dialect/base.py CHANGED
@@ -11,63 +11,82 @@ from trilogy.core.enums import (
11
11
  WindowType,
12
12
  )
13
13
  from trilogy.core.internal import DEFAULT_CONCEPTS
14
- from trilogy.core.models import (
15
- CTE,
16
- AggregateWrapper,
17
- CaseElse,
18
- CaseWhen,
19
- Comparison,
20
- CompiledCTE,
21
- Concept,
22
- ConceptDeclarationStatement,
23
- Conditional,
24
- CopyStatement,
25
- Datasource,
14
+ from trilogy.core.models.build import (
15
+ BuildAggregateWrapper,
16
+ BuildCaseElse,
17
+ BuildCaseWhen,
18
+ BuildComparison,
19
+ BuildConcept,
20
+ BuildConditional,
21
+ BuildFilterItem,
22
+ BuildFunction,
23
+ BuildMultiSelectLineage,
24
+ BuildOrderItem,
25
+ BuildParenthetical,
26
+ BuildRowsetItem,
27
+ BuildSubselectComparison,
28
+ BuildWindowItem,
29
+ Factory,
30
+ )
31
+ from trilogy.core.models.core import (
26
32
  DataType,
27
- Environment,
28
- FilterItem,
29
- Function,
30
- ImportStatement,
31
33
  ListType,
32
34
  ListWrapper,
33
35
  MapType,
34
36
  MapWrapper,
35
- MergeStatementV2,
36
- MultiSelectStatement,
37
37
  NumericType,
38
- OrderItem,
39
- Parenthetical,
40
- PersistStatement,
41
- ProcessedCopyStatement,
42
- ProcessedQuery,
43
- ProcessedQueryPersist,
44
- ProcessedRawSQLStatement,
45
- ProcessedShowStatement,
46
- RawColumnExpr,
47
- RawSQLStatement,
48
- RowsetDerivationStatement,
49
- RowsetItem,
50
- SelectStatement,
51
- ShowStatement,
52
38
  StructType,
53
- SubselectComparison,
54
39
  TupleWrapper,
55
- UnionCTE,
56
- WindowItem,
57
40
  )
41
+ from trilogy.core.models.datasource import Datasource, RawColumnExpr
42
+ from trilogy.core.models.environment import Environment
43
+ from trilogy.core.models.execute import CTE, CompiledCTE, UnionCTE
58
44
  from trilogy.core.processing.utility import (
59
45
  decompose_condition,
60
46
  is_scalar_condition,
61
47
  sort_select_output,
62
48
  )
63
49
  from trilogy.core.query_processor import process_copy, process_persist, process_query
50
+ from trilogy.core.statements.author import (
51
+ ConceptDeclarationStatement,
52
+ CopyStatement,
53
+ ImportStatement,
54
+ MergeStatementV2,
55
+ MultiSelectStatement,
56
+ PersistStatement,
57
+ RawSQLStatement,
58
+ RowsetDerivationStatement,
59
+ SelectStatement,
60
+ ShowStatement,
61
+ )
62
+ from trilogy.core.statements.execute import (
63
+ ProcessedCopyStatement,
64
+ ProcessedQuery,
65
+ ProcessedQueryPersist,
66
+ ProcessedRawSQLStatement,
67
+ ProcessedShowStatement,
68
+ )
64
69
  from trilogy.dialect.common import render_join, render_unnest
65
70
  from trilogy.hooks.base_hook import BaseHook
66
71
 
67
72
  LOGGER_PREFIX = "[RENDERING]"
68
73
 
74
+ WINDOW_ITEMS = (BuildWindowItem,)
75
+ FILTER_ITEMS = (BuildFilterItem,)
76
+ AGGREGATE_ITEMS = (BuildAggregateWrapper,)
77
+ FUNCTION_ITEMS = (BuildFunction,)
78
+ PARENTHETICAL_ITEMS = (BuildParenthetical,)
79
+ CASE_WHEN_ITEMS = (BuildCaseWhen,)
80
+ CASE_ELSE_ITEMS = (BuildCaseElse,)
81
+ SUBSELECT_COMPARISON_ITEMS = (BuildSubselectComparison,)
82
+ COMPARISON_ITEMS = (BuildComparison,)
83
+ CONDITIONAL_ITEMS = (BuildConditional,)
84
+
69
85
 
70
86
  def INVALID_REFERENCE_STRING(x: Any, callsite: str = ""):
87
+ # if CONFIG.validate_missing:
88
+ # raise SyntaxError(f"INVALID_REFERENCE_BUG_{callsite}<{x}>")
89
+
71
90
  return f"INVALID_REFERENCE_BUG_{callsite}<{x}>"
72
91
 
73
92
 
@@ -237,7 +256,7 @@ def safe_quote(string: str, quote_char: str):
237
256
  return ".".join([f"{quote_char}{string}{quote_char}" for string in components])
238
257
 
239
258
 
240
- def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c: Concept, quote_char: str):
259
+ def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c: BuildConcept, quote_char: str):
241
260
  address = c.address
242
261
  raw = cte.source_map.get(address, None)
243
262
 
@@ -265,25 +284,26 @@ class BaseDialect:
265
284
 
266
285
  def render_order_item(
267
286
  self,
268
- order_item: OrderItem,
287
+ order_item: BuildOrderItem,
269
288
  cte: CTE | UnionCTE,
270
289
  final: bool = False,
271
290
  alias: bool = True,
272
291
  ) -> str:
273
- if final:
274
- if not alias:
275
- return f"{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
292
+ # if final:
293
+ # if not alias:
294
+ # return f"{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
276
295
 
277
- return f"{cte.name}.{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
296
+ # return f"{cte.name}.{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
278
297
 
279
- return f"{self.render_concept_sql(order_item.expr, cte=cte, alias=False)} {order_item.order.value}"
298
+ return f"{self.render_expr(order_item.expr, cte=cte, qualify=False)} {order_item.order.value}"
280
299
 
281
300
  def render_concept_sql(
282
301
  self,
283
- c: Concept,
302
+ c: BuildConcept,
284
303
  cte: CTE | UnionCTE,
285
304
  alias: bool = True,
286
305
  raise_invalid: bool = False,
306
+ qualify: bool = True,
287
307
  ) -> str:
288
308
  result = None
289
309
  if c.pseudonyms:
@@ -297,20 +317,26 @@ class BaseDialect:
297
317
  f"{LOGGER_PREFIX} [{c.address}] Attempting rendering w/ candidate {candidate.address}"
298
318
  )
299
319
  result = self._render_concept_sql(
300
- candidate, cte, raise_invalid=True
320
+ candidate, cte, raise_invalid=True, qualify=qualify
301
321
  )
302
322
  if result:
303
323
  break
304
324
  except ValueError:
305
325
  continue
306
326
  if not result:
307
- result = self._render_concept_sql(c, cte, raise_invalid=raise_invalid)
327
+ result = self._render_concept_sql(
328
+ c, cte, raise_invalid=raise_invalid, qualify=qualify
329
+ )
308
330
  if alias:
309
331
  return f"{result} as {self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
310
332
  return result
311
333
 
312
334
  def _render_concept_sql(
313
- self, c: Concept, cte: CTE | UnionCTE, raise_invalid: bool = False
335
+ self,
336
+ c: BuildConcept,
337
+ cte: CTE | UnionCTE,
338
+ raise_invalid: bool = False,
339
+ qualify: bool = True,
314
340
  ) -> str:
315
341
  # only recurse while it's in sources of the current cte
316
342
  logger.debug(
@@ -322,9 +348,9 @@ class BaseDialect:
322
348
  logger.debug(
323
349
  f"{LOGGER_PREFIX} [{c.address}] rendering concept with lineage that is not already existing, have {cte.source_map}"
324
350
  )
325
- if isinstance(c.lineage, WindowItem):
351
+ if isinstance(c.lineage, WINDOW_ITEMS):
326
352
  rendered_order_components = [
327
- f"{self.render_concept_sql(x.expr, cte, alias=False, raise_invalid=raise_invalid)} {x.order.value}"
353
+ f"{self.render_expr(x.expr, cte, raise_invalid=raise_invalid)} {x.order.value}"
328
354
  for x in c.lineage.order_by
329
355
  ]
330
356
  rendered_over_components = [
@@ -345,17 +371,17 @@ class BaseDialect:
345
371
  sort=",".join(rendered_order_components),
346
372
  offset=c.lineage.index,
347
373
  )
348
- elif isinstance(c.lineage, FilterItem):
374
+ elif isinstance(c.lineage, FILTER_ITEMS):
349
375
  # for cases when we've optimized this
350
376
  if cte.condition == c.lineage.where.conditional:
351
377
  rval = self.render_expr(c.lineage.content, cte=cte)
352
378
  else:
353
379
  rval = f"CASE WHEN {self.render_expr(c.lineage.where.conditional, cte=cte)} THEN {self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid)} ELSE NULL END"
354
- elif isinstance(c.lineage, RowsetItem):
380
+ elif isinstance(c.lineage, BuildRowsetItem):
355
381
  rval = f"{self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid)}"
356
- elif isinstance(c.lineage, MultiSelectStatement):
382
+ elif isinstance(c.lineage, BuildMultiSelectLineage):
357
383
  rval = f"{self.render_concept_sql(c.lineage.find_source(c, cte), cte=cte, alias=False, raise_invalid=raise_invalid)}"
358
- elif isinstance(c.lineage, AggregateWrapper):
384
+ elif isinstance(c.lineage, AGGREGATE_ITEMS):
359
385
  args = [
360
386
  self.render_expr(v, cte) # , alias=False)
361
387
  for v in c.lineage.function.arguments
@@ -369,21 +395,21 @@ class BaseDialect:
369
395
  )
370
396
  rval = f"{self.FUNCTION_GRAIN_MATCH_MAP[c.lineage.function.operator](args)}"
371
397
  elif (
372
- isinstance(c.lineage, Function)
398
+ isinstance(c.lineage, FUNCTION_ITEMS)
373
399
  and c.lineage.operator == FunctionType.UNION
374
400
  ):
375
401
  local_matched = [
376
402
  x
377
403
  for x in c.lineage.arguments
378
- if isinstance(x, Concept) and x.address in cte.output_columns
404
+ if isinstance(x, BuildConcept) and x.address in cte.output_columns
379
405
  ]
406
+ # if we're sorting by the output of the union
380
407
  if not local_matched:
381
- raise SyntaxError(
382
- "Could not find appropriate source element for union"
383
- )
384
- rval = self.render_expr(local_matched[0], cte)
408
+ rval = c.safe_address
409
+ else:
410
+ rval = self.render_expr(local_matched[0], cte)
385
411
  elif (
386
- isinstance(c.lineage, Function)
412
+ isinstance(c.lineage, FUNCTION_ITEMS)
387
413
  and c.lineage.operator == FunctionType.CONSTANT
388
414
  and CONFIG.rendering.parameters is True
389
415
  and c.datatype.data_type != DataType.MAP
@@ -393,9 +419,9 @@ class BaseDialect:
393
419
  args = []
394
420
  for arg in c.lineage.arguments:
395
421
  if (
396
- isinstance(arg, Concept)
422
+ isinstance(arg, BuildConcept)
397
423
  and arg.lineage
398
- and isinstance(arg.lineage, Function)
424
+ and isinstance(arg.lineage, FUNCTION_ITEMS)
399
425
  and arg.lineage.operator
400
426
  in (
401
427
  FunctionType.ADD,
@@ -406,7 +432,7 @@ class BaseDialect:
406
432
  ):
407
433
  args.append(
408
434
  self.render_expr(
409
- Parenthetical(content=arg),
435
+ BuildParenthetical(content=arg),
410
436
  cte=cte,
411
437
  raise_invalid=raise_invalid,
412
438
  )
@@ -422,13 +448,13 @@ class BaseDialect:
422
448
  rval = f"{self.FUNCTION_GRAIN_MATCH_MAP[c.lineage.operator](args)}"
423
449
  else:
424
450
  logger.debug(
425
- f"{LOGGER_PREFIX} [{c.address}] Rendering basic lookup from {cte.source_map.get(c.address, INVALID_REFERENCE_STRING('Missing source reference'))}"
451
+ f"{LOGGER_PREFIX} [{c.address}] Rendering basic lookup from {cte.source_map.get(c.address,None)}"
426
452
  )
427
453
 
428
454
  raw_content = cte.get_alias(c)
429
455
  if isinstance(raw_content, RawColumnExpr):
430
456
  rval = raw_content.text
431
- elif isinstance(raw_content, Function):
457
+ elif isinstance(raw_content, FUNCTION_ITEMS):
432
458
  rval = self.render_expr(
433
459
  raw_content, cte=cte, raise_invalid=raise_invalid
434
460
  )
@@ -440,23 +466,34 @@ class BaseDialect:
440
466
  self.QUOTE_CHARACTER,
441
467
  )
442
468
  if not rval:
443
- if raise_invalid:
444
- raise ValueError(
445
- f"Invalid reference string found in query: {rval}, this should never occur. Please report this issue."
469
+ # unions won't have a specific source mapped; just use a generic column reference
470
+ # we shouldn't ever have an expression at this point, so will be safe
471
+ if isinstance(cte, UnionCTE):
472
+ rval = c.safe_address
473
+ else:
474
+ if raise_invalid:
475
+ raise ValueError(
476
+ f"Invalid reference string found in query: {rval}, this should never occur. Please report this issue."
477
+ )
478
+ rval = INVALID_REFERENCE_STRING(
479
+ f"Missing source reference to {c.address}"
446
480
  )
447
- rval = INVALID_REFERENCE_STRING(
448
- f"Missing source reference to {c.address}"
449
- )
450
481
  return rval
451
482
 
452
483
  def render_expr(
453
484
  self,
454
485
  e: Union[
455
- Function,
456
- Conditional,
457
- Comparison,
458
- SubselectComparison,
459
- Concept,
486
+ BuildConcept,
487
+ BuildFunction,
488
+ BuildConditional,
489
+ BuildAggregateWrapper,
490
+ BuildComparison,
491
+ BuildCaseWhen,
492
+ BuildCaseElse,
493
+ BuildSubselectComparison,
494
+ BuildWindowItem,
495
+ BuildFilterItem,
496
+ BuildParenthetical,
460
497
  str,
461
498
  int,
462
499
  list,
@@ -465,9 +502,6 @@ class BaseDialect:
465
502
  date,
466
503
  datetime,
467
504
  DataType,
468
- Function,
469
- Parenthetical,
470
- AggregateWrapper,
471
505
  MagicConstants,
472
506
  MapWrapper[Any, Any],
473
507
  MapType,
@@ -477,18 +511,14 @@ class BaseDialect:
477
511
  ListWrapper[Any],
478
512
  TupleWrapper[Any],
479
513
  DatePart,
480
- CaseWhen,
481
- CaseElse,
482
- WindowItem,
483
- FilterItem,
484
- # FilterItem
485
514
  ],
486
515
  cte: Optional[CTE | UnionCTE] = None,
487
516
  cte_map: Optional[Dict[str, CTE | UnionCTE]] = None,
488
517
  raise_invalid: bool = False,
518
+ qualify: bool = True,
489
519
  ) -> str:
490
- if isinstance(e, SubselectComparison):
491
- if isinstance(e.right, Concept):
520
+ if isinstance(e, SUBSELECT_COMPARISON_ITEMS):
521
+ if isinstance(e.right, BuildConcept):
492
522
  # we won't always have an existnce map
493
523
  # so fall back to the normal map
494
524
  lookup_cte = cte
@@ -513,7 +543,10 @@ class BaseDialect:
513
543
  f"Missing source CTE for {e.right.address}"
514
544
  )
515
545
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} (select {target}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} from {target} where {target}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} is not null)"
516
- elif isinstance(e.right, (ListWrapper, TupleWrapper, Parenthetical, list)):
546
+ elif isinstance(
547
+ e.right,
548
+ (ListWrapper, TupleWrapper, BuildParenthetical, list),
549
+ ):
517
550
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
518
551
 
519
552
  elif isinstance(
@@ -528,12 +561,12 @@ class BaseDialect:
528
561
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} ({self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)})"
529
562
  else:
530
563
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
531
- elif isinstance(e, Comparison):
564
+ elif isinstance(e, COMPARISON_ITEMS):
532
565
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
533
- elif isinstance(e, Conditional):
566
+ elif isinstance(e, CONDITIONAL_ITEMS):
534
567
  # conditions need to be nested in parentheses
535
568
  return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
536
- elif isinstance(e, WindowItem):
569
+ elif isinstance(e, WINDOW_ITEMS):
537
570
  rendered_order_components = [
538
571
  f"{self.render_expr(x.expr, cte, cte_map=cte_map, raise_invalid=raise_invalid)} {x.order.value}"
539
572
  for x in e.order_by
@@ -543,22 +576,22 @@ class BaseDialect:
543
576
  for x in e.over
544
577
  ]
545
578
  return f"{self.WINDOW_FUNCTION_MAP[e.type](concept = self.render_expr(e.content, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
546
- elif isinstance(e, Parenthetical):
579
+ elif isinstance(e, PARENTHETICAL_ITEMS):
547
580
  # conditions need to be nested in parentheses
548
581
  if isinstance(e.content, list):
549
582
  return f"( {','.join([self.render_expr(x, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid) for x in e.content])} )"
550
583
  return f"( {self.render_expr(e.content, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} )"
551
- elif isinstance(e, CaseWhen):
584
+ elif isinstance(e, CASE_WHEN_ITEMS):
552
585
  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, raise_invalid=raise_invalid) }"
553
- elif isinstance(e, CaseElse):
586
+ elif isinstance(e, CASE_ELSE_ITEMS):
554
587
  return f"ELSE {self.render_expr(e.expr, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid) }"
555
- elif isinstance(e, Function):
588
+ elif isinstance(e, FUNCTION_ITEMS):
556
589
  arguments = []
557
590
  for arg in e.arguments:
558
591
  if (
559
- isinstance(arg, Concept)
592
+ isinstance(arg, BuildConcept)
560
593
  and arg.lineage
561
- and isinstance(arg.lineage, Function)
594
+ and isinstance(arg.lineage, FUNCTION_ITEMS)
562
595
  and arg.lineage.operator
563
596
  in (
564
597
  FunctionType.ADD,
@@ -569,7 +602,7 @@ class BaseDialect:
569
602
  ):
570
603
  arguments.append(
571
604
  self.render_expr(
572
- Parenthetical(content=arg),
605
+ BuildParenthetical(content=arg),
573
606
  cte=cte,
574
607
  cte_map=cte_map,
575
608
  raise_invalid=raise_invalid,
@@ -586,15 +619,15 @@ class BaseDialect:
586
619
  return self.FUNCTION_MAP[e.operator](arguments)
587
620
 
588
621
  return self.FUNCTION_GRAIN_MATCH_MAP[e.operator](arguments)
589
- elif isinstance(e, AggregateWrapper):
622
+ elif isinstance(e, AGGREGATE_ITEMS):
590
623
  return self.render_expr(
591
624
  e.function, cte, cte_map=cte_map, raise_invalid=raise_invalid
592
625
  )
593
- elif isinstance(e, FilterItem):
626
+ elif isinstance(e, FILTER_ITEMS):
594
627
  return f"CASE WHEN {self.render_expr(e.where.conditional,cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} THEN {self.render_expr(e.content, cte, cte_map=cte_map, raise_invalid=raise_invalid)} ELSE NULL END"
595
- elif isinstance(e, Concept):
628
+ elif isinstance(e, BuildConcept):
596
629
  if (
597
- isinstance(e.lineage, Function)
630
+ isinstance(e.lineage, FUNCTION_ITEMS)
598
631
  and e.lineage.operator == FunctionType.CONSTANT
599
632
  and CONFIG.rendering.parameters is True
600
633
  and e.datatype.data_type != DataType.MAP
@@ -602,7 +635,11 @@ class BaseDialect:
602
635
  return f":{e.safe_address}"
603
636
  if cte:
604
637
  return self.render_concept_sql(
605
- e, cte, alias=False, raise_invalid=raise_invalid
638
+ e,
639
+ cte,
640
+ alias=False,
641
+ raise_invalid=raise_invalid,
642
+ qualify=qualify,
606
643
  )
607
644
  elif cte_map:
608
645
  return f"{cte_map[e.address].name}.{self.QUOTE_CHARACTER}{e.safe_address}{self.QUOTE_CHARACTER}"
@@ -643,6 +680,7 @@ class BaseDialect:
643
680
  [self.render_cte(child).statement for child in cte.internal_ctes]
644
681
  )
645
682
  if cte.order_by:
683
+
646
684
  ordering = [
647
685
  self.render_order_item(i, cte, final=True, alias=False)
648
686
  for i in cte.order_by.items
@@ -703,8 +741,8 @@ class BaseDialect:
703
741
  final_joins = []
704
742
  else:
705
743
  final_joins = cte.joins or []
706
- where: Conditional | Parenthetical | Comparison | None = None
707
- having: Conditional | Parenthetical | Comparison | None = None
744
+ where: BuildConditional | BuildParenthetical | BuildComparison | None = None
745
+ having: BuildConditional | BuildParenthetical | BuildComparison | None = None
708
746
  materialized = {x for x, v in cte.source_map.items() if v}
709
747
  if cte.condition:
710
748
  if not cte.group_to_grain or is_scalar_condition(
@@ -807,6 +845,7 @@ class BaseDialect:
807
845
  | ProcessedRawSQLStatement
808
846
  | ProcessedCopyStatement
809
847
  ] = []
848
+ factory = Factory(environment=environment)
810
849
  for statement in statements:
811
850
  if isinstance(statement, PersistStatement):
812
851
  if hooks:
@@ -837,12 +876,15 @@ class BaseDialect:
837
876
  elif isinstance(statement, ShowStatement):
838
877
  # TODO - encapsulate this a little better
839
878
  if isinstance(statement.content, SelectStatement):
879
+
840
880
  output.append(
841
881
  ProcessedShowStatement(
842
882
  output_columns=[
843
- environment.concepts[
844
- DEFAULT_CONCEPTS["query_text"].address
845
- ]
883
+ factory.build(
884
+ environment.concepts[
885
+ DEFAULT_CONCEPTS["query_text"].address
886
+ ]
887
+ )
846
888
  ],
847
889
  output_values=[
848
890
  process_query(
trilogy/dialect/common.py CHANGED
@@ -1,13 +1,12 @@
1
1
  from typing import Callable
2
2
 
3
3
  from trilogy.core.enums import Modifier, UnnestMode
4
- from trilogy.core.models import (
4
+ from trilogy.core.models.build import BuildConcept, BuildFunction
5
+ from trilogy.core.models.datasource import RawColumnExpr
6
+ from trilogy.core.models.execute import (
5
7
  CTE,
6
- Concept,
7
- Function,
8
8
  InstantiatedUnnestJoin,
9
9
  Join,
10
- RawColumnExpr,
11
10
  )
12
11
 
13
12
 
@@ -20,8 +19,8 @@ def null_wrapper(lval: str, rval: str, modifiers: list[Modifier]) -> str:
20
19
  def render_unnest(
21
20
  unnest_mode: UnnestMode,
22
21
  quote_character: str,
23
- concept: Concept,
24
- render_func: Callable[[Concept, CTE, bool], str],
22
+ concept: BuildConcept,
23
+ render_func: Callable[[BuildConcept, CTE, bool], str],
25
24
  cte: CTE,
26
25
  ):
27
26
  if unnest_mode == UnnestMode.CROSS_JOIN:
@@ -33,7 +32,7 @@ def render_join_concept(
33
32
  name: str,
34
33
  quote_character: str,
35
34
  cte: CTE,
36
- concept: Concept,
35
+ concept: BuildConcept,
37
36
  render_expr,
38
37
  inlined_ctes: set[str],
39
38
  ):
@@ -43,7 +42,7 @@ def render_join_concept(
43
42
  if isinstance(raw_content, RawColumnExpr):
44
43
  rval = raw_content.text
45
44
  return rval
46
- elif isinstance(raw_content, Function):
45
+ elif isinstance(raw_content, BuildFunction):
47
46
  rval = render_expr(raw_content, cte=cte)
48
47
  return rval
49
48
  return f"{name}.{quote_character}{raw_content}{quote_character}"
@@ -53,8 +52,8 @@ def render_join_concept(
53
52
  def render_join(
54
53
  join: Join | InstantiatedUnnestJoin,
55
54
  quote_character: str,
56
- render_func: Callable[[Concept, CTE, bool], str],
57
- render_expr_func: Callable[[Concept, CTE], str],
55
+ render_func: Callable[[BuildConcept, CTE, bool], str],
56
+ render_expr_func: Callable[[BuildConcept, CTE], str],
58
57
  cte: CTE,
59
58
  unnest_mode: UnnestMode = UnnestMode.CROSS_APPLY,
60
59
  ) -> str | None:
trilogy/dialect/duckdb.py CHANGED
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
3
3
  from jinja2 import Template
4
4
 
5
5
  from trilogy.core.enums import FunctionType, UnnestMode, WindowType
6
- from trilogy.core.models import DataType
6
+ from trilogy.core.models.core import DataType
7
7
  from trilogy.dialect.base import BaseDialect
8
8
 
9
9
  WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
trilogy/dialect/enums.py CHANGED
@@ -1,8 +1,10 @@
1
1
  from enum import Enum
2
2
  from typing import TYPE_CHECKING, Callable, List, Optional
3
3
 
4
+ from trilogy.core.models.environment import Environment
5
+
4
6
  if TYPE_CHECKING:
5
- from trilogy import Environment, Executor
7
+ from trilogy import Executor
6
8
  from trilogy.hooks.base_hook import BaseHook
7
9
 
8
10
  from trilogy.constants import logger
@@ -98,7 +100,7 @@ class Dialects(Enum):
98
100
  conf: DialectConfig | None = None,
99
101
  _engine_factory: Callable | None = None,
100
102
  ) -> "Executor":
101
- from trilogy import Environment, Executor
103
+ from trilogy import Executor
102
104
 
103
105
  if _engine_factory is not None:
104
106
  return Executor(
trilogy/dialect/presto.py CHANGED
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
3
3
  from jinja2 import Template
4
4
 
5
5
  from trilogy.core.enums import FunctionType, UnnestMode, WindowType
6
- from trilogy.core.models import DataType
6
+ from trilogy.core.models.core import DataType
7
7
  from trilogy.dialect.base import BaseDialect
8
8
 
9
9
  WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
3
3
  from jinja2 import Template
4
4
 
5
5
  from trilogy.core.enums import FunctionType, WindowType
6
- from trilogy.core.models import (
6
+ from trilogy.core.statements.execute import (
7
7
  ProcessedQuery,
8
8
  ProcessedQueryPersist,
9
9
  ProcessedRawSQLStatement,