EddytorDocs & API
07 · Tables A→ZL · Row & column security

Security limitations & caveats

Cardinality bounds, propagation delays, and the operations that shut off under a restrictive policy.

Row & column security fails closed: wherever a corner is cut, the cut hides data rather than leaking it. Know these edges before governing production tables.

RLS column constraints

  • The bound column must be low-cardinality categorical: at most 10,000 distinct values. Beyond the cap, queries on the table are rejected outright.
  • Values are compared as text - numeric columns are matched on their string form.
  • NULL rows are hidden from any caller with a partial row grant. Only callers with unrestricted access to the table keep its NULL rows.

Propagation

  • Policy writes reach the engine within ~60 seconds (policy cache TTL) - this includes an organisation's first policy.
  • The distinct-value allow-set refreshes every ~30 seconds - rows with a brand-new category value stay hidden until then (fail closed, never open).

Operations disabled under restriction

For callers restricted on a table, some operations can't be filtered meaningfully and are refused instead:

OperationBehaviour
Version diffRefused
Domain validation scanRefused
Allowed-values resolutionRefused
Table metadata with zero readable columns403 - no schema, no row counts

Write restrictions

  • Writes must include the RLS column, and its value must be inside your allow-set; NULL is rejected (it would create a row invisible to you).
  • Updates and deletes on a column-restricted table require the table to have primary key columns, and none of them may be hidden from you.
  • Constraint errors are redacted for restricted callers ("The write violates a table constraint") since raw messages can echo hidden values.

Authoring pitfalls

  • A broken policy denies the whole organisation. If any stored policy fails to compile, every table in the org is denied until it's fixed - disable or fix a bad policy immediately.
  • tableFqn binds by exact string match against catalog.schema.table - a typo stores an enabled policy that never enforces. Validation catches malformed names, not wrong ones.
  • An advanced-mode CLS policy with a columnName must actually scope itself to that column (resource.name or context.column) - an unscoped forbid would hide every column and is rejected at write time.
  • Only string attribute values participate in policies - nested objects, arrays, and nulls are dropped from the attribute bag.
  • Builder-to-advanced conversion is one-way; keep builder policies in builder mode as long as they fit the grammar.

Heads up

Test with a second account. The fastest way to verify a policy does what you meant is to assign a test member the target attributes and query the table as them - remember the ~60 s propagation delay before judging results.

On this page