pytrilogy 0.0.3.0__py3-none-any.whl → 0.0.3.2__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.0
3
+ Version: 0.0.3.2
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,4 +1,4 @@
1
- trilogy/__init__.py,sha256=NutBQIuxEOak6wIOXS3HLb_AXutozJpl1V0dd6o3D-Y,302
1
+ trilogy/__init__.py,sha256=u47oSxZB2gBUiSNHosZXaS5uhP_ad0NBnUKDXu5UQMw,302
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
4
4
  trilogy/engine.py,sha256=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
@@ -17,14 +17,14 @@ trilogy/core/functions.py,sha256=7Pq9jYSJd45L2pxT7AI-_rXVZmeLnmTPp8d1lA4z4Vk,244
17
17
  trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
18
18
  trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
19
19
  trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
20
- trilogy/core/query_processor.py,sha256=qh-72MBOpnhIrmBQW4_lLCvJS-H3Hq_vuy--mWamw7U,19336
20
+ trilogy/core/query_processor.py,sha256=HyDxBhQsD9KX-Y7pYznlpCAW6AvI76RqPTQNa1mreoE,19450
21
21
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  trilogy/core/models/author.py,sha256=Ai-AFBsr4sbm3T0WE3TxFwB73foGg7-uKUTyQtoBEXQ,67093
23
- trilogy/core/models/build.py,sha256=uy9sJUHTnsY9sdpyzA8egPENnyTU9fryCKz3p8H0b4I,55562
23
+ trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,56127
24
24
  trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
25
25
  trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
26
26
  trilogy/core/models/datasource.py,sha256=c0tGxyH2WwTmAD047tr69U0a6GNVf-ug26H68yii7DA,9257
27
- trilogy/core/models/environment.py,sha256=E7vdrhN0DR6_UKpxRP1KB3w9_GEcCfsKffQ4uv0_CzA,24779
27
+ trilogy/core/models/environment.py,sha256=QSl-H6nwarzKbQgNRjtwDKMJtA4F_GVQpRs-NMNt-6Q,24983
28
28
  trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
29
29
  trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
30
30
  trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
@@ -32,10 +32,10 @@ trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52d
32
32
  trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
33
33
  trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
34
34
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- trilogy/core/processing/concept_strategies_v3.py,sha256=StDlgfoUIeuh2IoZlRlLgkJp5hA8polgdMmOzL_wLCo,39265
35
+ trilogy/core/processing/concept_strategies_v3.py,sha256=0rFnasSQlkXTRIfAFMHyHVux1pTQ3ryKeQds-SFSot0,40290
36
36
  trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
37
37
  trilogy/core/processing/utility.py,sha256=Oc5tLGeDDpzhbfo2ZcF8ex1kez-NcJDMcG2Lm5BjS4c,20548
38
- trilogy/core/processing/node_generators/__init__.py,sha256=s_YV1OYc336DuS9591259qjI_K_CtOCuhkf4t2aOgYs,733
38
+ trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
39
39
  trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
40
40
  trilogy/core/processing/node_generators/common.py,sha256=ZsDzThjm_mAtdQpKAg8QIJiPVZ4KuUkKyilj4eOhSDs,9439
41
41
  trilogy/core/processing/node_generators/filter_node.py,sha256=rlY7TbgjJlGhahYgdCIJpJbaSREAGVJEsyUIGaA38O0,8271
@@ -43,12 +43,13 @@ trilogy/core/processing/node_generators/group_node.py,sha256=94uoGZWvBKJ1eqjbDHC
43
43
  trilogy/core/processing/node_generators/group_to_node.py,sha256=E5bEjovSx422d_MlAUCDFdY4P2WJVp61BmWwltkhzA8,3095
44
44
  trilogy/core/processing/node_generators/multiselect_node.py,sha256=z9FQOxxUvxW31a0TckFfAvnuvU8vP1GyN224RTbXUAk,7114
45
45
  trilogy/core/processing/node_generators/node_merge_node.py,sha256=sv55oynfqgpHEpo1OEtVDri-5fywzPhDlR85qaWikvY,16195
46
- trilogy/core/processing/node_generators/rowset_node.py,sha256=x3rrdUkywAFiaYRk8lv-ra8KAdmBa8dLXj0FO55lz08,6213
46
+ trilogy/core/processing/node_generators/rowset_node.py,sha256=8yeMWiyi9IFnza7qPn9YYC3WpA53weq3AY5WisIui8Y,6705
47
47
  trilogy/core/processing/node_generators/select_merge_node.py,sha256=VHCPMbnKfg7AOfoYa6PKxpNni-j5JEfliNUiltmZhds,19698
48
48
  trilogy/core/processing/node_generators/select_node.py,sha256=Y-zO0AFkTrpi2LyebjpyHU7WWANr7nKZSS9rY7DH4Wo,1888
49
+ trilogy/core/processing/node_generators/synonym_node.py,sha256=9LHK2XHDjbyTLjmDQieskG8fqbiSpRnFOkfrutDnOTE,2258
49
50
  trilogy/core/processing/node_generators/union_node.py,sha256=zuMSmgF170vzlp2BBQEhKbqUMjVl2xQDqUB82Dhv-VU,2536
50
51
  trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOlgn3E1-B38osng0dh2pDykwITY,2410
51
- trilogy/core/processing/node_generators/window_node.py,sha256=_W_bF-8nYi1fjeZ7rXg54Sf7SRDyjDOwk7S_RBoZCWM,3514
52
+ trilogy/core/processing/node_generators/window_node.py,sha256=6KoxhmpVOTN3HGWT0FIS96xqlm2Inouw9VL2D_0kSg0,3481
52
53
  trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
54
  trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=GMW07bb6hXurhF0hZLYoMAKSIS65tat5hwBjvqqPeSA,6516
54
55
  trilogy/core/processing/nodes/__init__.py,sha256=DqPG3Y8vl5-UTeox6hn1EE6iwPIJpsM-XeZALHSgLZQ,5058
@@ -66,7 +67,7 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
66
67
  trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
67
68
  trilogy/core/statements/execute.py,sha256=cSlvpHFOqpiZ89pPZ5GDp9Hu6j6uj-5_h21FWm_L-KM,1248
68
69
  trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- trilogy/dialect/base.py,sha256=uvow18PxtFdabf59x0mAXbazV9bqGAfbBxcCAe8JaFs,40212
70
+ trilogy/dialect/base.py,sha256=u00kIIl98as1QzcduiiyyoBzxRGVeBxfeO5hWlRCAJU,40222
70
71
  trilogy/dialect/bigquery.py,sha256=mKC3zoEU232h9RtIXJjqiZ72lWH8a6S28p6wAZKrAfg,2952
71
72
  trilogy/dialect/common.py,sha256=cbTo_vamdp8pj9spSjGSH-bSZpy4FpNJ12k5vMvyT2Y,3942
72
73
  trilogy/dialect/config.py,sha256=UiBY2tBbNk9owx-zxP_3lN9lErEUXhXIU_bcXA18AvU,2992
@@ -76,24 +77,24 @@ trilogy/dialect/postgres.py,sha256=VH4EB4myjIeZTHeFU6vK00GxY9c53rCBjg2mLbdaCEE,3
76
77
  trilogy/dialect/presto.py,sha256=bAxaDcLL21fivPg7hmBd3HJmd0yYJdPdwNgNA5ga7DE,3391
77
78
  trilogy/dialect/snowflake.py,sha256=wmao9p26jX5yIX5SC8sRAZTXkPGTvq6ixO693QTfhz8,2989
78
79
  trilogy/dialect/sql_server.py,sha256=IN91uEM0MpsiVAlsYC89uMQkiVn0i86B8Tst6v9uFkU,3129
79
- trilogy/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
+ trilogy/hooks/__init__.py,sha256=T3SF3phuUDPLXKGRVE_Lf9mzuwoXWyaLolncR_1kY30,144
80
81
  trilogy/hooks/base_hook.py,sha256=I_l-NBMNC7hKTDx1JgHZPVOOCvLQ36m2oIGaR5EUMXY,1180
81
82
  trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2940
82
- trilogy/hooks/query_debugger.py,sha256=qds0xnW1cxH6Nv9U1L3gqSqD9OrE3TDQKEjyHyuzkFI,5558
83
+ trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
83
84
  trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
85
  trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
- trilogy/parsing/common.py,sha256=ti_2s3EEMAz5NwmctchiB0DpVPVusTehc2l3bZQdMQI,20500
86
+ trilogy/parsing/common.py,sha256=lZSSu6Q8HvJVB0xCYsNpoTlkCgNNEVVCwHExVBWCDO8,20524
86
87
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
87
88
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
88
89
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
89
- trilogy/parsing/parse_engine.py,sha256=7WU9jWH8tc6PSgBo_FcDA51QuhytzPTrZYFagCRw9Ec,53869
90
+ trilogy/parsing/parse_engine.py,sha256=rtlwl-WzcPCd5pm0WhXHwe8JGOJCuQm_EAnRj6qG93w,54318
90
91
  trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,17837
91
92
  trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
92
93
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
94
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
94
- pytrilogy-0.0.3.0.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
95
- pytrilogy-0.0.3.0.dist-info/METADATA,sha256=z1DV_FX4ZThTyGzGCqQwyg78lSPcjDyOLO3NypXMJXk,8983
96
- pytrilogy-0.0.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
97
- pytrilogy-0.0.3.0.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
98
- pytrilogy-0.0.3.0.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
99
- pytrilogy-0.0.3.0.dist-info/RECORD,,
95
+ pytrilogy-0.0.3.2.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
96
+ pytrilogy-0.0.3.2.dist-info/METADATA,sha256=buA8GqwIT8kSii6cguT2dMcbI12XMVwQDoQya9jo-Gc,8983
97
+ pytrilogy-0.0.3.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
98
+ pytrilogy-0.0.3.2.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
99
+ pytrilogy-0.0.3.2.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
100
+ pytrilogy-0.0.3.2.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.0"
7
+ __version__ = "0.0.3.2"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -242,6 +242,9 @@ class BuildGrain(BaseModel):
242
242
  components: set[str] = Field(default_factory=set)
243
243
  where_clause: Optional[BuildWhereClause] = None
