Skip to content

Policy input

Each statement is prepared, not executed, and the policy receives what DuckDB says it does:

{
"user": "analyst",
"roles": ["analyst"],
"sql": "SELECT ...",
"statement_type": "SELECT",
"database": "sales",
"tables": ["sales.main.orders"],
"targets": [],
"functions": ["range"],
"databases": ["sales"],
"resolved": true
}
Field
user user name, API key name or OIDC user claim
roles roles from the users file, token or identities
sql statement text
statement_type SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, EXPLAIN, PRAGMA…
database catalog for unqualified names
tables base tables read, views expanded
targets objects written, created or dropped
functions table functions used as sources
databases catalogs touched by tables and targets
resolved false when tables or targets could not be fully determined
hidden_remote_scans lake scans behind views or macros
  • tables come from DuckDB’s unoptimized logical plan, so joins, USING, CTEs, subqueries and views cannot hide a read.
  • targets come from a tokenizer, because DuckDB does not expose them. Secrets appear as secret:<name> and schemas as db.schema.*.
  • functions such as read_parquet bypass table grants; allow them explicitly.
  • EXPLAIN ANALYZE <stmt> is authorized as <stmt>, since it executes.
  • External catalogs (Iceberg): for SELECT, tables come from the parser’s syntax tree, respecting CTE scope. Other statements reading these catalogs arrive with resolved: false.

Deny, unless the user is fully trusted. It is false when:

  • the target of a write cannot be determined;
  • the statement uses lexical constructs the tokenizer does not model exactly like DuckDB ($$...$$, nested comments, E'...');
  • the statement type has no explicit handling (CALL, VACUUM, COPY DATABASE…);
  • lake tables are read through a view or macro (hidden_remote_scans > 0).