Hello @community_user_ !
Yes, this is absolutely possible in Databricks, but it requires a workaround.
In Unity Catalog, permissions are secure by default. If you do not explicitly grant a user permissions on an object, it remains completely invisible to them. Therefore, keeping Tables B and C hidden is easy: simply do not grant the user any permissions on them, and they won't even see they exist.
However, Databricks does not currently have a native, table-level "metadata-only" privilege (their native BROWSE privilege can only be applied to the entire catalog, which would inadvertently expose Tables B and C's metadata).
To give the user metadata-only access to just Table A, you can use one of the two workarounds mentioned below. Here is a breakdown of how to implement them and their pros/cons:
Approach 1: SELECT Privilege + Restrictive Row Filter
You grant the user SELECT access so they can read the schema, but apply a row filter that always evaluates to FALSE for them so they can never see the data.
-- 1. Grant basic navigation access
GRANT USE CATALOG ON CATALOG <catalog> TO `<user_email>`;
GRANT USE SCHEMA ON SCHEMA <catalog>.<schema> TO `<user_email>`;
-- 2. Grant SELECT so the user can run DESCRIBE on Table A
GRANT SELECT ON TABLE <catalog>.<schema>.<table_A> TO `<user_email>`;
-- 3. Create a row filter function that blocks data for this specific user
CREATE OR REPLACE FUNCTION <catalog>.<schema>.no_data_filter()
RETURN CASE
WHEN current_user() = '<user_email>' THEN FALSE
ELSE TRUE
END;
-- 4. Apply the row filter to Table A
ALTER TABLE <catalog>.<schema>.<table_A> SET ROW FILTER <catalog>.<schema>.no_data_filter ON ();
- User Experience: The user can run DESCRIBE to see column names and types. If they run SELECT * FROM Table_A, the query executes successfully but returns 0 rows.
- Downside: While effective, the row filter approach has major downsides: queries still scan data and consume full compute and I/O costs just to return zero rows. It also fails to truly isolate metadata, as commands like DESCRIBE EXTENDED, DESCRIBE HISTORY, and queries against system.information_schema still expose cloud storage paths, table size, partitioning, and delta history.
Approach 2: Create a Metadata-Only View
Instead of filtering the base table, leave Table A completely hidden from the user. Instead, create an empty view of Table A and grant them access to that view.
-- 1. Create a view with zero rows
CREATE VIEW <catalog>.<schema>.<table_A_metadata> AS
SELECT * FROM <catalog>.<schema>.<table_A> WHERE 1=0;
-- 2. Grant access only to the view
GRANT USE CATALOG ON CATALOG <catalog> TO `<user_email>`;
GRANT USE SCHEMA ON SCHEMA <catalog>.<schema> TO `<user_email>`;
GRANT SELECT ON VIEW <catalog>.<schema>.<table_A_metadata> TO `<user_email>`;
- User Experience: The user only sees table_A_metadata in their catalog. They can see the schema, and querying it instantly returns 0 rows without scanning the underlying data. Base Table A, Table B, and Table C remain completely invisible to them.
- Downside: If you change the schema of Table A (e.g., adding or dropping columns), you will need to recreate the view to reflect those changes.
Hope this helps! Please feel free to reach out again if you need any more help with this setup.