pytrilogy 0.0.2.25__py3-none-any.whl → 0.0.2.26__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.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/RECORD +21 -21
- trilogy/__init__.py +1 -1
- trilogy/constants.py +1 -1
- trilogy/core/models.py +106 -67
- trilogy/core/processing/node_generators/common.py +0 -1
- trilogy/core/processing/node_generators/select_merge_node.py +49 -22
- trilogy/core/processing/nodes/merge_node.py +2 -2
- trilogy/core/processing/utility.py +236 -257
- trilogy/core/query_processor.py +47 -39
- trilogy/dialect/base.py +1 -0
- trilogy/dialect/common.py +4 -25
- trilogy/executor.py +12 -3
- trilogy/parsing/common.py +4 -6
- trilogy/parsing/parse_engine.py +3 -2
- trilogy/parsing/render.py +41 -17
- trilogy/parsing/trilogy.lark +2 -2
- {pytrilogy-0.0.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.25.dist-info → pytrilogy-0.0.2.26.dist-info}/top_level.txt +0 -0
trilogy/dialect/base.py
CHANGED
trilogy/dialect/common.py
CHANGED
|
@@ -68,39 +68,18 @@ def render_join(
|
|
|
68
68
|
if unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
|
|
69
69
|
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
70
70
|
return f"FULL JOIN {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
71
|
-
left_name = join.left_name
|
|
71
|
+
# left_name = join.left_name
|
|
72
72
|
right_name = join.right_name
|
|
73
73
|
right_base = join.right_ref
|
|
74
|
-
base_joinkeys = [
|
|
75
|
-
null_wrapper(
|
|
76
|
-
render_join_concept(
|
|
77
|
-
left_name,
|
|
78
|
-
quote_character,
|
|
79
|
-
join.left_cte,
|
|
80
|
-
key.concept,
|
|
81
|
-
render_expr_func,
|
|
82
|
-
join.inlined_ctes,
|
|
83
|
-
),
|
|
84
|
-
render_join_concept(
|
|
85
|
-
right_name,
|
|
86
|
-
quote_character,
|
|
87
|
-
join.right_cte,
|
|
88
|
-
key.concept,
|
|
89
|
-
render_expr_func,
|
|
90
|
-
join.inlined_ctes,
|
|
91
|
-
),
|
|
92
|
-
modifiers=key.concept.modifiers or [],
|
|
93
|
-
)
|
|
94
|
-
for key in join.joinkeys
|
|
95
|
-
]
|
|
74
|
+
base_joinkeys = []
|
|
96
75
|
if join.joinkey_pairs:
|
|
97
76
|
base_joinkeys.extend(
|
|
98
77
|
[
|
|
99
78
|
null_wrapper(
|
|
100
79
|
render_join_concept(
|
|
101
|
-
|
|
80
|
+
join.get_name(pair.cte),
|
|
102
81
|
quote_character,
|
|
103
|
-
|
|
82
|
+
pair.cte,
|
|
104
83
|
pair.left,
|
|
105
84
|
render_expr_func,
|
|
106
85
|
join.inlined_ctes,
|
trilogy/executor.py
CHANGED
|
@@ -334,7 +334,9 @@ class Executor(object):
|
|
|
334
334
|
text(command),
|
|
335
335
|
)
|
|
336
336
|
|
|
337
|
-
def execute_text(
|
|
337
|
+
def execute_text(
|
|
338
|
+
self, command: str, non_interactive: bool = False
|
|
339
|
+
) -> List[CursorResult]:
|
|
338
340
|
"""Run a preql text command"""
|
|
339
341
|
output = []
|
|
340
342
|
# connection = self.engine.connect()
|
|
@@ -351,11 +353,18 @@ class Executor(object):
|
|
|
351
353
|
)
|
|
352
354
|
)
|
|
353
355
|
continue
|
|
356
|
+
if non_interactive:
|
|
357
|
+
if not isinstance(
|
|
358
|
+
statement, (ProcessedCopyStatement, ProcessedQueryPersist)
|
|
359
|
+
):
|
|
360
|
+
continue
|
|
354
361
|
output.append(self.execute_query(statement))
|
|
355
362
|
return output
|
|
356
363
|
|
|
357
|
-
def execute_file(
|
|
364
|
+
def execute_file(
|
|
365
|
+
self, file: str | Path, non_interactive: bool = False
|
|
366
|
+
) -> List[CursorResult]:
|
|
358
367
|
file = Path(file)
|
|
359
368
|
with open(file, "r") as f:
|
|
360
369
|
command = f.read()
|
|
361
|
-
return self.execute_text(command)
|
|
370
|
+
return self.execute_text(command, non_interactive=non_interactive)
|
trilogy/parsing/common.py
CHANGED
|
@@ -292,21 +292,19 @@ def arbitrary_to_concept(
|
|
|
292
292
|
|
|
293
293
|
if isinstance(parent, AggregateWrapper):
|
|
294
294
|
if not name:
|
|
295
|
-
name = (
|
|
296
|
-
f"_agg_{parent.function.operator.value}_{string_to_hash(str(parent))}"
|
|
297
|
-
)
|
|
295
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_agg_{parent.function.operator.value}_{string_to_hash(str(parent))}"
|
|
298
296
|
return agg_wrapper_to_concept(parent, namespace, name, metadata, purpose)
|
|
299
297
|
elif isinstance(parent, WindowItem):
|
|
300
298
|
if not name:
|
|
301
|
-
name = f"_window_{parent.type.value}_{string_to_hash(str(parent))}"
|
|
299
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_window_{parent.type.value}_{string_to_hash(str(parent))}"
|
|
302
300
|
return window_item_to_concept(parent, name, namespace, purpose, metadata)
|
|
303
301
|
elif isinstance(parent, FilterItem):
|
|
304
302
|
if not name:
|
|
305
|
-
name = f"_filter_{parent.content.name}_{string_to_hash(str(parent))}"
|
|
303
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_filter_{parent.content.name}_{string_to_hash(str(parent))}"
|
|
306
304
|
return filter_item_to_concept(parent, name, namespace, purpose, metadata)
|
|
307
305
|
elif isinstance(parent, Function):
|
|
308
306
|
if not name:
|
|
309
|
-
name = f"_func_{parent.operator.value}_{string_to_hash(str(parent))}"
|
|
307
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_func_{parent.operator.value}_{string_to_hash(str(parent))}"
|
|
310
308
|
return function_to_concept(parent, name, namespace)
|
|
311
309
|
elif isinstance(parent, ListWrapper):
|
|
312
310
|
if not name:
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -254,8 +254,9 @@ class ParseToObjects(Transformer):
|
|
|
254
254
|
def IDENTIFIER(self, args) -> str:
|
|
255
255
|
return args.value
|
|
256
256
|
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
@v_args(meta=True)
|
|
258
|
+
def concept_lit(self, meta: Meta, args) -> Concept:
|
|
259
|
+
return self.environment.concepts.__getitem__(args[0], meta.line)
|
|
259
260
|
|
|
260
261
|
def ADDRESS(self, args) -> Address:
|
|
261
262
|
return Address(location=args.value, quoted=False)
|
trilogy/parsing/render.py
CHANGED
|
@@ -17,6 +17,7 @@ from trilogy.core.models import (
|
|
|
17
17
|
SelectItem,
|
|
18
18
|
WhereClause,
|
|
19
19
|
Conditional,
|
|
20
|
+
SubselectComparison,
|
|
20
21
|
Comparison,
|
|
21
22
|
Environment,
|
|
22
23
|
ConceptDeclarationStatement,
|
|
@@ -25,6 +26,7 @@ from trilogy.core.models import (
|
|
|
25
26
|
WindowItem,
|
|
26
27
|
FilterItem,
|
|
27
28
|
ColumnAssignment,
|
|
29
|
+
RawColumnExpr,
|
|
28
30
|
CaseElse,
|
|
29
31
|
CaseWhen,
|
|
30
32
|
ImportStatement,
|
|
@@ -50,13 +52,16 @@ from collections import defaultdict
|
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
QUERY_TEMPLATE = Template(
|
|
53
|
-
"""
|
|
54
|
-
{{
|
|
55
|
-
|
|
56
|
-
{{
|
|
55
|
+
"""{% if where %}WHERE
|
|
56
|
+
{{ where }}
|
|
57
|
+
{% endif %}SELECT{%- for select in select_columns %}
|
|
58
|
+
{{ select }},{% endfor %}{% if having %}
|
|
59
|
+
HAVING
|
|
60
|
+
{{ having }}
|
|
61
|
+
{% endif %}{%- if order_by %}
|
|
57
62
|
ORDER BY{% for order in order_by %}
|
|
58
|
-
{{ order }}{% if not loop.last %},{% endif %}
|
|
59
|
-
{%
|
|
63
|
+
{{ order }}{% if not loop.last %},{% endif %}{% endfor %}
|
|
64
|
+
{% endif %}{%- if limit is not none %}
|
|
60
65
|
LIMIT {{ limit }}{% endif %};"""
|
|
61
66
|
)
|
|
62
67
|
|
|
@@ -133,14 +138,14 @@ class Renderer:
|
|
|
133
138
|
|
|
134
139
|
@to_string.register
|
|
135
140
|
def _(self, arg: Datasource):
|
|
136
|
-
assignments = ",\n
|
|
141
|
+
assignments = ",\n ".join([self.to_string(x) for x in arg.columns])
|
|
137
142
|
base = f"""datasource {arg.name} (
|
|
138
143
|
{assignments}
|
|
139
|
-
)
|
|
140
|
-
{self.to_string(arg.grain)}
|
|
144
|
+
)
|
|
145
|
+
{self.to_string(arg.grain)}
|
|
141
146
|
{self.to_string(arg.address)}"""
|
|
142
147
|
if arg.where:
|
|
143
|
-
base += f"\
|
|
148
|
+
base += f"\nwhere {self.to_string(arg.where)}"
|
|
144
149
|
base += ";"
|
|
145
150
|
return base
|
|
146
151
|
|
|
@@ -209,7 +214,13 @@ class Renderer:
|
|
|
209
214
|
|
|
210
215
|
@to_string.register
|
|
211
216
|
def _(self, arg: "ColumnAssignment"):
|
|
212
|
-
|
|
217
|
+
if isinstance(arg.alias, str):
|
|
218
|
+
return f"{arg.alias}: {self.to_string(arg.concept)}"
|
|
219
|
+
return f"{self.to_string(arg.alias)}: {self.to_string(arg.concept)}"
|
|
220
|
+
|
|
221
|
+
@to_string.register
|
|
222
|
+
def _(self, arg: "RawColumnExpr"):
|
|
223
|
+
return f"raw('''{arg.text}''')"
|
|
213
224
|
|
|
214
225
|
@to_string.register
|
|
215
226
|
def _(self, arg: "ConceptDeclarationStatement"):
|
|
@@ -266,6 +277,7 @@ class Renderer:
|
|
|
266
277
|
return QUERY_TEMPLATE.render(
|
|
267
278
|
select_columns=[self.to_string(c) for c in arg.selection],
|
|
268
279
|
where=self.to_string(arg.where_clause) if arg.where_clause else None,
|
|
280
|
+
having=self.to_string(arg.having_clause) if arg.having_clause else None,
|
|
269
281
|
order_by=(
|
|
270
282
|
[self.to_string(c) for c in arg.order_by.items]
|
|
271
283
|
if arg.order_by
|
|
@@ -301,16 +313,23 @@ class Renderer:
|
|
|
301
313
|
|
|
302
314
|
@to_string.register
|
|
303
315
|
def _(self, arg: OrderBy):
|
|
304
|
-
return ",\
|
|
316
|
+
return ",\n".join([self.to_string(c) for c in arg.items])
|
|
305
317
|
|
|
306
318
|
@to_string.register
|
|
307
319
|
def _(self, arg: "WhereClause"):
|
|
308
|
-
|
|
320
|
+
base = f"{self.to_string(arg.conditional)}"
|
|
321
|
+
if base[0] == "(" and base[-1] == ")":
|
|
322
|
+
return base[1:-1]
|
|
323
|
+
return base
|
|
309
324
|
|
|
310
325
|
@to_string.register
|
|
311
326
|
def _(self, arg: "Conditional"):
|
|
312
327
|
return f"({self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)})"
|
|
313
328
|
|
|
329
|
+
@to_string.register
|
|
330
|
+
def _(self, arg: "SubselectComparison"):
|
|
331
|
+
return f"{self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)}"
|
|
332
|
+
|
|
314
333
|
@to_string.register
|
|
315
334
|
def _(self, arg: "Comparison"):
|
|
316
335
|
return f"{self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)}"
|
|
@@ -319,10 +338,12 @@ class Renderer:
|
|
|
319
338
|
def _(self, arg: "WindowItem"):
|
|
320
339
|
over = ",".join(self.to_string(c) for c in arg.over)
|
|
321
340
|
order = ",".join(self.to_string(c) for c in arg.order_by)
|
|
322
|
-
if over:
|
|
341
|
+
if over and order:
|
|
323
342
|
return (
|
|
324
|
-
f"{arg.type.value} {self.to_string(arg.content)} by {order}
|
|
343
|
+
f"{arg.type.value} {self.to_string(arg.content)} by {order} over {over}"
|
|
325
344
|
)
|
|
345
|
+
elif over:
|
|
346
|
+
return f"{arg.type.value} {self.to_string(arg.content)} over {over}"
|
|
326
347
|
return f"{arg.type.value} {self.to_string(arg.content)} by {order}"
|
|
327
348
|
|
|
328
349
|
@to_string.register
|
|
@@ -343,13 +364,15 @@ class Renderer:
|
|
|
343
364
|
|
|
344
365
|
@to_string.register
|
|
345
366
|
def _(self, arg: "ConceptTransform"):
|
|
346
|
-
return f"{self.to_string(arg.function)}->{arg.output.name}"
|
|
367
|
+
return f"{self.to_string(arg.function)} -> {arg.output.name}"
|
|
347
368
|
|
|
348
369
|
@to_string.register
|
|
349
370
|
def _(self, arg: "Function"):
|
|
350
371
|
inputs = ",".join(self.to_string(c) for c in arg.arguments)
|
|
351
372
|
if arg.operator == FunctionType.CONSTANT:
|
|
352
373
|
return f"{inputs}"
|
|
374
|
+
if arg.operator == FunctionType.CAST:
|
|
375
|
+
return f"CAST({self.to_string(arg.arguments[0])} AS {self.to_string(arg.arguments[1])})"
|
|
353
376
|
if arg.operator == FunctionType.INDEX_ACCESS:
|
|
354
377
|
return f"{self.to_string(arg.arguments[0])}[{self.to_string(arg.arguments[1])}]"
|
|
355
378
|
return f"{arg.operator.value}({inputs})"
|
|
@@ -361,7 +384,8 @@ class Renderer:
|
|
|
361
384
|
@to_string.register
|
|
362
385
|
def _(self, arg: AggregateWrapper):
|
|
363
386
|
if arg.by:
|
|
364
|
-
|
|
387
|
+
by = ", ".join([self.to_string(x) for x in arg.by])
|
|
388
|
+
return f"{self.to_string(arg.function)} by {by}"
|
|
365
389
|
return f"{self.to_string(arg.function)}"
|
|
366
390
|
|
|
367
391
|
@to_string.register
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -289,8 +289,8 @@
|
|
|
289
289
|
|
|
290
290
|
// base language constructs
|
|
291
291
|
concept_lit: IDENTIFIER
|
|
292
|
-
IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_
|
|
293
|
-
QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_
|
|
292
|
+
IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.]*/
|
|
293
|
+
QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:]*`/
|
|
294
294
|
ADDRESS: IDENTIFIER
|
|
295
295
|
|
|
296
296
|
MULTILINE_STRING: /\'{3}(.*?)\'{3}/s
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|