Skip to content

Commit 5b2feed

Browse files
hbarthelsclaude
andcommitted
Model TargetRelations CDC/non-CDC modes with a oneof
Replace the flat relations/inserts/deletes fields on TargetRelations with a `oneof body { PlainTargets plain; CdcTargets cdc; }`, making the mutually-exclusive plain (non-CDC) and CDC modes explicit instead of inferred from which repeated field happens to be populated. PlainTargets wraps `targets`; CdcTargets wraps `inserts`/`deletes` (oneof can't hold repeated fields directly, hence the wrapper messages). The LQP s-expression syntax is unchanged — only the grammar's construct/deconstruct helpers and the deconstruct guard (now switching on the oneof case via has_proto_field) change, so pretty/pretty_debug snapshots are untouched; only .bin wire encodings shift. Regenerated all three SDKs; updated hand-written Julia equality (PlainTargets/CdcTargets + oneof-based TargetRelations), properties global_ids, and equality tests. All Python/Go/Julia suites pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0c3cc76 commit 5b2feed

19 files changed

Lines changed: 1479 additions & 1007 deletions

File tree

meta/src/meta/grammar.y

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,13 +1153,13 @@ cdc_deletes
11531153
relation_body
11541154
: non_cdc_relations
11551155
construct: $$ = construct_non_cdc_relations($1)
1156-
deconstruct if builtin.is_empty($$.inserts) and builtin.is_empty($$.deletes):
1157-
$1: Sequence[logic.TargetRelation] = $$.relations
1156+
deconstruct if builtin.has_proto_field($$, 'plain'):
1157+
$1: Sequence[logic.TargetRelation] = $$.plain.targets
11581158
| cdc_inserts cdc_deletes
11591159
construct: $$ = construct_cdc_relations($1, $2)
1160-
deconstruct if not (builtin.is_empty($$.inserts) and builtin.is_empty($$.deletes)):
1161-
$1: Sequence[logic.TargetRelation] = $$.inserts
1162-
$2: Sequence[logic.TargetRelation] = $$.deletes
1160+
deconstruct if builtin.has_proto_field($$, 'cdc'):
1161+
$1: Sequence[logic.TargetRelation] = $$.cdc.inserts
1162+
$2: Sequence[logic.TargetRelation] = $$.cdc.deletes
11631163

11641164
target_relations
11651165
: "(" "relations" relation_keys relation_body ")"
@@ -1527,13 +1527,11 @@ def _try_extract_value_string_list(value: Optional[logic.Value]) -> Optional[Seq
15271527

15281528

15291529
def construct_non_cdc_relations(
1530-
relations: Sequence[logic.TargetRelation],
1530+
targets: Sequence[logic.TargetRelation],
15311531
) -> logic.TargetRelations:
15321532
return logic.TargetRelations(
15331533
keys=list[logic.NamedColumn](),
1534-
relations=relations,
1535-
inserts=list[logic.TargetRelation](),
1536-
deletes=list[logic.TargetRelation](),
1534+
plain=logic.PlainTargets(targets=targets),
15371535
)
15381536

15391537

@@ -1543,22 +1541,17 @@ def construct_cdc_relations(
15431541
) -> logic.TargetRelations:
15441542
return logic.TargetRelations(
15451543
keys=list[logic.NamedColumn](),
1546-
relations=list[logic.TargetRelation](),
1547-
inserts=inserts,
1548-
deletes=deletes,
1544+
cdc=logic.CdcTargets(inserts=inserts, deletes=deletes),
15491545
)
15501546

15511547

15521548
def construct_relations(
15531549
keys: Sequence[logic.NamedColumn],
15541550
body: logic.TargetRelations,
15551551
) -> logic.TargetRelations:
1556-
return logic.TargetRelations(
1557-
keys=keys,
1558-
relations=body.relations,
1559-
inserts=body.inserts,
1560-
deletes=body.deletes,
1561-
)
1552+
if builtin.has_proto_field(body, "plain"):
1553+
return logic.TargetRelations(keys=keys, plain=body.plain)
1554+
return logic.TargetRelations(keys=keys, cdc=body.cdc)
15621555

15631556

15641557
def construct_csv_data(

proto/relationalai/lqp/v1/logic.proto

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -301,15 +301,25 @@ message TargetRelation {
301301
repeated NamedColumn values = 2; // Value columns for this relation (may be empty)
302302
}
303303

304-
// Generalized loading: a shared set of key columns and one or more target relations.
305-
// CDC vs non-CDC is implied by which group is populated:
306-
// - `relations` populated => non-CDC outputs
307-
// - `inserts`/`deletes` => CDC insert/delete groups
304+
// Plain (non-CDC) load: each input row becomes a tuple in every target relation.
305+
message PlainTargets {
306+
repeated TargetRelation targets = 1; // Target relations (each row feeds all of them)
307+
}
308+
309+
// CDC load: input rows are routed by METADATA$ACTION into insert and delete deltas.
310+
message CdcTargets {
311+
repeated TargetRelation inserts = 1; // INSERT-action rows feed these
312+
repeated TargetRelation deletes = 2; // DELETE-action rows feed these
313+
}
314+
315+
// Generalized loading: shared key columns plus the target relations, loaded either as a
316+
// plain snapshot or as CDC insert/delete deltas. The two modes are mutually exclusive.
308317
message TargetRelations {
309-
repeated NamedColumn keys = 1; // Shared key columns (name "METADATA$KEY" => derived hash)
310-
repeated TargetRelation relations = 2; // Non-CDC outputs
311-
repeated TargetRelation inserts = 3; // CDC insert group
312-
repeated TargetRelation deletes = 4; // CDC delete group
318+
repeated NamedColumn keys = 1; // Shared key columns (name "METADATA$KEY" => derived hash)
319+
oneof body {
320+
PlainTargets plain = 2;
321+
CdcTargets cdc = 3;
322+
}
313323
}
314324

315325
message CSVData {

0 commit comments

Comments
 (0)