Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/silly-baths-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@dataplan/pg": patch
"grafast": patch
"postgraphile": patch
---

Fix a bug where `trap()` would prevent connection-capable list steps from
resolving correctly (often replacing them with `null`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
%%{init: {'themeVariables': { 'fontSize': '12px'}}}%%
graph TD
classDef path fill:#eee,stroke:#000,color:#000
classDef plan fill:#fff,stroke-width:1px,color:#000
classDef itemplan fill:#fff,stroke-width:2px,color:#000
classDef unbatchedplan fill:#dff,stroke-width:1px,color:#000
classDef sideeffectplan fill:#fcc,stroke-width:2px,color:#000
classDef bucket fill:#f6f6f6,color:#000,stroke-width:2px,text-align:left

subgraph "Buckets for queries/trap/trapped-empty-pg-select"
Bucket0("Bucket 0 (root)<br /><br />1: Error[27], Error[34]<br />ᐳ: 6, 10, 11, 14, 7, 12, 15<br />2: InhibitIf[8], InhibitIf[16]<br />3: PgSelect[9], PgSelect[17]<br />4: PgSelectRows[35], PgSelectRows[37]<br />5: __Flag[36], __Flag[38]"):::bucket
Bucket1("Bucket 1 (listItem)<br /><br />ROOT __Item{1}ᐸ36ᐳ[39]"):::bucket
Bucket2("Bucket 2 (listItem)<br /><br />ROOT __Item{2}ᐸ38ᐳ[41]"):::bucket
Bucket3("Bucket 3 (nullableBoundary)<br />Deps: 40<br /><br />ROOT PgSelectSingle{1}ᐸmessagesᐳ[40]"):::bucket
Bucket4("Bucket 4 (nullableBoundary)<br />Deps: 42<br /><br />ROOT PgSelectSingle{2}ᐸmessagesᐳ[42]"):::bucket
end
Bucket0 --> Bucket1 & Bucket2
Bucket1 --> Bucket3
Bucket2 --> Bucket4

%% plan dependencies
InhibitIf8[["InhibitIf[8∈0] ➊<br />ᐸ6, if(7)ᐳ<br />More deps:<br />- Constantᐸ[]ᐳ[6]"]]:::plan
Lambda7{{"Lambda[7∈0] ➊<br />ᐸisEmptyᐳ<br />More deps:<br />- Constantᐸ[]ᐳ[6]"}}:::plan
Lambda7 --> InhibitIf8
PgSelect9[["PgSelect[9∈0] ➊<br />ᐸmessagesᐳ"]]:::plan
Object12{{"Object[12∈0] ➊<br />ᐸ{pgSettings,withPgClient}ᐳ"}}:::plan
Object12 & InhibitIf8 --> PgSelect9
Access10{{"Access[10∈0] ➊<br />ᐸ2.pgSettingsᐳ"}}:::plan
Access11{{"Access[11∈0] ➊<br />ᐸ2.withPgClientᐳ"}}:::plan
Access10 & Access11 --> Object12
InhibitIf16[["InhibitIf[16∈0] ➊<br />ᐸ14, if(15)ᐳ<br />More deps:<br />- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"]]:::plan
Lambda15{{"Lambda[15∈0] ➊<br />ᐸisEmptyᐳ<br />More deps:<br />- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"}}:::plan
Lambda15 --> InhibitIf16
PgSelect17[["PgSelect[17∈0] ➊<br />ᐸmessagesᐳ"]]:::plan
Object12 & InhibitIf16 --> PgSelect17
__Value2["__Value[2∈0] ➊<br />ᐸcontextᐳ"]:::plan
__Value2 --> Access10
__Value2 --> Access11
PgSelectRows35[["PgSelectRows[35∈0] ➊"]]:::plan
PgSelect9 --> PgSelectRows35
__Flag36[["__Flag[36∈0] ➊<br />ᐸ35, trapInhibited, onReject: INHIBITᐳ"]]:::plan
PgSelectRows35 -->|"trapError;trapInhibited"| __Flag36
PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan
PgSelect17 --> PgSelectRows37
__Flag38[["__Flag[38∈0] ➊<br />ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan
PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38
Error27[["Error[27∈0] ➊"]]:::unbatchedplan
Error34[["Error[34∈0] ➊"]]:::unbatchedplan
__Item39[/"__Item[39∈1]<br />ᐸ36ᐳ"\]:::itemplan
__Flag36 ==> __Item39
PgSelectSingle40{{"PgSelectSingle[40∈1]<br />ᐸmessagesᐳ"}}:::plan
__Item39 --> PgSelectSingle40
__Item41[/"__Item[41∈2]<br />ᐸ38ᐳ"\]:::itemplan
__Flag38 ==> __Item41
PgSelectSingle42{{"PgSelectSingle[42∈2]<br />ᐸmessagesᐳ"}}:::plan
__Item41 --> PgSelectSingle42
PgClassExpression43{{"PgClassExpression[43∈3]<br />ᐸ__messages__.”id”ᐳ"}}:::plan
PgSelectSingle40 --> PgClassExpression43
PgClassExpression44{{"PgClassExpression[44∈4]<br />ᐸ__messages__.”id”ᐳ"}}:::plan
PgSelectSingle42 --> PgClassExpression44

%% define steps
classDef bucket0 stroke:#696969
class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Error27,Error34,PgSelectRows35,__Flag36,PgSelectRows37,__Flag38 bucket0
classDef bucket1 stroke:#00bfff
class Bucket1,__Item39,PgSelectSingle40 bucket1
classDef bucket2 stroke:#7f007f
class Bucket2,__Item41,PgSelectSingle42 bucket2
classDef bucket3 stroke:#ffa500
class Bucket3,PgClassExpression43 bucket3
classDef bucket4 stroke:#0000ff
class Bucket4,PgClassExpression44 bucket4

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
select
__messages__."id" as "0"
from app_public.messages as __messages__
where
(
__messages__.id = any($1::"uuid"[])
) and (
true /* authorization checks */
)
order by __messages__."id" asc;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
message: 'connection() cannot be used with trap() or inhibit...() at this time.',
locations: [
{
line: 26,
column: 3,
},
],
path: [
'connection',
],
},
{
message: 'connection() cannot be used with trap() or inhibit...() at this time.',
locations: [
{
line: 8,
column: 3,
},
],
path: [
'emptyConnection',
],
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
emptyList: [],
list: [
{
id: "ca70ca70-0000-0000-0000-a11ce000ca70",
},
],
emptyConnection: null,
connection: null,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
%%{init: {'themeVariables': { 'fontSize': '12px'}}}%%
graph TD
classDef path fill:#eee,stroke:#000,color:#000
classDef plan fill:#fff,stroke-width:1px,color:#000
classDef itemplan fill:#fff,stroke-width:2px,color:#000
classDef unbatchedplan fill:#dff,stroke-width:1px,color:#000
classDef sideeffectplan fill:#fcc,stroke-width:2px,color:#000
classDef bucket fill:#f6f6f6,color:#000,stroke-width:2px,text-align:left

subgraph "Buckets for queries/trap/trapped-empty-pg-select"
Bucket0("Bucket 0 (root)<br /><br />1: Error[27], Error[34]<br />ᐳ: 6, 10, 11, 14, 7, 12, 15<br />2: InhibitIf[8], InhibitIf[16]<br />3: PgSelect[9], PgSelect[17]<br />4: PgSelectRows[35], PgSelectRows[37]<br />5: __Flag[36], __Flag[38]"):::bucket
Bucket1("Bucket 1 (listItem)<br /><br />ROOT __Item{1}ᐸ36ᐳ[39]"):::bucket
Bucket2("Bucket 2 (listItem)<br /><br />ROOT __Item{2}ᐸ38ᐳ[41]"):::bucket
Bucket3("Bucket 3 (nullableBoundary)<br />Deps: 40<br /><br />ROOT PgSelectSingle{1}ᐸmessagesᐳ[40]"):::bucket
Bucket4("Bucket 4 (nullableBoundary)<br />Deps: 42<br /><br />ROOT PgSelectSingle{2}ᐸmessagesᐳ[42]"):::bucket
end
Bucket0 --> Bucket1 & Bucket2
Bucket1 --> Bucket3
Bucket2 --> Bucket4

%% plan dependencies
InhibitIf8[["InhibitIf[8∈0] ➊<br />ᐸ6, if(7)ᐳ<br />More deps:<br />- Constantᐸ[]ᐳ[6]"]]:::plan
Lambda7{{"Lambda[7∈0] ➊<br />ᐸisEmptyᐳ<br />More deps:<br />- Constantᐸ[]ᐳ[6]"}}:::plan
Lambda7 --> InhibitIf8
PgSelect9[["PgSelect[9∈0] ➊<br />ᐸmessagesᐳ"]]:::plan
Object12{{"Object[12∈0] ➊<br />ᐸ{pgSettings,withPgClient}ᐳ"}}:::plan
Object12 & InhibitIf8 --> PgSelect9
Access10{{"Access[10∈0] ➊<br />ᐸ2.pgSettingsᐳ"}}:::plan
Access11{{"Access[11∈0] ➊<br />ᐸ2.withPgClientᐳ"}}:::plan
Access10 & Access11 --> Object12
InhibitIf16[["InhibitIf[16∈0] ➊<br />ᐸ14, if(15)ᐳ<br />More deps:<br />- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"]]:::plan
Lambda15{{"Lambda[15∈0] ➊<br />ᐸisEmptyᐳ<br />More deps:<br />- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"}}:::plan
Lambda15 --> InhibitIf16
PgSelect17[["PgSelect[17∈0] ➊<br />ᐸmessagesᐳ"]]:::plan
Object12 & InhibitIf16 --> PgSelect17
__Value2["__Value[2∈0] ➊<br />ᐸcontextᐳ"]:::plan
__Value2 --> Access10
__Value2 --> Access11
PgSelectRows35[["PgSelectRows[35∈0] ➊"]]:::plan
PgSelect9 --> PgSelectRows35
__Flag36[["__Flag[36∈0] ➊<br />ᐸ35, trapInhibited, onReject: INHIBITᐳ"]]:::plan
PgSelectRows35 -->|"trapError;trapInhibited"| __Flag36
PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan
PgSelect17 --> PgSelectRows37
__Flag38[["__Flag[38∈0] ➊<br />ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan
PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38
Error27[["Error[27∈0] ➊"]]:::unbatchedplan
Error34[["Error[34∈0] ➊"]]:::unbatchedplan
__Item39[/"__Item[39∈1]<br />ᐸ36ᐳ"\]:::itemplan
__Flag36 ==> __Item39
PgSelectSingle40{{"PgSelectSingle[40∈1]<br />ᐸmessagesᐳ"}}:::plan
__Item39 --> PgSelectSingle40
__Item41[/"__Item[41∈2]<br />ᐸ38ᐳ"\]:::itemplan
__Flag38 ==> __Item41
PgSelectSingle42{{"PgSelectSingle[42∈2]<br />ᐸmessagesᐳ"}}:::plan
__Item41 --> PgSelectSingle42
PgClassExpression43{{"PgClassExpression[43∈3]<br />ᐸ__messages__.”id”ᐳ"}}:::plan
PgSelectSingle40 --> PgClassExpression43
PgClassExpression44{{"PgClassExpression[44∈4]<br />ᐸ__messages__.”id”ᐳ"}}:::plan
PgSelectSingle42 --> PgClassExpression44

%% define steps
classDef bucket0 stroke:#696969
class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Error27,Error34,PgSelectRows35,__Flag36,PgSelectRows37,__Flag38 bucket0
classDef bucket1 stroke:#00bfff
class Bucket1,__Item39,PgSelectSingle40 bucket1
classDef bucket2 stroke:#7f007f
class Bucket2,__Item41,PgSelectSingle42 bucket2
classDef bucket3 stroke:#ffa500
class Bucket3,PgClassExpression43 bucket3
classDef bucket4 stroke:#0000ff
class Bucket4,PgClassExpression44 bucket4

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
select
__messages__."id" as "0"
from app_public.messages as __messages__
where
(
__messages__.id = any($1::"uuid"[])
) and (
true /* authorization checks */
)
order by __messages__."id" asc;
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## expect(errors).toHaveLength(2)
## expect(data.emptyList).toEqual([])
## expect(data.list).toEqual([{ id: "ca70ca70-0000-0000-0000-a11ce000ca70" }])
#> dontLogErrors: true
{
emptyList: trappedMessages(ids: []) {
id
}
list: trappedMessages(ids: ["ca70ca70-0000-0000-0000-a11ce000ca70"]) {
id
}
emptyConnection: trappedMessagesConnection(ids: [], first: 1) {
edges {
cursor
node {
id
}
}
nodes {
id
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
connection: trappedMessagesConnection(
ids: [
"ca70ca70-0000-0000-0000-a11ce000ca70"
"ca70ca70-0000-0000-0000-b0b00000ca70"
]
first: 1
) {
edges {
cursor
node {
id
}
}
nodes {
id
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
100 changes: 100 additions & 0 deletions grafast/dataplan-pg/src/examples/exampleSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
graphqlHasStreamDefer,
GraphQLStreamDirective,
groupBy,
inhibitOnEmpty,
inhibitOnNull,
lambda,
listen,
Expand All @@ -53,6 +54,8 @@ import {
newObjectTypeBuilder,
object,
Step,
trap,
TRAP_INHIBITED,
} from "grafast";
import type { GraphQLOutputType } from "grafast/graphql";
import {
Expand Down Expand Up @@ -4047,6 +4050,103 @@ export function makeExampleSchema(
),
},

trappedMessages: {
type: new GraphQLList(Message),
args: {
ids: {
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(GraphQLString)),
),
},
},
plan: EXPORTABLE(
(
inhibitOnEmpty,
listOfCodec,
messageResource,
trap,
TRAP_INHIBITED,
TYPES,
) =>
function plan(_$root, { $ids }) {
const $nonEmptyIds = inhibitOnEmpty($ids);
const $messages = messageResource.find();
$messages.where(
(sql) =>
sql`${$messages.alias}.id = any(${$messages.placeholder($nonEmptyIds, listOfCodec(TYPES.uuid))})`,
);
return trap($messages, TRAP_INHIBITED, {
valueForInhibited: "EMPTY_LIST",
});
},
[
inhibitOnEmpty,
listOfCodec,
messageResource,
trap,
TRAP_INHIBITED,
TYPES,
],
),
},

trappedMessagesConnection: {
type: MessagesConnection,
args: {
ids: {
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(GraphQLString)),
),
},
first: {
type: GraphQLInt,
applyPlan: EXPORTABLE(
() =>
function plan(_$root, $connection: MessageConnectionStep, arg) {
$connection.setFirst(arg.getRaw());
},
[],
),
},
},
plan: EXPORTABLE(
(
connection,
inhibitOnEmpty,
listOfCodec,
messageResource,
sql,
trap,
TRAP_INHIBITED,
TYPES,
) =>
function plan(_$root, { $ids }) {
const $nonEmptyIds = inhibitOnEmpty($ids);
const $messages = messageResource.find();
// Prevent deduplication with trappedMessages
$messages.where(sql`true /* connection */`);
$messages.where(
(sql) =>
sql`${$messages}.id = any(${$messages.placeholder($nonEmptyIds, listOfCodec(TYPES.uuid))})`,
);
const $trappedMessages = trap($messages, TRAP_INHIBITED, {
valueForInhibited: "EMPTY_LIST",
});
return connection($trappedMessages);
},
[
connection,
inhibitOnEmpty,
listOfCodec,
messageResource,
sql,
trap,
TRAP_INHIBITED,
TYPES,
],
),
},

uniqueAuthorCount: {
type: GraphQLInt,
args: {
Expand Down
Loading
Loading