relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a5__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.
- relationalai/config/config.py +47 -21
- relationalai/config/connections/__init__.py +5 -2
- relationalai/config/connections/duckdb.py +2 -2
- relationalai/config/connections/local.py +31 -0
- relationalai/config/connections/snowflake.py +0 -1
- relationalai/config/external/raiconfig_converter.py +235 -0
- relationalai/config/external/raiconfig_models.py +202 -0
- relationalai/config/external/utils.py +31 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +10 -8
- relationalai/semantics/backends/sql/sql_compiler.py +1 -4
- relationalai/semantics/experimental/__init__.py +0 -0
- relationalai/semantics/experimental/builder.py +295 -0
- relationalai/semantics/experimental/builtins.py +154 -0
- relationalai/semantics/frontend/base.py +67 -42
- relationalai/semantics/frontend/core.py +34 -6
- relationalai/semantics/frontend/front_compiler.py +209 -37
- relationalai/semantics/frontend/pprint.py +6 -2
- relationalai/semantics/metamodel/__init__.py +7 -0
- relationalai/semantics/metamodel/metamodel.py +2 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +58 -16
- relationalai/semantics/metamodel/pprint.py +6 -1
- relationalai/semantics/metamodel/rewriter.py +11 -7
- relationalai/semantics/metamodel/typer.py +116 -41
- relationalai/semantics/reasoners/__init__.py +11 -0
- relationalai/semantics/reasoners/graph/__init__.py +35 -0
- relationalai/semantics/reasoners/graph/core.py +9028 -0
- relationalai/semantics/std/__init__.py +30 -10
- relationalai/semantics/std/aggregates.py +641 -12
- relationalai/semantics/std/common.py +146 -13
- relationalai/semantics/std/constraints.py +71 -1
- relationalai/semantics/std/datetime.py +904 -21
- relationalai/semantics/std/decimals.py +143 -2
- relationalai/semantics/std/floats.py +57 -4
- relationalai/semantics/std/integers.py +98 -4
- relationalai/semantics/std/math.py +857 -35
- relationalai/semantics/std/numbers.py +216 -20
- relationalai/semantics/std/re.py +213 -5
- relationalai/semantics/std/strings.py +437 -44
- relationalai/shims/executor.py +60 -52
- relationalai/shims/fixtures.py +85 -0
- relationalai/shims/helpers.py +26 -2
- relationalai/shims/hoister.py +28 -9
- relationalai/shims/mm2v0.py +204 -173
- relationalai/tools/cli/cli.py +192 -10
- relationalai/tools/cli/components/progress_reader.py +1 -1
- relationalai/tools/cli/docs.py +394 -0
- relationalai/tools/debugger.py +11 -4
- relationalai/tools/qb_debugger.py +435 -0
- relationalai/tools/typer_debugger.py +1 -2
- relationalai/util/dataclasses.py +3 -5
- relationalai/util/docutils.py +1 -2
- relationalai/util/error.py +2 -5
- relationalai/util/python.py +23 -0
- relationalai/util/runtime.py +1 -2
- relationalai/util/schema.py +2 -4
- relationalai/util/structures.py +4 -2
- relationalai/util/tracing.py +8 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/METADATA +8 -5
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/RECORD +118 -95
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/WHEEL +1 -1
- v0/relationalai/__init__.py +1 -1
- v0/relationalai/clients/client.py +52 -18
- v0/relationalai/clients/exec_txn_poller.py +122 -0
- v0/relationalai/clients/local.py +23 -8
- v0/relationalai/clients/resources/azure/azure.py +36 -11
- v0/relationalai/clients/resources/snowflake/__init__.py +4 -4
- v0/relationalai/clients/resources/snowflake/cli_resources.py +12 -1
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py +124 -100
- v0/relationalai/clients/resources/snowflake/engine_service.py +381 -0
- v0/relationalai/clients/resources/snowflake/engine_state_handlers.py +35 -29
- v0/relationalai/clients/resources/snowflake/error_handlers.py +43 -2
- v0/relationalai/clients/resources/snowflake/snowflake.py +277 -179
- v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
- v0/relationalai/clients/types.py +5 -0
- v0/relationalai/errors.py +19 -1
- v0/relationalai/semantics/lqp/algorithms.py +173 -0
- v0/relationalai/semantics/lqp/builtins.py +199 -2
- v0/relationalai/semantics/lqp/executor.py +68 -37
- v0/relationalai/semantics/lqp/ir.py +28 -2
- v0/relationalai/semantics/lqp/model2lqp.py +215 -45
- v0/relationalai/semantics/lqp/passes.py +13 -658
- v0/relationalai/semantics/lqp/rewrite/__init__.py +12 -0
- v0/relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
- v0/relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
- v0/relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
- v0/relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
- v0/relationalai/semantics/lqp/rewrite/period_math.py +77 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
- v0/relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
- v0/relationalai/semantics/lqp/utils.py +11 -1
- v0/relationalai/semantics/lqp/validators.py +14 -1
- v0/relationalai/semantics/metamodel/builtins.py +2 -1
- v0/relationalai/semantics/metamodel/compiler.py +2 -1
- v0/relationalai/semantics/metamodel/dependency.py +12 -3
- v0/relationalai/semantics/metamodel/executor.py +11 -1
- v0/relationalai/semantics/metamodel/factory.py +2 -2
- v0/relationalai/semantics/metamodel/helpers.py +7 -0
- v0/relationalai/semantics/metamodel/ir.py +3 -2
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
- v0/relationalai/semantics/metamodel/typer/checker.py +6 -4
- v0/relationalai/semantics/metamodel/typer/typer.py +4 -3
- v0/relationalai/semantics/metamodel/visitor.py +4 -3
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +336 -86
- v0/relationalai/semantics/rel/compiler.py +2 -1
- v0/relationalai/semantics/rel/executor.py +3 -2
- v0/relationalai/semantics/tests/lqp/__init__.py +0 -0
- v0/relationalai/semantics/tests/lqp/algorithms.py +345 -0
- v0/relationalai/tools/cli.py +339 -186
- v0/relationalai/tools/cli_controls.py +216 -67
- v0/relationalai/tools/cli_helpers.py +410 -6
- v0/relationalai/util/format.py +5 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/entry_points.txt +0 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/top_level.txt +0 -0
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
from v0.relationalai.semantics.metamodel import ir, builtins, helpers, types
|
|
2
2
|
from v0.relationalai.semantics.metamodel.visitor import collect_by_type
|
|
3
|
-
from v0.relationalai.semantics.metamodel.util import FrozenOrderedSet
|
|
4
|
-
from v0.relationalai.semantics.
|
|
3
|
+
from v0.relationalai.semantics.metamodel.util import FrozenOrderedSet, OrderedSet
|
|
4
|
+
from v0.relationalai.semantics.metamodel.compiler import group_tasks
|
|
5
|
+
from v0.relationalai.semantics.lqp import ir as lqp, utils, types as lqp_types
|
|
5
6
|
from v0.relationalai.semantics.lqp.primitives import lqp_avg_op, lqp_operator, build_primitive
|
|
6
7
|
from v0.relationalai.semantics.lqp.pragmas import pragma_to_lqp_name
|
|
7
8
|
from v0.relationalai.semantics.lqp.types import meta_type_to_lqp
|
|
8
9
|
from v0.relationalai.semantics.lqp.constructors import (
|
|
9
10
|
mk_abstraction, mk_and, mk_exists, mk_or, mk_pragma, mk_primitive,
|
|
10
|
-
mk_specialized_value, mk_type, mk_value, mk_attribute
|
|
11
|
+
mk_specialized_value, mk_type, mk_value, mk_attribute,
|
|
11
12
|
)
|
|
12
|
-
from v0.relationalai.semantics.lqp.
|
|
13
|
+
from v0.relationalai.semantics.lqp.algorithms import (
|
|
14
|
+
is_script, is_algorithm_script, is_algorithm_logical,
|
|
15
|
+
is_while_loop, is_while_script, construct_monoid, is_empty_instruction
|
|
16
|
+
)
|
|
17
|
+
from v0.relationalai.semantics.lqp.builtins import (
|
|
18
|
+
has_global_annotation, get_upsert_annotation, get_monoid_annotation,
|
|
19
|
+
get_monus_annotation, has_assign_annotation, get_arity, supported_lqp_annotations
|
|
20
|
+
)
|
|
21
|
+
from v0.relationalai.semantics.lqp.utils import TranslationCtx, ExportDescriptor, gen_unique_var, gen_rel_id
|
|
13
22
|
from v0.relationalai.semantics.lqp.validators import assert_valid_input
|
|
14
23
|
from v0.relationalai.semantics.lqp.rewrite.functional_dependencies import (
|
|
15
24
|
normalized_fd, contains_only_declarable_constraints
|
|
16
25
|
)
|
|
17
26
|
from decimal import Decimal as PyDecimal
|
|
18
27
|
from datetime import datetime, date, timezone
|
|
19
|
-
from typing import Tuple, cast, Union, Optional
|
|
28
|
+
from typing import Sequence, Tuple, cast, Union, Optional
|
|
20
29
|
from warnings import warn
|
|
21
30
|
import re
|
|
22
31
|
import uuid
|
|
@@ -42,8 +51,8 @@ def to_lqp(model: ir.Model, fragment_name: bytes, ctx: TranslationCtx) -> tuple[
|
|
|
42
51
|
reads.extend(_get_output_reads(ctx.output_ids))
|
|
43
52
|
|
|
44
53
|
export_info = None
|
|
45
|
-
if len(ctx.
|
|
46
|
-
export_filename, col_types, export_reads = _get_export_reads(ctx.
|
|
54
|
+
if len(ctx.export_descriptors) > 0:
|
|
55
|
+
export_filename, col_types, export_reads = _get_export_reads(ctx.export_descriptors)
|
|
47
56
|
reads.extend(export_reads)
|
|
48
57
|
export_info = (export_filename, col_types)
|
|
49
58
|
|
|
@@ -76,14 +85,17 @@ def _get_output_reads(output_ids: list[tuple[lqp.RelationId, str]]) -> list[lqp.
|
|
|
76
85
|
reads.append(lqp.Read(read_type=output, meta=None))
|
|
77
86
|
return reads
|
|
78
87
|
|
|
79
|
-
def _get_export_reads(
|
|
88
|
+
def _get_export_reads(descriptors: list[ExportDescriptor]) -> tuple[str, list, list[lqp.Read]]:
|
|
80
89
|
reads = []
|
|
81
90
|
csv_columns = []
|
|
82
91
|
col_info = []
|
|
83
|
-
for
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
for descriptor in sorted(descriptors, key=lambda x: x.column_number):
|
|
93
|
+
csv_columns.append(lqp.ExportCSVColumn(
|
|
94
|
+
column_name=descriptor.column_name,
|
|
95
|
+
column_data=descriptor.relation_id,
|
|
96
|
+
meta=None,
|
|
97
|
+
))
|
|
98
|
+
col_info.append((descriptor.column_name, descriptor.column_type))
|
|
87
99
|
|
|
88
100
|
# Generate a random name for the internal export path
|
|
89
101
|
export_filename = "export_" + str(uuid.uuid4()).replace("-", "_")
|
|
@@ -106,6 +118,8 @@ def _get_export_reads(export_ids: list[tuple[lqp.RelationId, int, lqp.Type]]) ->
|
|
|
106
118
|
def _translate_to_decls(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Declaration]:
|
|
107
119
|
if contains_only_declarable_constraints(rule):
|
|
108
120
|
return _translate_to_constraint_decls(ctx, rule)
|
|
121
|
+
elif is_algorithm_logical(rule):
|
|
122
|
+
return _translate_algorithms(ctx, rule)
|
|
109
123
|
else:
|
|
110
124
|
return _translate_to_standard_decl(ctx, rule)
|
|
111
125
|
|
|
@@ -140,39 +154,161 @@ def _translate_to_constraint_decls(ctx: TranslationCtx, rule: ir.Logical) -> lis
|
|
|
140
154
|
|
|
141
155
|
return constraint_decls
|
|
142
156
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
def _translate_algorithms(ctx: TranslationCtx, task: ir.Logical) -> list[lqp.Declaration]:
|
|
158
|
+
assert is_algorithm_logical(task)
|
|
159
|
+
decls: list[lqp.Declaration] = []
|
|
160
|
+
for subtask in task.body:
|
|
161
|
+
assert is_algorithm_script(subtask), "Expected all subtasks to be algorithm scripts"
|
|
162
|
+
decls.extend(_translate_algorithm_script(ctx, subtask))
|
|
163
|
+
return decls
|
|
164
|
+
|
|
165
|
+
def _translate_algorithm_script(ctx: TranslationCtx, alg_task: ir.Sequence) -> list[lqp.Declaration]:
|
|
166
|
+
assert is_algorithm_script(alg_task), "Expected Sequence @algorithm @script "
|
|
167
|
+
|
|
168
|
+
alg_globals = _find_algorithm_global_relation_ids(ctx, alg_task)
|
|
169
|
+
alg_body = _translate_script(ctx, alg_task)
|
|
170
|
+
|
|
171
|
+
return [lqp.Algorithm(global_=alg_globals, body=alg_body, meta=None)]
|
|
172
|
+
|
|
173
|
+
def _find_algorithm_global_relation_ids(ctx: TranslationCtx, alg_task: ir.Sequence) -> list[lqp.RelationId]:
|
|
174
|
+
result = []
|
|
175
|
+
updates = collect_by_type(ir.Update, alg_task)
|
|
176
|
+
for update in updates:
|
|
177
|
+
if has_global_annotation(update):
|
|
178
|
+
bindings = _effect_bindings(update)
|
|
179
|
+
projection, _ = _translate_bindings(ctx, bindings)
|
|
180
|
+
rel_id = get_relation_id(ctx, update.relation, projection)
|
|
181
|
+
result.append(rel_id)
|
|
182
|
+
return list(dict.fromkeys(result))
|
|
183
|
+
|
|
184
|
+
def _translate_script(ctx: TranslationCtx, task: ir.Sequence) -> lqp.Script:
|
|
185
|
+
assert is_script(task), "Expected a @script Sequence"
|
|
186
|
+
|
|
187
|
+
constructs: list[lqp.Construct] = []
|
|
188
|
+
|
|
189
|
+
for subtask in task.tasks:
|
|
190
|
+
if is_empty_instruction(subtask):
|
|
191
|
+
constructs.append(_translate_empty_instruction(ctx, subtask))
|
|
192
|
+
elif isinstance(subtask, ir.Logical):
|
|
193
|
+
constructs.extend(_translate_instruction(ctx, subtask))
|
|
194
|
+
elif isinstance(subtask, ir.Break):
|
|
195
|
+
constructs.append(_translate_break_instruction(ctx, subtask))
|
|
196
|
+
elif is_while_loop(subtask):
|
|
197
|
+
constructs.append(_translate_while_loop(ctx, subtask))
|
|
198
|
+
else:
|
|
199
|
+
raise Exception(f"Unsupported script instruction: {subtask}")
|
|
200
|
+
|
|
201
|
+
return lqp.Script(constructs=constructs, meta=None)
|
|
202
|
+
|
|
203
|
+
def _translate_while_loop(ctx: TranslationCtx, task: ir.Loop) -> lqp.Loop:
|
|
204
|
+
assert is_while_loop(task), "Expected a @while Loop"
|
|
205
|
+
assert len(task.iter) == 0, "Temporalized loops not supported"
|
|
206
|
+
|
|
207
|
+
while_script_task = task.body
|
|
208
|
+
assert is_while_script(while_script_task), "The body of a @while Loop must be a @while @script Sequence"
|
|
209
|
+
body_script = _translate_script(ctx, while_script_task)
|
|
210
|
+
|
|
211
|
+
# No init instructions in the translation of PyRel Loops to to LQP loops
|
|
212
|
+
return lqp.Loop(init=[], body=body_script, meta=None)
|
|
213
|
+
|
|
214
|
+
def _translate_break_instruction(ctx: TranslationCtx, task: ir.Break) -> lqp.Construct:
|
|
215
|
+
body = _translate_to_formula(ctx, task.check)
|
|
216
|
+
|
|
217
|
+
ctx.break_rule_counter += 1
|
|
218
|
+
|
|
219
|
+
rel_id = gen_rel_id(ctx, "break_cond_" + str(ctx.break_rule_counter))
|
|
220
|
+
return lqp.Break(
|
|
221
|
+
name = rel_id,
|
|
222
|
+
body = mk_abstraction([], body),
|
|
223
|
+
attrs = [],
|
|
224
|
+
meta = None,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def _translate_empty_instruction(ctx: TranslationCtx, rule: ir.Logical) -> lqp.Instruction:
|
|
228
|
+
assert is_empty_instruction(rule)
|
|
229
|
+
updates = collect_by_type(ir.Update, rule)
|
|
230
|
+
assert len(updates) == 1
|
|
231
|
+
update = updates[0]
|
|
232
|
+
bindings = _effect_bindings(update)
|
|
233
|
+
|
|
234
|
+
# We need to make sure that variable names have a leading underscore
|
|
235
|
+
normalized_bindings:Sequence[ir.Var] = []
|
|
236
|
+
for v in bindings:
|
|
237
|
+
assert isinstance(v, ir.Var)
|
|
238
|
+
if not v.name.startswith("_"):
|
|
239
|
+
v = ir.Var(v.type, "_" + v.name)
|
|
240
|
+
normalized_bindings.append(v)
|
|
241
|
+
|
|
242
|
+
projection, eqs = _translate_bindings(ctx, normalized_bindings)
|
|
243
|
+
assert len(eqs) == 0
|
|
244
|
+
rel_id = get_relation_id(ctx, update.relation, projection)
|
|
245
|
+
abstraction = mk_abstraction(projection, mk_or([])) # empty body = false
|
|
246
|
+
return lqp.Assign(name = rel_id, body = abstraction, attrs = [], meta = None)
|
|
247
|
+
|
|
248
|
+
def _translate_instruction(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Instruction]:
|
|
249
|
+
effects = collect_by_type((ir.Update, ir.Output), rule)
|
|
147
250
|
|
|
148
|
-
# TODO: should this ever actually come in as input?
|
|
149
251
|
if len(effects) == 0:
|
|
150
252
|
return []
|
|
151
253
|
|
|
152
|
-
|
|
254
|
+
conjuncts = _translate_to_formula(ctx, rule)
|
|
255
|
+
res = []
|
|
256
|
+
for effect in effects:
|
|
257
|
+
assert isinstance(effect, ir.Update), f"Got an effect of type {type(effect)} in a loop, which is invalid."
|
|
153
258
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
259
|
+
bindings = _effect_bindings(effect)
|
|
260
|
+
projection, eqs = _translate_bindings(ctx, bindings)
|
|
261
|
+
|
|
262
|
+
eqs.append(conjuncts)
|
|
263
|
+
new_body = mk_and(eqs)
|
|
264
|
+
|
|
265
|
+
rel_id = get_relation_id(ctx, effect.relation, projection)
|
|
266
|
+
abstraction = mk_abstraction(projection, new_body)
|
|
267
|
+
|
|
268
|
+
upsert = get_upsert_annotation(effect)
|
|
269
|
+
monoid = get_monoid_annotation(effect)
|
|
270
|
+
monus = get_monus_annotation(effect)
|
|
271
|
+
|
|
272
|
+
if has_assign_annotation(effect):
|
|
273
|
+
res.append(lqp.Assign(name = rel_id, body = abstraction, attrs = [], meta = None))
|
|
274
|
+
elif upsert is not None:
|
|
275
|
+
res.append(lqp.Upsert(value_arity=get_arity(upsert), name = rel_id, body = abstraction, attrs = [], meta = None))
|
|
276
|
+
elif monoid is not None:
|
|
277
|
+
res.append(lqp.MonoidDef(
|
|
278
|
+
value_arity=get_arity(monoid),
|
|
279
|
+
monoid=construct_monoid(monoid),
|
|
280
|
+
name = rel_id,
|
|
281
|
+
body = abstraction,
|
|
282
|
+
attrs = [],
|
|
283
|
+
meta = None
|
|
284
|
+
))
|
|
285
|
+
elif monus is not None:
|
|
286
|
+
res.append(lqp.MonusDef(
|
|
287
|
+
value_arity=get_arity(monus),
|
|
288
|
+
monoid=construct_monoid(monus),
|
|
289
|
+
name = rel_id,
|
|
290
|
+
body = abstraction,
|
|
291
|
+
attrs = [],
|
|
292
|
+
meta = None
|
|
293
|
+
))
|
|
294
|
+
|
|
295
|
+
return res
|
|
159
296
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
aggr_body = mk_and(conjuncts)
|
|
163
|
-
conjuncts = []
|
|
164
|
-
for aggr in aggregates:
|
|
165
|
-
conjuncts.append(_translate_aggregate(ctx, aggr, aggr_body))
|
|
166
|
-
for rank in ranks:
|
|
167
|
-
conjuncts.append(_translate_rank(ctx, rank, aggr_body))
|
|
297
|
+
def _translate_to_standard_decl(ctx: TranslationCtx, rule: ir.Logical) -> list[lqp.Declaration]:
|
|
298
|
+
effects = collect_by_type((ir.Output, ir.Update), rule)
|
|
168
299
|
|
|
169
|
-
|
|
300
|
+
# TODO: should this ever actually come in as input?
|
|
301
|
+
if len(effects) == 0:
|
|
302
|
+
return []
|
|
303
|
+
|
|
304
|
+
conjuncts = _translate_to_formula(ctx, rule)
|
|
305
|
+
return [_translate_effect(ctx, effect, conjuncts) for effect in effects]
|
|
170
306
|
|
|
171
307
|
def _translate_annotations(annotations: FrozenOrderedSet[ir.Annotation]) -> list[lqp.Attribute]:
|
|
172
308
|
attributes = []
|
|
173
309
|
for annotation in annotations:
|
|
174
310
|
|
|
175
|
-
if annotation.relation.name in
|
|
311
|
+
if annotation.relation.name in supported_lqp_annotations:
|
|
176
312
|
if any(not isinstance(a, ir.Literal) for a in annotation.args):
|
|
177
313
|
warn("LQP currently ignores annotation parameters with non-literal values")
|
|
178
314
|
continue
|
|
@@ -191,14 +327,11 @@ def _translate_annotations(annotations: FrozenOrderedSet[ir.Annotation]) -> list
|
|
|
191
327
|
def _translate_effect(ctx: TranslationCtx, effect: Union[ir.Output, ir.Update], body: lqp.Formula) -> lqp.Declaration:
|
|
192
328
|
bindings = _effect_bindings(effect)
|
|
193
329
|
|
|
194
|
-
def _is_export(e):
|
|
195
|
-
return isinstance(e, ir.Output) and builtins.export_annotation in e.annotations
|
|
196
|
-
|
|
197
330
|
if isinstance(effect, ir.Output):
|
|
198
331
|
projection, eqs, suffix = _translate_output_bindings(ctx, bindings)
|
|
199
332
|
meta_id = effect.id
|
|
200
333
|
|
|
201
|
-
if
|
|
334
|
+
if helpers.is_export(effect):
|
|
202
335
|
def_name = "export_relation" + suffix
|
|
203
336
|
else:
|
|
204
337
|
def_name = "output" + suffix
|
|
@@ -214,14 +347,23 @@ def _translate_effect(ctx: TranslationCtx, effect: Union[ir.Output, ir.Update],
|
|
|
214
347
|
new_body = mk_and(eqs)
|
|
215
348
|
|
|
216
349
|
# Context bookkeeping for exports and outputs
|
|
217
|
-
if
|
|
350
|
+
if helpers.is_export(effect):
|
|
218
351
|
# The row id is the first n-1 elements, and the actual data is the last element. Its
|
|
219
352
|
# type is stored in the first element of the tuple.
|
|
220
353
|
col_type = projection[-1][1]
|
|
221
354
|
_col_num_match = re.search(r"export_relation_col([0-9]+)", def_name)
|
|
222
355
|
assert _col_num_match, f"Could not find column number in suffix: {def_name}"
|
|
223
356
|
col_num = int(_col_num_match.group(1))
|
|
224
|
-
|
|
357
|
+
col_name = f"col{col_num}"
|
|
358
|
+
if isinstance(effect, ir.Output) and len(effect.aliases) > 0:
|
|
359
|
+
aliases_list = list(effect.aliases)
|
|
360
|
+
col_name = aliases_list[-1][0]
|
|
361
|
+
ctx.export_descriptors.append(ExportDescriptor(
|
|
362
|
+
relation_id=rel_id,
|
|
363
|
+
column_name=col_name,
|
|
364
|
+
column_number=col_num,
|
|
365
|
+
column_type=col_type,
|
|
366
|
+
))
|
|
225
367
|
elif isinstance(effect, ir.Output):
|
|
226
368
|
ctx.output_ids.append((rel_id, def_name))
|
|
227
369
|
|
|
@@ -544,7 +686,36 @@ def _translate_aggregate(ctx: TranslationCtx, aggr: ir.Aggregate, body: lqp.Form
|
|
|
544
686
|
|
|
545
687
|
def _translate_to_formula(ctx: TranslationCtx, task: ir.Task) -> lqp.Formula:
|
|
546
688
|
if isinstance(task, ir.Logical):
|
|
547
|
-
|
|
689
|
+
# For aggregates and ranks, the expected format is:
|
|
690
|
+
#
|
|
691
|
+
# Logical
|
|
692
|
+
# body_task1
|
|
693
|
+
# body_task2
|
|
694
|
+
# ...
|
|
695
|
+
# aggregate/rank task
|
|
696
|
+
#
|
|
697
|
+
# If we see that the Logical is in this format, it should be translated as an
|
|
698
|
+
# aggregate/rank node.
|
|
699
|
+
groups = group_tasks(task.body, {
|
|
700
|
+
"aggregates": ir.Aggregate,
|
|
701
|
+
"ranks": ir.Rank,
|
|
702
|
+
})
|
|
703
|
+
|
|
704
|
+
aggregates = groups.get("aggregates", OrderedSet[ir.Task]())
|
|
705
|
+
ranks = groups.get("ranks", OrderedSet[ir.Task]())
|
|
706
|
+
|
|
707
|
+
if aggregates or ranks:
|
|
708
|
+
conjuncts = []
|
|
709
|
+
body = mk_and([_translate_to_formula(ctx, t) for t in task.body])
|
|
710
|
+
for aggr in aggregates:
|
|
711
|
+
assert(isinstance(aggr, ir.Aggregate))
|
|
712
|
+
conjuncts.append(_translate_aggregate(ctx, aggr, body))
|
|
713
|
+
for rank in ranks:
|
|
714
|
+
assert(isinstance(rank, ir.Rank))
|
|
715
|
+
conjuncts.append(_translate_rank(ctx, rank, body))
|
|
716
|
+
else:
|
|
717
|
+
# If there are no aggregates or ranks, translate as a normal conjunction
|
|
718
|
+
conjuncts = [_translate_to_formula(ctx, child) for child in task.body]
|
|
548
719
|
return mk_and(conjuncts)
|
|
549
720
|
elif isinstance(task, ir.Lookup):
|
|
550
721
|
return _translate_to_atom(ctx, task)
|
|
@@ -564,14 +735,13 @@ def _translate_to_formula(ctx: TranslationCtx, task: ir.Task) -> lqp.Formula:
|
|
|
564
735
|
|
|
565
736
|
return mk_primitive("rel_primitive_hash_tuple_uint128", [v for v, _ in terms])
|
|
566
737
|
elif isinstance(task, ir.Union):
|
|
567
|
-
# TODO: handle hoisted vars if needed
|
|
568
738
|
disjs = [_translate_to_formula(ctx, child) for child in task.tasks]
|
|
569
739
|
return mk_or(disjs)
|
|
570
|
-
elif isinstance(task, (ir.
|
|
740
|
+
elif isinstance(task, (ir.Output, ir.Update)):
|
|
571
741
|
# Nothing to do here, handled in _translate_to_decls
|
|
572
742
|
return mk_and([])
|
|
573
|
-
elif isinstance(task, ir.Rank):
|
|
574
|
-
# Nothing to do here, handled
|
|
743
|
+
elif isinstance(task, (ir.Aggregate, ir.Rank)):
|
|
744
|
+
# Nothing to do here, handled at the Logical level
|
|
575
745
|
return mk_and([])
|
|
576
746
|
else:
|
|
577
747
|
raise NotImplementedError(f"Unknown task type (formula): {type(task)}")
|
|
@@ -666,7 +836,7 @@ def get_output_id(ctx: TranslationCtx, orig_name: str, metamodel_id: int) -> lqp
|
|
|
666
836
|
unique_name = ctx.output_names.get_name_by_id(metamodel_id, orig_name)
|
|
667
837
|
return utils.gen_rel_id(ctx, unique_name)
|
|
668
838
|
|
|
669
|
-
def _translate_bindings(ctx: TranslationCtx, bindings:
|
|
839
|
+
def _translate_bindings(ctx: TranslationCtx, bindings: Sequence[ir.Value]) -> Tuple[list[Tuple[lqp.Var, lqp.Type]], list[lqp.Formula]]:
|
|
670
840
|
lqp_vars = []
|
|
671
841
|
conjuncts = []
|
|
672
842
|
for binding in bindings:
|