You can't, at least not in any straightforward way that I am aware of.
Tables exist within the schema namespace, and schemas exist within the database. To give access to a user on a particular table means you must also give that user the USAGE
permission on the schema to which the table belongs. USAGE
does not grant permissions on the tables themselves, only the the schema in question. But table definitions are part of the schema, so USAGE
does allow the user to see table names (and the columns too).
But if there are other tables in the same schema, the user will not be able to SELECT
from those tables unless you also GRANT SELECT
on those tables, even though they will be able to see that they exist.
This answer gives a pretty clear explanation of the permission system.
Edit to add:
One way to achieve a similar outcome would be like this (using psql
):
sec_schema=# REVOKE ALL ON ALL TABLES IN SCHEMA sec_schema FROM restricted_user;
REVOKE
sec_schema=# REVOKE USAGE ON SCHEMA sec_schema FROM restricted_user;
REVOKE
sec_schema=# CREATE SCHEMA new_schema;
CREATE SCHEMA
sec_schema=# GRANT USAGE ON new_schema TO restricted_user;
GRANT
sec_schema=# CREATE VIEW new_schema.secret_view AS SELECT * from sec_schema.secret_table;
CREATE VIEW
sec_schema=# GRANT SELECT ON new_schema.secret_view TO restricted_user;
GRANT
This will remove all access to the schema sec_schema
for user restricted_user
, but then creates new_schema
and new_schema.secret_view
which is a cover view over sec_schema.secret_table
. After the GRANT SELECT
, the user will be able to read the data from table sec_schema.secret_table
through the view, but they will not be able to see any objects in sec_schema
.