feat(dataflow): model production SQL flow patterns
This commit is contained in:
@@ -1128,11 +1128,37 @@ def _join_config(
|
||||
) -> dict[str, Any]:
|
||||
kind = str(join.args.get("kind") or "").casefold()
|
||||
side = str(join.args.get("side") or "").casefold()
|
||||
if kind not in {"", "inner", "outer"} or side not in {"", "left", "right", "full"}:
|
||||
if kind not in {"", "inner", "outer", "semi", "anti"} or side not in {
|
||||
"",
|
||||
"left",
|
||||
"right",
|
||||
"full",
|
||||
}:
|
||||
raise SqlCompilationError(
|
||||
[_sql_error("sql.join_type", "JOIN supports INNER, LEFT, RIGHT, or FULL joins only.")]
|
||||
[
|
||||
_sql_error(
|
||||
"sql.join_type",
|
||||
(
|
||||
"JOIN supports INNER, LEFT, RIGHT, FULL, SEMI, "
|
||||
"or ANTI joins only."
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
join_type = side or ("inner" if kind in {"", "inner"} else "")
|
||||
if kind in {"semi", "anti"} and side:
|
||||
raise SqlCompilationError(
|
||||
[
|
||||
_sql_error(
|
||||
"sql.join_type",
|
||||
"SEMI and ANTI joins cannot use a side qualifier.",
|
||||
)
|
||||
]
|
||||
)
|
||||
join_type = (
|
||||
kind
|
||||
if kind in {"semi", "anti"}
|
||||
else side or ("inner" if kind in {"", "inner"} else "")
|
||||
)
|
||||
if not join_type:
|
||||
raise SqlCompilationError(
|
||||
[_sql_error("sql.join_type", "OUTER JOIN requires LEFT, RIGHT, or FULL.")]
|
||||
@@ -1471,6 +1497,41 @@ def _render_expression(
|
||||
state.selected = True
|
||||
|
||||
|
||||
def _render_calculate(
|
||||
node: GraphNode,
|
||||
state: _SqlRenderState,
|
||||
column_expression: Callable[[str], exp.Column],
|
||||
) -> None:
|
||||
_require_projection_slot(node, state)
|
||||
calculated_targets: set[str] = set()
|
||||
expressions: list[exp.Expression] = [exp.Star()]
|
||||
for item in node.config["calculations"]:
|
||||
parsed = parse_expression(str(item["expression"]))
|
||||
dependencies = set(parsed.columns) & calculated_targets
|
||||
if dependencies:
|
||||
raise SqlCompilationError(
|
||||
[
|
||||
_node_sql_error(
|
||||
node.id,
|
||||
"sql.sequential_calculation",
|
||||
(
|
||||
"SQL view cannot render a calculated column that "
|
||||
"depends on an earlier calculation in the same block."
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
expression = _qualified_expression(
|
||||
str(item["expression"]),
|
||||
column_expression=column_expression,
|
||||
)
|
||||
target = str(item["target_column"])
|
||||
expressions.append(expression.as_(target))
|
||||
calculated_targets.add(target)
|
||||
state.select_expressions = expressions
|
||||
state.selected = True
|
||||
|
||||
|
||||
def _render_derive(
|
||||
node: GraphNode,
|
||||
state: _SqlRenderState,
|
||||
@@ -1600,6 +1661,41 @@ def _render_sort(
|
||||
]
|
||||
|
||||
|
||||
def _render_rank(
|
||||
node: GraphNode,
|
||||
state: _SqlRenderState,
|
||||
column_expression: Callable[[str], exp.Column],
|
||||
) -> None:
|
||||
_require_projection_slot(node, state)
|
||||
function = {
|
||||
"row_number": exp.RowNumber,
|
||||
"rank": exp.Rank,
|
||||
"dense_rank": exp.DenseRank,
|
||||
}[str(node.config.get("method", "row_number"))]()
|
||||
window = exp.Window(
|
||||
this=function,
|
||||
partition_by=[
|
||||
column_expression(str(column))
|
||||
for column in node.config.get("partition_by", [])
|
||||
],
|
||||
order=exp.Order(
|
||||
expressions=[
|
||||
exp.Ordered(
|
||||
this=column_expression(str(item["column"])),
|
||||
desc=item.get("direction", "asc") == "desc",
|
||||
nulls_first=False,
|
||||
)
|
||||
for item in node.config["order_by"]
|
||||
]
|
||||
),
|
||||
)
|
||||
state.select_expressions = [
|
||||
exp.Star(),
|
||||
window.as_(str(node.config["target_column"])),
|
||||
]
|
||||
state.selected = True
|
||||
|
||||
|
||||
def _render_limit(
|
||||
node: GraphNode,
|
||||
state: _SqlRenderState,
|
||||
@@ -1642,7 +1738,10 @@ def _require_projection_slot(
|
||||
_node_sql_error(
|
||||
node.id,
|
||||
"sql.multiple_select",
|
||||
"Only one expression, conversion, derive, select, or aggregate transform is supported in SQL view.",
|
||||
(
|
||||
"Only one calculation, ranking, conversion, derive, "
|
||||
"select, or aggregate transform is supported in SQL view."
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -1660,10 +1759,12 @@ def _register_sql_renderers() -> None:
|
||||
"select": _render_select,
|
||||
"derive": _render_derive,
|
||||
"expression": _render_expression,
|
||||
"calculate": _render_calculate,
|
||||
"convert": _render_convert,
|
||||
"replace": _render_unsupported,
|
||||
"aggregate": _render_aggregate,
|
||||
"sort": _render_sort,
|
||||
"window.rank": _render_rank,
|
||||
"limit": _render_limit,
|
||||
"quality.rules": _render_unsupported,
|
||||
"reconcile.compare": _render_unsupported,
|
||||
|
||||
Reference in New Issue
Block a user