basalt / prisma/src / rlsSearchFunctionSql
Function: rlsSearchFunctionSql()
> rlsSearchFunctionSql(options): string
Defined in: prisma/src/rls-search.ts:235
SQL for a tenant-scoped full-text search function: a SECURITY DEFINER function that searches table inside the tenant currently in the session's setting, with the GIN index intact.
rlsSearchFunctionSql({
name: 'basalt_search_scoped',
table: 'basalt_search',
vectorColumn: 'tsv',
partitionColumn: 'idx',
filterColumn: 'document',
columns: [{ name: 'document', type: 'jsonb' }],
role: 'app', // the role the app connects as
owner: 'app_owner', // must bypass the table's RLS (BYPASSRLS / superuser)
})
Call it with the tenant already set for the transaction — the same set_config tenancyExtension({ rls: true }) issues:
SELECT * FROM basalt_search_scoped('invoice overdue', 'notes', NULL, 20, 0)
Parameters
options
Returns
string
Security
The function takes no tenant parameter. There is nothing for a caller to tamper with: the tenant comes from current_setting(<setting>, true), byte for byte the expression in the policy rlsPolicySql installs. An unset setting reads as NULL, tenant = NULL is never true, and the function returns no rows — it fails closed, exactly like the policy. Keep setting in sync with the policy's.
What the generated SQL does, statement by statement:
- drops and recreates the function (idempotent, like
rlsPolicySql); - pins
search_pathinside the function — aSECURITY DEFINERfunction without one is a privilege-escalation hole; - marks it
STABLEandPARALLEL SAFE(it only reads); - re-applies the tenant predicate itself, so the rows it can return are the rows the policy would have allowed;
- caps the result (
p_limitclamped tomaxRows) so one call can never pull the corpus, and returns the pre-LIMITmatch count astotal; REVOKE ALL … FROM PUBLIC, thenGRANT EXECUTEto the application role only — a new function is executable by PUBLIC by default.
Every identifier is validated and quoted; nothing is interpolated raw.