Skip to content
Merged
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
9 changes: 6 additions & 3 deletions snowcap/data_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -5238,8 +5238,9 @@ def list_tag_masking_policy_references(session: SnowflakeConnection) -> list[FQN
logger.debug(f" Found tag masking policy reference: {fqn}")
references.append(fqn)
except ProgrammingError as err:
if err.errno in (ACCESS_CONTROL_ERR, UNSUPPORTED_FEATURE, DOES_NOT_EXIST_ERR):
# Skip tags we can't access or don't exist
if err.errno in (ACCESS_CONTROL_ERR, UNSUPPORTED_FEATURE, DOES_NOT_EXIST_ERR, INVALID_IDENTIFIER):
# Skip tags we can't access, that don't exist, or whose database doesn't
# exist yet (Snowflake reports that as INVALID_IDENTIFIER, not DOES_NOT_EXIST_ERR).
logger.debug(f" Skipping tag {tag_fqn}: {err.msg}")
continue
else:
Expand Down Expand Up @@ -5299,7 +5300,9 @@ def fetch_tag_masking_policy_reference(session: SnowflakeConnection, fqn: FQN) -
"masking_policy_name": f"{policy_db}.{policy_schema}.{policy_name}",
}
except ProgrammingError as err:
if err.errno in (ACCESS_CONTROL_ERR, UNSUPPORTED_FEATURE, DOES_NOT_EXIST_ERR):
if err.errno in (ACCESS_CONTROL_ERR, UNSUPPORTED_FEATURE, DOES_NOT_EXIST_ERR, INVALID_IDENTIFIER):
# INVALID_IDENTIFIER covers the tag's own database not existing yet, e.g. when
# the same config that declares the reference also creates that database.
logger.debug(f"Cannot fetch tag masking policy reference {fqn}: {err.msg}")
return None
else:
Expand Down
60 changes: 60 additions & 0 deletions tests/test_data_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
fetch_shared_database,
fetch_security_integration,
fetch_task,
fetch_tag_masking_policy_reference,
fetch_user,
fetch_warehouse,
fetch_streamlit,
Expand All @@ -57,6 +58,7 @@
list_schema_scoped_resource,
list_stages,
list_tables,
list_tag_masking_policy_references,
list_views,
# Session functions
fetch_account_locator,
Expand Down Expand Up @@ -3736,3 +3738,61 @@ def test_an_empty_login_name_reads_as_none(self, mock_show, mock_execute):

assert data["login_name"] is None
assert data["display_name"] is None


class TestTagMaskingPolicyReferenceMissingDatabase:
"""A tag's own database can not exist yet when the same config also creates it.

Querying `<database>.INFORMATION_SCHEMA.POLICY_REFERENCES` for that database fails
with INVALID_IDENTIFIER (2004), not DOES_NOT_EXIST_ERR (2003) -- Snowflake can't even
resolve the qualified name. Treat it the same as a not-yet-created reference.
"""

@patch("snowcap.data_provider.execute")
def test_fetch_returns_none_when_database_does_not_exist_yet(self, mock_execute):
from snowflake.connector.errors import ProgrammingError

mock_execute.side_effect = ProgrammingError(
errno=2004, msg="Invalid identifier GOVERNANCE.INFORMATION_SCHEMA.POLICY_REFERENCES"
)
fqn = FQN(
database=ResourceName("GOVERNANCE"),
schema=ResourceName("TAGS"),
name=ResourceName("PII"),
params={"masking_policy": "governance.policies.mask_pii"},
)

result = fetch_tag_masking_policy_reference(MagicMock(), fqn)

assert result is None

@patch("snowcap.data_provider.execute")
def test_fetch_raises_on_other_programming_errors(self, mock_execute):
from snowflake.connector.errors import ProgrammingError

mock_execute.side_effect = ProgrammingError(errno=1234)
fqn = FQN(
database=ResourceName("GOVERNANCE"),
schema=ResourceName("TAGS"),
name=ResourceName("PII"),
params={"masking_policy": "governance.policies.mask_pii"},
)

with pytest.raises(ProgrammingError):
fetch_tag_masking_policy_reference(MagicMock(), fqn)

@patch("snowcap.data_provider.list_tags")
@patch("snowcap.data_provider.execute")
def test_list_skips_a_tag_whose_database_does_not_exist_yet(self, mock_execute, mock_list_tags):
from snowflake.connector.errors import ProgrammingError

mock_list_tags.return_value = [
FQN(database=ResourceName("GOVERNANCE"), schema=ResourceName("TAGS"), name=ResourceName("PII"))
]
mock_execute.side_effect = ProgrammingError(
errno=2004, msg="Invalid identifier GOVERNANCE.INFORMATION_SCHEMA.POLICY_REFERENCES"
)

result = list_tag_masking_policy_references(MagicMock())

assert result == []
Loading