Skip to content

Preserve role qualifying cube column identity - #2370

Open
israelhill wants to merge 4 commits into
mainfrom
preserve-role-qualifying-cube-column-identity
Open

Preserve role qualifying cube column identity#2370
israelhill wants to merge 4 commits into
mainfrom
preserve-role-qualifying-cube-column-identity

Conversation

@israelhill

Copy link
Copy Markdown
Contributor

Summary

Cubes can use the same business dimension in multiple roles. For example, one cube may contain both an order date and a ship date, even though both come from the same underlying date table.

Some DataJunction code paths currently identify these columns only as “date,” losing the role that distinguishes them. This can cause settings intended for one role to be applied to another, columns to disappear from output, or materializations to use the wrong partition. Because these failures can occur silently, they create a data-correctness and operational risk.

This pr hardens the full cube lifecycle so each role remains distinct across:

  • Cube creation and updates
  • Declarative deployment and export
  • API and GraphQL responses
  • SQL generation and cube matching
  • Materialization, partitioning, and backfills
  • Python clients, CLI, MCP, and the DataJunction UI

Expected outcome

An order-date column and a ship-date column will remain independently identifiable and configurable throughout DJ. Ambiguous requests will fail with a clear error instead of silently selecting a column.

Success criteria

  • Role-specific column settings are applied to the intended column
  • Cube updates and deployments preserve roles and partitions
  • Materializations use the correct role-specific partition
  • APIs, clients, and UI display all roles without collapsing them

Automated tests cover creation, updates, deployment, materialization, and user-facing workflows.

@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for thriving-cassata-78ae72 canceled.

Name Link
🔨 Latest commit 852c587
🔍 Latest deploy log https://app.netlify.com/projects/thriving-cassata-78ae72/deploys/6a67dd9c7d691a0008ad4c26


def to_spec(self):
@property
def cube_element_name(self) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be named semantic_name so that it's aligned with other usage like

semantic_name: str = Field(
validation_alias=AliasChoices("semantic_name", "semantic_entity"),
serialization_alias="semantic_entity",
)
?

@israelhill israelhill Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i called it cube_element_name to avoid misuse. Column is pretty overloaded since it can represent columns across the various node types. the dimension_column field behaves differently depending on the node type this column is associated with. For exmaple, it carries the referenced dimension name for a dimension node column:

dimension node column: orders.country_code

dimension_id -> common.country
dimension_column -> country_code

whereas for a cube column, dimension_column carries the bracketed role eg [ship_date]. So if the column object represents a column on a dimension node, this function would produce country_codecountry_code.

i do agree that it would be nice to have a more robust and canonical way of resolving column identifiers, but that change would have a wider blast radius. that's what i was alluding to in #2371

]
return (
f"{upsert.job.name.lower()}__{upsert.strategy.name.lower()}" # type: ignore
+ (f"__{partition_names[0]}" if temporal_partitions else "")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this assume at most one temporal partition? With two, partition_names[0] uses only the first and skips the second, so it vanishes from the identity.

return None # pragma: no cover

def categorical_expression(self):
def categorical_expression(self, column_name: Optional[str] = None):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could column_name not be passed in if this just uses self.column.cube_element_name instead?

"""
return self.name + (self.dimension_column or "")

def to_spec(self, *, include_cube_role: bool = False):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the flag include_cube_role be removed by doing something like:

def to_spec(self):
    role = None
    if self.node_revision.type == NodeType.CUBE and self.dimension_column:
        role = self.dimension_column.strip("[]")
    return ColumnSpec(name=self.name, role=role, ...)

This is roughly similar to what DimensionLinkSpec does for example

)


def _materialization_name(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the name is a persisted identity, this format change could orphan existing materializations right? I guess it should just be cases where cubes are referencing dimensions with roles, which wouldn't have been properly supported anyway. Worth doing a migration check though just in case.

"query": getattr(node, "query", None),
"columns": [
{"name": col.name, "type": col.type} for col in node.columns
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is just in the --format json branch and the default text branch doesn't get the change to display cube cols with roles.

self.order = order

@property
def cube_element_name(self) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment to below -- could this be semantic_name instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants