pylegend 0.3.0__py3-none-any.whl → 0.4.0__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.
- pylegend/__init__.py +7 -5
- pylegend/core/{databse → database}/sql_to_string/__init__.py +3 -3
- pylegend/core/{databse → database}/sql_to_string/db_extension.py +11 -5
- pylegend/core/{databse → database}/sql_to_string/generator.py +2 -2
- pylegend/core/language/__init__.py +10 -10
- pylegend/core/language/legacy_api/__init__.py +13 -0
- pylegend/core/language/{aggregate_specification.py → legacy_api/aggregate_specification.py} +10 -10
- pylegend/core/language/legacy_api/legacy_api_tds_row.py +32 -0
- pylegend/core/language/legendql_api/__init__.py +13 -0
- pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +541 -0
- pylegend/core/language/legendql_api/legendql_api_tds_row.py +292 -0
- pylegend/core/language/shared/__init__.py +13 -0
- pylegend/core/language/{column_expressions.py → shared/column_expressions.py} +32 -31
- pylegend/core/language/{expression.py → shared/expression.py} +8 -0
- pylegend/core/language/{functions.py → shared/functions.py} +3 -3
- pylegend/core/language/shared/helpers.py +75 -0
- pylegend/core/language/{literal_expressions.py → shared/literal_expressions.py} +39 -1
- pylegend/core/language/{operations → shared/operations}/binary_expression.py +34 -2
- pylegend/core/language/{operations → shared/operations}/boolean_operation_expressions.py +34 -6
- pylegend/core/language/{operations → shared/operations}/collection_operation_expressions.py +146 -26
- pylegend/core/language/{operations → shared/operations}/date_operation_expressions.py +164 -24
- pylegend/core/language/{operations → shared/operations}/float_operation_expressions.py +53 -8
- pylegend/core/language/{operations → shared/operations}/integer_operation_expressions.py +62 -9
- pylegend/core/language/{operations → shared/operations}/nullary_expression.py +9 -2
- pylegend/core/language/{operations → shared/operations}/number_operation_expressions.py +211 -30
- pylegend/core/language/{operations → shared/operations}/primitive_operation_expressions.py +42 -3
- pylegend/core/language/{operations → shared/operations}/string_operation_expressions.py +169 -21
- pylegend/core/language/{operations → shared/operations}/unary_expression.py +10 -2
- pylegend/core/language/{primitive_collection.py → shared/primitive_collection.py} +2 -2
- pylegend/core/language/{primitives → shared/primitives}/__init__.py +9 -9
- pylegend/core/language/{primitives → shared/primitives}/boolean.py +9 -5
- pylegend/core/language/{primitives → shared/primitives}/date.py +23 -15
- pylegend/core/language/{primitives → shared/primitives}/datetime.py +4 -5
- pylegend/core/language/{primitives → shared/primitives}/float.py +6 -6
- pylegend/core/language/{primitives → shared/primitives}/integer.py +6 -6
- pylegend/core/language/{primitives → shared/primitives}/number.py +16 -13
- pylegend/core/language/{primitives → shared/primitives}/primitive.py +25 -5
- pylegend/core/language/{primitives → shared/primitives}/strictdate.py +4 -5
- pylegend/core/language/{primitives → shared/primitives}/string.py +18 -19
- pylegend/core/language/{tds_row.py → shared/tds_row.py} +46 -16
- pylegend/core/request/__init__.py +7 -1
- pylegend/core/request/auth.py +55 -1
- pylegend/core/request/legend_client.py +32 -0
- pylegend/core/sql/metamodel_extension.py +16 -0
- pylegend/core/tds/abstract/__init__.py +13 -0
- pylegend/core/tds/abstract/frames/__init__.py +13 -0
- pylegend/core/tds/{legend_api/frames/legend_api_applied_function_tds_frame.py → abstract/frames/applied_function_tds_frame.py} +19 -13
- pylegend/core/tds/abstract/frames/base_tds_frame.py +125 -0
- pylegend/core/tds/{legend_api/frames/legend_api_input_tds_frame.py → abstract/frames/input_tds_frame.py} +9 -12
- pylegend/core/tds/{legend_api/frames/functions → abstract}/function_helpers.py +1 -1
- pylegend/core/tds/{legend_api/frames/functions/concatenate_function.py → legacy_api/frames/functions/legacy_api_concatenate_function.py} +25 -13
- pylegend/core/tds/{legend_api/frames/functions/distinct_function.py → legacy_api/frames/functions/legacy_api_distinct_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/drop_function.py → legacy_api/frames/functions/legacy_api_drop_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/extend_function.py → legacy_api/frames/functions/legacy_api_extend_function.py} +36 -16
- pylegend/core/tds/{legend_api/frames/functions/filter_function.py → legacy_api/frames/functions/legacy_api_filter_function.py} +25 -13
- pylegend/core/tds/{legend_api/frames/functions/group_by_function.py → legacy_api/frames/functions/legacy_api_group_by_function.py} +44 -17
- pylegend/core/tds/{legend_api/frames/functions/head_function.py → legacy_api/frames/functions/legacy_api_head_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/join_by_columns_function.py → legacy_api/frames/functions/legacy_api_join_by_columns_function.py} +40 -13
- pylegend/core/tds/{legend_api/frames/functions/join_function.py → legacy_api/frames/functions/legacy_api_join_function.py} +44 -20
- pylegend/core/tds/{legend_api/frames/functions/rename_columns_function.py → legacy_api/frames/functions/legacy_api_rename_columns_function.py} +20 -8
- pylegend/core/tds/{legend_api/frames/functions/restrict_function.py → legacy_api/frames/functions/legacy_api_restrict_function.py} +17 -8
- pylegend/core/tds/{legend_api/frames/functions/slice_function.py → legacy_api/frames/functions/legacy_api_slice_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/sort_function.py → legacy_api/frames/functions/legacy_api_sort_function.py} +19 -8
- pylegend/core/tds/legacy_api/frames/legacy_api_applied_function_tds_frame.py +37 -0
- pylegend/core/tds/legacy_api/frames/legacy_api_base_tds_frame.py +204 -0
- pylegend/core/tds/legacy_api/frames/legacy_api_input_tds_frame.py +51 -0
- pylegend/core/tds/{legend_api/frames/legend_api_tds_frame.py → legacy_api/frames/legacy_api_tds_frame.py} +28 -28
- pylegend/core/tds/legendql_api/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/functions/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_asofjoin_function.py +156 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_concatenate_function.py +139 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +69 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_drop_function.py +74 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_extend_function.py +256 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_filter_function.py +121 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_function_helpers.py +137 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_groupby_function.py +256 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_head_function.py +74 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_join_function.py +214 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_project_function.py +169 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_rename_function.py +189 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_select_function.py +131 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_slice_function.py +82 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_sort_function.py +93 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_window_extend_function.py +283 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_applied_function_tds_frame.py +37 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +419 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_input_tds_frame.py +50 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +327 -0
- pylegend/core/tds/pandas_api/frames/functions/assign_function.py +6 -6
- pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +4 -0
- pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +11 -3
- pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +2 -2
- pylegend/core/tds/tds_frame.py +32 -2
- pylegend/extensions/database/vendors/postgres/postgres_sql_to_string.py +1 -1
- pylegend/extensions/tds/abstract/legend_function_input_frame.py +4 -0
- pylegend/extensions/tds/abstract/legend_service_input_frame.py +4 -0
- pylegend/extensions/tds/abstract/table_spec_input_frame.py +4 -0
- pylegend/extensions/tds/{legend_api/frames/legend_api_legend_function_input_frame.py → legacy_api/frames/legacy_api_legend_function_input_frame.py} +5 -5
- pylegend/extensions/tds/{legend_api/frames/legend_api_legend_service_input_frame.py → legacy_api/frames/legacy_api_legend_service_input_frame.py} +6 -6
- pylegend/extensions/tds/{legend_api/frames/legend_api_table_spec_input_frame.py → legacy_api/frames/legacy_api_table_spec_input_frame.py} +5 -5
- pylegend/extensions/tds/legendql_api/__init__.py +13 -0
- pylegend/extensions/tds/legendql_api/frames/__init__.py +13 -0
- pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_service_input_frame.py +46 -0
- pylegend/extensions/tds/legendql_api/frames/legendql_api_table_spec_input_frame.py +36 -0
- pylegend/{legend_api_tds_client.py → legacy_api_tds_client.py} +15 -15
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/METADATA +7 -8
- pylegend-0.4.0.dist-info/NOTICE +5 -0
- pylegend-0.4.0.dist-info/RECORD +155 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/WHEEL +1 -1
- pylegend/core/tds/legend_api/frames/legend_api_base_tds_frame.py +0 -294
- pylegend-0.3.0.dist-info/RECORD +0 -115
- /pylegend/core/{databse → database}/__init__.py +0 -0
- /pylegend/core/{databse → database}/sql_to_string/config.py +0 -0
- /pylegend/core/language/{operations → shared/operations}/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/frames/functions/__init__.py +0 -0
- /pylegend/extensions/tds/{legend_api → legacy_api}/__init__.py +0 -0
- /pylegend/extensions/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/LICENSE +0 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/LICENSE.spdx +0 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# Copyright 2025 Goldman Sachs
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from abc import ABCMeta
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from pylegend.core.language import (
|
|
18
|
+
PyLegendPrimitive,
|
|
19
|
+
PyLegendBoolean,
|
|
20
|
+
PyLegendString,
|
|
21
|
+
PyLegendNumber,
|
|
22
|
+
PyLegendInteger,
|
|
23
|
+
PyLegendFloat,
|
|
24
|
+
PyLegendDate,
|
|
25
|
+
PyLegendDateTime,
|
|
26
|
+
PyLegendStrictDate,
|
|
27
|
+
PyLegendColumnExpression,
|
|
28
|
+
PyLegendExpressionIntegerReturn,
|
|
29
|
+
PyLegendExpressionFloatReturn,
|
|
30
|
+
)
|
|
31
|
+
from pylegend._typing import (
|
|
32
|
+
PyLegendSequence,
|
|
33
|
+
PyLegendOptional,
|
|
34
|
+
PyLegendList,
|
|
35
|
+
PyLegendDict,
|
|
36
|
+
)
|
|
37
|
+
from pylegend.core.language.shared.helpers import escape_column_name
|
|
38
|
+
from pylegend.core.sql.metamodel import (
|
|
39
|
+
QuerySpecification,
|
|
40
|
+
Expression,
|
|
41
|
+
SingleColumn,
|
|
42
|
+
SortItem,
|
|
43
|
+
SortItemOrdering,
|
|
44
|
+
SortItemNullOrdering,
|
|
45
|
+
Window,
|
|
46
|
+
FunctionCall,
|
|
47
|
+
QualifiedName, IntegerLiteral
|
|
48
|
+
)
|
|
49
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig, FrameToPureConfig
|
|
50
|
+
from typing import TYPE_CHECKING
|
|
51
|
+
|
|
52
|
+
__all__: PyLegendSequence[str] = [
|
|
53
|
+
"LegendQLApiPrimitive",
|
|
54
|
+
"LegendQLApiBoolean",
|
|
55
|
+
"LegendQLApiString",
|
|
56
|
+
"LegendQLApiNumber",
|
|
57
|
+
"LegendQLApiInteger",
|
|
58
|
+
"LegendQLApiFloat",
|
|
59
|
+
"LegendQLApiDate",
|
|
60
|
+
"LegendQLApiDateTime",
|
|
61
|
+
"LegendQLApiStrictDate",
|
|
62
|
+
"LegendQLApiSortInfo",
|
|
63
|
+
"LegendQLApiSortDirection",
|
|
64
|
+
"LegendQLApiWindow",
|
|
65
|
+
"LegendQLApiPartialFrame",
|
|
66
|
+
"LegendQLApiWindowReference",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class LegendQLApiPrimitive(PyLegendPrimitive, metaclass=ABCMeta):
|
|
71
|
+
def ascending(self) -> "LegendQLApiSortInfo":
|
|
72
|
+
val = self.value()
|
|
73
|
+
if isinstance(val, PyLegendColumnExpression):
|
|
74
|
+
return LegendQLApiSortInfo(column_expr=val, direction=LegendQLApiSortDirection.ASC)
|
|
75
|
+
else:
|
|
76
|
+
raise RuntimeError("'ascending' function can only be called on column expressions. "
|
|
77
|
+
"E.g. - r.col1.ascending() / r['col1'].ascending()\n."
|
|
78
|
+
f"Found expression type - {type(val)}")
|
|
79
|
+
|
|
80
|
+
def descending(self) -> "LegendQLApiSortInfo":
|
|
81
|
+
val = self.value()
|
|
82
|
+
if isinstance(val, PyLegendColumnExpression):
|
|
83
|
+
return LegendQLApiSortInfo(column_expr=val, direction=LegendQLApiSortDirection.DESC)
|
|
84
|
+
else:
|
|
85
|
+
raise RuntimeError("'descending' function can only be called on column expressions. "
|
|
86
|
+
"E.g. - r.col1.descending() / r['col1'].descending()\n."
|
|
87
|
+
f"Found expression type - {type(val)}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class LegendQLApiBoolean(LegendQLApiPrimitive, PyLegendBoolean):
|
|
91
|
+
def __init__(self, expr: PyLegendBoolean):
|
|
92
|
+
PyLegendBoolean.__init__(self, expr.value())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class LegendQLApiString(LegendQLApiPrimitive, PyLegendString):
|
|
96
|
+
def __init__(self, expr: PyLegendString):
|
|
97
|
+
PyLegendString.__init__(self, expr.value())
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class LegendQLApiNumber(LegendQLApiPrimitive, PyLegendNumber):
|
|
101
|
+
def __init__(self, expr: PyLegendNumber):
|
|
102
|
+
PyLegendNumber.__init__(self, expr.value())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class LegendQLApiInteger(LegendQLApiPrimitive, PyLegendInteger):
|
|
106
|
+
def __init__(self, expr: PyLegendInteger):
|
|
107
|
+
PyLegendInteger.__init__(self, expr.value())
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class LegendQLApiFloat(LegendQLApiPrimitive, PyLegendFloat):
|
|
111
|
+
def __init__(self, expr: PyLegendFloat):
|
|
112
|
+
PyLegendFloat.__init__(self, expr.value())
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LegendQLApiDate(LegendQLApiPrimitive, PyLegendDate):
|
|
116
|
+
def __init__(self, expr: PyLegendDate):
|
|
117
|
+
PyLegendDate.__init__(self, expr.value())
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class LegendQLApiDateTime(LegendQLApiPrimitive, PyLegendDateTime):
|
|
121
|
+
def __init__(self, expr: PyLegendDateTime):
|
|
122
|
+
PyLegendDateTime.__init__(self, expr.value())
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class LegendQLApiStrictDate(LegendQLApiPrimitive, PyLegendStrictDate):
|
|
126
|
+
def __init__(self, expr: PyLegendStrictDate):
|
|
127
|
+
PyLegendStrictDate.__init__(self, expr.value())
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class LegendQLApiSortDirection(Enum):
|
|
131
|
+
ASC = 1,
|
|
132
|
+
DESC = 2
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class LegendQLApiSortInfo:
|
|
136
|
+
__column: str
|
|
137
|
+
__direction: LegendQLApiSortDirection
|
|
138
|
+
|
|
139
|
+
def __init__(self, column_expr: PyLegendColumnExpression, direction: LegendQLApiSortDirection) -> None:
|
|
140
|
+
self.__column = column_expr.get_column()
|
|
141
|
+
self.__direction = direction
|
|
142
|
+
|
|
143
|
+
def to_sql_node(
|
|
144
|
+
self,
|
|
145
|
+
query: QuerySpecification,
|
|
146
|
+
config: FrameToSqlConfig
|
|
147
|
+
) -> SortItem:
|
|
148
|
+
return SortItem(
|
|
149
|
+
sortKey=self.__find_column_expression(query, config),
|
|
150
|
+
ordering=(SortItemOrdering.ASCENDING if self.__direction == LegendQLApiSortDirection.ASC
|
|
151
|
+
else SortItemOrdering.DESCENDING),
|
|
152
|
+
nullOrdering=SortItemNullOrdering.UNDEFINED
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def __find_column_expression(self, query: QuerySpecification, config: FrameToSqlConfig) -> Expression:
|
|
156
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
157
|
+
filtered = [
|
|
158
|
+
s for s in query.select.selectItems
|
|
159
|
+
if (isinstance(s, SingleColumn) and
|
|
160
|
+
s.alias == db_extension.quote_identifier(self.__column))
|
|
161
|
+
]
|
|
162
|
+
if len(filtered) == 0:
|
|
163
|
+
raise RuntimeError("Cannot find column: " + self.__column) # pragma: no cover
|
|
164
|
+
return filtered[0].expression
|
|
165
|
+
|
|
166
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
167
|
+
func = 'ascending' if self.__direction == LegendQLApiSortDirection.ASC else 'descending'
|
|
168
|
+
return f"{func}(~{escape_column_name(self.__column)})"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class LegendQLApiWindowFrame(metaclass=ABCMeta):
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class LegendQLApiWindow:
|
|
176
|
+
__partition_by: PyLegendOptional[PyLegendList[str]]
|
|
177
|
+
__order_by: PyLegendOptional[PyLegendList[LegendQLApiSortInfo]]
|
|
178
|
+
__frame: PyLegendOptional[LegendQLApiWindowFrame]
|
|
179
|
+
|
|
180
|
+
def __init__(
|
|
181
|
+
self,
|
|
182
|
+
partition_by: PyLegendOptional[PyLegendList[str]] = None,
|
|
183
|
+
order_by: PyLegendOptional[PyLegendList[LegendQLApiSortInfo]] = None,
|
|
184
|
+
frame: PyLegendOptional[LegendQLApiWindowFrame] = None
|
|
185
|
+
) -> None:
|
|
186
|
+
self.__partition_by = partition_by
|
|
187
|
+
self.__order_by = order_by
|
|
188
|
+
self.__frame = frame
|
|
189
|
+
|
|
190
|
+
def get_partition_by(self) -> PyLegendOptional[PyLegendList[str]]:
|
|
191
|
+
return self.__partition_by
|
|
192
|
+
|
|
193
|
+
def get_order_by(self) -> PyLegendOptional[PyLegendList[LegendQLApiSortInfo]]:
|
|
194
|
+
return self.__order_by
|
|
195
|
+
|
|
196
|
+
def get_frame(self) -> PyLegendOptional[LegendQLApiWindowFrame]:
|
|
197
|
+
return self.__frame
|
|
198
|
+
|
|
199
|
+
def to_sql_node(
|
|
200
|
+
self,
|
|
201
|
+
query: QuerySpecification,
|
|
202
|
+
config: FrameToSqlConfig
|
|
203
|
+
) -> Window:
|
|
204
|
+
return Window(
|
|
205
|
+
windowRef=None,
|
|
206
|
+
partitions=(
|
|
207
|
+
[] if self.__partition_by is None else
|
|
208
|
+
[LegendQLApiWindow.__find_column_expression(query, col, config) for col in self.__partition_by]
|
|
209
|
+
),
|
|
210
|
+
orderBy=(
|
|
211
|
+
[] if self.__order_by is None else
|
|
212
|
+
[sort_info.to_sql_node(query, config) for sort_info in self.__order_by]
|
|
213
|
+
),
|
|
214
|
+
windowFrame=None
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def __find_column_expression(query: QuerySpecification, col: str, config: FrameToSqlConfig) -> Expression:
|
|
219
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
220
|
+
filtered = [
|
|
221
|
+
s for s in query.select.selectItems
|
|
222
|
+
if (isinstance(s, SingleColumn) and
|
|
223
|
+
s.alias == db_extension.quote_identifier(col))
|
|
224
|
+
]
|
|
225
|
+
if len(filtered) == 0:
|
|
226
|
+
raise RuntimeError("Cannot find column: " + col) # pragma: no cover
|
|
227
|
+
return filtered[0].expression
|
|
228
|
+
|
|
229
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
230
|
+
partitions_str = (
|
|
231
|
+
"[]" if self.__partition_by is None or len(self.__partition_by) == 0
|
|
232
|
+
else "~[" + (', '.join(map(escape_column_name, self.__partition_by))) + "]"
|
|
233
|
+
)
|
|
234
|
+
sorts_str = (
|
|
235
|
+
"[]" if self.__order_by is None or len(self.__order_by) == 0
|
|
236
|
+
else "[" + (', '.join([s.to_pure_expression(config) for s in self.__order_by])) + "]"
|
|
237
|
+
)
|
|
238
|
+
return f"over({partitions_str}, {sorts_str})"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class LegendQLApiPartialFrame:
|
|
242
|
+
if TYPE_CHECKING:
|
|
243
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
244
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
245
|
+
|
|
246
|
+
__base_frame: "LegendQLApiBaseTdsFrame"
|
|
247
|
+
__var_name: str
|
|
248
|
+
|
|
249
|
+
def __init__(self, base_frame: "LegendQLApiBaseTdsFrame", var_name: str) -> None:
|
|
250
|
+
self.__base_frame = base_frame
|
|
251
|
+
self.__var_name = var_name
|
|
252
|
+
|
|
253
|
+
def row_number(
|
|
254
|
+
self,
|
|
255
|
+
row: "LegendQLApiTdsRow"
|
|
256
|
+
) -> PyLegendInteger:
|
|
257
|
+
return PyLegendInteger(LegendQLApiRowNumberExpression(self, row))
|
|
258
|
+
|
|
259
|
+
def rank(
|
|
260
|
+
self,
|
|
261
|
+
window: "LegendQLApiWindowReference",
|
|
262
|
+
row: "LegendQLApiTdsRow"
|
|
263
|
+
) -> PyLegendInteger:
|
|
264
|
+
return PyLegendInteger(LegendQLApiRankExpression(self, window, row))
|
|
265
|
+
|
|
266
|
+
def dense_rank(
|
|
267
|
+
self,
|
|
268
|
+
window: "LegendQLApiWindowReference",
|
|
269
|
+
row: "LegendQLApiTdsRow"
|
|
270
|
+
) -> PyLegendInteger:
|
|
271
|
+
return PyLegendInteger(LegendQLApiDenseRankExpression(self, window, row))
|
|
272
|
+
|
|
273
|
+
def percent_rank(
|
|
274
|
+
self,
|
|
275
|
+
window: "LegendQLApiWindowReference",
|
|
276
|
+
row: "LegendQLApiTdsRow"
|
|
277
|
+
) -> PyLegendFloat:
|
|
278
|
+
return PyLegendFloat(LegendQLApiPercentRankExpression(self, window, row))
|
|
279
|
+
|
|
280
|
+
def cume_dist(
|
|
281
|
+
self,
|
|
282
|
+
window: "LegendQLApiWindowReference",
|
|
283
|
+
row: "LegendQLApiTdsRow"
|
|
284
|
+
) -> PyLegendFloat:
|
|
285
|
+
return PyLegendFloat(LegendQLApiCumeDistExpression(self, window, row))
|
|
286
|
+
|
|
287
|
+
def ntile(
|
|
288
|
+
self,
|
|
289
|
+
row: "LegendQLApiTdsRow",
|
|
290
|
+
num_buckets: int
|
|
291
|
+
) -> PyLegendInteger:
|
|
292
|
+
return PyLegendInteger(LegendQLApiNtileExpression(self, row, num_buckets))
|
|
293
|
+
|
|
294
|
+
def lead(
|
|
295
|
+
self,
|
|
296
|
+
row: "LegendQLApiTdsRow"
|
|
297
|
+
) -> "LegendQLApiTdsRow":
|
|
298
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiLeadRow
|
|
299
|
+
return LegendQLApiLeadRow(self, row)
|
|
300
|
+
|
|
301
|
+
def lag(
|
|
302
|
+
self,
|
|
303
|
+
row: "LegendQLApiTdsRow"
|
|
304
|
+
) -> "LegendQLApiTdsRow":
|
|
305
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiLagRow
|
|
306
|
+
return LegendQLApiLagRow(self, row)
|
|
307
|
+
|
|
308
|
+
def first(
|
|
309
|
+
self,
|
|
310
|
+
window: "LegendQLApiWindowReference",
|
|
311
|
+
row: "LegendQLApiTdsRow"
|
|
312
|
+
) -> "LegendQLApiTdsRow":
|
|
313
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiFirstRow
|
|
314
|
+
return LegendQLApiFirstRow(self, window, row)
|
|
315
|
+
|
|
316
|
+
def last(
|
|
317
|
+
self,
|
|
318
|
+
window: "LegendQLApiWindowReference",
|
|
319
|
+
row: "LegendQLApiTdsRow"
|
|
320
|
+
) -> "LegendQLApiTdsRow":
|
|
321
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiLastRow
|
|
322
|
+
return LegendQLApiLastRow(self, window, row)
|
|
323
|
+
|
|
324
|
+
def nth(
|
|
325
|
+
self,
|
|
326
|
+
window: "LegendQLApiWindowReference",
|
|
327
|
+
row: "LegendQLApiTdsRow",
|
|
328
|
+
offset: int
|
|
329
|
+
) -> "LegendQLApiTdsRow":
|
|
330
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiNthRow
|
|
331
|
+
return LegendQLApiNthRow(self, window, row, offset)
|
|
332
|
+
|
|
333
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
334
|
+
return f"${self.__var_name}"
|
|
335
|
+
|
|
336
|
+
def get_base_frame(self) -> "LegendQLApiBaseTdsFrame":
|
|
337
|
+
return self.__base_frame
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class LegendQLApiWindowReference:
|
|
341
|
+
__window: LegendQLApiWindow
|
|
342
|
+
__var_name: str
|
|
343
|
+
|
|
344
|
+
def __init__(self, window: LegendQLApiWindow, var_name: str) -> None:
|
|
345
|
+
self.__window = window
|
|
346
|
+
self.__var_name = var_name
|
|
347
|
+
|
|
348
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
349
|
+
return f"${self.__var_name}"
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class LegendQLApiRowNumberExpression(PyLegendExpressionIntegerReturn):
|
|
353
|
+
if TYPE_CHECKING:
|
|
354
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
355
|
+
|
|
356
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
357
|
+
__row: "LegendQLApiTdsRow"
|
|
358
|
+
|
|
359
|
+
def __init__(
|
|
360
|
+
self,
|
|
361
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
362
|
+
row: "LegendQLApiTdsRow"
|
|
363
|
+
) -> None:
|
|
364
|
+
self.__partial_frame = partial_frame
|
|
365
|
+
self.__row = row
|
|
366
|
+
|
|
367
|
+
def to_sql_expression(
|
|
368
|
+
self,
|
|
369
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
370
|
+
config: FrameToSqlConfig
|
|
371
|
+
) -> Expression:
|
|
372
|
+
return FunctionCall(
|
|
373
|
+
name=QualifiedName(parts=["row_number"]), distinct=False, arguments=[], filter_=None, window=None
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
377
|
+
return f"{self.__partial_frame.to_pure_expression(config)}->rowNumber({self.__row.to_pure_expression(config)})"
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
class LegendQLApiRankExpression(PyLegendExpressionIntegerReturn):
|
|
381
|
+
if TYPE_CHECKING:
|
|
382
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
383
|
+
|
|
384
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
385
|
+
__window_ref: "LegendQLApiWindowReference"
|
|
386
|
+
__row: "LegendQLApiTdsRow"
|
|
387
|
+
|
|
388
|
+
def __init__(
|
|
389
|
+
self,
|
|
390
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
391
|
+
window_ref: "LegendQLApiWindowReference",
|
|
392
|
+
row: "LegendQLApiTdsRow"
|
|
393
|
+
) -> None:
|
|
394
|
+
self.__partial_frame = partial_frame
|
|
395
|
+
self.__window_ref = window_ref
|
|
396
|
+
self.__row = row
|
|
397
|
+
|
|
398
|
+
def to_sql_expression(
|
|
399
|
+
self,
|
|
400
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
401
|
+
config: FrameToSqlConfig
|
|
402
|
+
) -> Expression:
|
|
403
|
+
return FunctionCall(
|
|
404
|
+
name=QualifiedName(parts=["rank"]), distinct=False, arguments=[], filter_=None, window=None
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
408
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->rank("
|
|
409
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class LegendQLApiDenseRankExpression(PyLegendExpressionIntegerReturn):
|
|
413
|
+
if TYPE_CHECKING:
|
|
414
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
415
|
+
|
|
416
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
417
|
+
__window_ref: "LegendQLApiWindowReference"
|
|
418
|
+
__row: "LegendQLApiTdsRow"
|
|
419
|
+
|
|
420
|
+
def __init__(
|
|
421
|
+
self,
|
|
422
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
423
|
+
window_ref: "LegendQLApiWindowReference",
|
|
424
|
+
row: "LegendQLApiTdsRow"
|
|
425
|
+
) -> None:
|
|
426
|
+
self.__partial_frame = partial_frame
|
|
427
|
+
self.__window_ref = window_ref
|
|
428
|
+
self.__row = row
|
|
429
|
+
|
|
430
|
+
def to_sql_expression(
|
|
431
|
+
self,
|
|
432
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
433
|
+
config: FrameToSqlConfig
|
|
434
|
+
) -> Expression:
|
|
435
|
+
return FunctionCall(
|
|
436
|
+
name=QualifiedName(parts=["dense_rank"]), distinct=False, arguments=[], filter_=None, window=None
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
440
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->denseRank("
|
|
441
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class LegendQLApiPercentRankExpression(PyLegendExpressionFloatReturn):
|
|
445
|
+
if TYPE_CHECKING:
|
|
446
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
447
|
+
|
|
448
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
449
|
+
__window_ref: "LegendQLApiWindowReference"
|
|
450
|
+
__row: "LegendQLApiTdsRow"
|
|
451
|
+
|
|
452
|
+
def __init__(
|
|
453
|
+
self,
|
|
454
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
455
|
+
window_ref: "LegendQLApiWindowReference",
|
|
456
|
+
row: "LegendQLApiTdsRow"
|
|
457
|
+
) -> None:
|
|
458
|
+
self.__partial_frame = partial_frame
|
|
459
|
+
self.__window_ref = window_ref
|
|
460
|
+
self.__row = row
|
|
461
|
+
|
|
462
|
+
def to_sql_expression(
|
|
463
|
+
self,
|
|
464
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
465
|
+
config: FrameToSqlConfig
|
|
466
|
+
) -> Expression:
|
|
467
|
+
return FunctionCall(
|
|
468
|
+
name=QualifiedName(parts=["percent_rank"]), distinct=False, arguments=[], filter_=None, window=None
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
472
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->percentRank("
|
|
473
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
class LegendQLApiCumeDistExpression(PyLegendExpressionFloatReturn):
|
|
477
|
+
if TYPE_CHECKING:
|
|
478
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
479
|
+
|
|
480
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
481
|
+
__window_ref: "LegendQLApiWindowReference"
|
|
482
|
+
__row: "LegendQLApiTdsRow"
|
|
483
|
+
|
|
484
|
+
def __init__(
|
|
485
|
+
self,
|
|
486
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
487
|
+
window_ref: "LegendQLApiWindowReference",
|
|
488
|
+
row: "LegendQLApiTdsRow"
|
|
489
|
+
) -> None:
|
|
490
|
+
self.__partial_frame = partial_frame
|
|
491
|
+
self.__window_ref = window_ref
|
|
492
|
+
self.__row = row
|
|
493
|
+
|
|
494
|
+
def to_sql_expression(
|
|
495
|
+
self,
|
|
496
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
497
|
+
config: FrameToSqlConfig
|
|
498
|
+
) -> Expression:
|
|
499
|
+
return FunctionCall(
|
|
500
|
+
name=QualifiedName(parts=["cume_dist"]), distinct=False, arguments=[], filter_=None, window=None
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
504
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->cumulativeDistribution("
|
|
505
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class LegendQLApiNtileExpression(PyLegendExpressionIntegerReturn):
|
|
509
|
+
if TYPE_CHECKING:
|
|
510
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
511
|
+
|
|
512
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
513
|
+
__row: "LegendQLApiTdsRow"
|
|
514
|
+
__num_buckets: int
|
|
515
|
+
|
|
516
|
+
def __init__(
|
|
517
|
+
self,
|
|
518
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
519
|
+
row: "LegendQLApiTdsRow",
|
|
520
|
+
num_buckets: int
|
|
521
|
+
) -> None:
|
|
522
|
+
self.__partial_frame = partial_frame
|
|
523
|
+
self.__row = row
|
|
524
|
+
self.__num_buckets = num_buckets
|
|
525
|
+
|
|
526
|
+
def to_sql_expression(
|
|
527
|
+
self,
|
|
528
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
529
|
+
config: FrameToSqlConfig
|
|
530
|
+
) -> Expression:
|
|
531
|
+
return FunctionCall(
|
|
532
|
+
name=QualifiedName(parts=["ntile"]),
|
|
533
|
+
distinct=False,
|
|
534
|
+
arguments=[IntegerLiteral(self.__num_buckets)],
|
|
535
|
+
filter_=None,
|
|
536
|
+
window=None
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
540
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->ntile({self.__row.to_pure_expression(config)}, "
|
|
541
|
+
f"{self.__num_buckets})")
|