Skip to content

LQL: current_setting('namespace.key') builtin for arbitrary session context (not just current_user_id()) [NAP Tier 1] #44

@MelbourneDeveloper

Description

@MelbourneDeveloper

LQL today exposes a single fixed session reader: `current_user_id()`, which expands to `current_setting('rls.current_user_id', true)` on Postgres and a row from the `__rls_context` table on SQLite (per Migration/README.md).

NAP needs two distinct session-scoped values: `app.tenant_id` and `app.user_id`. Both are set per-transaction via `SET LOCAL app.tenant_id = '...'` and consumed by RLS policies through helper functions:

  • `app_tenant_id() RETURNS uuid` → `SELECT NULLIF(current_setting('app.tenant_id', true), '')::uuid`
  • `app_user_id() RETURNS uuid` → same shape against `app.user_id`

These two helpers are the only reason NAP needs SQL function bodies at all. Every other function (`is_member`, `is_tenant_writer`, `is_tenant_owner`) is just an EXISTS check that LQL's `exists(pipeline)` already expresses cleanly — once #(LQL function body issue) lands and once those bodies can call a portable session-reader builtin.

NAP request: add a generic LQL builtin that accepts a session-context key:

```yaml
functions:

  • name: app_tenant_id
    returns: uuid
    bodyLql: current_setting('app.tenant_id')::uuid
  • name: app_user_id
    returns: uuid
    bodyLql: current_setting('app.user_id')::uuid
    ```

Postgres transpiles to `current_setting('app.tenant_id', true)::uuid` (the `true` flag ⇒ NULL when missing instead of erroring). SQLite transpiles to a lookup against the `__rls_context` table — the namespace+key pair becomes the row id.

Acceptance:

  • LQL function: `current_setting()` → returns text, callable from `bodyLql:` and from policy `using:` / `withCheck:` predicates.
  • Postgres emission: `current_setting('namespace.key', true)` (always missing-OK; NULL on absence).
  • Cast support: composes with the existing LQL cast operator (`::uuid`, etc.) so callers can express `current_setting('app.tenant_id')::uuid` directly.
  • SQLite emission deferred until SQLite RLS emulation needs it.

NAP Tier 1 alongside the LQL-function-body issue. Together they let NAP delete the last raw SQL from `migrations/schema.yaml`.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions