Skip to main content
Databricks beginner Lesson 3 of 10

Unity Catalog: Namespaces, Grants, and Lineage

The three-level namespace, managed versus external tables, volumes for files, grants that new tables inherit, and lineage you get without instrumenting anything.

Unity Catalog is the governance layer over everything in the previous two lessons: one place that knows what tables and files exist, who may read them, and where each column came from. Its most useful property is that permissions are inherited, so adding a table does not mean adding a grant.

The namespace

metastore                    one per region, shared by workspaces
└── catalog        bookshop_prod
    └── schema     marts
        ├── table  customer_orders
        ├── view   revenue_summary
        ├── volume exports
        └── function mask_email
show catalogs;
catalog
---------------
bookshop_dev
bookshop_prod
system
samples
create catalog if not exists bookshop_prod
    managed location 's3://bookshop-lake/prod';

create schema if not exists bookshop_prod.marts;
use catalog bookshop_prod;
OK

Environment separation is a catalog boundary, not a workspace boundary. One workspace, two catalogs, different grants — and a promotion from dev to prod is a CREATE TABLE ... DEEP CLONE rather than a data copy between systems.

Managed and external tables

-- managed: Unity Catalog owns the storage
create table bookshop_prod.marts.customer_orders as
select c.customer_id, c.full_name, count(o.order_id) as order_count,
       coalesce(sum(o.amount), 0) as lifetime_value
from bookshop.raw.customers c
left join bookshop.raw.orders o using (customer_id)
group by 1, 2;
num_affected_rows  num_inserted_rows
-----------------  -----------------
                4                  4
-- external: you own the location
create table bookshop_prod.raw.legacy_orders
using delta
location 's3://partner-bucket/exports/orders';
OK
describe extended bookshop_prod.marts.customer_orders;
col_name       data_type
-------------  --------------------------------------------------
Type           MANAGED
Location       s3://bookshop-lake/prod/marts/customer_orders
Provider       delta
Owner          analytics-eng

DROP TABLE on a managed table removes the data (after a retention window); on an external table it removes only the registration. Use managed by default — Unity Catalog can then optimise, cluster and vacuum the files for you, which it will not do for storage it does not own.

Volumes, for files

create volume if not exists bookshop.raw.landing;
OK
dbutils.fs.ls("/Volumes/bookshop/raw/landing/")
[FileInfo(path='/Volumes/bookshop/raw/landing/orders_2026_01.csv', name='orders_2026_01.csv', size=124500),
 FileInfo(path='/Volumes/bookshop/raw/landing/orders_2025_12.csv', name='orders_2025_12.csv', size=118204)]
import pandas as pd
pd.read_csv("/Volumes/bookshop/raw/landing/orders_2026_01.csv").head(3)
   order_id  customer_id  ordered_at     status  amount
0      1001            1  2026-01-04  completed   25.50
1      1002            2  2026-01-05  completed   12.00
2      1003            1  2026-01-07   returned   40.00

A volume path works with ordinary local-file APIs — pandas, open(), shell commands — while still being access-controlled. That combination is why volumes replaced DBFS mounts, where a mount granted everyone in the workspace the same access.

Grants

create group if not exists analysts;

grant use catalog on catalog bookshop_prod to `analysts`;
grant use schema  on schema  bookshop_prod.marts to `analysts`;
grant select      on schema  bookshop_prod.marts to `analysts`;
OK

USE CATALOG and USE SCHEMA are traversal rights — without them a SELECT grant is invisible, and the error says the table does not exist. That is the most common Unity Catalog support question and it is always this.

Granting SELECT on the schema covers every table in it, including ones created tomorrow:

create table bookshop_prod.marts.new_mart as select 1 as x;
-- as a member of analysts
select * from bookshop_prod.marts.new_mart;
x
-
1

No new grant needed. Compare that with per-table grants, where the pipeline that adds a table must also remember to grant on it — and eventually does not.

show grants on schema bookshop_prod.marts;
principal   action_type  object_type  object_key
----------  -----------  -----------  ---------------------
analysts    USE SCHEMA   SCHEMA       bookshop_prod.marts
analysts    SELECT       SCHEMA       bookshop_prod.marts
data-eng    ALL PRIVILEGES SCHEMA     bookshop_prod.marts
show grants `analysts` on catalog bookshop_prod;
principal  action_type  object_type  object_key
---------  -----------  -----------  -------------
analysts   USE CATALOG  CATALOG      bookshop_prod

Ownership is separate from privileges: the owner can always grant, revoke and drop. Set owners to groups rather than individuals, or objects become unmanageable when someone leaves:

alter schema bookshop_prod.marts owner to `data-eng`;
OK

Masking columns and filtering rows

Unity Catalog applies these as functions attached to a table, so they follow the column everywhere it is referenced:

create or replace function bookshop_prod.marts.mask_email(email string)
returns string
return case when is_account_group_member('support') then email
            else regexp_replace(email, '.+@', '*****@') end;

