mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
A data table backed by the instance database resolved to exactly one Postgres connection, `custom_instance_user`, for everyone who could reach it at all. There was no way to say this job reads, that one writes, this one never sees the salaries table. A data table role is now a real Postgres login on the cluster, defined once for the instance by a superadmin and named exactly as they named it. A script that declares `-- role analytics` connects as `analytics`, and Postgres decides what it may touch — grants are ordinary SQL. Windmill answers only "may this caller ask for this role", from the tenant lists on the data table entry: `u/alice`, `g/analysts`, `f/finance` or `*`. A data table with no `permissions` block behaves exactly as before. Everything that opens a connection on someone's behalf goes through one chokepoint, `get_datatable_resource_from_db`, which takes the identity explicitly and fails closed when there is none. The role logs in as itself — never `SET ROLE`, which a script could `RESET ROLE` its way out of. A fork's data table entry becomes a pointer at the workspace that governs it rather than a copy of it. The settings clone used to hand a fork a byte-identical entry naming the parent's database, which a fork admin could edit to grant themselves `admin` there; a pointer has nothing local to edit, and its tenants are evaluated as a member of the governing workspace, by email. `permissions` is stripped from the workspace export and ignored on import: tenants name principals of one workspace, and a settings push is not where an access decision should be made. Operations that see the whole database whatever the roles grant stay with the governing workspace's admins: editing the roles, a migration that declares none, and opening a replication stream for a Postgres trigger or capture. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ti5HyeTikPMYyW8YSdiHR
1.9 KiB
1.9 KiB
Python Datatable API (wmill)
Import: import wmill
Get a DataTable client for SQL queries.
Args:
name: Database name (default: "main")
role: Connect as this data table role instead of the data table's default one.
Returns:
DataTableClient instance
def datatable(name: str = 'main', *, role: Optional[str] = None) -> DataTableClient
Client for executing SQL queries against Windmill DataTables.
class DataTableClient: # Initialize DataTableClient. # # Args: # client: Windmill client instance # name: DataTable name # role: Data table role to connect as, or None for the data table's default def init(client: Windmill, name: str, role: Optional[str] = None)
# Execute a SQL query against the DataTable.
#
# Args:
# sql: SQL query string with $1, $2, etc. placeholders
# *args: Positional arguments to bind to query placeholders
#
# Returns:
# SqlQuery instance for fetching results
def query(sql: str, *args) -> SqlQuery
Query result handler for DataTable and DuckLake queries.
class SqlQuery: # Initialize SqlQuery. # # Args: # sql: SQL query string # fetch_fn: Function to execute the query def init(sql: str, fetch_fn)
# Execute query and fetch results.
#
# Args:
# result_collection: Optional result collection mode
#
# Returns:
# Query results
def fetch(result_collection: str | None = None)
# Execute query and fetch first row of results.
#
# Returns:
# First row of query results
def fetch_one()
# Execute query and fetch first row of results. Return result as a scalar value.
#
# Returns:
# First row of query result as a scalar value
def fetch_one_scalar()
# Execute query and don't return any results.
#
def execute()