crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Composite domains don't resolve

Open tinleym opened this issue 2 years ago • 0 comments

Summary

Composite domains (what I'll call domains declared over composite types) are resolving to null.

When entered into psql, the Executed SQL Query output in PostGraphiQL outputs the expected composite domain values, however the fields remain as their sql field names and not their inflected GraphQL field name counterparts.

These issues go away if the domain is replaced with its underlying type.

Steps to reproduce

This isn't exactly a minimal reproduction, but I think it represents an exhaustive test case:

create type app_public._user_update_line_node_type as enum ('TEXT', 'MENTION');

create domain app_public.user_update_line_node_type as app_public._user_update_line_node_type not null;

create domain app_public.line_node_text as text not null;

create type app_public.base_user_update_content_line_node as (
    line_node_type app_public.user_update_line_node_type,
    line_node_text app_public.line_node_text
);

create domain app_public.user_update_content_line_node as app_public.base_user_update_content_line_node not null;

create domain app_public.user_update_content_line as app_public.user_update_content_line_node[] not null;

create type app_public.base_user_update_content as (
    lines app_public.user_update_content_line[],
    img_url text
);

create domain app_public.user_update_content as app_public.base_user_update_content not null
check ((
    (
        case
            when ((value).lines is null) then 0
            else 1
        end +
        case
            when ((value).img_url is null) then 0
            else 1
        end
    ) > 0
));

create table app_public.posts (
    post_id integer primary key generated by default as identity,
    user_id integer not null,
    content app_public.user_update_content not null,
    thread_content app_public.user_update_content[],
    created_at timestamp with time zone DEFAULT now() not null
);

Expected results

For composite domains to return the same results as their underlying types.

Actual results

Composite domains return null.

Additional context

PostGraphile 4.12.8 Node 16.13.1 MacOS 12.4

Possible Solution

Per @benjie's suggestion, I did a find and replace on affected domains to their underlying composite types. So far, so good!

Here's the gist (in line with the Steps To Reproduce DDL above):

  graphileBuildOptions: {
    pgAugmentIntrospectionResults(results: any) {
      function findAndReplaceType(
        original: { schema: string; field: string },
        replacement: { schema: string; field: string }
      ) {
        // get original typeId
        const originalType = results.type.find(
          (i: any) =>
            i.namespaceName === original.schema && i.name === original.field
        );
        if (originalType === undefined) {
          throw new Error(`originalType not found`);
        }
        // get replacement typeId
        const replacementType = results.type.find(
          (i: any) =>
            i.namespaceName === replacement.schema && i.name === replacement.field
        );
        if (replacementType === undefined) {
          throw new Error(`replacementType not found`);
        }
        // find and replace in types
        results.type.forEach((i: any, idx: number, arr: any[]) => {
          if (i.arrayItemTypeId === originalType.id) {
            arr[idx].arrayItemTypeId = replacementType.id;
          }
        });
        // find and replace in attributes
        results.attribute.forEach((i: any, idx: number, arr: any[]) => {
          if (i.typeId === originalType.id) {
            arr[idx].typeId = replacementType.id;
          }
        });
      }
    
      findAndReplaceType(
        { schema: "app_public", field: "user_update_content" },
        { schema: "app_public", field: "base_user_update_content" }
      );
    
      findAndReplaceType(
        { schema: "app_public", field: "user_update_content_line" },
        { schema: "app_public", field: "_user_update_content_line_node" }
      );
    
      findAndReplaceType(
        { schema: "app_public", field: "user_update_content_line_node" },
        { schema: "app_public", field: "base_user_update_content_line_node" }
      );

      return results;
    },
  },

Etc

Composite domains are generally nice because they allow a new class of constraints (like a composite type of two fields that doesn't permit two nulls).

In addition, they also offer a way around one of PostGraphile's trouble spots: multidimensional arrays.

Referencing the example in Steps To Reproduce DDL, app_public.user_update_content_line[] inflects to [[UserUpdateContentLineNode]] because another array (app_public.user_update_content_line_node[]) underlies app_public.user_update_content_line.

tinleym avatar Aug 05 '22 20:08 tinleym