pytrilogy 0.0.3.20__tar.gz → 0.0.3.22__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.
Files changed (134) hide show
  1. {pytrilogy-0.0.3.20/pytrilogy.egg-info → pytrilogy-0.0.3.22}/PKG-INFO +3 -2
  2. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22/pytrilogy.egg-info}/PKG-INFO +3 -2
  3. pytrilogy-0.0.3.22/pytrilogy.egg-info/entry_points.txt +2 -0
  4. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/setup.py +1 -1
  5. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_parsing.py +31 -1
  6. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/constants.py +9 -1
  8. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/execute.py +1 -1
  9. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/concept_strategies_v3.py +7 -5
  10. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/statements/author.py +2 -2
  11. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/base.py +11 -4
  12. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/common.py +4 -1
  13. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/config.py +10 -0
  14. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/enums.py +4 -1
  15. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/executor.py +3 -2
  16. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/parse_engine.py +23 -4
  17. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/render.py +13 -9
  18. pytrilogy-0.0.3.20/pytrilogy.egg-info/entry_points.txt +0 -2
  19. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/LICENSE.md +0 -0
  20. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/README.md +0 -0
  21. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/pyproject.toml +0 -0
  22. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/pytrilogy.egg-info/SOURCES.txt +0 -0
  23. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/pytrilogy.egg-info/dependency_links.txt +0 -0
  24. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/pytrilogy.egg-info/requires.txt +0 -0
  25. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/pytrilogy.egg-info/top_level.txt +0 -0
  26. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/setup.cfg +0 -0
  27. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_datatypes.py +0 -0
  28. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_declarations.py +0 -0
  29. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_derived_concepts.py +0 -0
  30. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_discovery_nodes.py +0 -0
  31. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_enums.py +0 -0
  32. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_environment.py +0 -0
  33. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_executor.py +0 -0
  34. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_functions.py +0 -0
  35. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_imports.py +0 -0
  36. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_metadata.py +0 -0
  37. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_models.py +0 -0
  38. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_multi_join_assignments.py +0 -0
  39. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_parse_engine.py +0 -0
  40. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_partial_handling.py +0 -0
  41. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_query_processing.py +0 -0
  42. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_query_render.py +0 -0
  43. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_select.py +0 -0
  44. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_show.py +0 -0
  45. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_statements.py +0 -0
  46. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_typing.py +0 -0
  47. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_undefined_concept.py +0 -0
  48. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_user_functions.py +0 -0
  49. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/tests/test_where_clause.py +0 -0
  50. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/authoring/__init__.py +0 -0
  51. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/compiler.py +0 -0
  52. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/__init__.py +0 -0
  53. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/constants.py +0 -0
  54. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/enums.py +0 -0
  55. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/env_processor.py +0 -0
  56. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/environment_helpers.py +0 -0
  57. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/ergonomics.py +0 -0
  58. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/exceptions.py +0 -0
  59. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/functions.py +0 -0
  60. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/graph_models.py +0 -0
  61. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/internal.py +0 -0
  62. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/__init__.py +0 -0
  63. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/author.py +0 -0
  64. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/build.py +0 -0
  65. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/build_environment.py +0 -0
  66. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/core.py +0 -0
  67. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/datasource.py +0 -0
  68. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/models/environment.py +0 -0
  69. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimization.py +0 -0
  70. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimizations/__init__.py +0 -0
  71. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimizations/base_optimization.py +0 -0
  72. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimizations/inline_constant.py +0 -0
  73. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimizations/inline_datasource.py +0 -0
  74. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  75. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/__init__.py +0 -0
  76. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/graph_utils.py +0 -0
  77. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/__init__.py +0 -0
  78. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  79. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/common.py +0 -0
  80. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  81. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/group_node.py +0 -0
  82. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  83. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  84. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  85. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  86. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  87. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  88. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  89. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/select_node.py +0 -0
  90. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  91. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/union_node.py +0 -0
  92. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  93. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/node_generators/window_node.py +0 -0
  94. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/__init__.py +0 -0
  95. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/base_node.py +0 -0
  96. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/filter_node.py +0 -0
  97. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/group_node.py +0 -0
  98. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/merge_node.py +0 -0
  99. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  100. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/union_node.py +0 -0
  101. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  102. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/nodes/window_node.py +0 -0
  103. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/processing/utility.py +0 -0
  104. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/query_processor.py +0 -0
  105. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/statements/__init__.py +0 -0
  106. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/statements/build.py +0 -0
  107. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/statements/common.py +0 -0
  108. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/core/statements/execute.py +0 -0
  109. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/__init__.py +0 -0
  110. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/bigquery.py +0 -0
  111. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/dataframe.py +0 -0
  112. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/duckdb.py +0 -0
  113. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/postgres.py +0 -0
  114. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/presto.py +0 -0
  115. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/snowflake.py +0 -0
  116. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/dialect/sql_server.py +0 -0
  117. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/engine.py +0 -0
  118. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/hooks/__init__.py +0 -0
  119. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/hooks/base_hook.py +0 -0
  120. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/hooks/graph_hook.py +0 -0
  121. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/hooks/query_debugger.py +0 -0
  122. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/metadata/__init__.py +0 -0
  123. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parser.py +0 -0
  124. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/__init__.py +0 -0
  125. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/common.py +0 -0
  126. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/config.py +0 -0
  127. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/exceptions.py +0 -0
  128. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/helpers.py +0 -0
  129. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/render.py +0 -0
  130. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/parsing/trilogy.lark +0 -0
  131. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/py.typed +0 -0
  132. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/scripts/__init__.py +0 -0
  133. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/scripts/trilogy.py +0 -0
  134. {pytrilogy-0.0.3.20 → pytrilogy-0.0.3.22}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.20
3
+ Version: 0.0.3.22
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -31,6 +31,7 @@ Dynamic: author-email
31
31
  Dynamic: classifier
32
32
  Dynamic: description
33
33
  Dynamic: description-content-type
34
+ Dynamic: license-file
34
35
  Dynamic: provides-extra
35
36
  Dynamic: requires-dist
36
37
  Dynamic: summary
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.20
3
+ Version: 0.0.3.22
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -31,6 +31,7 @@ Dynamic: author-email
31
31
  Dynamic: classifier
32
32
  Dynamic: description
33
33
  Dynamic: description-content-type
34
+ Dynamic: license-file
34
35
  Dynamic: provides-extra
35
36
  Dynamic: requires-dist
36
37
  Dynamic: summary
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ trilogy = trilogy.scripts.trilogy:cli
@@ -49,7 +49,7 @@ setuptools.setup(
49
49
  "snowflake": ["snowflake-sqlalchemy"],
50
50
  },
51
51
  entry_points={
52
- "console_scripts": ["trilogy=preql.scripts.trilogy:cli"],
52
+ "console_scripts": ["trilogy=trilogy.scripts.trilogy:cli"],
53
53
  },
54
54
  classifiers=[
55
55
  "Programming Language :: Python",
@@ -1,7 +1,7 @@
1
1
  from pytest import raises
2
2
 
3
3
  from trilogy import Dialects
4
- from trilogy.constants import MagicConstants
4
+ from trilogy.constants import MagicConstants, Parsing
5
5
  from trilogy.core.enums import BooleanOperator, ComparisonOperator, Purpose
6
6
  from trilogy.core.functions import argument_to_purpose, function_args_to_output_purpose
7
7
  from trilogy.core.models.author import Comparison
@@ -19,6 +19,7 @@ from trilogy.core.statements.author import SelectStatement, ShowStatement
19
19
  from trilogy.core.statements.execute import ProcessedQuery
20
20
  from trilogy.dialect.base import BaseDialect
21
21
  from trilogy.parsing.parse_engine import (
22
+ ParseError,
22
23
  arg_to_datatype,
23
24
  parse_text,
24
25
  )
@@ -704,3 +705,32 @@ key x int;
704
705
  )
705
706
  assert "TYPO" in str(e.value)
706
707
  assert 1 == 0
708
+
709
+
710
+ def test_concept_shadow_warning():
711
+ x = """
712
+ key scalar int;
713
+ property scalar.int_array list<int>;
714
+
715
+ key split <- unnest(int_array);
716
+
717
+ datasource avalues (
718
+ int_array: int_array,
719
+ scalar: scalar
720
+ )
721
+ grain (scalar)
722
+ query '''(
723
+ select [1,2,3,4] as int_array, 2 as scalar
724
+ union all
725
+ select [5,6,7,8] as int_array, 4 as scalar
726
+ )''';
727
+
728
+ SELECT
729
+ int_array,
730
+ sum(scalar)->scalar
731
+ ;
732
+ """
733
+ with raises(ParseError):
734
+ env, parsed = parse_text(
735
+ x, parse_config=Parsing(strict_name_shadow_enforcement=True)
736
+ )
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.20"
7
+ __version__ = "0.0.3.22"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -47,6 +47,14 @@ class Rendering:
47
47
  concise: bool = False