alter table bookshop_prod.marts.customers
    alter column email set mask bookshop_prod.marts.mask_email;
OK
select customer_id, email from bookshop_prod.marts.customers limit 2;
customer_id  email
-----------  -----------------
          1  *****@example.com
          2  *****@example.com
create or replace function bookshop_prod.marts.country_filter(country string)
returns boolean
return is_account_group_member('analytics-admin')
    or country = current_user_country();

alter table bookshop_prod.marts.customers
    set row filter bookshop_prod.marts.country_filter on (country_code);
OK

A view built on a masked table stays masked, and so does a downstream job — the rule lives with the column, not with the query.

Lineage, for free

Every read and write through Unity Catalog is recorded. No instrumentation, no manual documentation:

select source_table_full_name, target_table_full_name, entity_type, event_time
from system.access.table_lineage
where target_table_full_name = 'bookshop_prod.marts.customer_orders'
order by event_time desc
limit 3;
source_table_full_name          target_table_full_name               entity_type  event_time
------------------------------  -----------------------------------  -----------  -------------------
bookshop.raw.orders             bookshop_prod.marts.customer_orders  NOTEBOOK     2026-09-09 10:14:02
bookshop.raw.customers          bookshop_prod.marts.customer_orders  NOTEBOOK     2026-09-09 10:14:02

Column-level too:

select source_column_name, target_column_name
from system.access.column_lineage
where target_table_full_name = 'bookshop_prod.marts.customer_orders'
  and target_column_name = 'lifetime_value';
source_column_name  target_column_name
------------------  ------------------
amount              lifetime_value

“Where does lifetime_value come from” is answerable in one query, across notebooks, jobs, dashboards and SQL. On a platform where the same table is touched by several teams and three languages, that is worth more than any documentation effort.

Auditing

select event_time, user_identity.email, action_name,
       request_params.full_name_arg as object
from system.access.audit
where service_name = 'unityCatalog'
  and action_name in ('getTable', 'deleteTable', 'createTable')
  and event_date >= current_date() - interval 1 day
order by event_time desc
limit 5;
event_time           email                  action_name  object
-------------------  ---------------------  -----------  -----------------------------------
2026-09-09 10:41:02  [email protected]      getTable     bookshop_prod.marts.customer_orders
2026-09-09 10:38:55  [email protected]    createTable  bookshop_prod.marts.new_mart
2026-09-09 09:22:41  [email protected]        getTable     bookshop.raw.customers

Practice

1. Grant SELECT on a table without USE SCHEMA and query it.
[TABLE_OR_VIEW_NOT_FOUND] The table or view `bookshop_prod`.`marts`.`customer_orders`
cannot be found.

The grant exists but the traversal right does not, and the error implies the table is missing. Whenever a permission “does not work”, check USE CATALOG and USE SCHEMA before anything else.

2. Grant on a schema, then create a new table in it.
x
-
1

Readable immediately, with no grant on the new table. Schema-level grants are the setting that stops permissions drifting out of step with a pipeline that creates tables.

3. Compare a managed and an external table with DESCRIBE EXTENDED.
Type      MANAGED
Location  s3://bookshop-lake/prod/marts/customer_orders

Type      EXTERNAL
Location  s3://partner-bucket/exports/orders

Dropping the first removes the data; dropping the second removes only the registration. Know which one you have before typing DROP TABLE — the two commands look identical.

4. Trace column lineage for a derived column.
select source_table_full_name, source_column_name
from system.access.column_lineage
where target_table_full_name = 'bookshop_prod.marts.customer_orders'
  and target_column_name = 'lifetime_value';
source_table_full_name  source_column_name
----------------------  ------------------
bookshop.raw.orders     amount

Captured automatically from the query plan, so it cannot drift from reality the way a hand- maintained data dictionary does. It is also the fastest impact analysis before changing an upstream column.

Next: ingestion — Auto Loader, COPY INTO, and handling a schema that changes underneath you.

Frequently Asked Questions

What is the three-level namespace in Unity Catalog?
`catalog.schema.table`, one level deeper than the `schema.table` of the legacy Hive metastore. The extra level is what lets one workspace hold separate dev, staging and production catalogs, or one catalog per business domain, with independent permissions.
What is the difference between a managed and an external table?
Unity Catalog owns a managed table's storage, so dropping the table eventually deletes the data. An external table points at a location you control, and dropping it removes only the metadata. Prefer managed tables unless another system must write the same files.
What is a volume in Unity Catalog?
A governed directory for non-tabular files — CSVs waiting to be loaded, model artefacts, images. It is addressed as `/Volumes/catalog/schema/name/` and secured with the same grants as tables, which is why it replaces raw DBFS mounts.
Do I need to grant permissions on every new table?
No. Granting `SELECT` on a schema covers every table in it, present and future, and inheritance flows catalog to schema to table. Explicit per-table grants are for exceptions, not for routine work.