pg-order-by-related icon indicating copy to clipboard operation
pg-order-by-related copied to clipboard

Error when ordering by column in related table when using custom query

Open Mattusdk opened this issue 5 years ago • 8 comments
trafficstars

I'm submitting a ...

  • [ ] bug report
  • [ ] feature request
  • [x ] question

PostGraphile version: [email protected] pg-order-by-related version: @graphile-contrib/[email protected]

Minimal SQL file that can be loaded into a clean database:

create table foo (
  id integer primary key,
  foo_name text not null
);

create table bar (
  id integer primary key,
  bar_name text not null,
  foo_id integer references foo (id)
);

insert into foo (id, foo_name) values
  (1, 'a'),
  (2, 'b'),
  (3, 'c'),
  (4, 'd');

insert into bar (id, bar_name, foo_id) values
  (1, 'one', 1),
  (2, 'two', 2),
  (3, 'three', 3),
  (4, 'four', 4);

CREATE INDEX ON bar(foo_id);

CREATE FUNCTION get_bar_custom_query() RETURNS SETOF bar AS $$
SELECT * FROM bar
$$ LANGUAGE sql STABLE;

COMMENT ON FUNCTION get_bar_custom_query() IS E'@sortable';

Query:

query {
  first: getBarCustomQuery(orderBy: FOO_BY_FOO_ID__ID_ASC, first: 2) {
    pageInfo {
      endCursor
      hasNextPage
      hasPreviousPage
      startCursor
    }
    edges {
      node {
        id
        barName
        foo {
          fooName
          id
        }
      }
    }
  }
  second: getBarCustomQuery(orderBy: FOO_BY_FOO_ID__ID_ASC, first: 2, after: "WyJmb29fYnlfZm9vX2lkX19pZF9hc2MiLDJd") {
    pageInfo {
      endCursor
      hasNextPage
      hasPreviousPage
      startCursor
    }
    edges {
      node {
        id
        barName
        foo {
          fooName
          id
        }
      }
    }
  }
}

CLI command:

postgraphile \                                      
  --dynamic-json \
  --no-setof-functions-contain-nulls \
  --no-ignore-rbac \
  --no-ignore-indexes \
  --show-error-stack=json \
  --extended-errors hint,detail,errcode \
  --append-plugins @graphile-contrib/pg-simplify-inflector,@graphile-contrib/pg-order-by-related \
  --enhance-graphiql \
  --allow-explain \
  --enable-query-batching \
  --legacy-relations omit \
  --connection postgres://user:password@localhost:5432/my_db \
  --schema public

Current behavior:

Error: "The order supplied is not unique, so before/after cursors cannot be used. Please ensure the supplied order includes all the columns from the primary key or a unique constraint."

Expected behavior:

Result will be ordered by the column from the related table.

Mattusdk avatar Apr 16 '20 09:04 Mattusdk

Does this only happen on custom queries, or does it affect tables too? It's very hard to guarantee unique ordering with functions, and I'd recommend against custom ordering over large result sets from custom queries/computed columns for performance reasons. @pgQuery via makeExtendSchemaPlugin might be more appropriate for this.

benjie avatar Apr 17 '20 10:04 benjie

It works perfectly when using the base queries provided by PostGraphile, the problem only occurs when we try to do it with this custom query. I guess we will just have to find another solution, I think we have a pretty complex/unique use case for this, because we initially did not use a custom query for this, but switched to try to improve performance.

Mattusdk avatar Apr 17 '20 19:04 Mattusdk

Can you add ID_ASC to the order list? That should make it unique (we do this automatically for table types, but not for functions because ordering in functions is expensive).

benjie avatar Apr 20 '20 09:04 benjie

But if the ordering that you've specified is unique, then this plugin needs to indicate so.

benjie avatar Apr 20 '20 09:04 benjie

I have tested adding ID_ASC to the list and it does seem to have fixed the problem.

It does seem that, at least in the case I am testing, the ordering I specify should be unique, if I'm understanding everything correctly. Below is a simple reproduction of the setup we have:

create table foo (
  id integer primary key,
  foo_name text not null
);

create table bar (
  id integer primary key,
  bar_name text not null,
  foo_id integer unique references foo (id)
);

The only real difference between this example and the one above is that the foreign key foo_id has a unique constraint. If the orderBy clause is set to FOO_BY_FOO_ID__ID_ASC in a similar query to the one above, it should be unique, no?

Mattusdk avatar Apr 20 '20 14:04 Mattusdk

This one with a unique constraint would be unique, yes. Without wouldn't be. This is equivalent to just sorting by FOO_ID_ASC directly?

benjie avatar Apr 20 '20 16:04 benjie

Yes, exactly, and that is the strange thing when querying a custom query, the pagination works correctly when ordering by FOO_ID_ASC but not when ordering by FOO_BY_FOO_ID__ID_ASC, even though they should be exactly the same and should both be unique.

But like we found out before it does work correctly when ordering by both FOO_BY_FOO_ID__ID_ASC and ID_ASC.

Mattusdk avatar Apr 20 '20 16:04 Mattusdk

Basically the plugin needs to declare that the order is unique, like we do in core here:

https://github.com/graphile/graphile-engine/blob/9dca5c8631e6c336b59c499d901c774d41825c60/packages/graphile-build-pg/src/plugins/PgOrderAllColumnsPlugin.js#L37-L39

benjie avatar Apr 29 '20 14:04 benjie