48
48
 
49
49
 
50
+ @dataclass
51
+ class Parsing:
52
+ """Control Parsing"""
53
+
54
+ strict_name_shadow_enforcement: bool = False
55
+ select_as_definition: bool = True
56
+
57
+
50
58
  # TODO: support loading from environments
51
59
  @dataclass
52
60
  class Config:
@@ -57,7 +65,7 @@ class Config:
57
65
  comments: Comments = field(default_factory=Comments)
58
66
  optimizations: Optimizations = field(default_factory=Optimizations)
59
67
  rendering: Rendering = field(default_factory=Rendering)
60
- select_as_definition: bool = True
68
+ parsing: Parsing = field(default_factory=Parsing)
61
69
 
62
70
  @property
63
71
  def show_comments(self) -> bool:
@@ -162,7 +162,7 @@ class CTE(BaseModel):
162
162
  base += (
163
163
  f"\n-- Nullable: {', '.join([str(x) for x in self.nullable_concepts])}."
164
164
  )
165
-
165
+ base +='\n'
166
166
  return base
167
167
 
168
168
  def inline_parent_datasource(
@@ -787,15 +787,17 @@ def _search_concepts(
787
787
  )
788
788
  # if anything we need to get is in the filter set and it's a computed value
789
789
  # we need to get _everything_ in this loop
790
- if any(
791
- [
792
- x.derivation not in (Derivation.ROOT, Derivation.CONSTANT)
790
+ required_filters = [
791
+ x
792
+ for x in mandatory_list if x.derivation not in (Derivation.ROOT, Derivation.CONSTANT)
793
+ and not (x.derivation == Derivation.AGGREGATE and x.granularity == Granularity.SINGLE_ROW)
793
794
  and x.address in conditions.row_arguments
794
- for x in mandatory_list
795
795
  ]
796
+ if any(
797
+ required_filters
796
798
  ):
797
799
  logger.info(
798
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} derived condition row input present in mandatory list, forcing condition evaluation at this level. "
800
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} derived condition row inputs {[x.address for x in required_filters]} present in mandatory list, forcing condition evaluation at this level. "
799
801
  )
800
802
  mandatory_list = completion_mandatory
801
803
  must_evaluate_condition_on_this_level_not_push_down = True
@@ -142,7 +142,7 @@ class SelectStatement(HasUUID, SelectTypeMixin, BaseModel):
142
142
  if isinstance(x.content.output, UndefinedConcept):
143
143
  continue
144
144
  if (
145
- CONFIG.select_as_definition
145
+ CONFIG.parsing.select_as_definition
146
146
  and not environment.frozen
147
147
  and x.concept.address not in environment.concepts
148
148
  ):
@@ -156,7 +156,7 @@ class SelectStatement(HasUUID, SelectTypeMixin, BaseModel):
156
156
  elif isinstance(x.content, ConceptRef):
157
157
  output.local_concepts[x.content.address] = environment.concepts[
158
158
  x.content.address
159
- ] # .set_select_grain(output.grain, environment)
159
+ ]
160
160
  output.validate_syntax(environment)
161
161
  return output
162
162
 
@@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Union
3
3
 
4
4
  from jinja2 import Template
5
5
 
