feat(dataflow): model production SQL flow patterns
This commit is contained in:
@@ -185,6 +185,8 @@ def _join(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
field_name="right_keys",
|
||||
),
|
||||
]
|
||||
if node.config.get("join_type", "inner") in {"semi", "anti"}:
|
||||
return SchemaPropagationResult(left_state, tuple(diagnostics))
|
||||
prefix = str(node.config.get("right_prefix", "right_"))
|
||||
prefixed_right = {
|
||||
f"{prefix}{column}"
|
||||
@@ -377,6 +379,66 @@ def _expression(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
return SchemaPropagationResult(state, tuple(diagnostics))
|
||||
|
||||
|
||||
def _calculate(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
node = context.node
|
||||
state = context.input_state
|
||||
diagnostics: list[DataflowDiagnostic] = []
|
||||
for item in _mapping_items(node.config.get("calculations")):
|
||||
source = str(item.get("expression") or "")
|
||||
target = str(item.get("target_column") or "")
|
||||
try:
|
||||
parsed = parse_expression(source)
|
||||
except ExpressionError as exc:
|
||||
diagnostics.append(
|
||||
_error(
|
||||
"expression.invalid",
|
||||
str(exc),
|
||||
node_id=node.id,
|
||||
field="calculations",
|
||||
)
|
||||
)
|
||||
continue
|
||||
diagnostics.extend(
|
||||
_unknown_columns(
|
||||
node,
|
||||
state,
|
||||
list(parsed.columns),
|
||||
field_name="calculations",
|
||||
)
|
||||
)
|
||||
inferred = infer_expression_type(
|
||||
parsed,
|
||||
{
|
||||
name: state.type_of(name) # type: ignore[dict-item]
|
||||
for name in state.columns
|
||||
},
|
||||
)
|
||||
expected = str(item.get("result_type") or "unknown")
|
||||
if expected != "unknown" and inferred not in {
|
||||
"unknown",
|
||||
"null",
|
||||
expected,
|
||||
}:
|
||||
diagnostics.append(
|
||||
_warning(
|
||||
"calculate.type_mismatch",
|
||||
(
|
||||
f"Calculation for {target!r} infers {inferred}, "
|
||||
f"not {expected}."
|
||||
),
|
||||
node_id=node.id,
|
||||
field="calculations",
|
||||
)
|
||||
)
|
||||
if target:
|
||||
state = _with_column(
|
||||
state,
|
||||
target,
|
||||
expected if expected != "unknown" else inferred,
|
||||
)
|
||||
return SchemaPropagationResult(state, tuple(diagnostics))
|
||||
|
||||
|
||||
def _convert_or_replace(
|
||||
context: SchemaPropagationContext,
|
||||
) -> SchemaPropagationResult:
|
||||
@@ -463,6 +525,31 @@ def _sort(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
)
|
||||
|
||||
|
||||
def _rank(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
node = context.node
|
||||
columns = [
|
||||
*_text_items(node.config.get("partition_by")),
|
||||
*[
|
||||
str(item.get("column"))
|
||||
for item in _mapping_items(node.config.get("order_by"))
|
||||
if item.get("column")
|
||||
],
|
||||
]
|
||||
diagnostics = _unknown_columns(
|
||||
node,
|
||||
context.input_state,
|
||||
columns,
|
||||
field_name="order_by",
|
||||
)
|
||||
target = str(node.config.get("target_column") or "")
|
||||
state = (
|
||||
_with_column(context.input_state, target, "integer")
|
||||
if target
|
||||
else context.input_state
|
||||
)
|
||||
return SchemaPropagationResult(state, tuple(diagnostics))
|
||||
|
||||
|
||||
def _quality(context: SchemaPropagationContext) -> SchemaPropagationResult:
|
||||
rules = _mapping_items(context.node.config.get("rules"))
|
||||
diagnostics = _unknown_columns(
|
||||
@@ -874,10 +961,12 @@ def register_schema_propagators() -> None:
|
||||
"select": _select,
|
||||
"derive": _derive,
|
||||
"expression": _expression,
|
||||
"calculate": _calculate,
|
||||
"convert": _convert_or_replace,
|
||||
"replace": _convert_or_replace,
|
||||
"aggregate": _aggregate,
|
||||
"sort": _sort,
|
||||
"window.rank": _rank,
|
||||
"limit": _identity,
|
||||
"quality.rules": _quality,
|
||||
"reconcile.compare": _reconcile,
|
||||
|
||||
Reference in New Issue
Block a user