pytrilogy 0.0.3.89__py3-none-any.whl → 0.0.3.91__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.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.89
3
+ Version: 0.0.3.91
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,9 +1,9 @@
1
- pytrilogy-0.0.3.89.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=4z9yJ2J3LXCYhlRTHXCrS6JqfhW_hJ2IO2zM6sLdcV4,303
1
+ pytrilogy-0.0.3.91.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=LDWJhBs1R3AQp5R7V_F0oNbHt99WNfQUp0xPRujT930,303
3
3
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  trilogy/constants.py,sha256=eKb_EJvSqjN9tGbdVEViwdtwwh8fZ3-jpOEDqL71y70,1691
5
5
  trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
6
- trilogy/executor.py,sha256=iwrYs5hEaw2hTaNZOYW5Z0w6Va1RzdRpg5bn50tlslA,16731
6
+ trilogy/executor.py,sha256=tcowEz8I7zbwLnuTr7BlGJ5wnt1JKBNffXbm5ywkNv8,17032
7
7
  trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
8
8
  trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
@@ -23,10 +23,10 @@ trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,
23
23
  trilogy/core/query_processor.py,sha256=5aFgv-2LVM1Uku9cR_tFuTRDwyLnxc95bCMAHeFy2AY,20332
24
24
  trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
25
25
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- trilogy/core/models/author.py,sha256=wG91OmLZmKXI6K9Kv6VVcpD4l3kzu8kh_miNpGKWCmw,79978
26
+ trilogy/core/models/author.py,sha256=wiKmEIouIVuzaYSM30PGon_FA9nowtDgnyLO-x5znBI,80180
27
27
  trilogy/core/models/build.py,sha256=CyrSo4xgU-uDKW3xUVYs5cTk3Z3Z2BMWdGQNHnHZOqU,66127
28
28
  trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
29
- trilogy/core/models/core.py,sha256=6pWIL0FPMXECwaEwigNuYkN60YlaspSg7JbBnXx1gw0,12038
29
+ trilogy/core/models/core.py,sha256=NOvonI4Ip4thpz5WoJZWbbBa44PFfpd2hXGx2Cbi4CE,12521
30
30
  trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
31
31
  trilogy/core/models/environment.py,sha256=0IHSCFf5e5b4LPQN3vmjumtfM1iD1tN4WMoUr0UqxZI,27855
32
32
  trilogy/core/models/execute.py,sha256=sVWhjwWull-T6pUJizhrYVGCWHY3eZivVN6KNlhcHig,41839
@@ -46,7 +46,7 @@ trilogy/core/processing/node_generators/__init__.py,sha256=iVJ-crowPxYeut-hFjyEj
46
46
  trilogy/core/processing/node_generators/basic_node.py,sha256=TLZCv4WS196a-0g5xgKuJGthnGP8Ugm46iz85_3NIY4,5626
47
47
  trilogy/core/processing/node_generators/common.py,sha256=PdysdroW9DUADP7f5Wv_GKPUyCTROZV1g3L45fawxi8,9443
48
48
  trilogy/core/processing/node_generators/constant_node.py,sha256=LfpDq2WrBRZ3tGsLxw77LuigKfhbteWWh9L8BGdMGwk,1146
49
- trilogy/core/processing/node_generators/filter_node.py,sha256=oRRq2-T3ufgn4D23uQsc58f20eFk-djs4QI3WKA75K8,10908
49
+ trilogy/core/processing/node_generators/filter_node.py,sha256=ArBsQJl-4fWBJWCE28CRQ7UT7ErnFfbcseoQQZrBodY,11220
50
50
  trilogy/core/processing/node_generators/group_node.py,sha256=1QJhRxsTklJ5xq8wHlAURZaN9gL9FPpeCa1OJ7IwXnY,6769
51
51
  trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
52
52
  trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
@@ -77,8 +77,8 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
77
77
  trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
78
78
  trilogy/core/statements/execute.py,sha256=pfr1CZ_Cx1qQ-7LDyRI0JUfvtxBr_GGv-VeqiAjr43g,1406
79
79
  trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