6
- from trilogy.constants import CONFIG, MagicConstants, logger
6
+ from trilogy.constants import CONFIG, MagicConstants, Rendering, logger
7
7
  from trilogy.core.enums import (
8
8
  DatePart,
9
9
  FunctionType,
@@ -283,6 +283,9 @@ class BaseDialect:
283
283
  DATATYPE_MAP = DATATYPE_MAP
284
284
  UNNEST_MODE = UnnestMode.CROSS_APPLY
285
285
 
286
+ def __init__(self, rendering: Rendering | None = None):
287
+ self.rendering = rendering or CONFIG.rendering
288
+
286
289
  def render_order_item(
287
290
  self,
288
291
  order_item: BuildOrderItem,
@@ -416,7 +419,7 @@ class BaseDialect:
416
419
  elif (
417
420
  isinstance(c.lineage, FUNCTION_ITEMS)
418
421
  and c.lineage.operator == FunctionType.CONSTANT
419
- and CONFIG.rendering.parameters is True
422
+ and self.rendering.parameters is True
420
423
  and c.datatype.data_type != DataType.MAP
421
424
  ):
422
425
  rval = f":{c.safe_address}"
@@ -633,7 +636,7 @@ class BaseDialect:
633
636
  if (
634
637
  isinstance(e.lineage, FUNCTION_ITEMS)
635
638
  and e.lineage.operator == FunctionType.CONSTANT
636
- and CONFIG.rendering.parameters is True
639
+ and self.rendering.parameters is True
637
640
  and e.datatype.data_type != DataType.MAP
638
641
  ):
639
642
  return f":{e.safe_address}"
@@ -723,9 +726,13 @@ class BaseDialect:
723
726
  UnnestMode.CROSS_JOIN_ALIAS,
724
727
  UnnestMode.CROSS_JOIN,
725
728
  UnnestMode.CROSS_APPLY,
726
- UnnestMode.SNOWFLAKE,
727
729
  ):
728
730
 
731
+ source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_concept_sql, cte)}"
732
+ elif (
733
+ cte.join_derived_concepts
734
+ and self.UNNEST_MODE == UnnestMode.SNOWFLAKE
735
+ ):
729
736
  source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_concept_sql, cte)}"
730
737
  # direct - eg DUCK DB - can be directly selected inline
731
738
  elif (
@@ -28,7 +28,10 @@ def render_unnest(
28
28
  elif unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
29
29
  return f"{render_func(concept, cte, False)} as unnest_wrapper ({quote_character}{concept.safe_address}{quote_character})"
30
30
  elif unnest_mode == UnnestMode.SNOWFLAKE:
31
-
31
+ # if we don't actually have a join, we're directly unnesting a concept, and we can skip the flatten
32
+ if not cte.render_from_clause:
33
+ return f"{render_func(concept, cte, False)} as unnest_wrapper ( unnest1, unnest2, unnest3, unnest4, {quote_character}{cte.join_derived_concepts[0].safe_address}{quote_character})"
34
+ # otherwise, flatten the concept for the join
32
35
  return f"flatten({render_func(concept, cte, False)}) as unnest_wrapper ( unnest1, unnest2, unnest3, unnest4, {quote_character}{cte.join_derived_concepts[0].safe_address}{quote_character})"
33
36
  return f"{render_func(concept, cte, False)} as {quote_character}{concept.safe_address}{quote_character}"
34
37
 
@@ -76,12 +76,22 @@ class SnowflakeConfig(DialectConfig):
76
76
  account: str,
77
77
  username: str,
78
78
  password: str,
79
+ database: str | None = None,
80
+ schema: str | None = None,
79
81
  ):
80
82
  self.account = account
81
83
  self.username = username
82
84
  self.password = password
85
+ self.database = database
86
+ self.schema = schema
87
+ if self.schema and not self.database:
88
+ raise ValueError("Setting snowflake schema also requires setting database")
83
89
 
84
90
  def connection_string(self) -> str:
91
+ if self.schema:
92
+ return f"snowflake://{self.username}:{self.password}@{self.account}/{self.database}/{self.schema}"
93
+ if self.database:
94
+ return f"snowflake://{self.username}:{self.password}@{self.account}/{self.database}"
85
95
  return f"snowflake://{self.username}:{self.password}@{self.account}"
86
96
 
87
97
 
@@ -7,7 +7,7 @@ if TYPE_CHECKING:
7
7
  from trilogy import Executor
8
8
  from trilogy.hooks.base_hook import BaseHook
9
9
 
10
- from trilogy.constants import logger
10
+ from trilogy.constants import Rendering, logger
11
11
  from trilogy.dialect.config import DialectConfig
12
12
 
13
13
 
@@ -114,6 +114,7 @@ class Dialects(Enum):
114
114
  environment: Optional["Environment"] = None,
115
115
  hooks: List["BaseHook"] | None = None,
116
116
  conf: DialectConfig | None = None,
117
+ rendering: Rendering | None = None,
117
118
  _engine_factory: Callable | None = None,
118
119
  ) -> "Executor":
