pytrilogy 0.0.2.58__py3-none-any.whl → 0.0.3.1__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.58.dist-info → pytrilogy-0.0.3.1.dist-info}/METADATA +9 -2
- pytrilogy-0.0.3.1.dist-info/RECORD +99 -0
- {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +2 -2
- trilogy/core/enums.py +1 -7
- trilogy/core/env_processor.py +17 -5
- trilogy/core/environment_helpers.py +11 -25
- trilogy/core/exceptions.py +4 -0
- trilogy/core/functions.py +695 -261
- trilogy/core/graph_models.py +10 -10
- trilogy/core/internal.py +11 -2
- trilogy/core/models/__init__.py +0 -0
- trilogy/core/models/author.py +2110 -0
- trilogy/core/models/build.py +1859 -0
- trilogy/core/models/build_environment.py +151 -0
- trilogy/core/models/core.py +370 -0
- trilogy/core/models/datasource.py +297 -0
- trilogy/core/models/environment.py +701 -0
- trilogy/core/models/execute.py +931 -0
- trilogy/core/optimization.py +14 -16
- trilogy/core/optimizations/base_optimization.py +1 -1
- trilogy/core/optimizations/inline_constant.py +6 -6
- trilogy/core/optimizations/inline_datasource.py +17 -11
- trilogy/core/optimizations/predicate_pushdown.py +17 -16
- trilogy/core/processing/concept_strategies_v3.py +178 -145
- trilogy/core/processing/graph_utils.py +1 -1
- trilogy/core/processing/node_generators/basic_node.py +19 -18
- trilogy/core/processing/node_generators/common.py +50 -44
- trilogy/core/processing/node_generators/filter_node.py +26 -13
- trilogy/core/processing/node_generators/group_node.py +26 -21
- trilogy/core/processing/node_generators/group_to_node.py +11 -8
- trilogy/core/processing/node_generators/multiselect_node.py +60 -43
- trilogy/core/processing/node_generators/node_merge_node.py +76 -38
- trilogy/core/processing/node_generators/rowset_node.py +55 -36
- trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +27 -34
- trilogy/core/processing/node_generators/select_merge_node.py +161 -64
- trilogy/core/processing/node_generators/select_node.py +13 -13
- trilogy/core/processing/node_generators/union_node.py +12 -11
- trilogy/core/processing/node_generators/unnest_node.py +9 -7
- trilogy/core/processing/node_generators/window_node.py +18 -16
- trilogy/core/processing/nodes/__init__.py +21 -18
- trilogy/core/processing/nodes/base_node.py +82 -66
- trilogy/core/processing/nodes/filter_node.py +19 -13
- trilogy/core/processing/nodes/group_node.py +50 -35
- trilogy/core/processing/nodes/merge_node.py +45 -36
- trilogy/core/processing/nodes/select_node_v2.py +53 -39
- trilogy/core/processing/nodes/union_node.py +5 -7
- trilogy/core/processing/nodes/unnest_node.py +7 -11
- trilogy/core/processing/nodes/window_node.py +9 -4
- trilogy/core/processing/utility.py +103 -75
- trilogy/core/query_processor.py +70 -47
- trilogy/core/statements/__init__.py +0 -0
- trilogy/core/statements/author.py +413 -0
- trilogy/core/statements/build.py +0 -0
- trilogy/core/statements/common.py +30 -0
- trilogy/core/statements/execute.py +42 -0
- trilogy/dialect/base.py +148 -106
- trilogy/dialect/common.py +9 -10
- trilogy/dialect/duckdb.py +1 -1
- trilogy/dialect/enums.py +4 -2
- trilogy/dialect/presto.py +1 -1
- trilogy/dialect/sql_server.py +1 -1
- trilogy/executor.py +44 -32
- trilogy/hooks/__init__.py +4 -0
- trilogy/hooks/base_hook.py +6 -4
- trilogy/hooks/query_debugger.py +113 -97
- trilogy/parser.py +1 -1
- trilogy/parsing/common.py +307 -64
- trilogy/parsing/parse_engine.py +277 -618
- trilogy/parsing/render.py +50 -26
- trilogy/scripts/trilogy.py +2 -1
- pytrilogy-0.0.2.58.dist-info/RECORD +0 -87
- trilogy/core/models.py +0 -4960
- {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.58.dist-info → pytrilogy-0.0.3.1.dist-info}/top_level.txt +0 -0
trilogy/dialect/base.py
CHANGED
|
@@ -11,63 +11,82 @@ from trilogy.core.enums import (
|
|
|
11
11
|
WindowType,
|
|
12
12
|
)
|
|
13
13
|
from trilogy.core.internal import DEFAULT_CONCEPTS
|
|
14
|
-
from trilogy.core.models import (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
from trilogy.core.models.build import (
|
|
15
|
+
BuildAggregateWrapper,
|
|
16
|
+
BuildCaseElse,
|
|
17
|
+
BuildCaseWhen,
|
|
18
|
+
BuildComparison,
|
|
19
|
+
BuildConcept,
|
|
20
|
+
BuildConditional,
|
|
21
|
+
BuildFilterItem,
|
|
22
|
+
BuildFunction,
|
|
23
|
+
BuildMultiSelectLineage,
|
|
24
|
+
BuildOrderItem,
|
|
25
|
+
BuildParenthetical,
|
|
26
|
+
BuildRowsetItem,
|
|
27
|
+
BuildSubselectComparison,
|
|
28
|
+
BuildWindowItem,
|
|
29
|
+
Factory,
|
|
30
|
+
)
|
|
31
|
+
from trilogy.core.models.core import (
|
|
26
32
|
DataType,
|
|
27
|
-
Environment,
|
|
28
|
-
FilterItem,
|
|
29
|
-
Function,
|
|
30
|
-
ImportStatement,
|
|
31
33
|
ListType,
|
|
32
34
|
ListWrapper,
|
|
33
35
|
MapType,
|
|
34
36
|
MapWrapper,
|
|
35
|
-
MergeStatementV2,
|
|
36
|
-
MultiSelectStatement,
|
|
37
37
|
NumericType,
|
|
38
|
-
OrderItem,
|
|
39
|
-
Parenthetical,
|
|
40
|
-
PersistStatement,
|
|
41
|
-
ProcessedCopyStatement,
|
|
42
|
-
ProcessedQuery,
|
|
43
|
-
ProcessedQueryPersist,
|
|
44
|
-
ProcessedRawSQLStatement,
|
|
45
|
-
ProcessedShowStatement,
|
|
46
|
-
RawColumnExpr,
|
|
47
|
-
RawSQLStatement,
|
|
48
|
-
RowsetDerivationStatement,
|
|
49
|
-
RowsetItem,
|
|
50
|
-
SelectStatement,
|
|
51
|
-
ShowStatement,
|
|
52
38
|
StructType,
|
|
53
|
-
SubselectComparison,
|
|
54
39
|
TupleWrapper,
|
|
55
|
-
UnionCTE,
|
|
56
|
-
WindowItem,
|
|
57
40
|
)
|
|
41
|
+
from trilogy.core.models.datasource import Datasource, RawColumnExpr
|
|
42
|
+
from trilogy.core.models.environment import Environment
|
|
43
|
+
from trilogy.core.models.execute import CTE, CompiledCTE, UnionCTE
|
|
58
44
|
from trilogy.core.processing.utility import (
|
|
59
45
|
decompose_condition,
|
|
60
46
|
is_scalar_condition,
|
|
61
47
|
sort_select_output,
|
|
62
48
|
)
|
|
63
49
|
from trilogy.core.query_processor import process_copy, process_persist, process_query
|
|
50
|
+
from trilogy.core.statements.author import (
|
|
51
|
+
ConceptDeclarationStatement,
|
|
52
|
+
CopyStatement,
|
|
53
|
+
ImportStatement,
|
|
54
|
+
MergeStatementV2,
|
|
55
|
+
MultiSelectStatement,
|
|
56
|
+
PersistStatement,
|
|
57
|
+
RawSQLStatement,
|
|
58
|
+
RowsetDerivationStatement,
|
|
59
|
+
SelectStatement,
|
|
60
|
+
ShowStatement,
|
|
61
|
+
)
|
|
62
|
+
from trilogy.core.statements.execute import (
|
|
63
|
+
ProcessedCopyStatement,
|
|
64
|
+
ProcessedQuery,
|
|
65
|
+
ProcessedQueryPersist,
|
|
66
|
+
ProcessedRawSQLStatement,
|
|
67
|
+
ProcessedShowStatement,
|
|
68
|
+
)
|
|
64
69
|
from trilogy.dialect.common import render_join, render_unnest
|
|
65
70
|
from trilogy.hooks.base_hook import BaseHook
|
|
66
71
|
|
|
67
72
|
LOGGER_PREFIX = "[RENDERING]"
|
|
68
73
|
|
|
74
|
+
WINDOW_ITEMS = (BuildWindowItem,)
|
|
75
|
+
FILTER_ITEMS = (BuildFilterItem,)
|
|
76
|
+
AGGREGATE_ITEMS = (BuildAggregateWrapper,)
|
|
77
|
+
FUNCTION_ITEMS = (BuildFunction,)
|
|
78
|
+
PARENTHETICAL_ITEMS = (BuildParenthetical,)
|
|
79
|
+
CASE_WHEN_ITEMS = (BuildCaseWhen,)
|
|
80
|
+
CASE_ELSE_ITEMS = (BuildCaseElse,)
|
|
81
|
+
SUBSELECT_COMPARISON_ITEMS = (BuildSubselectComparison,)
|
|
82
|
+
COMPARISON_ITEMS = (BuildComparison,)
|
|
83
|
+
CONDITIONAL_ITEMS = (BuildConditional,)
|
|
84
|
+
|
|
69
85
|
|
|
70
86
|
def INVALID_REFERENCE_STRING(x: Any, callsite: str = ""):
|
|
87
|
+
# if CONFIG.validate_missing:
|
|
88
|
+
# raise SyntaxError(f"INVALID_REFERENCE_BUG_{callsite}<{x}>")
|
|
89
|
+
|
|
71
90
|
return f"INVALID_REFERENCE_BUG_{callsite}<{x}>"
|
|
72
91
|
|
|
73
92
|
|
|
@@ -237,7 +256,7 @@ def safe_quote(string: str, quote_char: str):
|
|
|
237
256
|
return ".".join([f"{quote_char}{string}{quote_char}" for string in components])
|
|
238
257
|
|
|
239
258
|
|
|
240
|
-
def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c:
|
|
259
|
+
def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c: BuildConcept, quote_char: str):
|
|
241
260
|
address = c.address
|
|
242
261
|
raw = cte.source_map.get(address, None)
|
|
243
262
|
|
|
@@ -265,25 +284,26 @@ class BaseDialect:
|
|
|
265
284
|
|
|
266
285
|
def render_order_item(
|
|
267
286
|
self,
|
|
268
|
-
order_item:
|
|
287
|
+
order_item: BuildOrderItem,
|
|
269
288
|
cte: CTE | UnionCTE,
|
|
270
289
|
final: bool = False,
|
|
271
290
|
alias: bool = True,
|
|
272
291
|
) -> str:
|
|
273
|
-
if final:
|
|
274
|
-
|
|
275
|
-
|
|
292
|
+
# if final:
|
|
293
|
+
# if not alias:
|
|
294
|
+
# return f"{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
|
|
276
295
|
|
|
277
|
-
|
|
296
|
+
# return f"{cte.name}.{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
|
|
278
297
|
|
|
279
|
-
return f"{self.
|
|
298
|
+
return f"{self.render_expr(order_item.expr, cte=cte, qualify=False)} {order_item.order.value}"
|
|
280
299
|
|
|
281
300
|
def render_concept_sql(
|
|
282
301
|
self,
|
|
283
|
-
c:
|
|
302
|
+
c: BuildConcept,
|
|
284
303
|
cte: CTE | UnionCTE,
|
|
285
304
|
alias: bool = True,
|
|
286
305
|
raise_invalid: bool = False,
|
|
306
|
+
qualify: bool = True,
|
|
287
307
|
) -> str:
|
|
288
308
|
result = None
|
|
289
309
|
if c.pseudonyms:
|
|
@@ -297,20 +317,26 @@ class BaseDialect:
|
|
|
297
317
|
f"{LOGGER_PREFIX} [{c.address}] Attempting rendering w/ candidate {candidate.address}"
|
|
298
318
|
)
|
|
299
319
|
result = self._render_concept_sql(
|
|
300
|
-
candidate, cte, raise_invalid=True
|
|
320
|
+
candidate, cte, raise_invalid=True, qualify=qualify
|
|
301
321
|
)
|
|
302
322
|
if result:
|
|
303
323
|
break
|
|
304
324
|
except ValueError:
|
|
305
325
|
continue
|
|
306
326
|
if not result:
|
|
307
|
-
result = self._render_concept_sql(
|
|
327
|
+
result = self._render_concept_sql(
|
|
328
|
+
c, cte, raise_invalid=raise_invalid, qualify=qualify
|
|
329
|
+
)
|
|
308
330
|
if alias:
|
|
309
331
|
return f"{result} as {self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
|
|
310
332
|
return result
|
|
311
333
|
|
|
312
334
|
def _render_concept_sql(
|
|
313
|
-
self,
|
|
335
|
+
self,
|
|
336
|
+
c: BuildConcept,
|
|
337
|
+
cte: CTE | UnionCTE,
|
|
338
|
+
raise_invalid: bool = False,
|
|
339
|
+
qualify: bool = True,
|
|
314
340
|
) -> str:
|
|
315
341
|
# only recurse while it's in sources of the current cte
|
|
316
342
|
logger.debug(
|
|
@@ -322,9 +348,9 @@ class BaseDialect:
|
|
|
322
348
|
logger.debug(
|
|
323
349
|
f"{LOGGER_PREFIX} [{c.address}] rendering concept with lineage that is not already existing, have {cte.source_map}"
|
|
324
350
|
)
|
|
325
|
-
if isinstance(c.lineage,
|
|
351
|
+
if isinstance(c.lineage, WINDOW_ITEMS):
|
|
326
352
|
rendered_order_components = [
|
|
327
|
-
f"{self.
|
|
353
|
+
f"{self.render_expr(x.expr, cte, raise_invalid=raise_invalid)} {x.order.value}"
|
|
328
354
|
for x in c.lineage.order_by
|
|
329
355
|
]
|
|
330
356
|
rendered_over_components = [
|
|
@@ -345,17 +371,17 @@ class BaseDialect:
|
|
|
345
371
|
sort=",".join(rendered_order_components),
|
|
346
372
|
offset=c.lineage.index,
|
|
347
373
|
)
|
|
348
|
-
elif isinstance(c.lineage,
|
|
374
|
+
elif isinstance(c.lineage, FILTER_ITEMS):
|
|
349
375
|
# for cases when we've optimized this
|
|
350
376
|
if cte.condition == c.lineage.where.conditional:
|
|
351
377
|
rval = self.render_expr(c.lineage.content, cte=cte)
|
|
352
378
|
else:
|
|
353
379
|
rval = f"CASE WHEN {self.render_expr(c.lineage.where.conditional, cte=cte)} THEN {self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid)} ELSE NULL END"
|
|
354
|
-
elif isinstance(c.lineage,
|
|
380
|
+
elif isinstance(c.lineage, BuildRowsetItem):
|
|
355
381
|
rval = f"{self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid)}"
|
|
356
|
-
elif isinstance(c.lineage,
|
|
382
|
+
elif isinstance(c.lineage, BuildMultiSelectLineage):
|
|
357
383
|
rval = f"{self.render_concept_sql(c.lineage.find_source(c, cte), cte=cte, alias=False, raise_invalid=raise_invalid)}"
|
|
358
|
-
elif isinstance(c.lineage,
|
|
384
|
+
elif isinstance(c.lineage, AGGREGATE_ITEMS):
|
|
359
385
|
args = [
|
|
360
386
|
self.render_expr(v, cte) # , alias=False)
|
|
361
387
|
for v in c.lineage.function.arguments
|
|
@@ -369,21 +395,21 @@ class BaseDialect:
|
|
|
369
395
|
)
|
|
370
396
|
rval = f"{self.FUNCTION_GRAIN_MATCH_MAP[c.lineage.function.operator](args)}"
|
|
371
397
|
elif (
|
|
372
|
-
isinstance(c.lineage,
|
|
398
|
+
isinstance(c.lineage, FUNCTION_ITEMS)
|
|
373
399
|
and c.lineage.operator == FunctionType.UNION
|
|
374
400
|
):
|
|
375
401
|
local_matched = [
|
|
376
402
|
x
|
|
377
403
|
for x in c.lineage.arguments
|
|
378
|
-
if isinstance(x,
|
|
404
|
+
if isinstance(x, BuildConcept) and x.address in cte.output_columns
|
|
379
405
|
]
|
|
406
|
+
# if we're sorting by the output of the union
|
|
380
407
|
if not local_matched:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
)
|
|
384
|
-
rval = self.render_expr(local_matched[0], cte)
|
|
408
|
+
rval = c.safe_address
|
|
409
|
+
else:
|
|
410
|
+
rval = self.render_expr(local_matched[0], cte)
|
|
385
411
|
elif (
|
|
386
|
-
isinstance(c.lineage,
|
|
412
|
+
isinstance(c.lineage, FUNCTION_ITEMS)
|
|
387
413
|
and c.lineage.operator == FunctionType.CONSTANT
|
|
388
414
|
and CONFIG.rendering.parameters is True
|
|
389
415
|
and c.datatype.data_type != DataType.MAP
|
|
@@ -393,9 +419,9 @@ class BaseDialect:
|
|
|
393
419
|
args = []
|
|
394
420
|
for arg in c.lineage.arguments:
|
|
395
421
|
if (
|
|
396
|
-
isinstance(arg,
|
|
422
|
+
isinstance(arg, BuildConcept)
|
|
397
423
|
and arg.lineage
|
|
398
|
-
and isinstance(arg.lineage,
|
|
424
|
+
and isinstance(arg.lineage, FUNCTION_ITEMS)
|
|
399
425
|
and arg.lineage.operator
|
|
400
426
|
in (
|
|
401
427
|
FunctionType.ADD,
|
|
@@ -406,7 +432,7 @@ class BaseDialect:
|
|
|
406
432
|
):
|
|
407
433
|
args.append(
|
|
408
434
|
self.render_expr(
|
|
409
|
-
|
|
435
|
+
BuildParenthetical(content=arg),
|
|
410
436
|
cte=cte,
|
|
411
437
|
raise_invalid=raise_invalid,
|
|
412
438
|
)
|
|
@@ -422,13 +448,13 @@ class BaseDialect:
|
|
|
422
448
|
rval = f"{self.FUNCTION_GRAIN_MATCH_MAP[c.lineage.operator](args)}"
|
|
423
449
|
else:
|
|
424
450
|
logger.debug(
|
|
425
|
-
f"{LOGGER_PREFIX} [{c.address}] Rendering basic lookup from {cte.source_map.get(c.address,
|
|
451
|
+
f"{LOGGER_PREFIX} [{c.address}] Rendering basic lookup from {cte.source_map.get(c.address,None)}"
|
|
426
452
|
)
|
|
427
453
|
|
|
428
454
|
raw_content = cte.get_alias(c)
|
|
429
455
|
if isinstance(raw_content, RawColumnExpr):
|
|
430
456
|
rval = raw_content.text
|
|
431
|
-
elif isinstance(raw_content,
|
|
457
|
+
elif isinstance(raw_content, FUNCTION_ITEMS):
|
|
432
458
|
rval = self.render_expr(
|
|
433
459
|
raw_content, cte=cte, raise_invalid=raise_invalid
|
|
434
460
|
)
|
|
@@ -440,23 +466,34 @@ class BaseDialect:
|
|
|
440
466
|
self.QUOTE_CHARACTER,
|
|
441
467
|
)
|
|
442
468
|
if not rval:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
469
|
+
# unions won't have a specific source mapped; just use a generic column reference
|
|
470
|
+
# we shouldn't ever have an expression at this point, so will be safe
|
|
471
|
+
if isinstance(cte, UnionCTE):
|
|
472
|
+
rval = c.safe_address
|
|
473
|
+
else:
|
|
474
|
+
if raise_invalid:
|
|
475
|
+
raise ValueError(
|
|
476
|
+
f"Invalid reference string found in query: {rval}, this should never occur. Please report this issue."
|
|
477
|
+
)
|
|
478
|
+
rval = INVALID_REFERENCE_STRING(
|
|
479
|
+
f"Missing source reference to {c.address}"
|
|
446
480
|
)
|
|
447
|
-
rval = INVALID_REFERENCE_STRING(
|
|
448
|
-
f"Missing source reference to {c.address}"
|
|
449
|
-
)
|
|
450
481
|
return rval
|
|
451
482
|
|
|
452
483
|
def render_expr(
|
|
453
484
|
self,
|
|
454
485
|
e: Union[
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
486
|
+
BuildConcept,
|
|
487
|
+
BuildFunction,
|
|
488
|
+
BuildConditional,
|
|
489
|
+
BuildAggregateWrapper,
|
|
490
|
+
BuildComparison,
|
|
491
|
+
BuildCaseWhen,
|
|
492
|
+
BuildCaseElse,
|
|
493
|
+
BuildSubselectComparison,
|
|
494
|
+
BuildWindowItem,
|
|
495
|
+
BuildFilterItem,
|
|
496
|
+
BuildParenthetical,
|
|
460
497
|
str,
|
|
461
498
|
int,
|
|
462
499
|
list,
|
|
@@ -465,9 +502,6 @@ class BaseDialect:
|
|
|
465
502
|
date,
|
|
466
503
|
datetime,
|
|
467
504
|
DataType,
|
|
468
|
-
Function,
|
|
469
|
-
Parenthetical,
|
|
470
|
-
AggregateWrapper,
|
|
471
505
|
MagicConstants,
|
|
472
506
|
MapWrapper[Any, Any],
|
|
473
507
|
MapType,
|
|
@@ -477,18 +511,14 @@ class BaseDialect:
|
|
|
477
511
|
ListWrapper[Any],
|
|
478
512
|
TupleWrapper[Any],
|
|
479
513
|
DatePart,
|
|
480
|
-
CaseWhen,
|
|
481
|
-
CaseElse,
|
|
482
|
-
WindowItem,
|
|
483
|
-
FilterItem,
|
|
484
|
-
# FilterItem
|
|
485
514
|
],
|
|
486
515
|
cte: Optional[CTE | UnionCTE] = None,
|
|
487
516
|
cte_map: Optional[Dict[str, CTE | UnionCTE]] = None,
|
|
488
517
|
raise_invalid: bool = False,
|
|
518
|
+
qualify: bool = True,
|
|
489
519
|
) -> str:
|
|
490
|
-
if isinstance(e,
|
|
491
|
-
if isinstance(e.right,
|
|
520
|
+
if isinstance(e, SUBSELECT_COMPARISON_ITEMS):
|
|
521
|
+
if isinstance(e.right, BuildConcept):
|
|
492
522
|
# we won't always have an existnce map
|
|
493
523
|
# so fall back to the normal map
|
|
494
524
|
lookup_cte = cte
|
|
@@ -513,7 +543,10 @@ class BaseDialect:
|
|
|
513
543
|
f"Missing source CTE for {e.right.address}"
|
|
514
544
|
)
|
|
515
545
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} (select {target}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} from {target} where {target}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} is not null)"
|
|
516
|
-
elif isinstance(
|
|
546
|
+
elif isinstance(
|
|
547
|
+
e.right,
|
|
548
|
+
(ListWrapper, TupleWrapper, BuildParenthetical, list),
|
|
549
|
+
):
|
|
517
550
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
|
|
518
551
|
|
|
519
552
|
elif isinstance(
|
|
@@ -528,12 +561,12 @@ class BaseDialect:
|
|
|
528
561
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} ({self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)})"
|
|
529
562
|
else:
|
|
530
563
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
|
|
531
|
-
elif isinstance(e,
|
|
564
|
+
elif isinstance(e, COMPARISON_ITEMS):
|
|
532
565
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
|
|
533
|
-
elif isinstance(e,
|
|
566
|
+
elif isinstance(e, CONDITIONAL_ITEMS):
|
|
534
567
|
# conditions need to be nested in parentheses
|
|
535
568
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)}"
|
|
536
|
-
elif isinstance(e,
|
|
569
|
+
elif isinstance(e, WINDOW_ITEMS):
|
|
537
570
|
rendered_order_components = [
|
|
538
571
|
f"{self.render_expr(x.expr, cte, cte_map=cte_map, raise_invalid=raise_invalid)} {x.order.value}"
|
|
539
572
|
for x in e.order_by
|
|
@@ -543,22 +576,22 @@ class BaseDialect:
|
|
|
543
576
|
for x in e.over
|
|
544
577
|
]
|
|
545
578
|
return f"{self.WINDOW_FUNCTION_MAP[e.type](concept = self.render_expr(e.content, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
|
|
546
|
-
elif isinstance(e,
|
|
579
|
+
elif isinstance(e, PARENTHETICAL_ITEMS):
|
|
547
580
|
# conditions need to be nested in parentheses
|
|
548
581
|
if isinstance(e.content, list):
|
|
549
582
|
return f"( {','.join([self.render_expr(x, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid) for x in e.content])} )"
|
|
550
583
|
return f"( {self.render_expr(e.content, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} )"
|
|
551
|
-
elif isinstance(e,
|
|
584
|
+
elif isinstance(e, CASE_WHEN_ITEMS):
|
|
552
585
|
return f"WHEN {self.render_expr(e.comparison, cte=cte, cte_map=cte_map) } THEN {self.render_expr(e.expr, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid) }"
|
|
553
|
-
elif isinstance(e,
|
|
586
|
+
elif isinstance(e, CASE_ELSE_ITEMS):
|
|
554
587
|
return f"ELSE {self.render_expr(e.expr, cte=cte, cte_map=cte_map, raise_invalid=raise_invalid) }"
|
|
555
|
-
elif isinstance(e,
|
|
588
|
+
elif isinstance(e, FUNCTION_ITEMS):
|
|
556
589
|
arguments = []
|
|
557
590
|
for arg in e.arguments:
|
|
558
591
|
if (
|
|
559
|
-
isinstance(arg,
|
|
592
|
+
isinstance(arg, BuildConcept)
|
|
560
593
|
and arg.lineage
|
|
561
|
-
and isinstance(arg.lineage,
|
|
594
|
+
and isinstance(arg.lineage, FUNCTION_ITEMS)
|
|
562
595
|
and arg.lineage.operator
|
|
563
596
|
in (
|
|
564
597
|
FunctionType.ADD,
|
|
@@ -569,7 +602,7 @@ class BaseDialect:
|
|
|
569
602
|
):
|
|
570
603
|
arguments.append(
|
|
571
604
|
self.render_expr(
|
|
572
|
-
|
|
605
|
+
BuildParenthetical(content=arg),
|
|
573
606
|
cte=cte,
|
|
574
607
|
cte_map=cte_map,
|
|
575
608
|
raise_invalid=raise_invalid,
|
|
@@ -586,15 +619,15 @@ class BaseDialect:
|
|
|
586
619
|
return self.FUNCTION_MAP[e.operator](arguments)
|
|
587
620
|
|
|
588
621
|
return self.FUNCTION_GRAIN_MATCH_MAP[e.operator](arguments)
|
|
589
|
-
elif isinstance(e,
|
|
622
|
+
elif isinstance(e, AGGREGATE_ITEMS):
|
|
590
623
|
return self.render_expr(
|
|
591
624
|
e.function, cte, cte_map=cte_map, raise_invalid=raise_invalid
|
|
592
625
|
)
|
|
593
|
-
elif isinstance(e,
|
|
626
|
+
elif isinstance(e, FILTER_ITEMS):
|
|
594
627
|
return f"CASE WHEN {self.render_expr(e.where.conditional,cte=cte, cte_map=cte_map, raise_invalid=raise_invalid)} THEN {self.render_expr(e.content, cte, cte_map=cte_map, raise_invalid=raise_invalid)} ELSE NULL END"
|
|
595
|
-
elif isinstance(e,
|
|
628
|
+
elif isinstance(e, BuildConcept):
|
|
596
629
|
if (
|
|
597
|
-
isinstance(e.lineage,
|
|
630
|
+
isinstance(e.lineage, FUNCTION_ITEMS)
|
|
598
631
|
and e.lineage.operator == FunctionType.CONSTANT
|
|
599
632
|
and CONFIG.rendering.parameters is True
|
|
600
633
|
and e.datatype.data_type != DataType.MAP
|
|
@@ -602,7 +635,11 @@ class BaseDialect:
|
|
|
602
635
|
return f":{e.safe_address}"
|
|
603
636
|
if cte:
|
|
604
637
|
return self.render_concept_sql(
|
|
605
|
-
e,
|
|
638
|
+
e,
|
|
639
|
+
cte,
|
|
640
|
+
alias=False,
|
|
641
|
+
raise_invalid=raise_invalid,
|
|
642
|
+
qualify=qualify,
|
|
606
643
|
)
|
|
607
644
|
elif cte_map:
|
|
608
645
|
return f"{cte_map[e.address].name}.{self.QUOTE_CHARACTER}{e.safe_address}{self.QUOTE_CHARACTER}"
|
|
@@ -643,6 +680,7 @@ class BaseDialect:
|
|
|
643
680
|
[self.render_cte(child).statement for child in cte.internal_ctes]
|
|
644
681
|
)
|
|
645
682
|
if cte.order_by:
|
|
683
|
+
|
|
646
684
|
ordering = [
|
|
647
685
|
self.render_order_item(i, cte, final=True, alias=False)
|
|
648
686
|
for i in cte.order_by.items
|
|
@@ -703,8 +741,8 @@ class BaseDialect:
|
|
|
703
741
|
final_joins = []
|
|
704
742
|
else:
|
|
705
743
|
final_joins = cte.joins or []
|
|
706
|
-
where:
|
|
707
|
-
having:
|
|
744
|
+
where: BuildConditional | BuildParenthetical | BuildComparison | None = None
|
|
745
|
+
having: BuildConditional | BuildParenthetical | BuildComparison | None = None
|
|
708
746
|
materialized = {x for x, v in cte.source_map.items() if v}
|
|
709
747
|
if cte.condition:
|
|
710
748
|
if not cte.group_to_grain or is_scalar_condition(
|
|
@@ -807,6 +845,7 @@ class BaseDialect:
|
|
|
807
845
|
| ProcessedRawSQLStatement
|
|
808
846
|
| ProcessedCopyStatement
|
|
809
847
|
] = []
|
|
848
|
+
factory = Factory(environment=environment)
|
|
810
849
|
for statement in statements:
|
|
811
850
|
if isinstance(statement, PersistStatement):
|
|
812
851
|
if hooks:
|
|
@@ -837,12 +876,15 @@ class BaseDialect:
|
|
|
837
876
|
elif isinstance(statement, ShowStatement):
|
|
838
877
|
# TODO - encapsulate this a little better
|
|
839
878
|
if isinstance(statement.content, SelectStatement):
|
|
879
|
+
|
|
840
880
|
output.append(
|
|
841
881
|
ProcessedShowStatement(
|
|
842
882
|
output_columns=[
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
883
|
+
factory.build(
|
|
884
|
+
environment.concepts[
|
|
885
|
+
DEFAULT_CONCEPTS["query_text"].address
|
|
886
|
+
]
|
|
887
|
+
)
|
|
846
888
|
],
|
|
847
889
|
output_values=[
|
|
848
890
|
process_query(
|
trilogy/dialect/common.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from typing import Callable
|
|
2
2
|
|
|
3
3
|
from trilogy.core.enums import Modifier, UnnestMode
|
|
4
|
-
from trilogy.core.models import
|
|
4
|
+
from trilogy.core.models.build import BuildConcept, BuildFunction
|
|
5
|
+
from trilogy.core.models.datasource import RawColumnExpr
|
|
6
|
+
from trilogy.core.models.execute import (
|
|
5
7
|
CTE,
|
|
6
|
-
Concept,
|
|
7
|
-
Function,
|
|
8
8
|
InstantiatedUnnestJoin,
|
|
9
9
|
Join,
|
|
10
|
-
RawColumnExpr,
|
|
11
10
|
)
|
|
12
11
|
|
|
13
12
|
|
|
@@ -20,8 +19,8 @@ def null_wrapper(lval: str, rval: str, modifiers: list[Modifier]) -> str:
|
|
|
20
19
|
def render_unnest(
|
|
21
20
|
unnest_mode: UnnestMode,
|
|
22
21
|
quote_character: str,
|
|
23
|
-
concept:
|
|
24
|
-
render_func: Callable[[
|
|
22
|
+
concept: BuildConcept,
|
|
23
|
+
render_func: Callable[[BuildConcept, CTE, bool], str],
|
|
25
24
|
cte: CTE,
|
|
26
25
|
):
|
|
27
26
|
if unnest_mode == UnnestMode.CROSS_JOIN:
|
|
@@ -33,7 +32,7 @@ def render_join_concept(
|
|
|
33
32
|
name: str,
|
|
34
33
|
quote_character: str,
|
|
35
34
|
cte: CTE,
|
|
36
|
-
concept:
|
|
35
|
+
concept: BuildConcept,
|
|
37
36
|
render_expr,
|
|
38
37
|
inlined_ctes: set[str],
|
|
39
38
|
):
|
|
@@ -43,7 +42,7 @@ def render_join_concept(
|
|
|
43
42
|
if isinstance(raw_content, RawColumnExpr):
|
|
44
43
|
rval = raw_content.text
|
|
45
44
|
return rval
|
|
46
|
-
elif isinstance(raw_content,
|
|
45
|
+
elif isinstance(raw_content, BuildFunction):
|
|
47
46
|
rval = render_expr(raw_content, cte=cte)
|
|
48
47
|
return rval
|
|
49
48
|
return f"{name}.{quote_character}{raw_content}{quote_character}"
|
|
@@ -53,8 +52,8 @@ def render_join_concept(
|
|
|
53
52
|
def render_join(
|
|
54
53
|
join: Join | InstantiatedUnnestJoin,
|
|
55
54
|
quote_character: str,
|
|
56
|
-
render_func: Callable[[
|
|
57
|
-
render_expr_func: Callable[[
|
|
55
|
+
render_func: Callable[[BuildConcept, CTE, bool], str],
|
|
56
|
+
render_expr_func: Callable[[BuildConcept, CTE], str],
|
|
58
57
|
cte: CTE,
|
|
59
58
|
unnest_mode: UnnestMode = UnnestMode.CROSS_APPLY,
|
|
60
59
|
) -> str | None:
|
trilogy/dialect/duckdb.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
|
|
|
3
3
|
from jinja2 import Template
|
|
4
4
|
|
|
5
5
|
from trilogy.core.enums import FunctionType, UnnestMode, WindowType
|
|
6
|
-
from trilogy.core.models import DataType
|
|
6
|
+
from trilogy.core.models.core import DataType
|
|
7
7
|
from trilogy.dialect.base import BaseDialect
|
|
8
8
|
|
|
9
9
|
WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
|
trilogy/dialect/enums.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
3
3
|
|
|
4
|
+
from trilogy.core.models.environment import Environment
|
|
5
|
+
|
|
4
6
|
if TYPE_CHECKING:
|
|
5
|
-
from trilogy import
|
|
7
|
+
from trilogy import Executor
|
|
6
8
|
from trilogy.hooks.base_hook import BaseHook
|
|
7
9
|
|
|
8
10
|
from trilogy.constants import logger
|
|
@@ -98,7 +100,7 @@ class Dialects(Enum):
|
|
|
98
100
|
conf: DialectConfig | None = None,
|
|
99
101
|
_engine_factory: Callable | None = None,
|
|
100
102
|
) -> "Executor":
|
|
101
|
-
from trilogy import
|
|
103
|
+
from trilogy import Executor
|
|
102
104
|
|
|
103
105
|
if _engine_factory is not None:
|
|
104
106
|
return Executor(
|
trilogy/dialect/presto.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
|
|
|
3
3
|
from jinja2 import Template
|
|
4
4
|
|
|
5
5
|
from trilogy.core.enums import FunctionType, UnnestMode, WindowType
|
|
6
|
-
from trilogy.core.models import DataType
|
|
6
|
+
from trilogy.core.models.core import DataType
|
|
7
7
|
from trilogy.dialect.base import BaseDialect
|
|
8
8
|
|
|
9
9
|
WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
|
trilogy/dialect/sql_server.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any, Callable, Mapping
|
|
|
3
3
|
from jinja2 import Template
|
|
4
4
|
|
|
5
5
|
from trilogy.core.enums import FunctionType, WindowType
|
|
6
|
-
from trilogy.core.
|
|
6
|
+
from trilogy.core.statements.execute import (
|
|
7
7
|
ProcessedQuery,
|
|
8
8
|
ProcessedQueryPersist,
|
|
9
9
|
ProcessedRawSQLStatement,
|