pytrilogy 0.0.2.34__tar.gz → 0.0.2.35__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 (110) hide show
  1. {pytrilogy-0.0.2.34/pytrilogy.egg-info → pytrilogy-0.0.2.35}/PKG-INFO +2 -1
  2. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35/pytrilogy.egg-info}/PKG-INFO +2 -1
  3. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pytrilogy.egg-info/SOURCES.txt +1 -0
  4. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pytrilogy.egg-info/requires.txt +1 -0
  5. pytrilogy-0.0.2.35/tests/test_enums.py +12 -0
  6. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_select.py +19 -0
  7. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/__init__.py +1 -1
  8. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/enums.py +17 -0
  9. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/models.py +39 -0
  10. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/parse_engine.py +1 -57
  11. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/LICENSE.md +0 -0
  12. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/README.md +0 -0
  13. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pyproject.toml +0 -0
  14. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pytrilogy.egg-info/dependency_links.txt +0 -0
  15. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pytrilogy.egg-info/entry_points.txt +0 -0
  16. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/pytrilogy.egg-info/top_level.txt +0 -0
  17. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/setup.cfg +0 -0
  18. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/setup.py +0 -0
  19. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_datatypes.py +0 -0
  20. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_declarations.py +0 -0
  21. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_derived_concepts.py +0 -0
  22. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_discovery_nodes.py +0 -0
  23. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_environment.py +0 -0
  24. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_executor.py +0 -0
  25. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_functions.py +0 -0
  26. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_imports.py +0 -0
  27. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_metadata.py +0 -0
  28. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_models.py +0 -0
  29. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_multi_join_assignments.py +0 -0
  30. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_parse_engine.py +0 -0
  31. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_parsing.py +0 -0
  32. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_partial_handling.py +0 -0
  33. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_query_processing.py +0 -0
  34. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_show.py +0 -0
  35. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_statements.py +0 -0
  36. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_undefined_concept.py +0 -0
  37. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/tests/test_where_clause.py +0 -0
  38. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/compiler.py +0 -0
  39. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/constants.py +0 -0
  40. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/__init__.py +0 -0
  41. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/constants.py +0 -0
  42. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/env_processor.py +0 -0
  43. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/environment_helpers.py +0 -0
  44. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/ergonomics.py +0 -0
  45. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/exceptions.py +0 -0
  46. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/functions.py +0 -0
  47. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/graph_models.py +0 -0
  48. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/internal.py +0 -0
  49. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimization.py +0 -0
  50. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimizations/__init__.py +0 -0
  51. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimizations/base_optimization.py +0 -0
  52. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimizations/inline_constant.py +0 -0
  53. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimizations/inline_datasource.py +0 -0
  54. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  55. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/__init__.py +0 -0
  56. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  57. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/graph_utils.py +0 -0
  58. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/__init__.py +0 -0
  59. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  60. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/common.py +0 -0
  61. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  62. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/group_node.py +0 -0
  63. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  64. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  65. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  66. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  67. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  68. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/select_node.py +0 -0
  69. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  70. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/node_generators/window_node.py +0 -0
  71. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/__init__.py +0 -0
  72. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/base_node.py +0 -0
  73. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/filter_node.py +0 -0
  74. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/group_node.py +0 -0
  75. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/merge_node.py +0 -0
  76. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  77. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  78. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/nodes/window_node.py +0 -0
  79. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/processing/utility.py +0 -0
  80. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/core/query_processor.py +0 -0
  81. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/__init__.py +0 -0
  82. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/base.py +0 -0
  83. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/bigquery.py +0 -0
  84. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/common.py +0 -0
  85. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/config.py +0 -0
  86. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/duckdb.py +0 -0
  87. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/enums.py +0 -0
  88. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/postgres.py +0 -0
  89. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/presto.py +0 -0
  90. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/snowflake.py +0 -0
  91. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/dialect/sql_server.py +0 -0
  92. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/engine.py +0 -0
  93. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/executor.py +0 -0
  94. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/hooks/__init__.py +0 -0
  95. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/hooks/base_hook.py +0 -0
  96. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/hooks/graph_hook.py +0 -0
  97. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/hooks/query_debugger.py +0 -0
  98. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/metadata/__init__.py +0 -0
  99. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parser.py +0 -0
  100. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/__init__.py +0 -0
  101. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/common.py +0 -0
  102. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/config.py +0 -0
  103. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/exceptions.py +0 -0
  104. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/helpers.py +0 -0
  105. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/render.py +0 -0
  106. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/parsing/trilogy.lark +0 -0
  107. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/py.typed +0 -0
  108. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/scripts/__init__.py +0 -0
  109. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/scripts/trilogy.py +0 -0
  110. {pytrilogy-0.0.2.34 → pytrilogy-0.0.2.35}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.34
3
+ Version: 0.0.2.35
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -20,6 +20,7 @@ Requires-Dist: networkx
20
20
  Requires-Dist: pyodbc
21
21
  Requires-Dist: pydantic