119
120
  from trilogy import Executor
@@ -123,6 +124,7 @@ class Dialects(Enum):
123
124
  engine=self.default_engine(conf=conf, _engine_factory=_engine_factory),
124
125
  environment=environment or Environment(),
125
126
  dialect=self,
127
+ rendering=rendering,
126
128
  hooks=hooks,
127
129
  )
128
130
 
@@ -130,5 +132,6 @@ class Dialects(Enum):
130
132
  engine=self.default_engine(conf=conf),
131
133
  environment=environment or Environment(),
132
134
  dialect=self,
135
+ rendering=rendering,
133
136
  hooks=hooks,
134
137
  )
@@ -6,7 +6,7 @@ from typing import Any, Generator, List, Optional, Protocol
6
6
  from sqlalchemy import text
7
7
  from sqlalchemy.engine import CursorResult
8
8
 
9
- from trilogy.constants import logger
9
+ from trilogy.constants import Rendering, logger
10
10
  from trilogy.core.enums import FunctionType, Granularity, IOType
11
11
  from trilogy.core.models.author import Concept, Function
12
12
  from trilogy.core.models.build import BuildConcept, BuildFunction
@@ -75,6 +75,7 @@ class Executor(object):
75
75
  dialect: Dialects,
76
76
  engine: ExecutionEngine,
77
77
  environment: Optional[Environment] = None,
78
+ rendering: Rendering | None = None,
78
79
  hooks: List[BaseHook] | None = None,
79
80
  ):
80
81
  self.dialect: Dialects = dialect
@@ -83,7 +84,7 @@ class Executor(object):
83
84
  self.generator: BaseDialect
84
85
  self.logger = logger
85
86
  self.hooks = hooks
86
- self.generator = get_dialect_generator(self.dialect)
87
+ self.generator = get_dialect_generator(self.dialect, rendering)
87
88
  self.connection = self.engine.connect()
88
89
  # TODO: make generic
89
90
  if self.dialect == Dialects.DATAFRAME:
@@ -18,9 +18,11 @@ from lark.tree import Meta
18
18
  from pydantic import ValidationError
19
19
 
20
20
  from trilogy.constants import (
21
+ CONFIG,
21
22
  DEFAULT_NAMESPACE,
22
23
  NULL_VALUE,
23
24
  MagicConstants,
25
+ Parsing,
24
26
  )
25
27
  from trilogy.core.enums import (
26
28
  BooleanOperator,
@@ -282,6 +284,7 @@ class ParseToObjects(Transformer):
282
284
  text_lookup: dict[Path | str, str] | None = None,
283
285
  environment_lookup: dict[str, Environment] | None = None,
284
286
  import_keys: list[str] | None = None,
287
+ parse_config: Parsing | None = None,
285
288
  ):
286
289
  Transformer.__init__(self, True)
287
290
  self.environment: Environment = environment
@@ -298,6 +301,7 @@ class ParseToObjects(Transformer):
298
301
  self.parse_pass = ParsePass.INITIAL
299
302
  self.function_factory = FunctionFactory(self.environment)
300
303
  self.import_keys: list[str] = import_keys or ["root"]
304
+ self.parse_config: Parsing = parse_config or CONFIG.parsing
301
305
 
302
306
  def set_text(self, text: str):
303
307
  self.text_lookup[self.token_address] = text
@@ -1014,6 +1018,7 @@ class ParseToObjects(Transformer):
1014
1018
  tokens=self.tokens,
1015
1019
  text_lookup=self.text_lookup,
1016
1020
  import_keys=self.import_keys + [cache_key],
1021
+ parse_config=self.parse_config,
1017
1022
  )
1018
1023
  nparser.transform(raw_tokens)
1019
1024
  self.parsed[cache_lookup] = nparser
@@ -1148,8 +1153,8 @@ class ParseToObjects(Transformer):
1148
1153
  elif isinstance(arg, HavingClause):
1149
1154
  having = arg
1150
1155
  if not select_items:
1151
- raise ValueError("Malformed select, missing select items")
1152
-
1156
+ raise ParseError("Malformed select, missing select items")
1157
+ pre_keys = set(self.environment.concepts.keys())
1153
1158
  base = SelectStatement.from_inputs(
1154
1159
  environment=self.environment,
1155
1160
  selection=select_items,
@@ -1159,6 +1164,15 @@ class ParseToObjects(Transformer):
1159
1164
  limit=limit,
1160
1165
  meta=Metadata(line_number=meta.line),
1161
1166
  )
