Apply SQL Edits to Workspace Node
apply_sql_to_workspace_nodeApplies user edits from rendered DDL/DML SQL back to the corresponding cloud workspace node, parsing columns and updating metadata via diff.
Instructions
Round-trip a user's edits on a rendered DDL/DML SQL document back to the cloud workspace node it came from. Parses the SQL's column list, diffs it against the node's current metadata.columns[], and updates the node via set_workspace_node (unless dryRun: true). Both DDL and DML inputs feed the same node metadata — a DDL edit and a DML edit on the same node update the same metadata.columns[].
Column-level diff (structural changes — joins, CTEs, WHERE/GROUP BY, custom expressions on the join itself — are NOT reverse-engineered; those live in the node YAML):
Removed columns (in YAML, missing in SQL) → dropped
Type changes (matching name, different dataType) → updated in place; existing sources / columnReferences / lineage metadata is preserved
Renamed columns (DML only — same transform, new AS alias) → renamed in place, lineage preserved (column id stays stable so downstream refs keep working). Detection requires unambiguous transform pairing; ambiguous cases fall back to drop+add.
Expression-changed columns (DML only — same name, edited transform) →
sources[].transformis updated and source-mapping inference is re-run to refreshcolumnReferencesif the new transform references different upstream columns.New columns → for DML inputs, source-mapping inference resolves the column's
transformagainst upstream predecessors (alias-prefix match, all-pred name fallback, multi-source COALESCE/GREATEST). For DDL inputs, new columns are added with emptycolumnReferences[](DDL doesn't carry transforms); fill them in via the cloud UI.
Block-level diff (DML only — single-sourceMapping nodes):
WHERE / ON-condition edits → written to
sourceMapping[0].join.joinConditionwith table refs rewritten to{{ ref('LOC', 'NAME') }}form.Adding or removing a predecessor source via SQL is REJECTED — add/remove the predecessor in the cloud UI first, then re-render.
Tail-clause diff (DML only):
GROUP BY presence toggle → flips
config.groupByAll. Coalesce derives the actual GROUP BY column list from non-aggregate columns at render time.ORDER BY adds / changes / removes →
config.orderbyflag andconfig.orderbycolumn.items[]array of{sortColName, sortOrder}.LIMIT adds / changes / removes → appended to / stripped from the trailing position of
sourceMapping[0].join.joinCondition. Coalesce has no native limit config field for typical SQL nodes; the apply path warns when groupByAll/orderby is set (the auto-rendered tail clauses would land AFTER LIMIT, producing invalid SQL).
INSERT header diff (DML only — INSERT-shaped inputs):
Target identifier change (
INSERT INTO "DB"."LOC"."NAME") → updates the cloud node's top-leveldatabase/locationName/namefields. Renames/relocates the node; downstream references are unaffected (they use the node id, not the name).INSERT column list out-of-sync with the SELECT projection → REJECTED. Coalesce auto-generates the INSERT list from
metadata.columns[]; edit the SELECT (using AS aliases) to add/rename/reorder columns.
Args:
workspaceID (string, required)
nodeID (string, required)
sql (string, required): Either a CREATE [OR REPLACE] TABLE statement (DDL, from coa_dry_run_create) OR a SELECT/INSERT statement (DML, from coa_dry_run_run). The shape is auto-detected.
dryRun (boolean, optional): If true, return the diff without writing.
Returns: { applied, dryRun, inputKind, diff: { unchanged, typeChanged, renamed, expressionChanged, removed, added }, joinDiff: { kind: 'identical' | 'whereOrJoinEdit' | 'newSource' | 'removedSource' | 'unsupported', ... }, tailDiff: { groupBy: { kind: 'identical' | 'added' | 'removed', ... }, orderBy: { kind: 'identical' | 'added' | 'changed' | 'removed', ... } }, limitDiff: { kind: 'identical' | 'added' | 'changed' | 'removed' | 'unsupported', ... }, insertHeaderDiff: { kind: 'identical' | 'targetChanged' | 'columnListMismatch' | 'notApplicable', ... }, body?, warnings?, error? }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | The edited SQL to apply. Accepts either: (a) a CREATE [OR REPLACE] TABLE statement (`coa_dry_run_create` output) — name + dataType per column, no transforms; or (b) a SELECT/INSERT/MERGE statement (`coa_dry_run_run` output) — name + expression per column, dataType inferred from the expression. For MERGE shapes (Coalesce dim / Type-2 SCD nodes) only the inner SELECT inside USING (...) is editable; the MERGE envelope (target, ON, WHEN MATCHED / WHEN NOT MATCHED) is Coalesce-managed and ignored by the apply path. The tool extracts the column list and diffs it against the node's current metadata.columns[]. | |
| dryRun | No | When true, returns the diff and updated metadata.columns[] without writing to the workspace. Useful for previewing what would change before pushing. | |
| nodeID | Yes | The node ID to update. | |
| workspaceID | Yes | The workspace ID containing the node. |