22
22
  Requires-Dist: duckdb-engine
23
+ Requires-Dist: click
23
24
  Provides-Extra: postgres
24
25
  Requires-Dist: psycopg2-binary; extra == "postgres"
25
26
  Provides-Extra: bigquery
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.34
3
+ Version: 0.0.2.35
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -20,6 +20,7 @@ Requires-Dist: networkx
20
20
  Requires-Dist: pyodbc
21
21
  Requires-Dist: pydantic
22
22
  Requires-Dist: duckdb-engine
23
+ Requires-Dist: click
23
24
  Provides-Extra: postgres
24
25
  Requires-Dist: psycopg2-binary; extra == "postgres"
25
26
  Provides-Extra: bigquery
@@ -12,6 +12,7 @@ tests/test_datatypes.py
12
12
  tests/test_declarations.py
13
13
  tests/test_derived_concepts.py
14
14
  tests/test_discovery_nodes.py
15
+ tests/test_enums.py
15
16
  tests/test_environment.py
16
17
  tests/test_executor.py
17
18
  tests/test_functions.py
@@ -5,6 +5,7 @@ networkx
5
5
  pyodbc
6
6
  pydantic
7
7
  duckdb-engine
8
+ click
8
9
 
9
10
  [bigquery]
10
11
  sqlalchemy-bigquery
@@ -0,0 +1,12 @@
1
+ from trilogy.core.enums import BooleanOperator, Boolean
2
+
3
+
4
+ def test_boolean_operator():
5
+ assert BooleanOperator("AND") == BooleanOperator.AND
6
+ assert BooleanOperator("and") == BooleanOperator.AND
7
+
8
+
9
+ def test_boolean():
10
+ assert Boolean("TRUE") == Boolean.TRUE
11
+ assert Boolean("true") == Boolean.TRUE
12
+ assert Boolean(True) == Boolean.TRUE
@@ -128,3 +128,22 @@ def test_modifiers():
128
128
  text = generator.compile_statement(query)
129
129
  assert "2 = 2" in text
130
130
  assert "as `b`" not in text
131
+
132
+
133
+ def test_having_without_select():
134
+ q1 = """
135
+ const a <- 1;
136
+ const b <- 2;
137
+
138
+ select
139
+ a,
140
+
141
+ having b =2
142
+ ;"""
143
+ failed = False
144
+ try:
145
+ env, parsed = parse(q1)
146
+ except Exception as e:
147
+ assert "that is not in the select projection in the HAVING clause" in str(e)
148
+ failed = True
149
+ assert failed
@@ -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.34"
7
+ __version__ = "0.0.2.35"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -215,11 +215,28 @@ class Boolean(Enum):
215
215
  TRUE = "true"
216
216
  FALSE = "false"
217
217
 
218
+ @classmethod
219
+ def _missing_(cls, value):
220
+ if value is True:
221
+ return Boolean.TRUE
222
+ elif value is False:
223
+ return Boolean.FALSE
224
+ strval = str(value)
225
+ if strval.lower() != strval:
226
+ return Boolean(strval.lower())
227
+
218
228
 
219
229
  class BooleanOperator(Enum):
220
230
  AND = "and"
221
231
  OR = "or"
222
232
 
233
+ @classmethod
234
+ def _missing_(cls, value):
235
+ strval = str(value)
236
+ if strval.lower() != strval:
237
+ return BooleanOperator(strval.lower())
238
+ return None
239
+
223
240
 
224
241
  class ComparisonOperator(Enum):
225
242
  LT = "<"
@@ -1629,6 +1629,45 @@ class SelectStatement(HasUUID, Mergeable, Namespaced, SelectTypeMixin, BaseModel
1629
1629
  self.grain
1630
1630
  )
1631
1631
 
1632
+ def validate_syntax(self):
1633
+ all_in_output = [x.address for x in self.output_components]
1634
+ if self.where_clause:
1635
+ for concept in self.where_clause.concept_arguments:
1636
+
1637
+ if (
1638
+ concept.lineage
1639
+ and isinstance(concept.lineage, Function)
1640
+ and concept.lineage.operator
1641
+ in FunctionClass.AGGREGATE_FUNCTIONS.value
1642
+ ):
1643
+ if concept.address in self.locally_derived:
1644
+ raise SyntaxError(
1645
+ f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {self.meta.line_number}"
1646
+ )
1647
+
1648
+ if (
1649
+ concept.lineage
1650
+ and isinstance(concept.lineage, AggregateWrapper)
1651
+ and concept.lineage.function.operator
1652
+ in FunctionClass.AGGREGATE_FUNCTIONS.value
1653
+ ):
1654
+ if concept.address in self.locally_derived:
1655
+ raise SyntaxError(
1656
+ f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {self.meta.line_number}"
1657
+ )
1658
+ if self.having_clause:
1659
+ for concept in self.having_clause.concept_arguments:
1660
+ if concept.address not in [x.address for x in self.output_components]:
1661
+ raise SyntaxError(
1662
+ f"Cannot reference a column ({concept.address}) that is not in the select projection in the HAVING clause, move to WHERE; Line: {self.meta.line_number}"
1663
+ )
1664
+ if self.order_by:
1665
+ for concept in self.order_by.concept_arguments:
1666
+ if concept.address not in all_in_output:
1667
+ raise SyntaxError(
1668
+ f"Cannot order by a column that is not in the output projection; {self.meta.line_number}"
1669
+ )
1670
+
1632
1671
  def __str__(self):
1633
1672
  from trilogy.parsing.render import render_query
1634
1673
 
@@ -30,7 +30,6 @@ from trilogy.core.enums import (
30
30
  WindowType,
31
31
  DatePart,
32
32
  ShowCategory,
33
- FunctionClass,
34
33
  IOType,
35
34
  ConceptSource,
36
35
  )
@@ -264,7 +263,6 @@ class ParseToObjects(Transformer):
264
263
  for k, v in self.parsed.items():
265
264
  if v.pass_count == 2:
266
265
  continue
267
- print(f"Hydrating {k}")
268
266
  v.hydrate_missing()
269
267
  self.environment.concepts.fail_on_missing = True
270
268
  reparsed = self.transform(self.tokens[self.token_address])
@@ -988,8 +986,6 @@ class ParseToObjects(Transformer):
988
986
  order_by=order_by,
989
987
  meta=Metadata(line_number=meta.line),
990
988
  )
991
- locally_derived: set[str] = set()
992
- all_in_output: set[str] = set()
993
989
  for item in select_items:
994
990
  # we don't know the grain of an aggregate at assignment time
995
991
  # so rebuild at this point in the tree
@@ -998,25 +994,16 @@ class ParseToObjects(Transformer):
998
994
  new_concept = item.content.output.with_select_context(
999
995
  output.grain,
1000
996
  conditional=None,
1001
- # conditional=(
1002
- # output.where_clause.conditional
1003
- # if output.where_clause
1004
- # and output.where_clause_category == SelectFiltering.IMPLICIT
1005
- # else None
1006
- # ),
1007
997
  environment=self.environment,
1008
998
  )
1009
999
  self.environment.add_concept(new_concept, meta=meta)
1010
1000
  item.content.output = new_concept
1011
- locally_derived.add(new_concept.address)
1012
- all_in_output.add(new_concept.address)
1013
1001
  elif isinstance(item.content, Concept):
1014
1002
  # Sometimes cached values here don't have the latest info
1015
1003
  # but we can't just use environment, as it might not have the right grain.
1016
1004
  item.content = self.environment.concepts[
1017
1005
  item.content.address
1018
1006
  ].with_grain(item.content.grain)
1019
- all_in_output.add(item.content.address)
1020
1007
  if order_by:
1021
1008
  for orderitem in order_by.items:
1022
1009
  if isinstance(orderitem.expr, Concept):
@@ -1024,52 +1011,9 @@ class ParseToObjects(Transformer):
1024
1011
  orderitem.expr = orderitem.expr.with_select_context(
1025
1012
  output.grain,
1026
1013
  conditional=None,
1027
- # conditional=(
1028
- # output.where_clause.conditional
1029
- # if output.where_clause
1030
- # and output.where_clause_category
1031
- # == SelectFiltering.IMPLICIT
1032
- # else None
1033
- # ),
1034
1014
  environment=self.environment,
1035
1015
  )
1036
- if output.where_clause:
1037
- for concept in output.where_clause.concept_arguments:
1038
-
1039
- if (
1040
- concept.lineage
1041
- and isinstance(concept.lineage, Function)
1042
- and concept.lineage.operator
1043
- in FunctionClass.AGGREGATE_FUNCTIONS.value
1044
- ):
1045
- if concept.address in locally_derived:
1046
- raise SyntaxError(
1047
- f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {meta.line}"
1048
- )
1049
-
1050
- if (
1051
- concept.lineage
1052
- and isinstance(concept.lineage, AggregateWrapper)
1053
- and concept.lineage.function.operator
1054
- in FunctionClass.AGGREGATE_FUNCTIONS.value
1055
- ):
1056
- if concept.address in locally_derived:
1057
- raise SyntaxError(
1058
- f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {meta.line}"
1059
- )
1060
- if output.having_clause:
1061
- for concept in output.having_clause.concept_arguments:
1062
- if concept.address not in all_in_output:
1063
- raise SyntaxError(
1064
- f"Cannot reference a column ({concept.address}) that is not in the select projection in the HAVING clause, move to WHERE; Line: {meta.line}"
1065
- )
1066
- if output.order_by:
1067
- for concept in output.order_by.concept_arguments:
1068
- if concept.address not in all_in_output:
1069
- raise SyntaxError(
1070
- f"Cannot order by a column that is not in the output projection; {meta.line}"
1071
- )
1072
-
1016
+ output.validate_syntax()
1073
1017
  return output
1074
1018
 
1075
1019
  @v_args(meta=True)
File without changes
File without changes
File without changes
File without changes