pytrilogy 0.0.1.109__py3-none-any.whl → 0.0.1.111__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 (34) hide show
  1. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/METADATA +1 -1
  2. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/RECORD +34 -34
  3. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/WHEEL +1 -1
  4. trilogy/__init__.py +1 -1
  5. trilogy/constants.py +11 -3
  6. trilogy/core/enums.py +1 -0
  7. trilogy/core/models.py +94 -67
  8. trilogy/core/optimization.py +134 -12
  9. trilogy/core/processing/concept_strategies_v3.py +44 -19
  10. trilogy/core/processing/node_generators/basic_node.py +2 -0
  11. trilogy/core/processing/node_generators/common.py +3 -1
  12. trilogy/core/processing/node_generators/concept_merge_node.py +24 -8
  13. trilogy/core/processing/node_generators/filter_node.py +36 -6
  14. trilogy/core/processing/node_generators/node_merge_node.py +34 -23
  15. trilogy/core/processing/node_generators/rowset_node.py +37 -8
  16. trilogy/core/processing/node_generators/select_node.py +23 -9
  17. trilogy/core/processing/node_generators/unnest_node.py +24 -3
  18. trilogy/core/processing/node_generators/window_node.py +4 -2
  19. trilogy/core/processing/nodes/__init__.py +7 -6
  20. trilogy/core/processing/nodes/base_node.py +40 -6
  21. trilogy/core/processing/nodes/filter_node.py +15 -1
  22. trilogy/core/processing/nodes/group_node.py +20 -1
  23. trilogy/core/processing/nodes/merge_node.py +37 -10
  24. trilogy/core/processing/nodes/select_node_v2.py +34 -39
  25. trilogy/core/processing/nodes/unnest_node.py +12 -0
  26. trilogy/core/processing/nodes/window_node.py +11 -0
  27. trilogy/core/processing/utility.py +0 -14
  28. trilogy/core/query_processor.py +125 -29
  29. trilogy/dialect/base.py +45 -40
  30. trilogy/executor.py +31 -3
  31. trilogy/parsing/parse_engine.py +49 -17
  32. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/LICENSE.md +0 -0
  33. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/entry_points.txt +0 -0
  34. {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/top_level.txt +0 -0
trilogy/executor.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Any
1
+ from typing import List, Optional, Any, Generator
2
2
  from functools import singledispatchmethod
3
3
  from sqlalchemy import text
4
4
  from sqlalchemy.engine import Engine, CursorResult
@@ -222,6 +222,35 @@ class Executor(object):
222
222
  sql.append(x)
223
223
  return sql
224
224
 
225
+ def parse_text_generator(
226
+ self, command: str, persist: bool = False
227
+ ) -> Generator[
228
+ ProcessedQuery | ProcessedQueryPersist | ProcessedShowStatement, None, None
229
+ ]:
230
+ """Process a preql text command"""
231
+ _, parsed = parse_text(command, self.environment)
232
+ generatable = [
233
+ x
234
+ for x in parsed
235
+ if isinstance(
236
+ x,
237
+ (
238
+ SelectStatement,
239
+ PersistStatement,
240
+ MultiSelectStatement,
241
+ ShowStatement,
242
+ ),
243
+ )
244
+ ]
245
+ while generatable:
246
+ t = generatable.pop(0)
247
+ x = self.generator.generate_queries(
248
+ self.environment, [t], hooks=self.hooks
249
+ )[0]
250
+ if persist and isinstance(x, ProcessedQueryPersist):
251
+ self.environment.add_datasource(x.datasource)
252
+ yield x
253
+
225
254
  def execute_raw_sql(self, command: str) -> CursorResult:
226
255
  """Run a command against the raw underlying
227
256
  execution engine"""
@@ -229,10 +258,9 @@ class Executor(object):
229
258
 
230
259
  def execute_text(self, command: str) -> List[CursorResult]:
231
260
  """Run a preql text command"""
232
- sql = self.parse_text(command)
233
261
  output = []
234
262
  # connection = self.engine.connect()
235
- for statement in sql:
263
+ for statement in self.parse_text_generator(command):
236
264
  if isinstance(statement, ProcessedShowStatement):
237
265
  output.append(
238
266
  generate_result_set(
@@ -101,6 +101,7 @@ from trilogy.core.models import (
101
101
  ConceptDerivation,
102
102
  RowsetDerivationStatement,
103
103
  LooseConceptList,
104
+ list_to_wrapper,
104
105
  )
105
106
  from trilogy.parsing.exceptions import ParseError
106
107
  from trilogy.utility import string_to_hash
@@ -113,6 +114,7 @@ from trilogy.parsing.common import (
113
114
  arbitrary_to_concept,
114
115
  )
115
116
 
117
+
116
118
  CONSTANT_TYPES = (int, float, str, bool, ListWrapper)
117
119
 
118
120
  grammar = r"""
@@ -138,7 +140,7 @@ grammar = r"""
138
140
  //<customer_id,country>.property local_alias STRING
139
141
  concept_property_declaration: PROPERTY (prop_ident | IDENTIFIER) data_type concept_nullable_modifier? metadata?
140
142
  //metric post_length <- len(post_text);
141
- concept_derivation: (PURPOSE | AUTO | PROPERTY ) IDENTIFIER "<" "-" expr
143
+ concept_derivation: (PURPOSE | AUTO | PROPERTY ) (prop_ident | IDENTIFIER) "<" "-" expr
142
144
 
143
145
  rowset_derivation_statement: ("rowset"i IDENTIFIER "<" "-" (multi_select_statement | select_statement)) | ("with"i IDENTIFIER "as"i (multi_select_statement | select_statement))
144
146
 
@@ -179,13 +181,11 @@ grammar = r"""
179
181
  // multiple_selects
180
182
  multi_select_statement: select_statement ("merge" select_statement)+ "align"i align_clause where? comment* order_by? comment* limit? comment*
181
183
 
182
-
183
184
  align_item: IDENTIFIER ":" IDENTIFIER ("," IDENTIFIER)* ","?
184
185
 
185
186
  align_clause: align_item ("," align_item)* ","?
186
187
 
187
188
  // merge statemment
188
-
189
189
  merge_statement: "merge" IDENTIFIER ("," IDENTIFIER)* ","? comment*
190
190
 
191
191
  // FUNCTION blocks
@@ -193,7 +193,6 @@ grammar = r"""
193
193
  function_binding_item: IDENTIFIER ":" data_type
194
194
  function_binding_list: (function_binding_item ",")* function_binding_item ","?
195
195
  raw_function: "bind" "sql" IDENTIFIER "(" function_binding_list ")" "-" ">" data_type "as"i MULTILINE_STRING
196
-
197
196
 
198
197
  // user_id where state = Mexico
199
198
  filter_item: "filter"i IDENTIFIER where
@@ -249,9 +248,9 @@ grammar = r"""
249
248
 
250
249
  COMPARISON_OPERATOR: (/is[\s]+not/ | "is" |"=" | ">" | "<" | ">=" | "<=" | "!=" )
251
250
 
252
- comparison: (expr COMPARISON_OPERATOR expr) | (expr array_comparison expr_tuple)
251
+ comparison: (expr COMPARISON_OPERATOR expr)
253
252
 
254
- subselect_comparison: expr array_comparison expr
253
+ subselect_comparison: expr array_comparison expr | (expr array_comparison expr_tuple)
255
254
 
256
255
  expr_tuple: "(" (expr ",")* expr ","? ")"
257
256
 
@@ -267,7 +266,6 @@ grammar = r"""
267
266
 
268
267
  // functions
269
268
 
270
- //math TODO: add syntactic sugar
271
269
  fadd: ("add"i "(" expr "," expr ")" ) | ( expr "+" expr )
272
270
  fsub: ("subtract"i "(" expr "," expr ")" ) | ( expr "-" expr )
273
271
  fmul: ("multiply"i "(" expr "," expr ")" ) | ( expr "*" expr )
@@ -738,10 +736,17 @@ class ParseToObjects(Transformer):
738
736
  purpose = args[0]
739
737
  if purpose == Purpose.AUTO:
740
738
  purpose = None
741
- name = args[1]
742
- lookup, namespace, name, parent_concept = parse_concept_reference(
743
- name, self.environment, purpose
744
- )
739
+ raw_name = args[1]
740
+ if isinstance(raw_name, str):
741
+ lookup, namespace, name, parent_concept = parse_concept_reference(
742
+ raw_name, self.environment, purpose
743
+ )
744
+ else:
745
+ keys, name = raw_name
746
+ if "." in name:
747
+ namespace, name = name.rsplit(".", 1)
748
+ else:
749
+ namespace = self.environment.namespace or DEFAULT_NAMESPACE
745
750
  source_value = args[2]
746
751
  # we need to strip off every parenthetical to see what is being assigned.
747
752
  while isinstance(source_value, Parenthetical):
@@ -971,7 +976,26 @@ class ParseToObjects(Transformer):
971
976
  return Ordering(args.lower())
972
977
 
973
978
  def order_list(self, args):
974
- return [OrderItem(expr=x, order=y) for x, y in zip(args[::2], args[1::2])]
979
+
980
+ def handle_order_item(x, namespace: str):
981
+ if not isinstance(x, Concept):
982
+ x = arbitrary_to_concept(
983
+ x,
984
+ namespace=namespace,
985
+ name=f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(x))}",
986
+ )
987
+ return x
988
+
989
+ return [
990
+ OrderItem(
991
+ expr=handle_order_item(
992
+ x,
993
+ self.environment.namespace,
994
+ ),
995
+ order=y,
996
+ )
997
+ for x, y in zip(args[::2], args[1::2])
998
+ ]
975
999
 
976
1000
  def order_by(self, args):
977
1001
  return OrderBy(items=args[0])
@@ -1207,26 +1231,34 @@ class ParseToObjects(Transformer):
1207
1231
  return float(args[0])
1208
1232
 
1209
1233
  def array_lit(self, args):
1210
- types = [arg_to_datatype(arg) for arg in args]
1211
- assert len(set(types)) == 1
1212
- return ListWrapper(args, type=types[0])
1234
+ return list_to_wrapper(args)
1213
1235
 
1214
1236
  def literal(self, args):
1215
1237
  return args[0]
1216
1238
 
1217
1239
  def comparison(self, args) -> Comparison:
1240
+ if args[1] == ComparisonOperator.IN:
1241
+ raise SyntaxError
1218
1242
  return Comparison(left=args[0], right=args[2], operator=args[1])
1219
1243
 
1220
1244
  @v_args(meta=True)
1221
1245
  def subselect_comparison(self, meta: Meta, args) -> SubselectComparison:
1222
1246
  right = args[2]
1223
- if not isinstance(right, Concept):
1247
+
1248
+ while isinstance(right, Parenthetical) and isinstance(
1249
+ right.content,
1250
+ (Concept, Function, FilterItem, WindowItem, AggregateWrapper, ListWrapper),
1251
+ ):
1252
+ right = right.content
1253
+ if isinstance(
1254
+ right, (Function, FilterItem, WindowItem, AggregateWrapper, ListWrapper)
1255
+ ):
1224
1256
  right = arbitrary_to_concept(
1225
1257
  right,
1226
1258
  namespace=self.environment.namespace,
1227
1259
  name=f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(right))}",
1228
1260
  )
1229
- self.environment.add_concept(right)
1261
+ self.environment.add_concept(right, meta=meta)
1230
1262
  return SubselectComparison(
1231
1263
  left=args[0],
1232
1264
  right=right,