rtgl 0.0.3__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.
- rtgl/__init__.py +9 -0
- rtgl/base/__init__.py +6 -0
- rtgl/base/database.py +57 -0
- rtgl/base/table.py +65 -0
- rtgl/converter/__init__.py +7 -0
- rtgl/converter/converter.py +484 -0
- rtgl/converter/static_converter.py +250 -0
- rtgl/converter/temporal_converter.py +565 -0
- rtgl/converter/utils.py +117 -0
- rtgl/parser/.antlr/LexerRTGL.interp +156 -0
- rtgl/parser/.antlr/LexerRTGL.java +670 -0
- rtgl/parser/.antlr/LexerRTGL.tokens +50 -0
- rtgl/parser/.antlr/ParserRTGL.interp +121 -0
- rtgl/parser/.antlr/ParserRTGL.java +1743 -0
- rtgl/parser/.antlr/ParserRTGL.tokens +50 -0
- rtgl/parser/.antlr/ParserRTGLBaseListener.java +303 -0
- rtgl/parser/.antlr/ParserRTGLListener.java +229 -0
- rtgl/parser/LexerRTGL.g4 +252 -0
- rtgl/parser/ParserRTGL.g4 +134 -0
- rtgl/parser/__init__.py +7 -0
- rtgl/parser/gen/LexerRTGL.interp +156 -0
- rtgl/parser/gen/LexerRTGL.py +415 -0
- rtgl/parser/gen/LexerRTGL.tokens +50 -0
- rtgl/parser/gen/ParserRTGL.interp +121 -0
- rtgl/parser/gen/ParserRTGL.py +1911 -0
- rtgl/parser/gen/ParserRTGL.tokens +50 -0
- rtgl/parser/gen/ParserRTGLListener.py +210 -0
- rtgl/parser/gen/ParserRTGLVisitor.py +123 -0
- rtgl/validator/__init__.py +8 -0
- rtgl/validator/error.py +124 -0
- rtgl/validator/static_validator.py +132 -0
- rtgl/validator/temporal_validator.py +229 -0
- rtgl/validator/validator.py +458 -0
- rtgl/visitor/__init__.py +6 -0
- rtgl/visitor/parsed_value.py +32 -0
- rtgl/visitor/visitor.py +531 -0
- rtgl-0.0.3.dist-info/METADATA +227 -0
- rtgl-0.0.3.dist-info/RECORD +40 -0
- rtgl-0.0.3.dist-info/WHEEL +4 -0
- rtgl-0.0.3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Static RTGL converter class for non-temporal queries."""
|
|
2
|
+
|
|
3
|
+
from rtgl.base import Database, Table
|
|
4
|
+
from rtgl.converter.converter import Converter
|
|
5
|
+
from rtgl.converter.utils import get_div_line
|
|
6
|
+
from rtgl.validator import SValidator
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SConverter(Converter):
|
|
10
|
+
r"""Static RTGL converter class for static conversion RTGL -> SQL.
|
|
11
|
+
|
|
12
|
+
Converts static (non-temporal) RTGL queries into SQL queries.
|
|
13
|
+
Extends the base Converter class with concrete implementations
|
|
14
|
+
for static prediction tasks.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, db: Database) -> None:
|
|
18
|
+
r"""Initializes a static RTGL converter.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
db (Database): Database object containing the tables.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
out (None):
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(db)
|
|
27
|
+
# initialize static validator
|
|
28
|
+
self.validator = SValidator(self.collector, self.db)
|
|
29
|
+
|
|
30
|
+
def convert(self, rtgl_query: str, execute: bool = False) -> str | Table:
|
|
31
|
+
r"""Converts the static RTGL query string into an executable SQL query.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
rtgl_query (str): The RTGL query string to be converted and executed.
|
|
35
|
+
execute (bool): If True, executes the generated SQL query and returns the result as a Table.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
out (str | Table): The *`Table`* object containing the result of the executed SQL query (if execute=True),
|
|
39
|
+
with columns (*fk*, *label*) corresponding to the translated RTGL query output.
|
|
40
|
+
Otherwise, returns the generated SQL query string (if execute=False).
|
|
41
|
+
"""
|
|
42
|
+
# parse RTGL query into dictionary
|
|
43
|
+
query_dict = self.parse_query(rtgl_query)
|
|
44
|
+
query_dict = query_dict["QueryStat"].value
|
|
45
|
+
|
|
46
|
+
# build FOR EACH query
|
|
47
|
+
for_each_dict = query_dict["ForEach"].value
|
|
48
|
+
ptable, ppk, for_each_query = self.build_for_each(for_each_dict)
|
|
49
|
+
|
|
50
|
+
# build PREDICT query using FOR EACH query as base
|
|
51
|
+
predict_dict = query_dict["Predict"].value
|
|
52
|
+
sql_query = self.build_predict(predict_dict, ptable, ppk, for_each_query)
|
|
53
|
+
|
|
54
|
+
# build WHERE query if exists, using PREDICT query as base
|
|
55
|
+
if where := query_dict["Where"]:
|
|
56
|
+
where_dict = where.value
|
|
57
|
+
sql_query = self.build_where(where_dict, ptable, ppk, sql_query)
|
|
58
|
+
|
|
59
|
+
# fiter and add semicolon to end of SQL query
|
|
60
|
+
label_fk = None
|
|
61
|
+
select_clause = "*"
|
|
62
|
+
filt = "label IS NOT NULL"
|
|
63
|
+
if aggr := predict_dict["Aggregation"]:
|
|
64
|
+
aggr_dict = aggr.value
|
|
65
|
+
if aggr_dict["AggrType"].value.lower() == "list_distinct":
|
|
66
|
+
filt = f"{filt} AND label != [NULL]"
|
|
67
|
+
select_clause = "fk, list_filter(label, x -> x IS NOT NULL) AS label"
|
|
68
|
+
table, table_obj = self._find_table(aggr_dict["Table"].value)
|
|
69
|
+
column = self._find_column(table, aggr_dict["Column"].value)
|
|
70
|
+
|
|
71
|
+
label_fk = table if table_obj.pkey_col == column else table_obj.fkey_col_to_pkey_table.get(column)
|
|
72
|
+
|
|
73
|
+
sql_query = f"SELECT\n {select_clause}\nFROM\n ({sql_query}\n)\nWHERE {filt}\nORDER BY fk ASC\n;\n"
|
|
74
|
+
|
|
75
|
+
if not execute:
|
|
76
|
+
return sql_query
|
|
77
|
+
|
|
78
|
+
self._register_db()
|
|
79
|
+
|
|
80
|
+
ptable_orig, _ = self._find_table(ptable)
|
|
81
|
+
|
|
82
|
+
# execute SQL query and return result as Table
|
|
83
|
+
df = self.conn.sql(sql_query).df()
|
|
84
|
+
fkey_col_to_pkey_table = {"fk": ptable_orig} # fk column in output table corresponds to pk of parent table
|
|
85
|
+
if label_fk: # label column in output table corresponds to pk or fk of aggregation table
|
|
86
|
+
fkey_col_to_pkey_table["label"] = label_fk # if aggregarion operation is LIST_DISTINCT
|
|
87
|
+
|
|
88
|
+
return Table(
|
|
89
|
+
df=df,
|
|
90
|
+
fkey_col_to_pkey_table=fkey_col_to_pkey_table,
|
|
91
|
+
pkey_col=None,
|
|
92
|
+
time_col=None,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def build_for_each(self, for_each_dict: dict) -> tuple[str, str, str]:
|
|
96
|
+
r"""Builds a SQL query for the FOR EACH clause in static conversion.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
for_each_dict (dict): Parsed dictionary of the FOR EACH clause.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
ptable (str): Name of the parent table.
|
|
103
|
+
ppk (str): Name of the primary key column in the parent table.
|
|
104
|
+
for_each_query (str): SQL subquery returning the foreign keys of
|
|
105
|
+
the parent table (optionally filtered).
|
|
106
|
+
"""
|
|
107
|
+
# extract parent table and primary key column
|
|
108
|
+
ptable = ptable_name = for_each_dict["Table"].value
|
|
109
|
+
ppk = self._find_column(ptable, for_each_dict["Column"].value)
|
|
110
|
+
|
|
111
|
+
# build static WHERE query if exists to filter parent table rows before prediction
|
|
112
|
+
if where := for_each_dict["Where"]:
|
|
113
|
+
ptable = self.build_stat_where(where.value, ptable, ppk)
|
|
114
|
+
ptable = ptable.replace("\n", "\n" + 4 * " ") + "\n"
|
|
115
|
+
ptable = f"({ptable})"
|
|
116
|
+
|
|
117
|
+
# create division markers for formatted output
|
|
118
|
+
div_line1 = get_div_line("FOR_EACH_START")
|
|
119
|
+
div_line2 = get_div_line("FOR_EACH_END")
|
|
120
|
+
|
|
121
|
+
# build final FOR EACH query
|
|
122
|
+
for_each_query = f"{div_line1}\nSELECT\n {ppk} AS fk\nFROM\n {ptable}\n{div_line2}"
|
|
123
|
+
|
|
124
|
+
return ptable_name, ppk, for_each_query
|
|
125
|
+
|
|
126
|
+
def build_predict(self, query_dict: dict, ptable: str, ppk: str, for_each_query: str) -> str:
|
|
127
|
+
r"""Builds a SQL query for the PREDICT clause in static conversion.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
query_dict (dict): Parsed dictionary of the PREDICT clause.
|
|
131
|
+
ptable (str): Name of the parent table.
|
|
132
|
+
ppk (str): Name of the primary key column in the parent table.
|
|
133
|
+
for_each_query (str): SQL subquery from the FOR_EACH WHERE clause, providing base fk column.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
predict_query (str): SQL subquery returning (fk, label) pairs.
|
|
137
|
+
"""
|
|
138
|
+
# check predict type, build main_query and label_query accordingly
|
|
139
|
+
# expr / id_dot_id
|
|
140
|
+
pred_type = query_dict["PredType"]
|
|
141
|
+
if pred_type == "aggregation":
|
|
142
|
+
main_query = self.build_aggregation(query_dict["Aggregation"].value, ptable, ppk)
|
|
143
|
+
label_query = "__MAIN__.comp_col"
|
|
144
|
+
if pred_type == "expr":
|
|
145
|
+
main_query = self.build_expr(query_dict["Expr"].value, ptable, ppk)
|
|
146
|
+
label_query = "CASE\n WHEN __MAIN__.fk IS NOT NULL THEN TRUE\n ELSE FALSE\nEND"
|
|
147
|
+
elif pred_type == "id_dot_id":
|
|
148
|
+
main_query = self.build_id_dot_id(query_dict, ptable, ppk)
|
|
149
|
+
label_query = "__MAIN__.comp_col"
|
|
150
|
+
else:
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
main_query = main_query.replace("\n", "\n" + 4 * " ") + "\n"
|
|
154
|
+
label_query = label_query.replace("\n", "\n" + 4 * " ") + "\n"
|
|
155
|
+
for_each_query = for_each_query.replace("\n", "\n" + 4 * " ") + "\n"
|
|
156
|
+
|
|
157
|
+
# create division markers for formatted output
|
|
158
|
+
div_line_pred1 = get_div_line("PREDICT_START")
|
|
159
|
+
div_line_pred2 = get_div_line("PREDICT_END")
|
|
160
|
+
|
|
161
|
+
# build final PREDICT query
|
|
162
|
+
predict_query = (
|
|
163
|
+
f"{div_line_pred1}\n"
|
|
164
|
+
"SELECT\n"
|
|
165
|
+
" __FOR_EACH__.fk AS fk,\n"
|
|
166
|
+
f" {label_query} AS label\n"
|
|
167
|
+
"FROM\n"
|
|
168
|
+
f" ({for_each_query}) __FOR_EACH__\n"
|
|
169
|
+
"LEFT JOIN\n"
|
|
170
|
+
f" ({main_query}) __MAIN__\n"
|
|
171
|
+
"ON\n"
|
|
172
|
+
" __MAIN__.fk = __FOR_EACH__.fk\n"
|
|
173
|
+
f"{div_line_pred2}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return predict_query
|
|
177
|
+
|
|
178
|
+
def build_expr(self, expr_dict: dict, ptable: str, ppk: str) -> str:
|
|
179
|
+
r"""Builds a SQL query for a logical expression tree.
|
|
180
|
+
|
|
181
|
+
Just uses existing *build_stat_expr* method from base *`Converter`* class.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
expr_dict (dict): Parsed dictionary of the expression (can contain 'Op',
|
|
185
|
+
'Left', 'Right' keys or a single condition).
|
|
186
|
+
ptable (str): Name of the parent table.
|
|
187
|
+
ppk (str): Name of the primary key column in the parent table.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
expr_query (str): SQL query returning foreign keys where the expression is true.
|
|
191
|
+
"""
|
|
192
|
+
expr_query = self.build_stat_expr(expr_dict, ptable, ppk)
|
|
193
|
+
|
|
194
|
+
return expr_query
|
|
195
|
+
|
|
196
|
+
def build_where(self, where_dict: dict, ptable: str, ppk: str, predict_query: str) -> str:
|
|
197
|
+
r"""Builds a SQL query for the WHERE clause in static conversion.
|
|
198
|
+
|
|
199
|
+
Combines the PREDICT query with the expression from the WHERE clause using JOIN
|
|
200
|
+
to filter the predicted foreign keys based on the expression.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
where_dict (dict): Parsed dictionary of the WHERE clause.
|
|
204
|
+
ptable (str): Name of the parent table.
|
|
205
|
+
ppk (str): Name of the primary key column in the parent table.
|
|
206
|
+
predict_query (str): SQL query from the PREDICT clause, providing fk and label columns.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
where_query (str): SQL query returning (fk, label) pairs filtered by the WHERE expression.
|
|
210
|
+
"""
|
|
211
|
+
expr_query = self.build_expr(where_dict["Expr"].value, ptable, ppk)
|
|
212
|
+
expr_query = expr_query.replace("\n", "\n" + 4 * " ") + "\n"
|
|
213
|
+
|
|
214
|
+
# create division markers for formatted output
|
|
215
|
+
div_line1 = get_div_line("WHERE_START")
|
|
216
|
+
div_line2 = get_div_line("WHERE_END")
|
|
217
|
+
|
|
218
|
+
where_query = (
|
|
219
|
+
f"{div_line1}\n"
|
|
220
|
+
"SELECT\n"
|
|
221
|
+
" *\n"
|
|
222
|
+
"FROM\n"
|
|
223
|
+
f" ({predict_query}\n) __PREDICT__\n"
|
|
224
|
+
"JOIN\n"
|
|
225
|
+
f" ({expr_query}\n) __EXPR__\n"
|
|
226
|
+
"ON\n"
|
|
227
|
+
" __PREDICT__.fk = __EXPR__.fk\n"
|
|
228
|
+
"ORDER BY\n"
|
|
229
|
+
" __PREDICT__.fk ASC\n"
|
|
230
|
+
f"{div_line2}"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return where_query
|
|
234
|
+
|
|
235
|
+
def build_aggregation(self, aggr_dict: dict, ptable: str, ppk: str) -> str:
|
|
236
|
+
r"""Builds a SQL query for a static RTGL aggregation.
|
|
237
|
+
|
|
238
|
+
Just uses existing *build_stat_aggregation* method from base *`Converter`* class.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
aggr_dict (dict): Parsed aggregation dictionary containing 'Table', 'Column', 'Where'(optional) keys.
|
|
242
|
+
ptable (str): Name of the parent table.
|
|
243
|
+
ppk (str): Name of the primary key column in the parent table.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
aggr_query (str): SQL query returning pairs (fk, comp_col).
|
|
247
|
+
"""
|
|
248
|
+
aggr_query = self.build_stat_aggregation(aggr_dict, ptable, ppk)
|
|
249
|
+
|
|
250
|
+
return aggr_query
|