Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No Nulls Array: [String!]! #1677

Closed
ssendev opened this issue Oct 31, 2022 · 3 comments
Closed

No Nulls Array: [String!]! #1677

ssendev opened this issue Oct 31, 2022 · 3 comments

Comments

@ssendev
Copy link

ssendev commented Oct 31, 2022

Summary

https://www.graphile.org/postgraphile/why-nullable/ doesn't mention arrays which can't contain nulls.

I tried:

array text[] default '{}' not null check (array_position(array, null) is null)

but that doesn't change anything.

With this plugin i was able to change the GraphQL type

export function NonNullArraysPlugin(builder: SchemaBuilder) {
  builder.hook('GraphQLObjectType:fields:field', (field, build, context) => {
    const {
      scope: { pgFieldIntrospection, isPgRowType },
    } = context
    const {
      graphql: { GraphQLNonNull, GraphQLList, isNonNullType, isListType },
    } = build

    if (
      isPgRowType &&
      pgFieldIntrospection.type.isPgArray &&
      pgFieldIntrospection.tags.noNullsArray
    ) {
      if (isNonNullType(field.type)) {
        const list = field.type.ofType
        if (isListType(list)) {
          const inner = list.ofType
          if (!isNonNullType(inner)) {

            return {
              ...field,
              type: GraphQLNonNull(GraphQLList(GraphQLNonNull(inner))),
            }
          }
        }
      }
    }
    return field
  })
}

but the no nulls constraint doesn't show up in pgFieldIntrospection or pgIntrospection is there a way to automatically detect it?
If yes i think it would be possible to always add the inner non null since if row level constraints are present the whole row is null.

Then there is the sql codegen. Currently selecting an array generates this query

case when (__local_1__."array") is null then null when coalesce(
  array_length(
    (__local_1__."array"),
    1
  ),
  0
) = 0 then '[]'::json else (
  select json_agg(__local_2__)
  from unnest((__local_1__."array")) as __local_2__
) end

which is i guess the paranoid version to guarantee an empty array even when row level security denied access. Is it possible to tell postgraphile to assume it's always allowed and thus shorten the query to

to_json(__local_1__."array")

inside a json_build_object even the to_json could be omitted

Additional context

postgraphile: 4.12.11
PostgreSQL: 14.5

@benjie
Copy link
Member

benjie commented Oct 4, 2023

I believe you can solve this with domains, like:

create domain non_null_text as text not null;

create table foo (
  array non_null_text[] not null default ARRAY[]::non_null_text[]
);

Alternatively you can use makeChangeNullabilityPlugin in V5, which allows you to specify the nullability of any field or argument including the nullability of of list items.

@benjie benjie closed this as completed Oct 4, 2023
@supersnager
Copy link

@benjie Your example does not work.

The following sql:

create domain app.non_null_text as text not null;

create table app.foo (
  array_field app.non_null_text[] not null default ARRAY[]::app.non_null_text[]
);

generates a type where element of an array is nullable

type Foo {
  arrayField: [NonNullText]!
}

Is it possible to do something with this in v4?

The expected result is:

type Foo {
  arrayField: [NonNullText!]!
}

@benjie
Copy link
Member

benjie commented Mar 17, 2024

Using a domain gives you a way to automatically detect it, you then need to hook it with a plugin. I think the domain trick probably already works without an extra plugin in v5 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

3 participants