Skip to content

patch_materialized_view cannot change to_table, so a per-role destination needs a full override #238

Closed

Description

@orian

What happens

PatchMaterializedViewSpec carries Query, Columns, ModifyColumns and DropColumns, but no to_table. So a materialized view whose destination differs between compositions cannot be expressed as a patch — the only way through is restating the whole view with override = true.

Why it comes up

The same ingest view runs on two node classes. On one, the destination table is local, so the view writes straight into it. On the other, the destination lives on a different node, so the view writes through a Distributed proxy instead. Everything else about the view — its query, its columns — is identical.

# shared layer: the node that holds the storage writes into it directly
materialized_view "kafka_metrics_avro_mv" {
  to_table = "posthog.metrics1"
  query    = file("sql/kafka_metrics_avro_mv.sql")
  # ...columns
}

# the other role wants only this, and cannot say it:
patch_materialized_view "kafka_metrics_avro_mv" {
  to_table = "posthog.writable_metrics1"   # not a supported argument
}

Instead the whole view has to be copied into the second layer with override = true, query and column list included. In our case that is six views, and the copies are byte-identical to the shared ones apart from the one to_table line.

Why the copy is worse than it looks

locate -duplicates counts override = true as zero duplicates, by design. So a schema that is now authored in two places passes the once-only guard silently. The copies then drift apart on their own: a query edit lands in one and not the other, and nothing reports it. Every other per-composition difference has a patch form precisely so this does not happen, and a destination change is the one gap.

Suggestion

Add ToTable *string to PatchMaterializedViewSpec, matching how patch_table already carries engine, order_by and partition_by — attributes that replace rather than merge. A patch that sets only to_table would then leave the query and columns inherited from the declaration.

Relevant code: PatchMaterializedViewSpec in internal/loader/hcl/types.go, applied alongside the other patch kinds during resolution.

Related: #231, which was the same shape of gap for view and dictionary and was fixed in #232 by allowing override everywhere. This one is narrower — the override already works, it is just a much bigger hammer than the difference warrants.

Activity

  1. added a commit that references this issue on Sep 8, 2026
    9a52b78
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions