Pure-reference catalog of row-level security for tenant isolation, Postgres-first. Covers enabling RLS (ALTER TABLE ... ENABLE ROW LEVEL SECURITY, default-deny semantics), CREATE POLICY syntax (USING vs WITH CHECK clauses, FOR SELECT/INSERT/UPDATE/DELETE/ALL, permissive vs restrictive, TO role_name), bypassing RLS (superuser / BYPASSRLS / table owner / FORCE ROW LEVEL SECURITY), tenant context patterns (current_user, current_setting, JWT claims via Supabase auth.uid() / auth.jwt()), and performance discipline (wrapping auth functions in SELECT, index on policy-referenced columns). Row/tenant isolation on the non-Postgres engines - MySQL / MariaDB invoker views, CockroachDB native RLS, Vitess vindex sharding, SQL Server security policies - lives in references/other-engines.md. Use as the RLS-pattern reference for tenant isolation on any of these engines. Consumed by cross-tenant-data-leak-tests.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
MySQL 8.x and MariaDB have no native row-level security statement.
Isolation combines a view defined SQL SECURITY INVOKER with an
application role that holds no direct grants on the base table.
The CREATE VIEW default is SQL SECURITY DEFINER: the view runs with
the creator's privileges, so any user with SELECT on the view reads
every row the creator can read, including other tenants'. With
SQL SECURITY INVOKER the view runs with the caller's privileges, so the
view's WHERE clause becomes the tenant boundary. The only clauses
material to isolation are SQL SECURITY { DEFINER | INVOKER } and
WITH [CASCADED | LOCAL] CHECK OPTION; the MySQL 8.4 CREATE VIEW
reference has the full grammar.
CREATE VIEW tenant_docs AS
SELECT *
FROM documents
WHERE tenant_id = /* app sets via session var or stored function */ ...
SQL SECURITY INVOKER
WITH CASCADED CHECK OPTION;WITH CHECK OPTION rejects inserts or updates that would create rows
outside the view's WHERE clause.
Views do not block TRUNCATE, constraint checks, or direct table access
when the role holds table-level grants. The application must:
SELECT/INSERT/UPDATE/DELETE
on the base table (only on the view).WHERE clause as the sole row gate and re-validate it
in every schema migration.MariaDB shares this model. Its DEFINER clause also accepts
role | CURRENT_ROLE, which makes a schema-per-tenant view owned by a
per-tenant role viable at low tenant counts. Atomic DDL for CREATE VIEW
landed in MariaDB 10.6.1.
| Risk | Why |
|---|---|
SQL SECURITY DEFINER (default) | View runs as creator; tenant filter is advisory only |
| Direct base-table grants on the app role | App can bypass the view entirely |
TRUNCATE | Never filtered by views; requires separate role restriction |
| Schema changes to the view | Migration that widens WHERE clause removes isolation |
Sources: MySQL 8.4 CREATE VIEW
dev.mysql.com/doc/refman/8.4/en/create-view.html;
MariaDB CREATE VIEW
mariadb.com/kb/en/create-view/.