For AI agents: the complete documentation index is at llms.txt. Every page is also available as markdown by appending .md to its URL, or by sending an Accept: text/markdown request header.

Granting and revoking permissions

Permissions are granted with GRANT and removed with REVOKE. This page covers the level a grant applies at, the semantics that are easy to get wrong, and the behaviour that surprises people the first time they hit it.

For the list of permissions themselves, see the permissions reference.

The examples use the trades(timestamp, symbol, side, price, amount) table from the Create your first database guide.

Permission levels​

Permissions have different granularities determining where they can be applied:

GranularityCan be granted at
DatabaseDatabase only
TableDatabase or specific tables
ColumnDatabase, tables, or specific columns

Examples:

-- Database-level: applies to all tables
GRANT SELECT ON ALL TABLES TO user;

-- Table-level: applies to a specific table
GRANT SELECT ON trades TO user;

-- Column-level: applies to specific columns
GRANT SELECT ON trades(timestamp, symbol, price) TO user;

Column-level access​

Restrict users to see only certain columns:

-- User can only see timestamp and price, not symbol, side, or amount
GRANT SELECT ON trades(timestamp, price) TO analyst;

To grant on every column except a few, use the * wildcard with an EXCLUDE list:

-- User can see all columns except side
GRANT SELECT ON trades(* EXCLUDE (side)) TO analyst;

The wildcard covers the columns that exist when the statement runs, not columns added later. See GRANT for details.

Row-level access with views​

For row-level security, create a view that filters rows, then grant access to the view instead of the underlying table:

-- Create a view that only shows ETH-USDT trades
CREATE VIEW eth_usdt_trades AS (
SELECT * FROM trades WHERE symbol = 'ETH-USDT'
);

-- Grant access to the view, not the base table
GRANT SELECT ON eth_usdt_trades TO eth_analyst;
-- No GRANT on trades table = user cannot see other symbols

The user eth_analyst can only see ETH-USDT trades. They have no access to the underlying trades table.

Creating a view needs the CREATE VIEW permission; dropping or redefining one needs DROP VIEW or ALTER VIEW on that view.

Granting ALL​

ALL expands to every permission valid at the level you grant it, and at the database level that set includes DATABASE ADMIN:

-- Makes database_admin a full database administrator
GRANT ALL TO database_admin;

-- Every permission on one table only
GRANT ALL ON trades TO trade_manager;
warning

GRANT ALL TO <user> with no table or column list is an administrator grant, not a broad data grant. Scope it to tables, or name the permissions you mean.

The GRANT option​

When granting permissions, you can allow the recipient to grant that permission to others:

GRANT SELECT ON trades TO team_lead WITH GRANT OPTION;

-- team_lead can now grant SELECT on trades to others

Owner permissions​

When a user creates a table, they automatically receive all permissions on it with the GRANT option. This ownership does not persist - if revoked, they cannot get it back without someone re-granting it.

Permission re-adjustment​

Database-level permissions include access to future tables. If you revoke access to one table, QuestDB automatically converts the database-level grant to individual table-level grants:

GRANT SELECT ON ALL TABLES TO user;  -- Database level
REVOKE SELECT ON secret_table FROM user;

-- Result: user now has table-level SELECT on all tables EXCEPT secret_table
-- Future tables will NOT be accessible

The same applies from table to column level:

GRANT SELECT ON trades TO user;           -- Table level
REVOKE SELECT ON trades(side) FROM user; -- Revoke one column

-- Result: user has column-level SELECT on all columns EXCEPT side
-- Future columns will NOT be accessible
note

Dropping a table removes every permission granted on it, from every user and group. Recreating a table with the same name does not bring them back, so a drop-and-recreate has to re-grant them. The same applies to views, materialized views and live views.

Implicit timestamp permissions​

If a user has SELECT or UPDATE on any column of a table, they automatically get the same permission on the designated timestamp column. This ensures time-series operations (SAMPLE BY, LATEST ON, etc.) work correctly.

Granting on non-existent objects​

You can grant permissions on tables/columns that don't exist yet:

GRANT INSERT ON future_table TO app;
-- Permission activates when future_table is created

Use WITH VERIFICATION to catch typos:

GRANT SELECT ON trdaes TO user WITH VERIFICATION;
-- Fails immediately because 'trdaes' doesn't exist

See also​