- trilogy/dialect/base.py,sha256=bkCJsJvUQLlrKzHCTbWlLikas5o-JluCVI12RSSbYuE,48228
81
- trilogy/dialect/bigquery.py,sha256=8xhEu0z_lKANjbvzvBbC7CeKrJf1iP8YyrHqNale-ug,4351
80
+ trilogy/dialect/base.py,sha256=_L5wHBHz8v4Us3tu4QIupKuaObmyhWhyDuroT95wUbo,48228
81
+ trilogy/dialect/bigquery.py,sha256=XS3hpybeowgfrOrkycAigAF3NX2YUzTzfgE6f__2fT4,4316
82
82
  trilogy/dialect/common.py,sha256=tSthIZOXXRPQ4KeMKnDDsH7KlTmf2EVqigVtLyoc4zc,6071
83
83
  trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
84
84
  trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
@@ -98,7 +98,7 @@ trilogy/parsing/common.py,sha256=yV1AckK0h8u1OFeGQBTMu-wuW5m63c5CcZuPicsTH_w,306
98
98
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
99
99
  trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
100
100
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
101
- trilogy/parsing/parse_engine.py,sha256=LOvhHZ92s6Tw_spQK8ev4BdVUrzT2QnvzVhkIVHBe_8,79800
101
+ trilogy/parsing/parse_engine.py,sha256=fgqCtV6sf9HrkViEjf6XXdRpPf4hJ1gSyzLXZ9sLBHs,80148
102
102
  trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
103
103
  trilogy/parsing/trilogy.lark,sha256=ySzMMLxyPjn74MjFHZxXPTW-jHW68KLPJpiszPvZaO0,15780
104
104
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -111,8 +111,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
111
111
  trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
112
112
  trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
113
113
  trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
114
- pytrilogy-0.0.3.89.dist-info/METADATA,sha256=i-vj95n5yQ7Bn7RqoD6rSWVUF1mtHv9tzaFH0h8TBvk,9589
115
- pytrilogy-0.0.3.89.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
116
- pytrilogy-0.0.3.89.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
117
- pytrilogy-0.0.3.89.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
118
- pytrilogy-0.0.3.89.dist-info/RECORD,,
114
+ pytrilogy-0.0.3.91.dist-info/METADATA,sha256=_W2oS79HhEjdCvg8ZApmI5siy5k513pXXCO541NVziQ,9589
115
+ pytrilogy-0.0.3.91.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
116
+ pytrilogy-0.0.3.91.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
117
+ pytrilogy-0.0.3.91.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
118
+ pytrilogy-0.0.3.91.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.89"
7
+ __version__ = "0.0.3.91"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -659,6 +659,12 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
659
659
  def validate_comparison(self):
660
660
  left_type = arg_to_datatype(self.left)
661
661
  right_type = arg_to_datatype(self.right)
662
+ left_name = (
663
+ left_type.name if isinstance(left_type, DataType) else str(left_type)
664
+ )
665
+ right_name = (
666
+ right_type.name if isinstance(right_type, DataType) else str(right_type)
667
+ )
662
668
  if self.operator in (ComparisonOperator.IS, ComparisonOperator.IS_NOT):
663
669
  if self.right != MagicConstants.NULL and DataType.BOOL != right_type:
664
670
  raise SyntaxError(
@@ -676,12 +682,12 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
676
682
  left_type, right_type
677
683
  ):
678
684
  raise SyntaxError(
679
- f"Cannot compare {left_type.name} and {right_type.name} with operator {self.operator} in {str(self)}"
685
+ f"Cannot compare {left_name} and {right_name} with operator {self.operator} in {str(self)}"
680
686
  )
681
687
  else:
682
688
  if not is_compatible_datatype(left_type, right_type):
683
689
  raise SyntaxError(
684
- f"Cannot compare {left_type.name} ({self.left}) and {right_type.name} ({self.right}) of different types with operator {self.operator.value} in {str(self)}"
690
+ f"Cannot compare {left_name} ({self.left}) and {right_name} ({self.right}) of different types with operator {self.operator.value} in {str(self)}"
685
691
  )
686
692
 
687
693
  return self
@@ -1660,7 +1666,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1660
1666
  def parse_output_datatype(cls, v, info: ValidationInfo):
1661
1667
  values = info.data
1662
1668
  if values.get("operator") == FunctionType.ATTR_ACCESS:
1663
- print(v)
1664
1669
  if isinstance(v, StructType):
1665
1670
  raise SyntaxError
1666
1671
  return v
