Skip to content

feat(database): support shared databases (FROM SHARE) and IMPORTED PRIVILEGES grants#39

Open
GClunies wants to merge 1 commit into
datacoves:mainfrom
GClunies:snowcap-28
Open

feat(database): support shared databases (FROM SHARE) and IMPORTED PRIVILEGES grants#39
GClunies wants to merge 1 commit into
datacoves:mainfrom
GClunies:snowcap-28

Conversation

@GClunies

Copy link
Copy Markdown
Contributor

Summary

Shared databases (direct shares and Marketplace listings) and the IMPORTED PRIVILEGES grant can now be managed in snowcap instead of manual SQL. Activates the dormant SharedDatabase resource as a polymorphic sibling of Database (same databases: YAML key, discriminated by from_share) and wires the full fetch/plan round-trip.

Closes #28.

Changes

  • SharedDatabase resource: CREATE DATABASE <name> FROM SHARE <provider_account>.<share_name>; read-only by design (no schemas/tags/params), default owner ACCOUNTADMIN, created as ACCOUNTADMIN since FROM SHARE requires IMPORT SHARE.
  • IMPORTED PRIVILEGES enabled in DatabasePriv, excluded from ALL expansion; fetch_grant handles Snowflake reporting the grant as USAGE in SHOW GRANTS, gated on the database actually being imported so a mistaken declaration on a regular database still surfaces as drift.
  • fetch_database delegates to fetch_shared_database for kind = IMPORTED DATABASE; a declared class that disagrees with the fetched kind raises a clear, actionable error instead of aborting the whole plan with a TypeError.
  • Blueprint: container-only attach/merge paths raise OrphanResourceException with guidance when the target is a shared database; PUBLIC-schema ownership transfer skipped for shared databases; changing from_share errors at plan time (replacement not implemented — documented).
  • Docs: new docs/resources/shared_database.md, SUMMARY.md nav entry, IMPORTED PRIVILEGES example in grant.md.
  • Tests: 30+ new unit tests + json/sql fixtures; full suite green (1569 passed, 1 xfailed documenting a pre-existing quoted-identifier limitation in ResourceName).

Notes for reviewers

  • SYSTEM() output fields are undocumented; whether owner is present in all editions needs a live-account check (fetch relies on it via _get_owner_identifier).

…IVILEGES grants

Databases created from inbound shares (direct shares and Marketplace
listings) previously had to be managed with manual SQL. Activate the
dormant SharedDatabase resource as a polymorphic sibling of Database
under ResourceType.DATABASE, resolved by the presence of from_share,
and enable IMPORTED PRIVILEGES so the RBAC around shared databases can
live in YAML.

- SharedDatabase: CREATE DATABASE <name> FROM SHARE <provider>.<share>,
  declared under the existing databases: key via from_share. Read-only
  by design: no schemas, tags, or params; not a ResourceContainer.
  Creation runs as ACCOUNTADMIN since FROM SHARE requires IMPORT SHARE.
- IMPORTED PRIVILEGES: enabled in DatabasePriv, excluded from ALL
  expansion (only valid on shared databases). fetch_grant handles
  Snowflake reporting the grant as USAGE in SHOW GRANTS, gated on the
  database actually being imported so a mistaken declaration on a
  regular database still surfaces as drift.
- Fetch round-trip: fetch_database delegates to fetch_shared_database
  for kind IMPORTED DATABASE. A declared class that disagrees with the
  fetched kind raises a clear error instead of a TypeError that aborts
  the whole plan.
- Blueprint guards: container-only attach/merge paths raise
  OrphanResourceException with guidance when the target is a shared
  database; the PUBLIC-schema ownership transfer is skipped for shared
  databases. Changing from_share errors at plan time (replacement is
  not implemented) - documented in the resource docs.
- Docs: new resources/shared_database.md page, SUMMARY.md entry, and an
  IMPORTED PRIVILEGES example in grant.md.
@noel

noel commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Thanks @GClunies — this is the most intricate PR in the batch and the polymorphic handling is done properly. I checked out the branch and verified:

  • Polymorphic dispatch is sound. _resolver discriminates on from_share; fetch_database delegates to fetch_shared_database for IMPORTED DATABASE. The kind-mismatch guard raises a clear InvalidResourceException before spec(**data) would blow up with a raw TypeError — nicely defensive.
  • IMPORTED PRIVILEGES fetch gating is correct. The USAGE fallback only fires after confirming the DB is actually IMPORTED DATABASE, so a mistaken declaration on a regular DB still surfaces as drift rather than false-matching a plain USAGE grant. Correctly excluded from ALL expansion too.
  • Read-only enforcement is comprehensive_require_container funnels every attach path into a guided OrphanResourceException, PUBLIC-schema transfer skipped, create uses IMPORT SHARE, from_share change fails at plan time.
  • Ran test_blueprint / test_data_provider / test_privs / test_resource_types / test_yaml_config: 469 passed, 1 xfailed (the documented quoted-identifier limitation).

Requested before merge

  1. Live check of SYSTEM$SHOW_IMPORTED_DATABASES() (the gate — you flagged owner). fetch_shared_database's data has no owner_role_type, so _get_owner_identifier takes the data["owner"] bracket-access branch — a KeyError that crashes the whole plan if that field is absent on some edition. Please confirm the fields against a live account, and consider .get("owner") with a fallback so a missing field degrades to drift instead of a crash.
  2. Confirm from_share round-trips without drift. Fetch maps from_share = data["origin"]; whether origin's format/case matches a user's declared value after ResourceName normalization is exactly what silently drifts. Worth checking in the same live run.

Minor / awareness

  • On a shared DB, both a declared IMPORTED PRIVILEGES grant and a declared plain USAGE grant map to the same underlying USAGE row. Unusual to declare both, but worth being aware of.

Looks good

  • Actionable error messages throughout (kind mismatch, orphan resource) rather than cryptic failures.
  • 30+ new tests and honest documentation of the pre-existing ResourceName xfail.
  • The comment explaining why the resolver lives in shared_database.py (grep-ability) is a good call.

Really solid — just needs the live verification you already called out.

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.

feat(database): support shared databases (FROM SHARE) and IMPORTED PRIVILEGES grants

2 participants