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.
- {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/RECORD +34 -34
- {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +1 -1
- trilogy/constants.py +11 -3
- trilogy/core/enums.py +1 -0
- trilogy/core/models.py +94 -67
- trilogy/core/optimization.py +134 -12
- trilogy/core/processing/concept_strategies_v3.py +44 -19
- trilogy/core/processing/node_generators/basic_node.py +2 -0
- trilogy/core/processing/node_generators/common.py +3 -1
- trilogy/core/processing/node_generators/concept_merge_node.py +24 -8
- trilogy/core/processing/node_generators/filter_node.py +36 -6
- trilogy/core/processing/node_generators/node_merge_node.py +34 -23
- trilogy/core/processing/node_generators/rowset_node.py +37 -8
- trilogy/core/processing/node_generators/select_node.py +23 -9
- trilogy/core/processing/node_generators/unnest_node.py +24 -3
- trilogy/core/processing/node_generators/window_node.py +4 -2
- trilogy/core/processing/nodes/__init__.py +7 -6
- trilogy/core/processing/nodes/base_node.py +40 -6
- trilogy/core/processing/nodes/filter_node.py +15 -1
- trilogy/core/processing/nodes/group_node.py +20 -1
- trilogy/core/processing/nodes/merge_node.py +37 -10
- trilogy/core/processing/nodes/select_node_v2.py +34 -39
- trilogy/core/processing/nodes/unnest_node.py +12 -0
- trilogy/core/processing/nodes/window_node.py +11 -0
- trilogy/core/processing/utility.py +0 -14
- trilogy/core/query_processor.py +125 -29
- trilogy/dialect/base.py +45 -40
- trilogy/executor.py +31 -3
- trilogy/parsing/parse_engine.py +49 -17
- {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.1.109.dist-info → pytrilogy-0.0.1.111.dist-info}/entry_points.txt +0 -0
- {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
|
|
263
|
+
for statement in self.parse_text_generator(command):
|
|
236
264
|
if isinstance(statement, ProcessedShowStatement):
|
|
237
265
|
output.append(
|
|
238
266
|
generate_result_set(
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|