@@ -2470,6 +2475,7 @@ FuncArgs = (
2470
2475
  | CaseElse
2471
2476
  | WindowItem
2472
2477
  | FilterItem
2478
+ | bool
2473
2479
  | int
2474
2480
  | float
2475
2481
  | DatePart
@@ -264,9 +264,10 @@ class StructType(BaseModel):
264
264
  class ListWrapper(Generic[VT], UserList):
265
265
  """Used to distinguish parsed list objects from other lists"""
266
266
 
267
- def __init__(self, *args, type: DataType, **kwargs):
267
+ def __init__(self, *args, type: DataType, nullable: bool = False, **kwargs):
268
268
  super().__init__(*args, **kwargs)
269
269
  self.type = type
270
+ self.nullable = nullable
270
271
 
271
272
  @classmethod
272
273
  def __get_pydantic_core_schema__(
@@ -315,10 +316,11 @@ class MapWrapper(Generic[KT, VT], UserDict):
315
316
  class TupleWrapper(Generic[VT], tuple):
316
317
  """Used to distinguish parsed tuple objects from other tuples"""
317
318
 
318
- def __init__(self, val, type: DataType, **kwargs):
319
+ def __init__(self, val, type: DataType, nullable: bool = False, **kwargs):
319
320
  super().__init__()
320
321
  self.type = type
321
322
  self.val = val
323
+ self.nullable = nullable
322
324
 
323
325
  def __getnewargs__(self):
324
326
  return (self.val, self.type)
@@ -344,15 +346,19 @@ class TupleWrapper(Generic[VT], tuple):
344
346
 
345
347
 
346
348
  def list_to_wrapper(args):
347
- types = [arg_to_datatype(arg) for arg in args]
348
- assert len(set(types)) == 1
349
- return ListWrapper(args, type=types[0])
349
+ rtypes = [arg_to_datatype(arg) for arg in args]
350
+ types = [arg for arg in rtypes if arg != DataType.NULL]
351
+ if not len(set(types)) == 1:
352
+ raise SyntaxError(f"Cannot create a list with this set of types: {set(types)}")
353
+ return ListWrapper(args, type=types[0], nullable=DataType.NULL in rtypes)
350
354
 
351
355
 
352
356
  def tuple_to_wrapper(args):
353
- types = [arg_to_datatype(arg) for arg in args]
354
- assert len(set(types)) == 1
355
- return TupleWrapper(args, type=types[0])
357
+ rtypes = [arg_to_datatype(arg) for arg in args]
358
+ types = [arg for arg in rtypes if arg != DataType.NULL]
359
+ if not len(set(types)) == 1:
360
+ raise SyntaxError(f"Cannot create a tuple with this set of types: {set(types)}")
361
+ return TupleWrapper(args, type=types[0], nullable=DataType.NULL in rtypes)
356
362
 
357
363
 
358
364
  def dict_to_map_wrapper(arg):
@@ -77,11 +77,17 @@ def build_parent_concepts(
77
77
  True if (conditions and conditions == filter_where) else False
78
78
  )
79
79
 
80
+ exact_partial_matches = True
80
81
  for x in local_optional:
81
82
  if isinstance(x.lineage, FILTER_TYPES):
82
- if concept.lineage.where == filter_where:
83
+ if set([x.address for x in x.lineage.where.concept_arguments]) == set(
84
+ [x.address for x in filter_where.concept_arguments]
85
+ ):
86
+ exact_partial_matches = (
87
+ exact_partial_matches and x.lineage.where == filter_where
88
+ )
83
89
  logger.info(
84
- f"{padding(depth)}{LOGGER_PREFIX} fetching parents for peer {x} with same filter conditions"
90
+ f"{padding(depth)}{LOGGER_PREFIX} fetching parents for peer {x.address} (of {concept.address})"
85
91
  )
86
92
 
87
93
  for arg in x.lineage.content_concept_arguments:
@@ -100,7 +106,7 @@ def build_parent_concepts(
100
106
  if x.address in same_filter_optional:
101
107
  continue
102
108
  extra_row_level_optional.append(x)
103
- is_optimized_pushdown = pushdown_filter_to_parent(
109
+ is_optimized_pushdown = exact_partial_matches and pushdown_filter_to_parent(
104
110
  local_optional, conditions, filter_where, same_filter_optional, depth
105
111
  )
106
112
  if not is_optimized_pushdown:
trilogy/dialect/base.py CHANGED
@@ -174,7 +174,7 @@ FUNCTION_MAP = {
174
174
  FunctionType.CAST: lambda x: f"cast({x[0]} as {x[1]})",
175
175
  FunctionType.CASE: lambda x: render_case(x),
176
176
  FunctionType.SPLIT: lambda x: f"split({x[0]}, {x[1]})",
177
- FunctionType.IS_NULL: lambda x: f"isnull({x[0]})",
177
+ FunctionType.IS_NULL: lambda x: f"{x[0]} is null",
178
178
  FunctionType.BOOL: lambda x: f"CASE WHEN {x[0]} THEN TRUE ELSE FALSE END",
179
179
  FunctionType.PARENTHETICAL: lambda x: f"({x[0]})",
180
180
  # Complex
@@ -24,7 +24,7 @@ FUNCTION_MAP = {
24
24
  FunctionType.LIKE: lambda x: (
25
25
  f" CASE WHEN {x[0]} like {x[1]} THEN True ELSE False END"
26
26
  ),
27
- FunctionType.IS_NULL: lambda x: f"CASE WHEN {x[0]} IS NULL THEN True ELSE False END",
27
+ FunctionType.IS_NULL: lambda x: f"{x[0]} IS NULL",
28
28
  FunctionType.MINUTE: lambda x: f"EXTRACT(MINUTE from {x[0]})",
29
29
  FunctionType.SECOND: lambda x: f"EXTRACT(SECOND from {x[0]})",
30
30
  FunctionType.HOUR: lambda x: f"EXTRACT(HOUR from {x[0]})",
trilogy/executor.py CHANGED
@@ -6,7 +6,7 @@ from typing import Any, Generator, List, Optional, Protocol
6
6
  from sqlalchemy import text
7
7
  from sqlalchemy.engine import CursorResult
8
8
 
9
- from trilogy.constants import Rendering, logger
9
+ from trilogy.constants import MagicConstants, Rendering, logger
10
10
  from trilogy.core.enums import FunctionType, Granularity, IOType
11
11
  from trilogy.core.models.author import Concept, ConceptRef, Function
12
12
  from trilogy.core.models.build import BuildFunction
@@ -388,6 +388,11 @@ class Executor(object):
388
388
  if persist and isinstance(x, ProcessedQueryPersist):
389
389
  self.environment.add_datasource(x.datasource)
390
390
 
391
+ def _atom_to_value(self, val: Any) -> Any:
392
+ if val == MagicConstants.NULL:
393
+ return None
394
+ return val
395
+
391
396
  def _concept_to_value(
392
397
  self,
393
398
  concept: Concept,
@@ -402,12 +407,15 @@ class Executor(object):
402
407
  ):
403
408
  rval = concept.lineage.arguments[0]
404
409
  if isinstance(rval, ListWrapper):
405
- return [x for x in rval]
410
+ return [self._atom_to_value(x) for x in rval]
406
411
  if isinstance(rval, MapWrapper):
407
412
  # duckdb expects maps in this format as variables
408
413
  if self.dialect == Dialects.DUCK_DB:
409
- return {"key": [x for x in rval], "value": [rval[x] for x in rval]}
410
- return {k: v for k, v in rval.items()}
414
+ return {
415
+ "key": [self._atom_to_value(x) for x in rval],
416
+ "value": [self._atom_to_value(rval[x]) for x in rval],
417
+ }
418
+ return {k: self._atom_to_value(v) for k, v in rval.items()}
411
419
  # if isinstance(rval, ConceptRef):
412
420
  # return self._concept_to_value(self.environment.concepts[rval.address], local_concepts=local_concepts)
413
421
  return rval
@@ -2040,6 +2040,15 @@ class ParseToObjects(Transformer):
2040
2040
 
2041
2041
  @v_args(meta=True)
2042
2042
  def fnot(self, meta, args):
2043
+ if arg_to_datatype(args[0]) == DataType.BOOL:
2044
+ return Comparison(
2045
+ left=self.function_factory.create_function(
2046
+ [args[0], False], FunctionType.COALESCE, meta
2047
+ ),
2048
+ operator=ComparisonOperator.EQ,
2049
+ right=False,
2050
+ meta=meta,
2051
+ )
2043
2052
  return self.function_factory.create_function(args, FunctionType.IS_NULL, meta)
2044
2053
 
2045
2054
  @v_args(meta=True)