Skip to content

Document that remapping NULL/null is not supported with domain representations #4612

@austin-beer

Description

@austin-beer

Problem

I've defined the following domain representation for a non-nullable TIMESTAMPTZ column that reports -infinity as null in the JSON representation and when filtering.

CREATE DOMAIN common."TimeValidFromType" AS TIMESTAMPTZ;

CREATE FUNCTION common."TimeValidFromTypeToJson"(ts common."TimeValidFromType")
RETURNS JSON
AS $$
  SELECT to_json(CASE WHEN NOT isfinite(ts)
                      THEN NULL
                      ELSE ts
                 END)
$$
LANGUAGE SQL IMMUTABLE;

CREATE CAST (common."TimeValidFromType" AS JSON) WITH FUNCTION common."TimeValidFromTypeToJson"(common."TimeValidFromType") AS IMPLICIT;

CREATE FUNCTION common."JsonToTimeValidFromType"(js JSON)
RETURNS common."TimeValidFromType"
AS $$
  SELECT (CASE WHEN js IS NULL
               THEN '-infinity'
               ELSE js::TEXT
          END)::common."TimeValidFromType"
$$
LANGUAGE SQL IMMUTABLE;

CREATE CAST (JSON AS common."TimeValidFromType") WITH FUNCTION common."JsonToTimeValidFromType"(JSON) AS IMPLICIT;

CREATE FUNCTION common."TextToTimeValidFromType"(tx TEXT)
RETURNS common."TimeValidFromType"
AS $$
  -- Convert an empty string as well since that's what the Supabase.Postgrest client
  -- sends when it creates a primary key filter from a null value.
  -- Convert the string 'null' as well since "is.null" will not work as a PostgREST
  -- filter and so we have to use "eq.null" instead.
  SELECT (CASE WHEN tx IS NULL OR tx = '' OR tx = 'null'
               THEN '-infinity'
               ELSE tx
          END)::common."TimeValidFromType"
$$
LANGUAGE SQL IMMUTABLE;

CREATE CAST (TEXT AS common."TimeValidFromType") WITH FUNCTION common."TextToTimeValidFromType"(TEXT) AS IMPLICIT;

The issue is that clients are not able to use the is.null/not.is.null filters on this column because internally the column is never null. But the domain representation reports the field as null, and so for API consistency it would be great if is.null/not.is.null worked as expected from the client's perspective.

The workaround that I've implemented above is to convert the "null" string in filter requests so that eq.null/not.eq.null works for this field. That's obviously not ideal since it means a different syntax for this one column.

Solution

Whenever is.null/not.is.null is applied to a field that contains a TEXT domain representation conversion function, pass null into the function and use the return value of the function in the SQL IS check.

Versions

PostgreSQL: 18.0
PostgREST: v14.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsOnly related to documentation

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions