Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions sidemantic/adapters/lookml.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,23 @@ def _parse_view(self, view_def: dict) -> Model | None:
elif isinstance(extends_list, str):
extends = extends_list

# A LookML view with no sql_table_name and no derived_table implicitly
# uses a table named after the view (Looker's default). Apply that so the
# model is valid instead of failing validation with "must have a table".
# Skip refinements (merged into their base), views that inherit a table
# via extends or are abstract (extension: required), and fieldless
# placeholder views (which have nothing to query anyway).
if (
table is None
and sql is None
and not view_def.get("derived_table")
and not extends
and not name.startswith("+")
and view_def.get("extension") != "required"
and (dimensions or measures)
Comment on lines +597 to +599

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply default tables after merging refinements

When the base view is fieldless and a +view refinement supplies the dimensions/measures, neither parsed model gets the implicit table: the base fails the (dimensions or measures) guard while the refinement is excluded by name.startswith("+"). After merge_model the final view has queryable fields but still no table, so adding or compiling that valid LookML pattern (view: orders {} plus view: +orders { dimension: id ... }) still fails validation even though Looker would default it to orders; the default needs to run on the merged view or otherwise account for fields added by refinements.

Useful? React with 👍 / 👎.

):
table = name

# Build kwargs conditionally so that unset scalars don't appear in
# model_fields_set. This matters for refinements: merge_model treats
# every field in model_fields_set as an explicit child override, so
Expand Down
31 changes: 31 additions & 0 deletions tests/adapters/lookml/test_edge_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2198,6 +2198,37 @@ def test_lookml_self_referential_dimension_terminates():
assert selfd.sql.count("+ 1") <= 2


def test_lookml_view_with_unsupported_derived_table_not_defaulted():
"""A view with a derived_table the adapter can't read must NOT get a default physical table."""
adapter = LookMLAdapter()
model = adapter._parse_view(
{
"name": "ndt_unknown",
# derived_table with no sql / explore_source the adapter understands
"derived_table": {"persist_for": "1 hour"},
"dimensions": [{"name": "id", "type": "number", "sql": "${TABLE}.id"}],
}
)
assert model.table is None # not fabricated as a physical table named after the view


def test_lookml_view_without_table_defaults_to_view_name():
"""A view with no sql_table_name/derived_table should default its table to the view name."""
from sidemantic import SemanticLayer

graph = _parse_lkml(
"view: just_fields { "
"dimension: id { primary_key: yes type: number sql: ${TABLE}.id ;; } "
"measure: c { type: count } }"
)
model = graph.get_model("just_fields")
assert model.table == "just_fields"
# And it must be a valid, queryable model (previously raised ModelValidationError).
layer = SemanticLayer()
layer.add_model(model)
assert "just_fields" in layer.compile(metrics=["just_fields.c"])


def test_lookml_export_unmapped_aggregations_not_count():
"""Unmapped aggregations / complex types must not be silently exported as COUNT."""
import tempfile
Expand Down
3 changes: 2 additions & 1 deletion tests/adapters/test_added_fixture_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@
"tests/fixtures/lookml/ga360_ga_block.view.lkml",
"tests/fixtures/lookml/lkml_model_all_fields.model.lkml",
"tests/fixtures/lookml/lkml_parameter_join.model.lkml",
"tests/fixtures/lookml/node_lookml_refinement_merging.model.lkml",
# node_lookml_refinement_merging now compiles: its `deep_merging` view has a
# dimension and (per Looker semantics) defaults its table to the view name.
"tests/fixtures/lookml/node_lookml_refinement_sequencing.model.lkml",
"tests/fixtures/lookml/pylookml_aggregate_tables.model.lkml",
"tests/fixtures/lookml/pylookml_manifest.lkml",
Expand Down
Loading