244
244
 
245
+ def __init__(self, **kwargs):
246
+ super().__init__(**kwargs)
247
+
245
248
  def without_condition(self):
246
249
  return BuildGrain(components=self.components)
247
250
 
@@ -1171,7 +1174,7 @@ class BuildFilterItem(BuildConceptArgs, BaseModel):
1171
1174
 
1172
1175
  class BuildRowsetLineage(BuildConceptArgs, BaseModel):
1173
1176
  name: str
1174
- derived_concepts: List[BuildConcept]
1177
+ derived_concepts: List[str]
1175
1178
  select: SelectLineage | MultiSelectLineage
1176
1179
 
1177
1180
 
@@ -1222,8 +1225,8 @@ class BuildSelectLineage(BaseModel):
1222
1225
  limit: Optional[int] = None
1223
1226
  meta: Metadata = Field(default_factory=lambda: Metadata())
1224
1227
  grain: BuildGrain = Field(default_factory=BuildGrain)
1225
- where_clause: WhereClause | BuildWhereClause | None = Field(default=None)
1226
- having_clause: HavingClause | BuildHavingClause | None = Field(default=None)
1228
+ where_clause: BuildWhereClause | None = Field(default=None)
1229
+ having_clause: BuildHavingClause | None = Field(default=None)
1227
1230
 
1228
1231
  @property
1229
1232
  def output_components(self) -> List[BuildConcept]:
@@ -1447,7 +1450,9 @@ class Factory:
1447
1450
  ):
1448
1451
  self.grain = grain or Grain()
1449
1452
  self.environment = environment
1450
- self.local_concepts: dict[str, BuildConcept] = local_concepts or {}
1453
+ self.local_concepts: dict[str, BuildConcept] = (
1454
+ {} if local_concepts is None else local_concepts
1455
+ )
1451
1456
 
1452
1457
  @singledispatchmethod
1453
1458
  def build(self, base):
@@ -1494,7 +1499,7 @@ class Factory:
1494
1499
  @build.register
1495
1500
  def _(self, base: Function) -> BuildFunction:
1496
1501
 
1497
- new = BuildFunction(
1502
+ new = BuildFunction.model_construct(
1498
1503
  operator=base.operator,
1499
1504
  arguments=[self.build(c) for c in base.arguments],
1500
1505
  output_datatype=base.output_datatype,
@@ -1515,14 +1520,14 @@ class Factory:
1515
1520
 
1516
1521
  @build.register
1517
1522
  def _(self, base: CaseWhen) -> BuildCaseWhen:
1518
- return BuildCaseWhen(
1523
+ return BuildCaseWhen.model_construct(
1519
1524
  comparison=self.build(base.comparison),
1520
1525
  expr=(self.build(base.expr)),
1521
1526
  )
1522
1527
 
1523
1528
  @build.register
1524
1529
  def _(self, base: CaseElse) -> BuildCaseElse:
1525
- return BuildCaseElse(expr=self.build(base.expr))
1530
+ return BuildCaseElse.model_construct(expr=self.build(base.expr))
1526
1531
 
1527
1532
  @build.register
1528
1533
  def _(self, base: Concept) -> BuildConcept:
@@ -1537,7 +1542,7 @@ class Factory:
1537
1542
  derivation, final_grain, build_lineage
1538
1543
  )
1539
1544
  is_aggregate = Concept.calculate_is_aggregate(build_lineage)
1540
- rval = BuildConcept(
1545
+ rval = BuildConcept.model_construct(
1541
1546
  name=base.name,
1542
1547
  datatype=base.datatype,
1543
1548
  purpose=base.purpose,
@@ -1565,12 +1570,12 @@ class Factory:
1565
1570
  else:
1566
1571
  by = [self.build(x) for x in base.by]
1567
1572
  parent = self.build(base.function)
1568
- return BuildAggregateWrapper(function=parent, by=by)
1573
+ return BuildAggregateWrapper.model_construct(function=parent, by=by)
1569
1574
 
1570
1575
  @build.register
1571
1576
  def _(self, base: ColumnAssignment) -> BuildColumnAssignment:
1572
1577
 
1573
- return BuildColumnAssignment(
1578
+ return BuildColumnAssignment.model_construct(
1574
1579
  alias=(
1575
1580
  self.build(base.alias)
1576
1581
  if isinstance(base.alias, Function)
@@ -1584,26 +1589,31 @@ class Factory:
1584
1589
 
1585
1590
  @build.register
1586
1591
  def _(self, base: OrderBy) -> BuildOrderBy:
1587
- return BuildOrderBy(items=[self.build(x) for x in base.items])
1592
+ return BuildOrderBy.model_construct(items=[self.build(x) for x in base.items])
1588
1593
 
1589
1594
  @build.register
1590
1595
  def _(self, base: OrderItem) -> BuildOrderItem:
1591
- return BuildOrderItem(
1596
+ return BuildOrderItem.model_construct(
1592
1597
  expr=(self.build(base.expr)),
1593
1598
  order=base.order,
1594
1599
  )
1595
1600
 
1596
1601
  @build.register
1597
1602
  def _(self, base: WhereClause) -> BuildWhereClause:
1598
- return BuildWhereClause(conditional=self.build(base.conditional))
1603
+ return BuildWhereClause.model_construct(
1604
+ conditional=self.build(base.conditional)
1605
+ )
1599
1606
 
1600
1607
  @build.register
1601
1608
  def _(self, base: HavingClause) -> BuildHavingClause:
1602
- return BuildHavingClause(conditional=self.build(base.conditional))
1609
+ return BuildHavingClause.model_construct(
1610
+ conditional=self.build(base.conditional)
1611
+ )
1603
1612
 
1604
1613
  @build.register
1605
1614
  def _(self, base: WindowItem) -> BuildWindowItem:
1606
- return BuildWindowItem(
1615
+
1616
+ return BuildWindowItem.model_construct(
1607
1617
  type=base.type,
1608
1618
  content=self.build(base.content),
1609
1619
  order_by=[self.build(x) for x in base.order_by],
@@ -1613,7 +1623,7 @@ class Factory:
1613
1623
 
1614
1624
  @build.register
1615
1625
  def _(self, base: Conditional) -> BuildConditional:
1616
- return BuildConditional(
1626
+ return BuildConditional.model_construct(
1617
1627
  left=(self.build(base.left)),
1618
1628
  right=(self.build(base.right)),
1619
1629
  operator=base.operator,
@@ -1621,7 +1631,7 @@ class Factory:
1621
1631
 
1622
1632
  @build.register
1623
1633
  def _(self, base: SubselectComparison) -> BuildSubselectComparison:
1624
- return BuildSubselectComparison(
1634
+ return BuildSubselectComparison.model_construct(
1625
1635
  left=(self.build(base.left)),
1626
1636
  right=(self.build(base.right)),
1627
1637
  operator=base.operator,
@@ -1629,7 +1639,7 @@ class Factory:
1629
1639
 
1630
1640
  @build.register
1631
1641
  def _(self, base: Comparison) -> BuildComparison:
1632
- return BuildComparison(
1642
+ return BuildComparison.model_construct(
1633
1643
  left=(self.build(base.left)),
1634
1644
  right=(self.build(base.right)),
1635
1645
  operator=base.operator,
@@ -1637,7 +1647,7 @@ class Factory:
1637
1647
 
1638
1648
  @build.register
1639
1649
  def _(self, base: AlignItem) -> BuildAlignItem:
1640
- return BuildAlignItem(
1650
+ return BuildAlignItem.model_construct(
1641
1651
  alias=base.alias,
1642
1652
  concepts=[self.build(x) for x in base.concepts],
1643
1653
  namespace=base.namespace,
@@ -1645,7 +1655,9 @@ class Factory:
1645
1655
 
1646
1656
  @build.register
1647
1657
  def _(self, base: AlignClause) -> BuildAlignClause:
1648
- return BuildAlignClause(items=[self.build(x) for x in base.items])
1658
+ return BuildAlignClause.model_construct(
1659
+ items=[self.build(x) for x in base.items]
1660
+ )
1649
1661
 
1650
1662
  @build.register
1651
1663
  def _(self, base: RowsetItem):
@@ -1663,7 +1675,7 @@ class Factory:
1663
1675
  def _(self, base: RowsetLineage) -> BuildRowsetLineage:
1664
1676
  out = BuildRowsetLineage(
1665
1677
  name=base.name,
1666
- derived_concepts=[],
1678
+ derived_concepts=[x.address for x in base.derived_concepts],
1667
1679
  select=base.select,
1668
1680
  )
1669
1681
  return out
@@ -1675,17 +1687,19 @@ class Factory:
1675
1687
  where = factory.build(base.where_clause)
1676
1688
  else:
1677
1689
  where = None
1678
- return BuildGrain(components=base.components, where_clause=where)
1690
+ return BuildGrain.model_construct(
1691
+ components=base.components, where_clause=where
1692
+ )
1679
1693
 
1680
1694
  @build.register
1681
1695
  def _(self, base: FilterItem) -> BuildFilterItem:
1682
- return BuildFilterItem(
1696
+ return BuildFilterItem.model_construct(
1683
1697
  content=self.build(base.content), where=self.build(base.where)
1684
1698
  )
1685
1699
 
1686
1700
  @build.register
1687
1701
  def _(self, base: Parenthetical) -> BuildParenthetical:
1688
- return BuildParenthetical(content=(self.build(base.content)))
1702
+ return BuildParenthetical.model_construct(content=(self.build(base.content)))
1689
1703
 
1690
1704
  @build.register
1691
1705
  def _(self, base: SelectLineage) -> BuildSelectLineage:
@@ -1753,7 +1767,7 @@ class Factory:
1753
1767
  derived_base = []
1754
1768
  for k in base.derived_concepts:
1755
1769
  base_concept = self.environment.concepts[k]
1756
- x = BuildConcept(
1770
+ x = BuildConcept.model_construct(
1757
1771
  name=base_concept.name,
1758
1772
  datatype=base_concept.datatype,
1759
1773
  purpose=base_concept.purpose,
@@ -1778,7 +1792,7 @@ class Factory:
1778
1792
  local_concepts=local_build_cache,
1779
1793
  )
1780
1794
  where_factory = Factory(environment=self.environment)
1781
- lineage = BuildMultiSelectLineage(
1795
+ lineage = BuildMultiSelectLineage.model_construct(
1782
1796
  # we don't build selects here; they'll be built automatically in query discovery
1783
1797
  selects=base.selects,
1784
1798
  grain=final_grain,
@@ -1831,7 +1845,7 @@ class Factory:
1831
1845
  factory = Factory(
1832
1846
  grain=base.grain, environment=self.environment, local_concepts=local_cache
1833
1847
  )
1834
- return BuildDatasource(
1848
+ return BuildDatasource.model_construct(
1835
1849
  name=base.name,
1836
1850
  columns=[factory.build(c) for c in base.columns],
1837
1851
  address=base.address,
@@ -39,6 +39,7 @@ from trilogy.core.models.author import (
39
39
  Concept,
40
40
  ConceptRef,
41
41
  Function,
42
+ SelectLineage,
42
43
  UndefinedConcept,
43
44
  UndefinedConceptFull,
44
45
  address_with_namespace,
@@ -189,6 +190,7 @@ class Environment(BaseModel):
189
190
  ] = Field(default_factory=EnvironmentDatasourceDict)
190
191
  functions: Dict[str, Function] = Field(default_factory=dict)
191
192
  data_types: Dict[str, DataType] = Field(default_factory=dict)
193
+ named_statements: Dict[str, SelectLineage] = Field(default_factory=dict)
192
194
  imports: Dict[str, list[Import]] = Field(
193
195
  default_factory=lambda: defaultdict(list) # type: ignore
194
196
  )
@@ -217,6 +219,9 @@ class Environment(BaseModel):
217
219
 
218
220
  return Factory(self, local_concepts=local_concepts).build(self)
219
221
 
222
+ def add_rowset(self, name: str, lineage: SelectLineage):
223
+ self.named_statements[name] = lineage
224
+
220
225
  def duplicate(self):
221
226
  return Environment.model_construct(
222
227
  datasources=self.datasources.duplicate(),
@@ -24,6 +24,7 @@ from trilogy.core.processing.node_generators import (
24
24
  gen_merge_node,
25
25
  gen_multiselect_node,
26
26
  gen_rowset_node,
27
+ gen_synonym_node,
27
28
  gen_union_node,
28
29
  gen_unnest_node,
29
30
  gen_window_node,
@@ -364,7 +365,7 @@ def generate_node(
364
365
  x.address for x in local_optional if x.derivation != Derivation.CONSTANT
365
366
  ]
366
367
  logger.info(
367
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there is non root filter requirements {non_root}. Recursing with all of these as mandatory"
368
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there are non root/non constant concepts we should find first: {non_root}. Recursing with all of these as mandatory"
368
369
  )
369
370
 
370
371
  if not history.check_started(
@@ -448,7 +449,7 @@ def generate_node(
448
449
  if x.derivation not in (Derivation.ROOT, Derivation.CONSTANT)
449
450
  ]
450
451
  logger.info(
451
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there is non root filter requirements {non_root}. Recursing with all of these as mandatory"
452
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including any filters, there are non-root concepts we should expand first: {non_root}. Recursing with all of these as mandatory"
452
453
  )
453
454
 
454
455
  if not history.check_started(
@@ -491,7 +492,7 @@ def generate_node(
491
492
  all_concepts=root_targets,
492
493
  environment=environment,
493
494
  g=g,
494
- depth=depth,
495
+ depth=depth + 1,
495
496
  source_concepts=source_concepts,
496
497
  history=history,
497
498
  search_conditions=conditions,
@@ -516,9 +517,35 @@ def generate_node(
516
517
  f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Found connections for {[c.address for c in root_targets]} via concept addition; removing extra {[c.address for c in extra]}"
517
518
  )
518
519
  return expanded
520
+
521
+ logger.info(
522
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} could not find additional concept(s) to inject"
523
+ )
519
524
  logger.info(
520
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} could not find additional concept to inject"
525
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Could not resolve root concepts, checking for synonyms"
521
526
  )
527
+ if not history.check_started(
528
+ root_targets, accept_partial=accept_partial, conditions=conditions
529
+ ):
530
+ history.log_start(
531
+ root_targets, accept_partial=accept_partial, conditions=conditions
532
+ )
533
+ resolved = gen_synonym_node(
534
+ all_concepts=root_targets,
535
+ environment=environment,
536
+ g=g,
537
+ depth=depth + 1,
538
+ source_concepts=source_concepts,
539
+ history=history,
540
+ conditions=conditions,
541
+ accept_partial=accept_partial,
542
+ )
543
+ if resolved:
544
+ logger.info(
545
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} resolved concepts through synonyms"
546
+ )
547
+ return resolved
548
+
522
549
  return None
523
550
  else:
524
551
  raise ValueError(f"Unknown derivation {concept.derivation} on {concept}")
@@ -779,8 +806,6 @@ def _search_concepts(
779
806
  else:
780
807
 
781
808
  completion_mandatory = mandatory_list
782
- if "date.month_seq" in completion_mandatory:
783
- raise SyntaxError(completion_mandatory)
784
809
  attempted: set[str] = set()
785
810
 
786
811
  found: set[str] = set()
@@ -6,6 +6,7 @@ from .multiselect_node import gen_multiselect_node
6
6
  from .node_merge_node import gen_merge_node
7
7
  from .rowset_node import gen_rowset_node
8
8
  from .select_node import gen_select_node
9
+ from .synonym_node import gen_synonym_node
9
10
  from .union_node import gen_union_node
10
11
  from .unnest_node import gen_unnest_node
11
12
  from .window_node import gen_window_node
@@ -22,4 +23,5 @@ __all__ = [
22
23
  "gen_group_to_node",
23
24
  "gen_rowset_node",
24
25
  "gen_multiselect_node",
26
+ "gen_synonym_node",
25
27
  ]
@@ -51,27 +51,32 @@ def gen_rowset_node(
51
51
  enrichment = set([x.address for x in local_optional])
52
52
 
53
53
  factory = Factory(environment=history.base_environment, grain=select.grain)
54
+ logger.info(
55
+ f"{padding(depth)}{LOGGER_PREFIX} rowset derived concepts are {lineage.rowset.derived_concepts}"
56
+ )
57
+ concept_pool = list(environment.concepts.values()) + list(
58
+ environment.alias_origin_lookup.values()
59
+ )
60
+ rowset_outputs = [
61
+ x.address for x in concept_pool if x.address in lineage.rowset.derived_concepts
62
+ ]
54
63
  rowset_relevant: list[BuildConcept] = [
55
- v
56
- for v in environment.concepts.values()
57
- if isinstance(v.lineage, BuildRowsetItem)
58
- and v.lineage.rowset.name == rowset.name
64
+ v for v in concept_pool if v.address in rowset_outputs
59
65
  ]
60
- # logger.info(
61
- # f"{padding(depth)}{LOGGER_PREFIX} rowset relevant nodes are {rowset_relevant}"
62
- # )
66
+
63
67
  select_hidden = node.hidden_concepts
64
68
  rowset_hidden = [
65
69
  x
66
70
  for x in rowset_relevant
67
- if isinstance(x.lineage, BuildRowsetItem)
71
+ if x.address in lineage.rowset.derived_concepts
72
+ and isinstance(x.lineage, BuildRowsetItem)
68
73
  and x.lineage.content.address in select_hidden
69
74
  ]
70
75
  additional_relevant = [
71
76
  factory.build(x) for x in select.output_components if x.address in enrichment
72
77
  ]
73
78
  # add in other other concepts
74
- node.add_output_concepts(rowset_relevant + additional_relevant)
79
+ node.set_output_concepts(rowset_relevant + additional_relevant)
75
80
  if select.where_clause:
76
81
  for item in additional_relevant:
77
82
  logger.info(
@@ -84,7 +89,9 @@ def gen_rowset_node(
84
89
  for x in node.output_concepts
85
90
  if x.address not in local_optional + [concept]
86
91
  and x.derivation != Derivation.ROWSET
92
+ and not any(z in lineage.rowset.derived_concepts for z in x.pseudonyms)
87
93
  ]
94
+ logger.info(f"{padding(depth)}{LOGGER_PREFIX} hiding {final_hidden}")
88
95
  node.hide_output_concepts(final_hidden)
89
96
  assert node.resolution_cache
90
97
  # assume grain to be output of select
@@ -104,6 +111,9 @@ def gen_rowset_node(
104
111
  )
105
112
 
106
113
  node.rebuild_cache()
114
+ logger.info(
115
+ f"{padding(depth)}{LOGGER_PREFIX} final output is {[x.address for x in node.output_concepts]}"
116
+ )
107
117
  if not local_optional or all(
108
118
  x.address in node.output_concepts and x.address not in node.partial_concepts
109
119
  for x in local_optional
@@ -0,0 +1,68 @@
1
+ import itertools
2
+ from collections import defaultdict
3
+ from typing import List
4
+
5
+ from trilogy.constants import logger
6
+ from trilogy.core.enums import FunctionType
7
+ from trilogy.core.models.build import BuildConcept, BuildFunction, BuildWhereClause
8
+ from trilogy.core.models.build_environment import BuildEnvironment
9
+ from trilogy.core.processing.nodes import History, StrategyNode
10
+ from trilogy.core.processing.utility import padding
11
+
12
+ LOGGER_PREFIX = "[GEN_SYNONYM_NODE]"
13
+
14
+
15
+ def is_union(c: BuildConcept):
16
+ return (
17
+ isinstance(c.lineage, BuildFunction)
18
+ and c.lineage.operator == FunctionType.UNION
19
+ )
20
+
21
+
22
+ def gen_synonym_node(
23
+ all_concepts: List[BuildConcept],
24
+ environment: BuildEnvironment,
25
+ g,
26
+ depth: int,
27
+ source_concepts,
28
+ history: History | None = None,
29
+ conditions: BuildWhereClause | None = None,
30
+ accept_partial: bool = False,
31
+ ) -> StrategyNode | None:
32
+ local_prefix = f"[GEN_SYNONYM_NODE] {padding(depth)}"
33
+ base_fingerprint = tuple([x.address for x in all_concepts])
34
+ synonyms = defaultdict(list)
35
+ synonym_count = 0
36
+ for x in all_concepts:
37
+ synonyms[x.address] = [x]
38
+ for y in x.pseudonyms:
39
+
40
+ if y in environment.alias_origin_lookup:
41
+ synonyms[x.address].append(environment.alias_origin_lookup[y])
42
+ synonym_count += 1
43
+ elif y in environment.concepts:
44
+ synonyms[x.address].append(environment.concepts[y])
45
+ synonym_count += 1
46
+ if synonym_count == 0:
47
+ return None
48
+
49
+ logger.info(f"{local_prefix} Generating Synonym Node with {len(synonyms)} synonyms")
50
+
51
+ combinations = itertools.product(*(synonyms[obj] for obj in synonyms.keys()))
52
+ for combo in combinations:
53
+ fingerprint = tuple([x.address for x in combo])
54
+ if fingerprint == base_fingerprint:
55
+ continue
56
+ attempt: StrategyNode | None = source_concepts(
57
+ combo,
58
+ history=history,
59
+ environment=environment,
60
+ depth=depth,
61
+ conditions=conditions,
62
+ g=g,
63
+ accept_partial=accept_partial,
64
+ )
65
+ if attempt:
66
+ logger.info(f"{local_prefix} found inputs with {combo}")
67
+ return attempt
68
+ return None
@@ -19,7 +19,6 @@ def resolve_window_parent_concepts(
19
19
  if not isinstance(concept.lineage, WINDOW_TYPES):
20
20
  raise ValueError
21
21
  base = []
22
- logger.info(concept.lineage)
23
22
  if concept.lineage.over:
24
23
  base += concept.lineage.over
25
24
  if concept.lineage.order_by:
@@ -12,6 +12,8 @@ from trilogy.core.models.build import (
12
12
  BuildConcept,
13
13
  BuildConditional,
14
14
  BuildDatasource,
15
+ BuildMultiSelectLineage,
16
+ BuildSelectLineage,
15
17
  Factory,
16
18
  )
17
19
  from trilogy.core.models.environment import Environment
@@ -373,7 +375,10 @@ def get_query_node(
373
375
  raise ValueError(f"Statement has no output components {statement}")
374
376
 
375
377
  history = history or History(base_environment=environment)
376
- build_statement = Factory(environment=environment).build(statement)
378
+ build_statement: BuildSelectLineage | BuildMultiSelectLineage = Factory(
379
+ environment=environment
380
+ ).build(statement)
381
+
377
382
  # build_statement = statement
378
383
  build_environment = environment.materialize_for_select(
379
384
  build_statement.local_concepts
trilogy/dialect/base.py CHANGED
@@ -84,7 +84,9 @@ CONDITIONAL_ITEMS = (BuildConditional,)
84
84
 
85
85
 
86
86
  def INVALID_REFERENCE_STRING(x: Any, callsite: str = ""):
87
- # raise SyntaxError(x)
87
+ # if CONFIG.validate_missing:
88
+ # raise SyntaxError(f"INVALID_REFERENCE_BUG_{callsite}<{x}>")
89
+
88
90
  return f"INVALID_REFERENCE_BUG_{callsite}<{x}>"
89
91
 
90
92
 
@@ -293,7 +295,9 @@ class BaseDialect:
293
295
 
294
296
  # return f"{cte.name}.{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
295
297
 
296
- return f"{self.render_expr(order_item.expr, cte=cte, qualify=False)} {order_item.order.value}"
298
+ return (
299
+ f"{self.render_expr(order_item.expr, cte=cte, )} {order_item.order.value}"
300
+ )
297
301
 
298
302
  def render_concept_sql(
299
303
  self,
@@ -301,7 +305,6 @@ class BaseDialect:
301
305
  cte: CTE | UnionCTE,
302
306
  alias: bool = True,
303
307
  raise_invalid: bool = False,
304
- qualify: bool = True,
305
308
  ) -> str:
306
309
  result = None
307
310
  if c.pseudonyms:
@@ -315,7 +318,9 @@ class BaseDialect:
315
318
  f"{LOGGER_PREFIX} [{c.address}] Attempting rendering w/ candidate {candidate.address}"
316
319
  )
317
320
  result = self._render_concept_sql(
318
- candidate, cte, raise_invalid=True, qualify=qualify
321
+ candidate,
322
+ cte,
323
+ raise_invalid=True,
319
324
  )
320
325
  if result:
321
326
  break
@@ -323,7 +328,9 @@ class BaseDialect:
323
328
  continue
324
329
  if not result:
325
330
  result = self._render_concept_sql(
326
- c, cte, raise_invalid=raise_invalid, qualify=qualify
331
+ c,
332
+ cte,
333
+ raise_invalid=raise_invalid,
327
334
  )
328
335
  if alias:
329
336
  return f"{result} as {self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
@@ -334,7 +341,6 @@ class BaseDialect:
334
341
  c: BuildConcept,
335
342
  cte: CTE | UnionCTE,
336
343
  raise_invalid: bool = False,
337
- qualify: bool = True,
338
344
  ) -> str:
339
345
  # only recurse while it's in sources of the current cte
340
346
  logger.debug(
@@ -513,7 +519,6 @@ class BaseDialect:
513
519
  cte: Optional[CTE | UnionCTE] = None,
514
520
  cte_map: Optional[Dict[str, CTE | UnionCTE]] = None,
515
521
  raise_invalid: bool = False,
516
- qualify: bool = True,
517
522
  ) -> str:
518
523
  if isinstance(e, SUBSELECT_COMPARISON_ITEMS):
519
524
  if isinstance(e.right, BuildConcept):
@@ -637,7 +642,6 @@ class BaseDialect:
637
642
  cte,
638
643
  alias=False,
639
644
  raise_invalid=raise_invalid,
640
- qualify=qualify,
641
645
  )
642
646
  elif cte_map:
643
647
  return f"{cte_map[e.address].name}.{self.QUOTE_CHARACTER}{e.safe_address}{self.QUOTE_CHARACTER}"
trilogy/hooks/__init__.py CHANGED
@@ -0,0 +1,4 @@
1
+ from trilogy.hooks.graph_hook import GraphHook
2
+ from trilogy.hooks.query_debugger import DebuggingHook
3
+
4
+ __all__ = ["DebuggingHook", "GraphHook"]
@@ -12,7 +12,6 @@ from trilogy.core.models.execute import (
12
12
  )
13
13
  from trilogy.core.processing.nodes import StrategyNode
14
14
  from trilogy.core.statements.author import SelectStatement
15
- from trilogy.dialect.bigquery import BigqueryDialect
16
15
  from trilogy.hooks.base_hook import BaseHook
17
16
 
18
17
 
@@ -22,9 +21,6 @@ class PrintMode(Enum):
22
21
  FULL = 3
23
22
 
24
23
 
25
- renderer = BigqueryDialect()
26
-
27
-
28
24
  class DebuggingHook(BaseHook):
29
25
  def __init__(
30
26
  self,
@@ -46,6 +42,9 @@ class DebuggingHook(BaseHook):
46
42
  self.process_other = PrintMode(process_other)
47
43
  self.messages: list[str] = []
48
44
  self.uuid = uuid4()
45
+ from trilogy.dialect.bigquery import BigqueryDialect
46
+
47
+ self.renderer = BigqueryDialect()
49
48
 
50
49
  def print(self, *args):
51
50
  merged = " ".join([str(x) for x in args])
@@ -123,7 +122,7 @@ class DebuggingHook(BaseHook):
123
122
  self.print(
124
123
  " " * depth, input.name, "->", input.group_to_grain, "->", select_statement
125
124
  )
126
- sql = renderer.render_cte(input).statement
125
+ sql = self.renderer.render_cte(input).statement
127
126
  for line in sql.split("\n"):
128
127
  logger.debug(" " * (depth) + line)
129
128
  if isinstance(input, CTE):
trilogy/parsing/common.py CHANGED
@@ -503,6 +503,11 @@ def rowset_to_concepts(rowset: RowsetDerivationStatement, environment: Environme
503
503
  if isinstance(orig_concept.lineage, FilterItem):
504
504
  if orig_concept.lineage.where == rowset.select.where_clause:
505
505
  name = environment.concepts[orig_concept.lineage.content.address].name
506
+ base_namespace = (
507
+ f"{rowset.name}.{orig_concept.namespace}"
508
+ if orig_concept.namespace != rowset.namespace
509
+ else rowset.name
510
+ )
506
511
 
507
512
  new_concept = Concept(
508
513
  name=name,
@@ -512,11 +517,7 @@ def rowset_to_concepts(rowset: RowsetDerivationStatement, environment: Environme
512
517
  grain=orig_concept.grain,
513
518
  # TODO: add proper metadata
514
519
  metadata=Metadata(concept_source=ConceptSource.CTE),
515
- namespace=(
516
- f"{rowset.name}.{orig_concept.namespace}"
517
- if orig_concept.namespace != rowset.namespace
518
- else rowset.name
519
- ),
520
+ namespace=base_namespace,
520
521
  keys=orig_concept.keys,
521
522
  derivation=Derivation.ROWSET,
522
523
  granularity=orig_concept.granularity,
@@ -576,11 +576,24 @@ class ParseToObjects(Transformer):
576
576
  select=select,
577
577
  namespace=self.environment.namespace or DEFAULT_NAMESPACE,
578
578
  )
579
+
580
+ # clean up current definitions
581
+ # to_delete = set()
582
+ # if output.name in self.environment.named_statements:
583
+ # for k, v in self.environment.concepts.items():
584
+ # if v.derivation == Derivation.ROWSET and v.lineage.rowset.name == name:
585
+ # to_delete.add(k)
586
+ # for k in to_delete:
587
+ # self.environment.concepts.pop(k)
588
+
579
589
  for new_concept in rowset_to_concepts(output, self.environment):
580
590
  if new_concept.metadata:
581
591
  new_concept.metadata.line_number = meta.line
582
- # output.select.local_concepts[new_concept.address] = new_concept
583
- self.environment.add_concept(new_concept)
592
+ self.environment.add_concept(new_concept, force=True)
593
+
594
+ self.environment.add_rowset(
595
+ output.name, output.select.as_lineage(self.environment)
596
+ )
584
597
  return output
585
598
 
586
599
  @v_args(meta=True)