feat(datatables): opt-in role-based permissions

This commit is contained in:
Diego Imbert
2026-08-04 18:59:03 +02:00
parent 61f2d8dc6a
commit 23cd7737b6
22 changed files with 2079 additions and 89 deletions
+16 -5
View File
@@ -1402,16 +1402,18 @@ class Windmill:
},
)
def datatable(self, name: str = "main"):
def datatable(self, name: str = "main", role: Optional[str] = None):
"""Get a DataTable client for SQL queries.
Args:
name: Database name (default: "main")
role: DataTable role to run as, on a datatable with permissions
enabled (default: the "root" role)
Returns:
DataTableClient instance
"""
return DataTableClient(self, name)
return DataTableClient(self, name, role)
def ducklake(self, name: str = "main"):
"""Get a DuckLake client for DuckDB queries.
@@ -2228,16 +2230,18 @@ def username_to_email(username: str) -> str:
@init_global_client
def datatable(name: str = "main") -> DataTableClient:
def datatable(name: str = "main", role: Optional[str] = None) -> DataTableClient:
"""Get a DataTable client for SQL queries.
Args:
name: Database name (default: "main")
role: DataTable role to run as, on a datatable with permissions
enabled (default: the "root" role)
Returns:
DataTableClient instance
"""
return _client.datatable(name)
return _client.datatable(name, role)
@init_global_client
def ducklake(name: str = "main") -> DucklakeClient:
@@ -2315,14 +2319,17 @@ def stream_result(stream) -> None:
class DataTableClient:
"""Client for executing SQL queries against Windmill DataTables."""
def __init__(self, client: Windmill, name: str):
def __init__(self, client: Windmill, name: str, role: Optional[str] = None):
"""Initialize DataTableClient.
Args:
client: Windmill client instance
name: DataTable name
role: DataTable role to run as, on a datatable with permissions
enabled (default: the "root" role)
"""
self.client = client
self.role = role
self.name, self.schema = parse_sql_client_name(name)
def query(self, sql: str, *args) -> SqlQuery:
"""Execute a SQL query against the DataTable.
@@ -2343,6 +2350,10 @@ class DataTableClient:
args_dict[f"arg{i+1}"] = arg
args_def += f"-- ${i+1} arg{i+1} ({infer_sql_type(arg)})\n"
sql = args_def + sql
# First line: annotation parsing stops at the first non-comment line, so
# the role cannot follow the search_path preamble.
if self.role is not None:
sql = f"-- role {self.role}\n" + sql
return SqlQuery(
sql,
lambda sql: self.client.run_inline_script_preview(