1167
+ if (
1168
+ self.parse_pass == ParsePass.INITIAL
1169
+ and self.parse_config.strict_name_shadow_enforcement
1170
+ ):
1171
+ intersection = base.locally_derived.intersection(pre_keys)
1172
+ if intersection:
1173
+ raise ParseError(
1174
+ f"Select statement {base} has derived concepts {list(intersection)} that shadow existing environment concepts, which may cause unexpected behavior. Rename these."
1175
+ )
1162
1176
  return base
1163
1177
 
1164
1178
  @v_args(meta=True)
@@ -1740,7 +1754,10 @@ def parse_text_raw(text: str, environment: Optional[Environment] = None):
1740
1754
 
1741
1755
 
1742
1756
  def parse_text(
1743
- text: str, environment: Optional[Environment] = None, root: Path | None = None
1757
+ text: str,
1758
+ environment: Optional[Environment] = None,
1759
+ root: Path | None = None,
1760
+ parse_config: Parsing | None = None,
1744
1761
  ) -> Tuple[
1745
1762
  Environment,
1746
1763
  List[
@@ -1756,7 +1773,9 @@ def parse_text(
1756
1773
  environment = environment or (
1757
1774
  Environment(working_path=root) if root else Environment()
1758
1775
  )
1759
- parser = ParseToObjects(environment=environment, import_keys=["root"])
1776
+ parser = ParseToObjects(
1777
+ environment=environment, import_keys=["root"], parse_config=parse_config
1778
+ )
1760
1779
 
1761
1780
  try:
1762
1781
  parser.set_text(text)
@@ -1,38 +1,42 @@
1
+ from trilogy.constants import Rendering
2
+ from trilogy.dialect.base import BaseDialect
1
3
  from trilogy.dialect.enums import Dialects
2
4
 
3
5
 
4
- def get_dialect_generator(dialect: Dialects):
6
+ def get_dialect_generator(
7
+ dialect: Dialects, rendering: Rendering | None = None
8
+ ) -> BaseDialect:
5
9
  if dialect == Dialects.BIGQUERY:
6
10
  from trilogy.dialect.bigquery import BigqueryDialect
7
11
 
8
- return BigqueryDialect()
12
+ return BigqueryDialect(rendering=rendering)
9
13
  elif dialect == Dialects.SQL_SERVER:
10
14
  from trilogy.dialect.sql_server import SqlServerDialect
11
15
 
12
- return SqlServerDialect()
16
+ return SqlServerDialect(rendering=rendering)
13
17
  elif dialect == Dialects.DUCK_DB:
14
18
  from trilogy.dialect.duckdb import DuckDBDialect
15
19
 
16
- return DuckDBDialect()
20
+ return DuckDBDialect(rendering=rendering)
17
21
  elif dialect == Dialects.PRESTO:
18
22
  from trilogy.dialect.presto import PrestoDialect
19
23
 
20
- return PrestoDialect()
24
+ return PrestoDialect(rendering=rendering)
21
25
  elif dialect == Dialects.TRINO:
22
26
  from trilogy.dialect.presto import TrinoDialect
23
27
 
24
- return TrinoDialect()
28
+ return TrinoDialect(rendering=rendering)
25
29
  elif dialect == Dialects.POSTGRES:
26
30
  from trilogy.dialect.postgres import PostgresDialect
27
31
 
28
- return PostgresDialect()
32
+ return PostgresDialect(rendering=rendering)
29
33
  elif dialect == Dialects.SNOWFLAKE:
30
34
  from trilogy.dialect.snowflake import SnowflakeDialect
31
35
 
32
- return SnowflakeDialect()
36
+ return SnowflakeDialect(rendering=rendering)
33
37
  elif dialect == Dialects.DATAFRAME:
34
38
  from trilogy.dialect.dataframe import DataframeDialect
35
39
 
36
- return DataframeDialect()
40
+ return DataframeDialect(rendering=rendering)
37
41
  else:
38
42
  raise ValueError(f"Unsupported dialect {dialect}")
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- trilogy = preql.scripts.trilogy:cli
File without changes
File without changes
File without changes