From a66b40b36136febba27f4c0945c56a3f9a0dd9e2 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 09:29:28 +0100 Subject: [PATCH 01/14] Reproduce issue with `trap()` around lists/connections --- .../trapped-empty-pg-select.deopt.mermaid | 132 ++++++++++++++++++ .../trap/trapped-empty-pg-select.deopt.sql | 23 +++ .../trap/trapped-empty-pg-select.json5 | 22 +++ .../trap/trapped-empty-pg-select.mermaid | 132 ++++++++++++++++++ .../queries/trap/trapped-empty-pg-select.sql | 23 +++ .../trap/trapped-empty-pg-select.test.graphql | 29 ++++ .../dataplan-pg/src/examples/exampleSchema.ts | 88 ++++++++++++ 7 files changed, 449 insertions(+) create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid new file mode 100644 index 0000000000..f323de511a --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid @@ -0,0 +1,132 @@ +%%{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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 13, 19, 25, 33
5: Connection[26], Connection[34]
6: 40, 43"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 40

ROOT Connectionᐸ25ᐳ[26]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 43

ROOT Connectionᐸ33ᐳ[34]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ13ᐳ[36]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ19ᐳ[38]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 37

ROOT PgSelectSingle{3}ᐸmessagesᐳ[37]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 39

ROOT PgSelectSingle{4}ᐸmessagesᐳ[39]"):::bucket + Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ40ᐳ[48]"):::bucket + Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ43ᐳ[51]"):::bucket + Bucket11("Bucket 11 (nullableBoundary)
Deps: 50

ROOT PgSelectSingle{9}ᐸmessagesᐳ[50]"):::bucket + Bucket12("Bucket 12 (nullableBoundary)
Deps: 53

ROOT PgSelectSingle{10}ᐸmessagesᐳ[53]"):::bucket + end + Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 + Bucket1 --> Bucket9 + Bucket2 --> Bucket10 + Bucket3 --> Bucket5 + Bucket4 --> Bucket6 + Bucket9 --> Bucket11 + Bucket10 --> Bucket12 + + %% plan dependencies + InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan + Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan + Lambda7 --> InhibitIf8 + PgSelect9[["PgSelect[9∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12{{"Object[12∈0] ➊
ᐸ{pgSettings,withPgClient}ᐳ"}}:::plan + Object12 & InhibitIf8 --> PgSelect9 + Access10{{"Access[10∈0] ➊
ᐸ2.pgSettingsᐳ"}}:::plan + Access11{{"Access[11∈0] ➊
ᐸ2.withPgClientᐳ"}}:::plan + Access10 & Access11 --> Object12 + InhibitIf16[["InhibitIf[16∈0] ➊
ᐸ14, if(15)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"]]:::plan + Lambda15{{"Lambda[15∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"}}:::plan + Lambda15 --> InhibitIf16 + PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf16 --> PgSelect17 + InhibitIf22[["InhibitIf[22∈0] ➊
ᐸ20, if(21)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan + Lambda21{{"Lambda[21∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan + Lambda21 --> InhibitIf22 + PgSelect23[["PgSelect[23∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf22 --> PgSelect23 + Connection26[["Connection[26∈0] ➊
ᐸ25ᐳ
Dependents: 4"]]:::plan + ConnectionParams27{{"ConnectionParams[27∈0] ➊"}}:::plan + __Flag25[["__Flag[25∈0] ➊
ᐸ23, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams27 & __Flag25 --> Connection26 + InhibitIf30[["InhibitIf[30∈0] ➊
ᐸ28, if(29)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"]]:::plan + Lambda29{{"Lambda[29∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"}}:::plan + Lambda29 --> InhibitIf30 + PgSelect31[["PgSelect[31∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf30 --> PgSelect31 + Connection34[["Connection[34∈0] ➊
ᐸ33ᐳ
Dependents: 4"]]:::plan + __Flag33[["__Flag[33∈0] ➊
ᐸ31, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams27 & __Flag33 --> Connection34 + __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan + __Value2 --> Access10 + __Value2 --> Access11 + __Flag13[["__Flag[13∈0] ➊
ᐸ9, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelect9 -->|"trapError;trapInhibited"| __Flag13 + __Flag19[["__Flag[19∈0] ➊
ᐸ17, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelect17 -->|"trapError;trapInhibited"| __Flag19 + PgSelect23 -->|"trapError;trapInhibited"| __Flag25 + PgSelect31 -->|"trapError;trapInhibited"| __Flag33 + ConnectionItems40[["ConnectionItems[40∈0] ➊
More deps:
- Connection[26]"]]:::plan + ConnectionItems43[["ConnectionItems[43∈0] ➊
More deps:
- Connection[34]"]]:::plan + PageInfo46{{"PageInfo[46∈1] ➊
More deps:
- Connection[26]"}}:::plan + Access56{{"Access[56∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan + Access58{{"Access[58∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan + PageInfo47{{"PageInfo[47∈2] ➊
More deps:
- Connection[34]"}}:::plan + Access57{{"Access[57∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan + Access59{{"Access[59∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan + __Item36[/"__Item[36∈3]
ᐸ13ᐳ"\]:::itemplan + __Flag13 ==> __Item36 + PgSelectSingle37{{"PgSelectSingle[37∈3]
ᐸmessagesᐳ"}}:::plan + __Item36 --> PgSelectSingle37 + __Item38[/"__Item[38∈4]
ᐸ19ᐳ"\]:::itemplan + __Flag19 ==> __Item38 + PgSelectSingle39{{"PgSelectSingle[39∈4]
ᐸmessagesᐳ"}}:::plan + __Item38 --> PgSelectSingle39 + PgClassExpression54{{"PgClassExpression[54∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle37 --> PgClassExpression54 + PgClassExpression55{{"PgClassExpression[55∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle39 --> PgClassExpression55 + __Item48[/"__Item[48∈9]
ᐸ40ᐳ"\]:::itemplan + ConnectionItems40 ==> __Item48 + Access49{{"Access[49∈9]
ᐸ48.itemᐳ"}}:::plan + __Item48 --> Access49 + PgSelectSingle50{{"PgSelectSingle[50∈9]
ᐸmessagesᐳ"}}:::plan + Access49 --> PgSelectSingle50 + __Item51[/"__Item[51∈10]
ᐸ43ᐳ"\]:::itemplan + ConnectionItems43 ==> __Item51 + Access52{{"Access[52∈10]
ᐸ51.itemᐳ"}}:::plan + __Item51 --> Access52 + PgSelectSingle53{{"PgSelectSingle[53∈10]
ᐸmessagesᐳ"}}:::plan + Access52 --> PgSelectSingle53 + PgClassExpression60{{"PgClassExpression[60∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle50 --> PgClassExpression60 + PgClassExpression61{{"PgClassExpression[61∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle53 --> PgClassExpression61 + + %% define steps + classDef bucket0 stroke:#696969 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,__Flag13,Lambda15,InhibitIf16,PgSelect17,__Flag19,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,ConnectionItems40,ConnectionItems43 bucket0 + classDef bucket1 stroke:#00bfff + class Bucket1,PageInfo46,Access56,Access58 bucket1 + classDef bucket2 stroke:#7f007f + class Bucket2,PageInfo47,Access57,Access59 bucket2 + classDef bucket3 stroke:#ffa500 + class Bucket3,__Item36,PgSelectSingle37 bucket3 + classDef bucket4 stroke:#0000ff + class Bucket4,__Item38,PgSelectSingle39 bucket4 + classDef bucket5 stroke:#7fff00 + class Bucket5,PgClassExpression54 bucket5 + classDef bucket6 stroke:#ff1493 + class Bucket6,PgClassExpression55 bucket6 + classDef bucket9 stroke:#ff0000 + class Bucket9,__Item48,Access49,PgSelectSingle50 bucket9 + classDef bucket10 stroke:#ffff00 + class Bucket10,__Item51,Access52,PgSelectSingle53 bucket10 + classDef bucket11 stroke:#00ffff + class Bucket11,PgClassExpression60 bucket11 + classDef bucket12 stroke:#4169e1 + class Bucket12,PgClassExpression61 bucket12 + diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql new file mode 100644 index 0000000000..a6dc049140 --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql @@ -0,0 +1,23 @@ +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; + +select + __messages__."id" as "0" +from app_public.messages as __messages__ +where + ( + true /* connection */ + ) and ( + __messages__.id = any($1::"uuid"[]) + ) and ( + true /* authorization checks */ + ) +order by __messages__."id" asc; diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 new file mode 100644 index 0000000000..dd8c91bb4d --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 @@ -0,0 +1,22 @@ +{ + emptyList: [], + list: null, + emptyConnection: { + nodes: [], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }, + connection: { + nodes: [ + { + id: "ca70ca70-0000-0000-0000-a11ce000ca70", + }, + ], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }, +} diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid new file mode 100644 index 0000000000..f323de511a --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid @@ -0,0 +1,132 @@ +%%{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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 13, 19, 25, 33
5: Connection[26], Connection[34]
6: 40, 43"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 40

ROOT Connectionᐸ25ᐳ[26]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 43

ROOT Connectionᐸ33ᐳ[34]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ13ᐳ[36]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ19ᐳ[38]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 37

ROOT PgSelectSingle{3}ᐸmessagesᐳ[37]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 39

ROOT PgSelectSingle{4}ᐸmessagesᐳ[39]"):::bucket + Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ40ᐳ[48]"):::bucket + Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ43ᐳ[51]"):::bucket + Bucket11("Bucket 11 (nullableBoundary)
Deps: 50

ROOT PgSelectSingle{9}ᐸmessagesᐳ[50]"):::bucket + Bucket12("Bucket 12 (nullableBoundary)
Deps: 53

ROOT PgSelectSingle{10}ᐸmessagesᐳ[53]"):::bucket + end + Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 + Bucket1 --> Bucket9 + Bucket2 --> Bucket10 + Bucket3 --> Bucket5 + Bucket4 --> Bucket6 + Bucket9 --> Bucket11 + Bucket10 --> Bucket12 + + %% plan dependencies + InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan + Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan + Lambda7 --> InhibitIf8 + PgSelect9[["PgSelect[9∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12{{"Object[12∈0] ➊
ᐸ{pgSettings,withPgClient}ᐳ"}}:::plan + Object12 & InhibitIf8 --> PgSelect9 + Access10{{"Access[10∈0] ➊
ᐸ2.pgSettingsᐳ"}}:::plan + Access11{{"Access[11∈0] ➊
ᐸ2.withPgClientᐳ"}}:::plan + Access10 & Access11 --> Object12 + InhibitIf16[["InhibitIf[16∈0] ➊
ᐸ14, if(15)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"]]:::plan + Lambda15{{"Lambda[15∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[14]"}}:::plan + Lambda15 --> InhibitIf16 + PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf16 --> PgSelect17 + InhibitIf22[["InhibitIf[22∈0] ➊
ᐸ20, if(21)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan + Lambda21{{"Lambda[21∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan + Lambda21 --> InhibitIf22 + PgSelect23[["PgSelect[23∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf22 --> PgSelect23 + Connection26[["Connection[26∈0] ➊
ᐸ25ᐳ
Dependents: 4"]]:::plan + ConnectionParams27{{"ConnectionParams[27∈0] ➊"}}:::plan + __Flag25[["__Flag[25∈0] ➊
ᐸ23, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams27 & __Flag25 --> Connection26 + InhibitIf30[["InhibitIf[30∈0] ➊
ᐸ28, if(29)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"]]:::plan + Lambda29{{"Lambda[29∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"}}:::plan + Lambda29 --> InhibitIf30 + PgSelect31[["PgSelect[31∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf30 --> PgSelect31 + Connection34[["Connection[34∈0] ➊
ᐸ33ᐳ
Dependents: 4"]]:::plan + __Flag33[["__Flag[33∈0] ➊
ᐸ31, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams27 & __Flag33 --> Connection34 + __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan + __Value2 --> Access10 + __Value2 --> Access11 + __Flag13[["__Flag[13∈0] ➊
ᐸ9, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelect9 -->|"trapError;trapInhibited"| __Flag13 + __Flag19[["__Flag[19∈0] ➊
ᐸ17, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelect17 -->|"trapError;trapInhibited"| __Flag19 + PgSelect23 -->|"trapError;trapInhibited"| __Flag25 + PgSelect31 -->|"trapError;trapInhibited"| __Flag33 + ConnectionItems40[["ConnectionItems[40∈0] ➊
More deps:
- Connection[26]"]]:::plan + ConnectionItems43[["ConnectionItems[43∈0] ➊
More deps:
- Connection[34]"]]:::plan + PageInfo46{{"PageInfo[46∈1] ➊
More deps:
- Connection[26]"}}:::plan + Access56{{"Access[56∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan + Access58{{"Access[58∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan + PageInfo47{{"PageInfo[47∈2] ➊
More deps:
- Connection[34]"}}:::plan + Access57{{"Access[57∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan + Access59{{"Access[59∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan + __Item36[/"__Item[36∈3]
ᐸ13ᐳ"\]:::itemplan + __Flag13 ==> __Item36 + PgSelectSingle37{{"PgSelectSingle[37∈3]
ᐸmessagesᐳ"}}:::plan + __Item36 --> PgSelectSingle37 + __Item38[/"__Item[38∈4]
ᐸ19ᐳ"\]:::itemplan + __Flag19 ==> __Item38 + PgSelectSingle39{{"PgSelectSingle[39∈4]
ᐸmessagesᐳ"}}:::plan + __Item38 --> PgSelectSingle39 + PgClassExpression54{{"PgClassExpression[54∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle37 --> PgClassExpression54 + PgClassExpression55{{"PgClassExpression[55∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle39 --> PgClassExpression55 + __Item48[/"__Item[48∈9]
ᐸ40ᐳ"\]:::itemplan + ConnectionItems40 ==> __Item48 + Access49{{"Access[49∈9]
ᐸ48.itemᐳ"}}:::plan + __Item48 --> Access49 + PgSelectSingle50{{"PgSelectSingle[50∈9]
ᐸmessagesᐳ"}}:::plan + Access49 --> PgSelectSingle50 + __Item51[/"__Item[51∈10]
ᐸ43ᐳ"\]:::itemplan + ConnectionItems43 ==> __Item51 + Access52{{"Access[52∈10]
ᐸ51.itemᐳ"}}:::plan + __Item51 --> Access52 + PgSelectSingle53{{"PgSelectSingle[53∈10]
ᐸmessagesᐳ"}}:::plan + Access52 --> PgSelectSingle53 + PgClassExpression60{{"PgClassExpression[60∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle50 --> PgClassExpression60 + PgClassExpression61{{"PgClassExpression[61∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle53 --> PgClassExpression61 + + %% define steps + classDef bucket0 stroke:#696969 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,__Flag13,Lambda15,InhibitIf16,PgSelect17,__Flag19,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,ConnectionItems40,ConnectionItems43 bucket0 + classDef bucket1 stroke:#00bfff + class Bucket1,PageInfo46,Access56,Access58 bucket1 + classDef bucket2 stroke:#7f007f + class Bucket2,PageInfo47,Access57,Access59 bucket2 + classDef bucket3 stroke:#ffa500 + class Bucket3,__Item36,PgSelectSingle37 bucket3 + classDef bucket4 stroke:#0000ff + class Bucket4,__Item38,PgSelectSingle39 bucket4 + classDef bucket5 stroke:#7fff00 + class Bucket5,PgClassExpression54 bucket5 + classDef bucket6 stroke:#ff1493 + class Bucket6,PgClassExpression55 bucket6 + classDef bucket9 stroke:#ff0000 + class Bucket9,__Item48,Access49,PgSelectSingle50 bucket9 + classDef bucket10 stroke:#ffff00 + class Bucket10,__Item51,Access52,PgSelectSingle53 bucket10 + classDef bucket11 stroke:#00ffff + class Bucket11,PgClassExpression60 bucket11 + classDef bucket12 stroke:#4169e1 + class Bucket12,PgClassExpression61 bucket12 + diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql new file mode 100644 index 0000000000..a6dc049140 --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql @@ -0,0 +1,23 @@ +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; + +select + __messages__."id" as "0" +from app_public.messages as __messages__ +where + ( + true /* connection */ + ) and ( + __messages__.id = any($1::"uuid"[]) + ) and ( + true /* authorization checks */ + ) +order by __messages__."id" asc; diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql new file mode 100644 index 0000000000..a2154dd7bd --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql @@ -0,0 +1,29 @@ +## expect(errors).toBeFalsy() +{ + emptyList: trappedMessages(ids: []) { + id + } + list: trappedMessages(ids: ["ca70ca70-0000-0000-0000-a11ce000ca70"]) { + id + } + emptyConnection: trappedMessagesConnection(ids: []) { + nodes { + id + } + pageInfo { + hasNextPage + hasPreviousPage + } + } + connection: trappedMessagesConnection( + ids: ["ca70ca70-0000-0000-0000-a11ce000ca70"] + ) { + nodes { + id + } + pageInfo { + hasNextPage + hasPreviousPage + } + } +} diff --git a/grafast/dataplan-pg/src/examples/exampleSchema.ts b/grafast/dataplan-pg/src/examples/exampleSchema.ts index a7b6622250..024dc62f0b 100644 --- a/grafast/dataplan-pg/src/examples/exampleSchema.ts +++ b/grafast/dataplan-pg/src/examples/exampleSchema.ts @@ -45,6 +45,7 @@ import { graphqlHasStreamDefer, GraphQLStreamDirective, groupBy, + inhibitOnEmpty, inhibitOnNull, lambda, listen, @@ -53,6 +54,8 @@ import { newObjectTypeBuilder, object, Step, + trap, + TRAP_INHIBITED, } from "grafast"; import type { GraphQLOutputType } from "grafast/graphql"; import { @@ -4047,6 +4050,91 @@ 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)), + ), + }, + }, + plan: EXPORTABLE( + ( + connection, + inhibitOnEmpty, + listOfCodec, + messageResource, + 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 as any); + }, + [ + connection, + inhibitOnEmpty, + listOfCodec, + messageResource, + trap, + TRAP_INHIBITED, + TYPES, + ], + ), + }, + uniqueAuthorCount: { type: GraphQLInt, args: { From 20ee5dc610ff162d6de436508d532dcf0f42b99f Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 09:18:57 +0100 Subject: [PATCH 02/14] Fix __FlagStep around list steps: preserve .items() and wrap with flag --- .../trapped-empty-pg-select.deopt.mermaid | 128 +++++++++--------- .../trap/trapped-empty-pg-select.json5 | 6 +- .../trap/trapped-empty-pg-select.mermaid | 128 +++++++++--------- .../dataplan-pg/src/examples/exampleSchema.ts | 2 +- grafast/grafast/src/steps/__flag.ts | 44 +++++- grafast/grafast/src/steps/connection.ts | 2 +- 6 files changed, 179 insertions(+), 131 deletions(-) diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid index f323de511a..7b6409fc11 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid @@ -8,17 +8,17 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 13, 19, 25, 33
5: Connection[26], Connection[34]
6: 40, 43"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 40

ROOT Connectionᐸ25ᐳ[26]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 43

ROOT Connectionᐸ33ᐳ[34]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ13ᐳ[36]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ19ᐳ[38]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 37

ROOT PgSelectSingle{3}ᐸmessagesᐳ[37]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 39

ROOT PgSelectSingle{4}ᐸmessagesᐳ[39]"):::bucket - Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ40ᐳ[48]"):::bucket - Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ43ᐳ[51]"):::bucket - Bucket11("Bucket 11 (nullableBoundary)
Deps: 50

ROOT PgSelectSingle{9}ᐸmessagesᐳ[50]"):::bucket - Bucket12("Bucket 12 (nullableBoundary)
Deps: 53

ROOT PgSelectSingle{10}ᐸmessagesᐳ[53]"):::bucket + Bucket0("Bucket 0 (root)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 25, 33, 36, 38
5: 26, 34, 37, 39
6: 44, 47"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 44

ROOT Connectionᐸ25ᐳ[26]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 47

ROOT Connectionᐸ33ᐳ[34]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ37ᐳ[40]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ39ᐳ[42]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 41

ROOT PgSelectSingle{3}ᐸmessagesᐳ[41]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 43

ROOT PgSelectSingle{4}ᐸmessagesᐳ[43]"):::bucket + Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ44ᐳ[52]"):::bucket + Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ47ᐳ[55]"):::bucket + Bucket11("Bucket 11 (nullableBoundary)
Deps: 54

ROOT PgSelectSingle{9}ᐸmessagesᐳ[54]"):::bucket + Bucket12("Bucket 12 (nullableBoundary)
Deps: 57

ROOT PgSelectSingle{10}ᐸmessagesᐳ[57]"):::bucket end Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 Bucket1 --> Bucket9 @@ -63,70 +63,74 @@ graph TD __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - __Flag13[["__Flag[13∈0] ➊
ᐸ9, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelect9 -->|"trapError;trapInhibited"| __Flag13 - __Flag19[["__Flag[19∈0] ➊
ᐸ17, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelect17 -->|"trapError;trapInhibited"| __Flag19 PgSelect23 -->|"trapError;trapInhibited"| __Flag25 PgSelect31 -->|"trapError;trapInhibited"| __Flag33 - ConnectionItems40[["ConnectionItems[40∈0] ➊
More deps:
- Connection[26]"]]:::plan - ConnectionItems43[["ConnectionItems[43∈0] ➊
More deps:
- Connection[34]"]]:::plan - PageInfo46{{"PageInfo[46∈1] ➊
More deps:
- Connection[26]"}}:::plan - Access56{{"Access[56∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan - Access58{{"Access[58∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan - PageInfo47{{"PageInfo[47∈2] ➊
More deps:
- Connection[34]"}}:::plan - Access57{{"Access[57∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan - Access59{{"Access[59∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan - __Item36[/"__Item[36∈3]
ᐸ13ᐳ"\]:::itemplan - __Flag13 ==> __Item36 - PgSelectSingle37{{"PgSelectSingle[37∈3]
ᐸmessagesᐳ"}}:::plan - __Item36 --> PgSelectSingle37 - __Item38[/"__Item[38∈4]
ᐸ19ᐳ"\]:::itemplan - __Flag19 ==> __Item38 - PgSelectSingle39{{"PgSelectSingle[39∈4]
ᐸmessagesᐳ"}}:::plan - __Item38 --> PgSelectSingle39 - PgClassExpression54{{"PgClassExpression[54∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle37 --> PgClassExpression54 - PgClassExpression55{{"PgClassExpression[55∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle39 --> PgClassExpression55 - __Item48[/"__Item[48∈9]
ᐸ40ᐳ"\]:::itemplan - ConnectionItems40 ==> __Item48 - Access49{{"Access[49∈9]
ᐸ48.itemᐳ"}}:::plan - __Item48 --> Access49 - PgSelectSingle50{{"PgSelectSingle[50∈9]
ᐸmessagesᐳ"}}:::plan - Access49 --> PgSelectSingle50 - __Item51[/"__Item[51∈10]
ᐸ43ᐳ"\]:::itemplan - ConnectionItems43 ==> __Item51 - Access52{{"Access[52∈10]
ᐸ51.itemᐳ"}}:::plan - __Item51 --> Access52 - PgSelectSingle53{{"PgSelectSingle[53∈10]
ᐸmessagesᐳ"}}:::plan - Access52 --> PgSelectSingle53 - PgClassExpression60{{"PgClassExpression[60∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle50 --> PgClassExpression60 - PgClassExpression61{{"PgClassExpression[61∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle53 --> PgClassExpression61 + PgSelectRows36[["PgSelectRows[36∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows36 + __Flag37[["__Flag[37∈0] ➊
ᐸ36, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows36 -->|"trapError;trapInhibited"| __Flag37 + PgSelectRows38[["PgSelectRows[38∈0] ➊"]]:::plan + PgSelect17 --> PgSelectRows38 + __Flag39[["__Flag[39∈0] ➊
ᐸ38, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows38 -->|"trapError;trapInhibited"| __Flag39 + ConnectionItems44[["ConnectionItems[44∈0] ➊
More deps:
- Connection[26]"]]:::plan + ConnectionItems47[["ConnectionItems[47∈0] ➊
More deps:
- Connection[34]"]]:::plan + PageInfo50{{"PageInfo[50∈1] ➊
More deps:
- Connection[26]"}}:::plan + Access60{{"Access[60∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan + Access62{{"Access[62∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan + PageInfo51{{"PageInfo[51∈2] ➊
More deps:
- Connection[34]"}}:::plan + Access61{{"Access[61∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan + Access63{{"Access[63∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan + __Item40[/"__Item[40∈3]
ᐸ37ᐳ"\]:::itemplan + __Flag37 ==> __Item40 + PgSelectSingle41{{"PgSelectSingle[41∈3]
ᐸmessagesᐳ"}}:::plan + __Item40 --> PgSelectSingle41 + __Item42[/"__Item[42∈4]
ᐸ39ᐳ"\]:::itemplan + __Flag39 ==> __Item42 + PgSelectSingle43{{"PgSelectSingle[43∈4]
ᐸmessagesᐳ"}}:::plan + __Item42 --> PgSelectSingle43 + PgClassExpression58{{"PgClassExpression[58∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle41 --> PgClassExpression58 + PgClassExpression59{{"PgClassExpression[59∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle43 --> PgClassExpression59 + __Item52[/"__Item[52∈9]
ᐸ44ᐳ"\]:::itemplan + ConnectionItems44 ==> __Item52 + Access53{{"Access[53∈9]
ᐸ52.itemᐳ"}}:::plan + __Item52 --> Access53 + PgSelectSingle54{{"PgSelectSingle[54∈9]
ᐸmessagesᐳ"}}:::plan + Access53 --> PgSelectSingle54 + __Item55[/"__Item[55∈10]
ᐸ47ᐳ"\]:::itemplan + ConnectionItems47 ==> __Item55 + Access56{{"Access[56∈10]
ᐸ55.itemᐳ"}}:::plan + __Item55 --> Access56 + PgSelectSingle57{{"PgSelectSingle[57∈10]
ᐸmessagesᐳ"}}:::plan + Access56 --> PgSelectSingle57 + PgClassExpression64{{"PgClassExpression[64∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle54 --> PgClassExpression64 + PgClassExpression65{{"PgClassExpression[65∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle57 --> PgClassExpression65 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,__Flag13,Lambda15,InhibitIf16,PgSelect17,__Flag19,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,ConnectionItems40,ConnectionItems43 bucket0 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,PgSelectRows36,__Flag37,PgSelectRows38,__Flag39,ConnectionItems44,ConnectionItems47 bucket0 classDef bucket1 stroke:#00bfff - class Bucket1,PageInfo46,Access56,Access58 bucket1 + class Bucket1,PageInfo50,Access60,Access62 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo47,Access57,Access59 bucket2 + class Bucket2,PageInfo51,Access61,Access63 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item36,PgSelectSingle37 bucket3 + class Bucket3,__Item40,PgSelectSingle41 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item38,PgSelectSingle39 bucket4 + class Bucket4,__Item42,PgSelectSingle43 bucket4 classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression54 bucket5 + class Bucket5,PgClassExpression58 bucket5 classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression55 bucket6 + class Bucket6,PgClassExpression59 bucket6 classDef bucket9 stroke:#ff0000 - class Bucket9,__Item48,Access49,PgSelectSingle50 bucket9 + class Bucket9,__Item52,Access53,PgSelectSingle54 bucket9 classDef bucket10 stroke:#ffff00 - class Bucket10,__Item51,Access52,PgSelectSingle53 bucket10 + class Bucket10,__Item55,Access56,PgSelectSingle57 bucket10 classDef bucket11 stroke:#00ffff - class Bucket11,PgClassExpression60 bucket11 + class Bucket11,PgClassExpression64 bucket11 classDef bucket12 stroke:#4169e1 - class Bucket12,PgClassExpression61 bucket12 + class Bucket12,PgClassExpression65 bucket12 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 index dd8c91bb4d..77e411dd6b 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 @@ -1,6 +1,10 @@ { emptyList: [], - list: null, + list: [ + { + id: "ca70ca70-0000-0000-0000-a11ce000ca70", + }, + ], emptyConnection: { nodes: [], pageInfo: { diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid index f323de511a..7b6409fc11 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid @@ -8,17 +8,17 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 13, 19, 25, 33
5: Connection[26], Connection[34]
6: 40, 43"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 40

ROOT Connectionᐸ25ᐳ[26]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 43

ROOT Connectionᐸ33ᐳ[34]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ13ᐳ[36]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ19ᐳ[38]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 37

ROOT PgSelectSingle{3}ᐸmessagesᐳ[37]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 39

ROOT PgSelectSingle{4}ᐸmessagesᐳ[39]"):::bucket - Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ40ᐳ[48]"):::bucket - Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ43ᐳ[51]"):::bucket - Bucket11("Bucket 11 (nullableBoundary)
Deps: 50

ROOT PgSelectSingle{9}ᐸmessagesᐳ[50]"):::bucket - Bucket12("Bucket 12 (nullableBoundary)
Deps: 53

ROOT PgSelectSingle{10}ᐸmessagesᐳ[53]"):::bucket + Bucket0("Bucket 0 (root)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 25, 33, 36, 38
5: 26, 34, 37, 39
6: 44, 47"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 44

ROOT Connectionᐸ25ᐳ[26]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 47

ROOT Connectionᐸ33ᐳ[34]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ37ᐳ[40]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ39ᐳ[42]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 41

ROOT PgSelectSingle{3}ᐸmessagesᐳ[41]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 43

ROOT PgSelectSingle{4}ᐸmessagesᐳ[43]"):::bucket + Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ44ᐳ[52]"):::bucket + Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ47ᐳ[55]"):::bucket + Bucket11("Bucket 11 (nullableBoundary)
Deps: 54

ROOT PgSelectSingle{9}ᐸmessagesᐳ[54]"):::bucket + Bucket12("Bucket 12 (nullableBoundary)
Deps: 57

ROOT PgSelectSingle{10}ᐸmessagesᐳ[57]"):::bucket end Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 Bucket1 --> Bucket9 @@ -63,70 +63,74 @@ graph TD __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - __Flag13[["__Flag[13∈0] ➊
ᐸ9, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelect9 -->|"trapError;trapInhibited"| __Flag13 - __Flag19[["__Flag[19∈0] ➊
ᐸ17, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelect17 -->|"trapError;trapInhibited"| __Flag19 PgSelect23 -->|"trapError;trapInhibited"| __Flag25 PgSelect31 -->|"trapError;trapInhibited"| __Flag33 - ConnectionItems40[["ConnectionItems[40∈0] ➊
More deps:
- Connection[26]"]]:::plan - ConnectionItems43[["ConnectionItems[43∈0] ➊
More deps:
- Connection[34]"]]:::plan - PageInfo46{{"PageInfo[46∈1] ➊
More deps:
- Connection[26]"}}:::plan - Access56{{"Access[56∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan - Access58{{"Access[58∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan - PageInfo47{{"PageInfo[47∈2] ➊
More deps:
- Connection[34]"}}:::plan - Access57{{"Access[57∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan - Access59{{"Access[59∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan - __Item36[/"__Item[36∈3]
ᐸ13ᐳ"\]:::itemplan - __Flag13 ==> __Item36 - PgSelectSingle37{{"PgSelectSingle[37∈3]
ᐸmessagesᐳ"}}:::plan - __Item36 --> PgSelectSingle37 - __Item38[/"__Item[38∈4]
ᐸ19ᐳ"\]:::itemplan - __Flag19 ==> __Item38 - PgSelectSingle39{{"PgSelectSingle[39∈4]
ᐸmessagesᐳ"}}:::plan - __Item38 --> PgSelectSingle39 - PgClassExpression54{{"PgClassExpression[54∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle37 --> PgClassExpression54 - PgClassExpression55{{"PgClassExpression[55∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle39 --> PgClassExpression55 - __Item48[/"__Item[48∈9]
ᐸ40ᐳ"\]:::itemplan - ConnectionItems40 ==> __Item48 - Access49{{"Access[49∈9]
ᐸ48.itemᐳ"}}:::plan - __Item48 --> Access49 - PgSelectSingle50{{"PgSelectSingle[50∈9]
ᐸmessagesᐳ"}}:::plan - Access49 --> PgSelectSingle50 - __Item51[/"__Item[51∈10]
ᐸ43ᐳ"\]:::itemplan - ConnectionItems43 ==> __Item51 - Access52{{"Access[52∈10]
ᐸ51.itemᐳ"}}:::plan - __Item51 --> Access52 - PgSelectSingle53{{"PgSelectSingle[53∈10]
ᐸmessagesᐳ"}}:::plan - Access52 --> PgSelectSingle53 - PgClassExpression60{{"PgClassExpression[60∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle50 --> PgClassExpression60 - PgClassExpression61{{"PgClassExpression[61∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle53 --> PgClassExpression61 + PgSelectRows36[["PgSelectRows[36∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows36 + __Flag37[["__Flag[37∈0] ➊
ᐸ36, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows36 -->|"trapError;trapInhibited"| __Flag37 + PgSelectRows38[["PgSelectRows[38∈0] ➊"]]:::plan + PgSelect17 --> PgSelectRows38 + __Flag39[["__Flag[39∈0] ➊
ᐸ38, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows38 -->|"trapError;trapInhibited"| __Flag39 + ConnectionItems44[["ConnectionItems[44∈0] ➊
More deps:
- Connection[26]"]]:::plan + ConnectionItems47[["ConnectionItems[47∈0] ➊
More deps:
- Connection[34]"]]:::plan + PageInfo50{{"PageInfo[50∈1] ➊
More deps:
- Connection[26]"}}:::plan + Access60{{"Access[60∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan + Access62{{"Access[62∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan + PageInfo51{{"PageInfo[51∈2] ➊
More deps:
- Connection[34]"}}:::plan + Access61{{"Access[61∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan + Access63{{"Access[63∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan + __Item40[/"__Item[40∈3]
ᐸ37ᐳ"\]:::itemplan + __Flag37 ==> __Item40 + PgSelectSingle41{{"PgSelectSingle[41∈3]
ᐸmessagesᐳ"}}:::plan + __Item40 --> PgSelectSingle41 + __Item42[/"__Item[42∈4]
ᐸ39ᐳ"\]:::itemplan + __Flag39 ==> __Item42 + PgSelectSingle43{{"PgSelectSingle[43∈4]
ᐸmessagesᐳ"}}:::plan + __Item42 --> PgSelectSingle43 + PgClassExpression58{{"PgClassExpression[58∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle41 --> PgClassExpression58 + PgClassExpression59{{"PgClassExpression[59∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle43 --> PgClassExpression59 + __Item52[/"__Item[52∈9]
ᐸ44ᐳ"\]:::itemplan + ConnectionItems44 ==> __Item52 + Access53{{"Access[53∈9]
ᐸ52.itemᐳ"}}:::plan + __Item52 --> Access53 + PgSelectSingle54{{"PgSelectSingle[54∈9]
ᐸmessagesᐳ"}}:::plan + Access53 --> PgSelectSingle54 + __Item55[/"__Item[55∈10]
ᐸ47ᐳ"\]:::itemplan + ConnectionItems47 ==> __Item55 + Access56{{"Access[56∈10]
ᐸ55.itemᐳ"}}:::plan + __Item55 --> Access56 + PgSelectSingle57{{"PgSelectSingle[57∈10]
ᐸmessagesᐳ"}}:::plan + Access56 --> PgSelectSingle57 + PgClassExpression64{{"PgClassExpression[64∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle54 --> PgClassExpression64 + PgClassExpression65{{"PgClassExpression[65∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle57 --> PgClassExpression65 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,__Flag13,Lambda15,InhibitIf16,PgSelect17,__Flag19,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,ConnectionItems40,ConnectionItems43 bucket0 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,PgSelectRows36,__Flag37,PgSelectRows38,__Flag39,ConnectionItems44,ConnectionItems47 bucket0 classDef bucket1 stroke:#00bfff - class Bucket1,PageInfo46,Access56,Access58 bucket1 + class Bucket1,PageInfo50,Access60,Access62 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo47,Access57,Access59 bucket2 + class Bucket2,PageInfo51,Access61,Access63 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item36,PgSelectSingle37 bucket3 + class Bucket3,__Item40,PgSelectSingle41 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item38,PgSelectSingle39 bucket4 + class Bucket4,__Item42,PgSelectSingle43 bucket4 classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression54 bucket5 + class Bucket5,PgClassExpression58 bucket5 classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression55 bucket6 + class Bucket6,PgClassExpression59 bucket6 classDef bucket9 stroke:#ff0000 - class Bucket9,__Item48,Access49,PgSelectSingle50 bucket9 + class Bucket9,__Item52,Access53,PgSelectSingle54 bucket9 classDef bucket10 stroke:#ffff00 - class Bucket10,__Item51,Access52,PgSelectSingle53 bucket10 + class Bucket10,__Item55,Access56,PgSelectSingle57 bucket10 classDef bucket11 stroke:#00ffff - class Bucket11,PgClassExpression60 bucket11 + class Bucket11,PgClassExpression64 bucket11 classDef bucket12 stroke:#4169e1 - class Bucket12,PgClassExpression61 bucket12 + class Bucket12,PgClassExpression65 bucket12 diff --git a/grafast/dataplan-pg/src/examples/exampleSchema.ts b/grafast/dataplan-pg/src/examples/exampleSchema.ts index 024dc62f0b..8d9dba0ac4 100644 --- a/grafast/dataplan-pg/src/examples/exampleSchema.ts +++ b/grafast/dataplan-pg/src/examples/exampleSchema.ts @@ -4121,7 +4121,7 @@ export function makeExampleSchema( const $trappedMessages = trap($messages, TRAP_INHIBITED, { valueForInhibited: "EMPTY_LIST", }); - return connection($trappedMessages as any); + return connection($trappedMessages); }, [ connection, diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index 825648373c..bf97ff8744 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -17,10 +17,13 @@ import type { ExecutionDetails, ExecutionEntryFlags, GrafastResultsList, + Maybe, } from "../interfaces.ts"; import { isListCapableStep, Step } from "../step.ts"; import { sudo } from "../utils.ts"; import type { __ItemStep } from "./__item.ts"; +import type { StepWithItems } from "./connection.ts"; +import { itemsOrStep } from "./connection.ts"; // PUBLIC FLAGS export const TRAP_ERROR = FLAG_ERROR as ExecutionEntryFlags; @@ -91,7 +94,10 @@ function resolveTrapValue(tv: TrapValue): ResolvedTrapValue { } } -export class __FlagStep extends Step> { +export class __FlagStep + extends Step> + implements StepWithItems +{ static $$export = { moduleName: "grafast", exportName: "__FlagStep", @@ -104,16 +110,24 @@ export class __FlagStep extends Step> { private valueForInhibited: ResolvedTrapValue; private valueForError: ResolvedTrapValue; private canBeInlined: boolean; + private baseOptions: Omit; constructor(step: TStep, options: FlagStepOptions) { super(); const { acceptFlags = DEFAULT_ACCEPT_FLAGS, onReject, dataOnly, - if: $cond, valueForInhibited = "PASS_THROUGH", valueForError = "PASS_THROUGH", + if: $cond, } = options; + this.baseOptions = { + acceptFlags, + onReject, + dataOnly, + valueForInhibited, + valueForError, + }; this.forbiddenFlags = ALL_FLAGS & ~acceptFlags; this.onRejectReturnValue = onReject == null ? $$inhibit : flagError(onReject, step.id); @@ -155,12 +169,34 @@ export class __FlagStep extends Step> { [$$deepDepSkip](): Step { return this.getDepOptions(0).step; } + + /** + * Makes `__FlagStep` compatible with `ConnectionStep`; importantly, this + * copies our flagging over to the derived step. + */ + public items(): Step> { + const $dep = this.getDepOptions(0).step; + const $items = itemsOrStep($dep); + if ($dep === $items) { + // If the underlying step didn't use `.items()` then we don't need to + // re-wrap, avoid creating more steps and just return ourself. + return this; + } + const $if = this.ifDep != null ? this.getDepOptions(0).step : undefined; + return new __FlagStep($items, { ...this.baseOptions, if: $if }); + } + listItem?: ($item: __ItemStep) => Step; - // Copied over listItem if the dependent step is a list capable step - _listItem($item: __ItemStep) { + /** + * Copied over `this.listItem` if the dependent step is a list capable step. + * Does **NOT** copy flagging over to the derived step, since `$item` already + * handles that. + */ + private _listItem($item: __ItemStep) { const $dep = this.dependencies[0]; return isListCapableStep($dep) ? $dep.listItem($item) : $item; } + /** Return inlining instructions if we can be inlined. @internal */ inline( options: Omit, diff --git a/grafast/grafast/src/steps/connection.ts b/grafast/grafast/src/steps/connection.ts index 48ec1216c6..8201699033 100644 --- a/grafast/grafast/src/steps/connection.ts +++ b/grafast/grafast/src/steps/connection.ts @@ -1231,7 +1231,7 @@ export function connection< return $connection; } -interface StepWithItems extends Step { +export interface StepWithItems extends Step { items(): Step>>; } export type ItemsStep> = From 56e8708a7467f3902ca68b1b745b688f06bbd800 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 09:53:24 +0100 Subject: [PATCH 03/14] docs(changeset): Fix a bug where `trap()` would prevent connection-capable list steps from resolving correctly (often replacing them with `null`). --- .changeset/silly-baths-design.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/silly-baths-design.md diff --git a/.changeset/silly-baths-design.md b/.changeset/silly-baths-design.md new file mode 100644 index 0000000000..066c227eae --- /dev/null +++ b/.changeset/silly-baths-design.md @@ -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`). From 4261032c2aa72b02e1145777e54b7e8ad88f13a7 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 10:24:33 +0100 Subject: [PATCH 04/14] Unbreak exporting --- grafast/dataplan-pg/src/examples/exampleSchema.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grafast/dataplan-pg/src/examples/exampleSchema.ts b/grafast/dataplan-pg/src/examples/exampleSchema.ts index 8d9dba0ac4..94a7cc6abf 100644 --- a/grafast/dataplan-pg/src/examples/exampleSchema.ts +++ b/grafast/dataplan-pg/src/examples/exampleSchema.ts @@ -4105,6 +4105,7 @@ export function makeExampleSchema( inhibitOnEmpty, listOfCodec, messageResource, + sql, trap, TRAP_INHIBITED, TYPES, @@ -4128,6 +4129,7 @@ export function makeExampleSchema( inhibitOnEmpty, listOfCodec, messageResource, + sql, trap, TRAP_INHIBITED, TYPES, From 4db754cd0689e82e29f73b3303732b4851c3d1ec Mon Sep 17 00:00:00 2001 From: BenjAIe Date: Fri, 7 Aug 2026 11:56:35 +0100 Subject: [PATCH 05/14] test(dataplan-pg): cover trapped connections --- .../trapped-empty-pg-select.deopt.mermaid | 274 ++++++++++++------ .../trap/trapped-empty-pg-select.deopt.sql | 12 + .../trap/trapped-empty-pg-select.json5 | 17 +- .../trap/trapped-empty-pg-select.mermaid | 274 ++++++++++++------ .../queries/trap/trapped-empty-pg-select.sql | 12 + .../trap/trapped-empty-pg-select.test.graphql | 31 +- .../dataplan-pg/src/examples/exampleSchema.ts | 23 +- grafast/grafast/src/steps/__flag.ts | 24 ++ grafast/grafast/src/steps/connection.ts | 18 +- 9 files changed, 492 insertions(+), 193 deletions(-) diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid index 7b6409fc11..43ee3ea364 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid @@ -8,27 +8,52 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 25, 33, 36, 38
5: 26, 34, 37, 39
6: 44, 47"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 44

ROOT Connectionᐸ25ᐳ[26]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 47

ROOT Connectionᐸ33ᐳ[34]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ37ᐳ[40]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ39ᐳ[42]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 41

ROOT PgSelectSingle{3}ᐸmessagesᐳ[41]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 43

ROOT PgSelectSingle{4}ᐸmessagesᐳ[43]"):::bucket - Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ44ᐳ[52]"):::bucket - Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ47ᐳ[55]"):::bucket - Bucket11("Bucket 11 (nullableBoundary)
Deps: 54

ROOT PgSelectSingle{9}ᐸmessagesᐳ[54]"):::bucket - Bucket12("Bucket 12 (nullableBoundary)
Deps: 57

ROOT PgSelectSingle{10}ᐸmessagesᐳ[57]"):::bucket + Bucket0("Bucket 0 (root)

1:
ᐳ: 6, 10, 11, 14, 20, 29, 125, 7, 12, 15, 22, 28, 30, 86, 87
2: 8, 16, 23, 31
3: 9, 17, 24, 32, 57, 64
4: 26, 34, 37, 39, 59, 66
ᐳ: 58, 60, 61, 65, 67, 68
5: 27, 35, 38, 40, 62, 69
6: 45, 48, 63, 70
ᐳ: 85, 88, 89, 91, 94, 95, 97, 99, 100, 102, 104, 105
7: 90, 96, 101, 106"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 27, 86, 87, 45, 63, 90, 101

ROOT Connectionᐸ26ᐳ[27]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 35, 86, 87, 48, 70, 96, 106

ROOT Connectionᐸ34ᐳ[35]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ38ᐳ[41]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ40ᐳ[43]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{3}ᐸmessagesᐳ[42]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 44

ROOT PgSelectSingle{4}ᐸmessagesᐳ[44]"):::bucket + Bucket11("Bucket 11 (listItem)
Deps: 86, 87

ROOT __Item{11}ᐸ45ᐳ[71]
1:
ᐳ: 75, 109, 76, 110
2: Lambda[111]
ᐳ: Edge[72]"):::bucket + Bucket12("Bucket 12 (listItem)
Deps: 86, 87

ROOT __Item{12}ᐸ48ᐳ[73]
1:
ᐳ: 77, 114, 78, 115
2: Lambda[116]
ᐳ: Edge[74]"):::bucket + Bucket13("Bucket 13 (nullableBoundary)
Deps: 72, 76, 111

ROOT Edge{11}[72]"):::bucket + Bucket14("Bucket 14 (nullableBoundary)
Deps: 74, 78, 116

ROOT Edge{12}[74]"):::bucket + Bucket15("Bucket 15 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket + Bucket16("Bucket 16 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket + Bucket17("Bucket 17 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket + Bucket18("Bucket 18 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket end Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 - Bucket1 --> Bucket9 - Bucket2 --> Bucket10 + Bucket1 --> Bucket11 + Bucket2 --> Bucket12 Bucket3 --> Bucket5 Bucket4 --> Bucket6 - Bucket9 --> Bucket11 - Bucket10 --> Bucket12 + Bucket11 --> Bucket13 & Bucket15 + Bucket12 --> Bucket14 & Bucket16 + Bucket13 --> Bucket17 + Bucket14 --> Bucket18 %% plan dependencies + Connection27[["Connection[27∈0] ➊
ᐸ26ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan + ConnectionParams28{{"ConnectionParams[28∈0] ➊
More deps:
- Constantᐸ1ᐳ[125]"}}:::plan + __Flag26[["__Flag[26∈0] ➊
ᐸ24, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams28 & __Flag26 --> Connection27 + Connection35[["Connection[35∈0] ➊
ᐸ34ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan + __Flag34[["__Flag[34∈0] ➊
ᐸ32, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams28 & __Flag34 --> Connection35 + List89{{"List[89∈0] ➊
ᐸ86,87,88ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access88{{"Access[88∈0] ➊
ᐸ85.indexᐳ"}}:::plan + Access88 --> List89 + List95{{"List[95∈0] ➊
ᐸ86,87,94ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access94{{"Access[94∈0] ➊
ᐸ91.indexᐳ"}}:::plan + Access94 --> List95 + List100{{"List[100∈0] ➊
ᐸ86,87,99ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access99{{"Access[99∈0] ➊
ᐸ97.indexᐳ"}}:::plan + Access99 --> List100 + List105{{"List[105∈0] ➊
ᐸ86,87,104ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access104{{"Access[104∈0] ➊
ᐸ102.indexᐳ"}}:::plan + Access104 --> List105 InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan Lambda7 --> InhibitIf8 @@ -43,94 +68,161 @@ graph TD Lambda15 --> InhibitIf16 PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan Object12 & InhibitIf16 --> PgSelect17 - InhibitIf22[["InhibitIf[22∈0] ➊
ᐸ20, if(21)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan - Lambda21{{"Lambda[21∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan - Lambda21 --> InhibitIf22 - PgSelect23[["PgSelect[23∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf22 --> PgSelect23 - Connection26[["Connection[26∈0] ➊
ᐸ25ᐳ
Dependents: 4"]]:::plan - ConnectionParams27{{"ConnectionParams[27∈0] ➊"}}:::plan - __Flag25[["__Flag[25∈0] ➊
ᐸ23, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams27 & __Flag25 --> Connection26 - InhibitIf30[["InhibitIf[30∈0] ➊
ᐸ28, if(29)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"]]:::plan - Lambda29{{"Lambda[29∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"}}:::plan - Lambda29 --> InhibitIf30 - PgSelect31[["PgSelect[31∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf30 --> PgSelect31 - Connection34[["Connection[34∈0] ➊
ᐸ33ᐳ
Dependents: 4"]]:::plan - __Flag33[["__Flag[33∈0] ➊
ᐸ31, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams27 & __Flag33 --> Connection34 + InhibitIf23[["InhibitIf[23∈0] ➊
ᐸ20, if(22)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan + Lambda22{{"Lambda[22∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan + Lambda22 --> InhibitIf23 + PgSelect24[["PgSelect[24∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf23 --> PgSelect24 + InhibitIf31[["InhibitIf[31∈0] ➊
ᐸ29, if(30)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"]]:::plan + Lambda30{{"Lambda[30∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"}}:::plan + Lambda30 --> InhibitIf31 + PgSelect32[["PgSelect[32∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf31 --> PgSelect32 + PgSelect57[["PgSelect[57∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan + Object12 & InhibitIf23 --> PgSelect57 + PgSelect64[["PgSelect[64∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan + Object12 & InhibitIf31 --> PgSelect64 __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - PgSelect23 -->|"trapError;trapInhibited"| __Flag25 - PgSelect31 -->|"trapError;trapInhibited"| __Flag33 - PgSelectRows36[["PgSelectRows[36∈0] ➊"]]:::plan - PgSelect9 --> PgSelectRows36 - __Flag37[["__Flag[37∈0] ➊
ᐸ36, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows36 -->|"trapError;trapInhibited"| __Flag37 - PgSelectRows38[["PgSelectRows[38∈0] ➊"]]:::plan - PgSelect17 --> PgSelectRows38 - __Flag39[["__Flag[39∈0] ➊
ᐸ38, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows38 -->|"trapError;trapInhibited"| __Flag39 - ConnectionItems44[["ConnectionItems[44∈0] ➊
More deps:
- Connection[26]"]]:::plan - ConnectionItems47[["ConnectionItems[47∈0] ➊
More deps:
- Connection[34]"]]:::plan - PageInfo50{{"PageInfo[50∈1] ➊
More deps:
- Connection[26]"}}:::plan - Access60{{"Access[60∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan - Access62{{"Access[62∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan - PageInfo51{{"PageInfo[51∈2] ➊
More deps:
- Connection[34]"}}:::plan - Access61{{"Access[61∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan - Access63{{"Access[63∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan - __Item40[/"__Item[40∈3]
ᐸ37ᐳ"\]:::itemplan - __Flag37 ==> __Item40 - PgSelectSingle41{{"PgSelectSingle[41∈3]
ᐸmessagesᐳ"}}:::plan - __Item40 --> PgSelectSingle41 - __Item42[/"__Item[42∈4]
ᐸ39ᐳ"\]:::itemplan - __Flag39 ==> __Item42 - PgSelectSingle43{{"PgSelectSingle[43∈4]
ᐸmessagesᐳ"}}:::plan - __Item42 --> PgSelectSingle43 - PgClassExpression58{{"PgClassExpression[58∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle41 --> PgClassExpression58 - PgClassExpression59{{"PgClassExpression[59∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle43 --> PgClassExpression59 - __Item52[/"__Item[52∈9]
ᐸ44ᐳ"\]:::itemplan - ConnectionItems44 ==> __Item52 - Access53{{"Access[53∈9]
ᐸ52.itemᐳ"}}:::plan - __Item52 --> Access53 - PgSelectSingle54{{"PgSelectSingle[54∈9]
ᐸmessagesᐳ"}}:::plan - Access53 --> PgSelectSingle54 - __Item55[/"__Item[55∈10]
ᐸ47ᐳ"\]:::itemplan - ConnectionItems47 ==> __Item55 - Access56{{"Access[56∈10]
ᐸ55.itemᐳ"}}:::plan - __Item55 --> Access56 - PgSelectSingle57{{"PgSelectSingle[57∈10]
ᐸmessagesᐳ"}}:::plan - Access56 --> PgSelectSingle57 - PgClassExpression64{{"PgClassExpression[64∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle54 --> PgClassExpression64 - PgClassExpression65{{"PgClassExpression[65∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle57 --> PgClassExpression65 + PgSelect24 -->|"trapError;trapInhibited"| __Flag26 + PgSelect32 -->|"trapError;trapInhibited"| __Flag34 + PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows37 + __Flag38[["__Flag[38∈0] ➊
ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38 + PgSelectRows39[["PgSelectRows[39∈0] ➊"]]:::plan + PgSelect17 --> PgSelectRows39 + __Flag40[["__Flag[40∈0] ➊
ᐸ39, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows39 -->|"trapError;trapInhibited"| __Flag40 + ConnectionItems45[["ConnectionItems[45∈0] ➊
Dependents: 3
More deps:
- Connection[27]"]]:::plan + ConnectionItems48[["ConnectionItems[48∈0] ➊
Dependents: 3
More deps:
- Connection[35]"]]:::plan + First58{{"First[58∈0] ➊"}}:::plan + PgSelectRows59[["PgSelectRows[59∈0] ➊"]]:::plan + PgSelectRows59 --> First58 + PgSelect57 --> PgSelectRows59 + PgSelectSingle60{{"PgSelectSingle[60∈0] ➊
ᐸmessagesᐳ"}}:::plan + First58 --> PgSelectSingle60 + PgClassExpression61{{"PgClassExpression[61∈0] ➊
ᐸcount(*)ᐳ"}}:::plan + PgSelectSingle60 --> PgClassExpression61 + __Flag62[["__Flag[62∈0] ➊
ᐸ61, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgClassExpression61 -->|"trapError;trapInhibited"| __Flag62 + Lambda63[["Lambda[63∈0] ➊"]]:::unbatchedplan + __Flag62 --> Lambda63 + First65{{"First[65∈0] ➊"}}:::plan + PgSelectRows66[["PgSelectRows[66∈0] ➊"]]:::plan + PgSelectRows66 --> First65 + PgSelect64 --> PgSelectRows66 + PgSelectSingle67{{"PgSelectSingle[67∈0] ➊
ᐸmessagesᐳ"}}:::plan + First65 --> PgSelectSingle67 + PgClassExpression68{{"PgClassExpression[68∈0] ➊
ᐸcount(*)ᐳ"}}:::plan + PgSelectSingle67 --> PgClassExpression68 + __Flag69[["__Flag[69∈0] ➊
ᐸ68, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgClassExpression68 -->|"trapError;trapInhibited"| __Flag69 + Lambda70[["Lambda[70∈0] ➊"]]:::unbatchedplan + __Flag69 --> Lambda70 + First85{{"First[85∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan + Access86{{"Access[86∈0] ➊
ᐸ28.__skipOverᐳ
Dependents: 6"}}:::plan + ConnectionParams28 --> Access86 + Access87{{"Access[87∈0] ➊
ᐸ28.offsetᐳ
Dependents: 6"}}:::plan + ConnectionParams28 --> Access87 + First85 --> Access88 + Lambda90[["Lambda[90∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List89 --> Lambda90 + First91{{"First[91∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan + First91 --> Access94 + Lambda96[["Lambda[96∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List95 --> Lambda96 + Last97{{"Last[97∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan + Last97 --> Access99 + Lambda101[["Lambda[101∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List100 --> Lambda101 + Last102{{"Last[102∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan + Last102 --> Access104 + Lambda106[["Lambda[106∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List105 --> Lambda106 + PageInfo55{{"PageInfo[55∈1] ➊
More deps:
- Connection[27]"}}:::plan + Access81{{"Access[81∈1] ➊
ᐸ27.hasNextPageᐳ
More deps:
- Connection[27]"}}:::plan + Access83{{"Access[83∈1] ➊
ᐸ27.hasPreviousPageᐳ
More deps:
- Connection[27]"}}:::plan + PageInfo56{{"PageInfo[56∈2] ➊
More deps:
- Connection[35]"}}:::plan + Access82{{"Access[82∈2] ➊
ᐸ35.hasNextPageᐳ
More deps:
- Connection[35]"}}:::plan + Access84{{"Access[84∈2] ➊
ᐸ35.hasPreviousPageᐳ
More deps:
- Connection[35]"}}:::plan + __Item41[/"__Item[41∈3]
ᐸ38ᐳ"\]:::itemplan + __Flag38 ==> __Item41 + PgSelectSingle42{{"PgSelectSingle[42∈3]
ᐸmessagesᐳ"}}:::plan + __Item41 --> PgSelectSingle42 + __Item43[/"__Item[43∈4]
ᐸ40ᐳ"\]:::itemplan + __Flag40 ==> __Item43 + PgSelectSingle44{{"PgSelectSingle[44∈4]
ᐸmessagesᐳ"}}:::plan + __Item43 --> PgSelectSingle44 + PgClassExpression79{{"PgClassExpression[79∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle42 --> PgClassExpression79 + PgClassExpression80{{"PgClassExpression[80∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle44 --> PgClassExpression80 + List110{{"List[110∈11]
ᐸ86,87,109ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access109{{"Access[109∈11]
ᐸ71.indexᐳ"}}:::plan + Access109 --> List110 + Edge72{{"Edge[72∈11]"}}:::plan + __Item71[/"__Item[71∈11]
ᐸ45ᐳ
More deps:
- ConnectionItems[45]"\]:::itemplan + Lambda111[["Lambda[111∈11]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + __Item71 & Lambda111 --> Edge72 + Access75{{"Access[75∈11]
ᐸ71.itemᐳ"}}:::plan + __Item71 --> Access75 + PgSelectSingle76{{"PgSelectSingle[76∈11]
ᐸmessagesᐳ"}}:::plan + Access75 --> PgSelectSingle76 + __Item71 --> Access109 + List110 --> Lambda111 + List115{{"List[115∈12]
ᐸ86,87,114ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access114{{"Access[114∈12]
ᐸ73.indexᐳ"}}:::plan + Access114 --> List115 + Edge74{{"Edge[74∈12]"}}:::plan + __Item73[/"__Item[73∈12]
ᐸ48ᐳ
More deps:
- ConnectionItems[48]"\]:::itemplan + Lambda116[["Lambda[116∈12]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + __Item73 & Lambda116 --> Edge74 + Access77{{"Access[77∈12]
ᐸ73.itemᐳ"}}:::plan + __Item73 --> Access77 + PgSelectSingle78{{"PgSelectSingle[78∈12]
ᐸmessagesᐳ"}}:::plan + Access77 --> PgSelectSingle78 + __Item73 --> Access114 + List115 --> Lambda116 + PgClassExpression117{{"PgClassExpression[117∈15]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle76 --> PgClassExpression117 + PgClassExpression118{{"PgClassExpression[118∈16]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle78 --> PgClassExpression118 + PgClassExpression123{{"PgClassExpression[123∈17]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgClassExpression117 o--o PgClassExpression123 + PgClassExpression124{{"PgClassExpression[124∈18]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgClassExpression118 o--o PgClassExpression124 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,PgSelectRows36,__Flag37,PgSelectRows38,__Flag39,ConnectionItems44,ConnectionItems47 bucket0 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda22,InhibitIf23,PgSelect24,__Flag26,Connection27,ConnectionParams28,Lambda30,InhibitIf31,PgSelect32,__Flag34,Connection35,PgSelectRows37,__Flag38,PgSelectRows39,__Flag40,ConnectionItems45,ConnectionItems48,PgSelect57,First58,PgSelectRows59,PgSelectSingle60,PgClassExpression61,__Flag62,Lambda63,PgSelect64,First65,PgSelectRows66,PgSelectSingle67,PgClassExpression68,__Flag69,Lambda70,First85,Access86,Access87,Access88,List89,Lambda90,First91,Access94,List95,Lambda96,Last97,Access99,List100,Lambda101,Last102,Access104,List105,Lambda106 bucket0 classDef bucket1 stroke:#00bfff - class Bucket1,PageInfo50,Access60,Access62 bucket1 + class Bucket1,PageInfo55,Access81,Access83 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo51,Access61,Access63 bucket2 + class Bucket2,PageInfo56,Access82,Access84 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item40,PgSelectSingle41 bucket3 + class Bucket3,__Item41,PgSelectSingle42 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item42,PgSelectSingle43 bucket4 + class Bucket4,__Item43,PgSelectSingle44 bucket4 classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression58 bucket5 + class Bucket5,PgClassExpression79 bucket5 classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression59 bucket6 - classDef bucket9 stroke:#ff0000 - class Bucket9,__Item52,Access53,PgSelectSingle54 bucket9 - classDef bucket10 stroke:#ffff00 - class Bucket10,__Item55,Access56,PgSelectSingle57 bucket10 + class Bucket6,PgClassExpression80 bucket6 classDef bucket11 stroke:#00ffff - class Bucket11,PgClassExpression64 bucket11 + class Bucket11,__Item71,Edge72,Access75,PgSelectSingle76,Access109,List110,Lambda111 bucket11 classDef bucket12 stroke:#4169e1 - class Bucket12,PgClassExpression65 bucket12 + class Bucket12,__Item73,Edge74,Access77,PgSelectSingle78,Access114,List115,Lambda116 bucket12 + classDef bucket13 stroke:#3cb371 + class Bucket13 bucket13 + classDef bucket14 stroke:#a52a2a + class Bucket14 bucket14 + classDef bucket15 stroke:#ff00ff + class Bucket15,PgClassExpression117 bucket15 + classDef bucket16 stroke:#f5deb3 + class Bucket16,PgClassExpression118 bucket16 + classDef bucket17 stroke:#696969 + class Bucket17,PgClassExpression123 bucket17 + classDef bucket18 stroke:#00bfff + class Bucket18,PgClassExpression124 bucket18 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql index a6dc049140..ca0de6402d 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql @@ -21,3 +21,15 @@ where true /* authorization checks */ ) order by __messages__."id" asc; + +select + (count(*))::text as "0" +from app_public.messages as __messages__ +where + ( + true /* connection */ + ) and ( + __messages__.id = any($1::"uuid"[]) + ) and ( + true /* authorization checks */ + ); diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 index 77e411dd6b..59121d1658 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 @@ -6,21 +6,36 @@ }, ], emptyConnection: { + edges: [], nodes: [], pageInfo: { hasNextPage: false, hasPreviousPage: false, + startCursor: null, + endCursor: null, }, + totalCount: 0, }, connection: { + edges: [ + { + cursor: "MA==", + node: { + id: "ca70ca70-0000-0000-0000-a11ce000ca70", + }, + }, + ], nodes: [ { id: "ca70ca70-0000-0000-0000-a11ce000ca70", }, ], pageInfo: { - hasNextPage: false, + hasNextPage: true, hasPreviousPage: false, + startCursor: "MA==", + endCursor: "MA==", }, + totalCount: 2, }, } diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid index 7b6409fc11..43ee3ea364 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid @@ -8,27 +8,52 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 27, 28, 7, 12, 15, 21, 29
2: 8, 16, 22, 30
3: 9, 17, 23, 31
4: 25, 33, 36, 38
5: 26, 34, 37, 39
6: 44, 47"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 26, 44

ROOT Connectionᐸ25ᐳ[26]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 34, 47

ROOT Connectionᐸ33ᐳ[34]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ37ᐳ[40]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ39ᐳ[42]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 41

ROOT PgSelectSingle{3}ᐸmessagesᐳ[41]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 43

ROOT PgSelectSingle{4}ᐸmessagesᐳ[43]"):::bucket - Bucket9("Bucket 9 (listItem)

ROOT __Item{9}ᐸ44ᐳ[52]"):::bucket - Bucket10("Bucket 10 (listItem)

ROOT __Item{10}ᐸ47ᐳ[55]"):::bucket - Bucket11("Bucket 11 (nullableBoundary)
Deps: 54

ROOT PgSelectSingle{9}ᐸmessagesᐳ[54]"):::bucket - Bucket12("Bucket 12 (nullableBoundary)
Deps: 57

ROOT PgSelectSingle{10}ᐸmessagesᐳ[57]"):::bucket + Bucket0("Bucket 0 (root)

1:
ᐳ: 6, 10, 11, 14, 20, 29, 125, 7, 12, 15, 22, 28, 30, 86, 87
2: 8, 16, 23, 31
3: 9, 17, 24, 32, 57, 64
4: 26, 34, 37, 39, 59, 66
ᐳ: 58, 60, 61, 65, 67, 68
5: 27, 35, 38, 40, 62, 69
6: 45, 48, 63, 70
ᐳ: 85, 88, 89, 91, 94, 95, 97, 99, 100, 102, 104, 105
7: 90, 96, 101, 106"):::bucket + Bucket1("Bucket 1 (nullableBoundary)
Deps: 27, 86, 87, 45, 63, 90, 101

ROOT Connectionᐸ26ᐳ[27]"):::bucket + Bucket2("Bucket 2 (nullableBoundary)
Deps: 35, 86, 87, 48, 70, 96, 106

ROOT Connectionᐸ34ᐳ[35]"):::bucket + Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ38ᐳ[41]"):::bucket + Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ40ᐳ[43]"):::bucket + Bucket5("Bucket 5 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{3}ᐸmessagesᐳ[42]"):::bucket + Bucket6("Bucket 6 (nullableBoundary)
Deps: 44

ROOT PgSelectSingle{4}ᐸmessagesᐳ[44]"):::bucket + Bucket11("Bucket 11 (listItem)
Deps: 86, 87

ROOT __Item{11}ᐸ45ᐳ[71]
1:
ᐳ: 75, 109, 76, 110
2: Lambda[111]
ᐳ: Edge[72]"):::bucket + Bucket12("Bucket 12 (listItem)
Deps: 86, 87

ROOT __Item{12}ᐸ48ᐳ[73]
1:
ᐳ: 77, 114, 78, 115
2: Lambda[116]
ᐳ: Edge[74]"):::bucket + Bucket13("Bucket 13 (nullableBoundary)
Deps: 72, 76, 111

ROOT Edge{11}[72]"):::bucket + Bucket14("Bucket 14 (nullableBoundary)
Deps: 74, 78, 116

ROOT Edge{12}[74]"):::bucket + Bucket15("Bucket 15 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket + Bucket16("Bucket 16 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket + Bucket17("Bucket 17 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket + Bucket18("Bucket 18 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket end Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 - Bucket1 --> Bucket9 - Bucket2 --> Bucket10 + Bucket1 --> Bucket11 + Bucket2 --> Bucket12 Bucket3 --> Bucket5 Bucket4 --> Bucket6 - Bucket9 --> Bucket11 - Bucket10 --> Bucket12 + Bucket11 --> Bucket13 & Bucket15 + Bucket12 --> Bucket14 & Bucket16 + Bucket13 --> Bucket17 + Bucket14 --> Bucket18 %% plan dependencies + Connection27[["Connection[27∈0] ➊
ᐸ26ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan + ConnectionParams28{{"ConnectionParams[28∈0] ➊
More deps:
- Constantᐸ1ᐳ[125]"}}:::plan + __Flag26[["__Flag[26∈0] ➊
ᐸ24, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams28 & __Flag26 --> Connection27 + Connection35[["Connection[35∈0] ➊
ᐸ34ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan + __Flag34[["__Flag[34∈0] ➊
ᐸ32, trapInhibited, onReject: INHIBITᐳ"]]:::plan + ConnectionParams28 & __Flag34 --> Connection35 + List89{{"List[89∈0] ➊
ᐸ86,87,88ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access88{{"Access[88∈0] ➊
ᐸ85.indexᐳ"}}:::plan + Access88 --> List89 + List95{{"List[95∈0] ➊
ᐸ86,87,94ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access94{{"Access[94∈0] ➊
ᐸ91.indexᐳ"}}:::plan + Access94 --> List95 + List100{{"List[100∈0] ➊
ᐸ86,87,99ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access99{{"Access[99∈0] ➊
ᐸ97.indexᐳ"}}:::plan + Access99 --> List100 + List105{{"List[105∈0] ➊
ᐸ86,87,104ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access104{{"Access[104∈0] ➊
ᐸ102.indexᐳ"}}:::plan + Access104 --> List105 InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan Lambda7 --> InhibitIf8 @@ -43,94 +68,161 @@ graph TD Lambda15 --> InhibitIf16 PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan Object12 & InhibitIf16 --> PgSelect17 - InhibitIf22[["InhibitIf[22∈0] ➊
ᐸ20, if(21)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan - Lambda21{{"Lambda[21∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan - Lambda21 --> InhibitIf22 - PgSelect23[["PgSelect[23∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf22 --> PgSelect23 - Connection26[["Connection[26∈0] ➊
ᐸ25ᐳ
Dependents: 4"]]:::plan - ConnectionParams27{{"ConnectionParams[27∈0] ➊"}}:::plan - __Flag25[["__Flag[25∈0] ➊
ᐸ23, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams27 & __Flag25 --> Connection26 - InhibitIf30[["InhibitIf[30∈0] ➊
ᐸ28, if(29)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"]]:::plan - Lambda29{{"Lambda[29∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70' ]ᐳ[28]"}}:::plan - Lambda29 --> InhibitIf30 - PgSelect31[["PgSelect[31∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf30 --> PgSelect31 - Connection34[["Connection[34∈0] ➊
ᐸ33ᐳ
Dependents: 4"]]:::plan - __Flag33[["__Flag[33∈0] ➊
ᐸ31, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams27 & __Flag33 --> Connection34 + InhibitIf23[["InhibitIf[23∈0] ➊
ᐸ20, if(22)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan + Lambda22{{"Lambda[22∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan + Lambda22 --> InhibitIf23 + PgSelect24[["PgSelect[24∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf23 --> PgSelect24 + InhibitIf31[["InhibitIf[31∈0] ➊
ᐸ29, if(30)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"]]:::plan + Lambda30{{"Lambda[30∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"}}:::plan + Lambda30 --> InhibitIf31 + PgSelect32[["PgSelect[32∈0] ➊
ᐸmessagesᐳ"]]:::plan + Object12 & InhibitIf31 --> PgSelect32 + PgSelect57[["PgSelect[57∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan + Object12 & InhibitIf23 --> PgSelect57 + PgSelect64[["PgSelect[64∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan + Object12 & InhibitIf31 --> PgSelect64 __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - PgSelect23 -->|"trapError;trapInhibited"| __Flag25 - PgSelect31 -->|"trapError;trapInhibited"| __Flag33 - PgSelectRows36[["PgSelectRows[36∈0] ➊"]]:::plan - PgSelect9 --> PgSelectRows36 - __Flag37[["__Flag[37∈0] ➊
ᐸ36, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows36 -->|"trapError;trapInhibited"| __Flag37 - PgSelectRows38[["PgSelectRows[38∈0] ➊"]]:::plan - PgSelect17 --> PgSelectRows38 - __Flag39[["__Flag[39∈0] ➊
ᐸ38, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows38 -->|"trapError;trapInhibited"| __Flag39 - ConnectionItems44[["ConnectionItems[44∈0] ➊
More deps:
- Connection[26]"]]:::plan - ConnectionItems47[["ConnectionItems[47∈0] ➊
More deps:
- Connection[34]"]]:::plan - PageInfo50{{"PageInfo[50∈1] ➊
More deps:
- Connection[26]"}}:::plan - Access60{{"Access[60∈1] ➊
ᐸ26.hasNextPageᐳ
More deps:
- Connection[26]"}}:::plan - Access62{{"Access[62∈1] ➊
ᐸ26.hasPreviousPageᐳ
More deps:
- Connection[26]"}}:::plan - PageInfo51{{"PageInfo[51∈2] ➊
More deps:
- Connection[34]"}}:::plan - Access61{{"Access[61∈2] ➊
ᐸ34.hasNextPageᐳ
More deps:
- Connection[34]"}}:::plan - Access63{{"Access[63∈2] ➊
ᐸ34.hasPreviousPageᐳ
More deps:
- Connection[34]"}}:::plan - __Item40[/"__Item[40∈3]
ᐸ37ᐳ"\]:::itemplan - __Flag37 ==> __Item40 - PgSelectSingle41{{"PgSelectSingle[41∈3]
ᐸmessagesᐳ"}}:::plan - __Item40 --> PgSelectSingle41 - __Item42[/"__Item[42∈4]
ᐸ39ᐳ"\]:::itemplan - __Flag39 ==> __Item42 - PgSelectSingle43{{"PgSelectSingle[43∈4]
ᐸmessagesᐳ"}}:::plan - __Item42 --> PgSelectSingle43 - PgClassExpression58{{"PgClassExpression[58∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle41 --> PgClassExpression58 - PgClassExpression59{{"PgClassExpression[59∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle43 --> PgClassExpression59 - __Item52[/"__Item[52∈9]
ᐸ44ᐳ"\]:::itemplan - ConnectionItems44 ==> __Item52 - Access53{{"Access[53∈9]
ᐸ52.itemᐳ"}}:::plan - __Item52 --> Access53 - PgSelectSingle54{{"PgSelectSingle[54∈9]
ᐸmessagesᐳ"}}:::plan - Access53 --> PgSelectSingle54 - __Item55[/"__Item[55∈10]
ᐸ47ᐳ"\]:::itemplan - ConnectionItems47 ==> __Item55 - Access56{{"Access[56∈10]
ᐸ55.itemᐳ"}}:::plan - __Item55 --> Access56 - PgSelectSingle57{{"PgSelectSingle[57∈10]
ᐸmessagesᐳ"}}:::plan - Access56 --> PgSelectSingle57 - PgClassExpression64{{"PgClassExpression[64∈11]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle54 --> PgClassExpression64 - PgClassExpression65{{"PgClassExpression[65∈12]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle57 --> PgClassExpression65 + PgSelect24 -->|"trapError;trapInhibited"| __Flag26 + PgSelect32 -->|"trapError;trapInhibited"| __Flag34 + PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows37 + __Flag38[["__Flag[38∈0] ➊
ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38 + PgSelectRows39[["PgSelectRows[39∈0] ➊"]]:::plan + PgSelect17 --> PgSelectRows39 + __Flag40[["__Flag[40∈0] ➊
ᐸ39, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows39 -->|"trapError;trapInhibited"| __Flag40 + ConnectionItems45[["ConnectionItems[45∈0] ➊
Dependents: 3
More deps:
- Connection[27]"]]:::plan + ConnectionItems48[["ConnectionItems[48∈0] ➊
Dependents: 3
More deps:
- Connection[35]"]]:::plan + First58{{"First[58∈0] ➊"}}:::plan + PgSelectRows59[["PgSelectRows[59∈0] ➊"]]:::plan + PgSelectRows59 --> First58 + PgSelect57 --> PgSelectRows59 + PgSelectSingle60{{"PgSelectSingle[60∈0] ➊
ᐸmessagesᐳ"}}:::plan + First58 --> PgSelectSingle60 + PgClassExpression61{{"PgClassExpression[61∈0] ➊
ᐸcount(*)ᐳ"}}:::plan + PgSelectSingle60 --> PgClassExpression61 + __Flag62[["__Flag[62∈0] ➊
ᐸ61, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgClassExpression61 -->|"trapError;trapInhibited"| __Flag62 + Lambda63[["Lambda[63∈0] ➊"]]:::unbatchedplan + __Flag62 --> Lambda63 + First65{{"First[65∈0] ➊"}}:::plan + PgSelectRows66[["PgSelectRows[66∈0] ➊"]]:::plan + PgSelectRows66 --> First65 + PgSelect64 --> PgSelectRows66 + PgSelectSingle67{{"PgSelectSingle[67∈0] ➊
ᐸmessagesᐳ"}}:::plan + First65 --> PgSelectSingle67 + PgClassExpression68{{"PgClassExpression[68∈0] ➊
ᐸcount(*)ᐳ"}}:::plan + PgSelectSingle67 --> PgClassExpression68 + __Flag69[["__Flag[69∈0] ➊
ᐸ68, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgClassExpression68 -->|"trapError;trapInhibited"| __Flag69 + Lambda70[["Lambda[70∈0] ➊"]]:::unbatchedplan + __Flag69 --> Lambda70 + First85{{"First[85∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan + Access86{{"Access[86∈0] ➊
ᐸ28.__skipOverᐳ
Dependents: 6"}}:::plan + ConnectionParams28 --> Access86 + Access87{{"Access[87∈0] ➊
ᐸ28.offsetᐳ
Dependents: 6"}}:::plan + ConnectionParams28 --> Access87 + First85 --> Access88 + Lambda90[["Lambda[90∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List89 --> Lambda90 + First91{{"First[91∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan + First91 --> Access94 + Lambda96[["Lambda[96∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List95 --> Lambda96 + Last97{{"Last[97∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan + Last97 --> Access99 + Lambda101[["Lambda[101∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List100 --> Lambda101 + Last102{{"Last[102∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan + Last102 --> Access104 + Lambda106[["Lambda[106∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + List105 --> Lambda106 + PageInfo55{{"PageInfo[55∈1] ➊
More deps:
- Connection[27]"}}:::plan + Access81{{"Access[81∈1] ➊
ᐸ27.hasNextPageᐳ
More deps:
- Connection[27]"}}:::plan + Access83{{"Access[83∈1] ➊
ᐸ27.hasPreviousPageᐳ
More deps:
- Connection[27]"}}:::plan + PageInfo56{{"PageInfo[56∈2] ➊
More deps:
- Connection[35]"}}:::plan + Access82{{"Access[82∈2] ➊
ᐸ35.hasNextPageᐳ
More deps:
- Connection[35]"}}:::plan + Access84{{"Access[84∈2] ➊
ᐸ35.hasPreviousPageᐳ
More deps:
- Connection[35]"}}:::plan + __Item41[/"__Item[41∈3]
ᐸ38ᐳ"\]:::itemplan + __Flag38 ==> __Item41 + PgSelectSingle42{{"PgSelectSingle[42∈3]
ᐸmessagesᐳ"}}:::plan + __Item41 --> PgSelectSingle42 + __Item43[/"__Item[43∈4]
ᐸ40ᐳ"\]:::itemplan + __Flag40 ==> __Item43 + PgSelectSingle44{{"PgSelectSingle[44∈4]
ᐸmessagesᐳ"}}:::plan + __Item43 --> PgSelectSingle44 + PgClassExpression79{{"PgClassExpression[79∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle42 --> PgClassExpression79 + PgClassExpression80{{"PgClassExpression[80∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle44 --> PgClassExpression80 + List110{{"List[110∈11]
ᐸ86,87,109ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access109{{"Access[109∈11]
ᐸ71.indexᐳ"}}:::plan + Access109 --> List110 + Edge72{{"Edge[72∈11]"}}:::plan + __Item71[/"__Item[71∈11]
ᐸ45ᐳ
More deps:
- ConnectionItems[45]"\]:::itemplan + Lambda111[["Lambda[111∈11]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + __Item71 & Lambda111 --> Edge72 + Access75{{"Access[75∈11]
ᐸ71.itemᐳ"}}:::plan + __Item71 --> Access75 + PgSelectSingle76{{"PgSelectSingle[76∈11]
ᐸmessagesᐳ"}}:::plan + Access75 --> PgSelectSingle76 + __Item71 --> Access109 + List110 --> Lambda111 + List115{{"List[115∈12]
ᐸ86,87,114ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan + Access114{{"Access[114∈12]
ᐸ73.indexᐳ"}}:::plan + Access114 --> List115 + Edge74{{"Edge[74∈12]"}}:::plan + __Item73[/"__Item[73∈12]
ᐸ48ᐳ
More deps:
- ConnectionItems[48]"\]:::itemplan + Lambda116[["Lambda[116∈12]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan + __Item73 & Lambda116 --> Edge74 + Access77{{"Access[77∈12]
ᐸ73.itemᐳ"}}:::plan + __Item73 --> Access77 + PgSelectSingle78{{"PgSelectSingle[78∈12]
ᐸmessagesᐳ"}}:::plan + Access77 --> PgSelectSingle78 + __Item73 --> Access114 + List115 --> Lambda116 + PgClassExpression117{{"PgClassExpression[117∈15]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle76 --> PgClassExpression117 + PgClassExpression118{{"PgClassExpression[118∈16]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle78 --> PgClassExpression118 + PgClassExpression123{{"PgClassExpression[123∈17]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgClassExpression117 o--o PgClassExpression123 + PgClassExpression124{{"PgClassExpression[124∈18]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgClassExpression118 o--o PgClassExpression124 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda21,InhibitIf22,PgSelect23,__Flag25,Connection26,ConnectionParams27,Lambda29,InhibitIf30,PgSelect31,__Flag33,Connection34,PgSelectRows36,__Flag37,PgSelectRows38,__Flag39,ConnectionItems44,ConnectionItems47 bucket0 + class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda22,InhibitIf23,PgSelect24,__Flag26,Connection27,ConnectionParams28,Lambda30,InhibitIf31,PgSelect32,__Flag34,Connection35,PgSelectRows37,__Flag38,PgSelectRows39,__Flag40,ConnectionItems45,ConnectionItems48,PgSelect57,First58,PgSelectRows59,PgSelectSingle60,PgClassExpression61,__Flag62,Lambda63,PgSelect64,First65,PgSelectRows66,PgSelectSingle67,PgClassExpression68,__Flag69,Lambda70,First85,Access86,Access87,Access88,List89,Lambda90,First91,Access94,List95,Lambda96,Last97,Access99,List100,Lambda101,Last102,Access104,List105,Lambda106 bucket0 classDef bucket1 stroke:#00bfff - class Bucket1,PageInfo50,Access60,Access62 bucket1 + class Bucket1,PageInfo55,Access81,Access83 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo51,Access61,Access63 bucket2 + class Bucket2,PageInfo56,Access82,Access84 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item40,PgSelectSingle41 bucket3 + class Bucket3,__Item41,PgSelectSingle42 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item42,PgSelectSingle43 bucket4 + class Bucket4,__Item43,PgSelectSingle44 bucket4 classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression58 bucket5 + class Bucket5,PgClassExpression79 bucket5 classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression59 bucket6 - classDef bucket9 stroke:#ff0000 - class Bucket9,__Item52,Access53,PgSelectSingle54 bucket9 - classDef bucket10 stroke:#ffff00 - class Bucket10,__Item55,Access56,PgSelectSingle57 bucket10 + class Bucket6,PgClassExpression80 bucket6 classDef bucket11 stroke:#00ffff - class Bucket11,PgClassExpression64 bucket11 + class Bucket11,__Item71,Edge72,Access75,PgSelectSingle76,Access109,List110,Lambda111 bucket11 classDef bucket12 stroke:#4169e1 - class Bucket12,PgClassExpression65 bucket12 + class Bucket12,__Item73,Edge74,Access77,PgSelectSingle78,Access114,List115,Lambda116 bucket12 + classDef bucket13 stroke:#3cb371 + class Bucket13 bucket13 + classDef bucket14 stroke:#a52a2a + class Bucket14 bucket14 + classDef bucket15 stroke:#ff00ff + class Bucket15,PgClassExpression117 bucket15 + classDef bucket16 stroke:#f5deb3 + class Bucket16,PgClassExpression118 bucket16 + classDef bucket17 stroke:#696969 + class Bucket17,PgClassExpression123 bucket17 + classDef bucket18 stroke:#00bfff + class Bucket18,PgClassExpression124 bucket18 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql index a6dc049140..ca0de6402d 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql @@ -21,3 +21,15 @@ where true /* authorization checks */ ) order by __messages__."id" asc; + +select + (count(*))::text as "0" +from app_public.messages as __messages__ +where + ( + true /* connection */ + ) and ( + __messages__.id = any($1::"uuid"[]) + ) and ( + true /* authorization checks */ + ); diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql index a2154dd7bd..dc98ddcf2b 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql @@ -1,4 +1,9 @@ ## expect(errors).toBeFalsy() +## expect(data.emptyConnection.pageInfo.startCursor).toBeNull() +## expect(data.emptyConnection.pageInfo.endCursor).toBeNull() +## expect(data.emptyConnection.totalCount).toBe(0) +## expect(data.connection.pageInfo.hasNextPage).toBe(true) +## expect(data.connection.totalCount).toBe(2) { emptyList: trappedMessages(ids: []) { id @@ -6,24 +11,46 @@ list: trappedMessages(ids: ["ca70ca70-0000-0000-0000-a11ce000ca70"]) { id } - emptyConnection: trappedMessagesConnection(ids: []) { + 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"] + 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 } } diff --git a/grafast/dataplan-pg/src/examples/exampleSchema.ts b/grafast/dataplan-pg/src/examples/exampleSchema.ts index 94a7cc6abf..cdbf78e8cc 100644 --- a/grafast/dataplan-pg/src/examples/exampleSchema.ts +++ b/grafast/dataplan-pg/src/examples/exampleSchema.ts @@ -2190,12 +2190,17 @@ export function makeExampleSchema( totalCount: { type: new GraphQLNonNull(GraphQLInt), plan: EXPORTABLE( - (TYPES, sql) => ($connection) => - $connection + (TYPES, lambda, sql, trap, TRAP_INHIBITED) => ($connection) => { + const $count = $connection .cloneSubplanWithoutPagination("aggregate") .single() - .select(sql`count(*)`, TYPES.bigint, false), - [TYPES, sql], + .select(sql`count(*)`, TYPES.bigint, false); + const $trappedCount = trap($count, TRAP_INHIBITED, { + valueForInhibited: "NULL", + }); + return lambda($trappedCount, (count) => count ?? 0); + }, + [TYPES, lambda, sql, trap, TRAP_INHIBITED], ), }, }, @@ -4098,6 +4103,16 @@ export function makeExampleSchema( new GraphQLList(new GraphQLNonNull(GraphQLString)), ), }, + first: { + type: GraphQLInt, + applyPlan: EXPORTABLE( + () => + function plan(_$root, $connection: MessageConnectionStep, arg) { + $connection.setFirst(arg.getRaw()); + }, + [], + ), + }, }, plan: EXPORTABLE( ( diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index bf97ff8744..416278b5c8 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -152,6 +152,30 @@ export class __FlagStep if (isListCapableStep(step)) { this.listItem = this._listItem; } + if ( + "connectionClone" in step && + typeof step.connectionClone === "function" + ) { + const $connectionStep = step as any; + Object.assign(this, { + connectionClone: $connectionStep.connectionClone.bind($connectionStep), + ...(typeof $connectionStep.parseCursor === "function" + ? { parseCursor: $connectionStep.parseCursor.bind($connectionStep) } + : null), + ...(typeof $connectionStep.nodeForItem === "function" + ? { nodeForItem: $connectionStep.nodeForItem.bind($connectionStep) } + : null), + ...(typeof $connectionStep.edgeForItem === "function" + ? { edgeForItem: $connectionStep.edgeForItem.bind($connectionStep) } + : null), + ...(typeof $connectionStep.cursorForItem === "function" + ? { + cursorForItem: + $connectionStep.cursorForItem.bind($connectionStep), + } + : null), + }); + } sudo(this).implicitSideEffectStep = null; this.layerPlan.latestSideEffectStep = null; // Can't be `this`, because __FlagStep can be optimized away. } diff --git a/grafast/grafast/src/steps/connection.ts b/grafast/grafast/src/steps/connection.ts index 8201699033..dafbefeb9b 100644 --- a/grafast/grafast/src/steps/connection.ts +++ b/grafast/grafast/src/steps/connection.ts @@ -762,7 +762,10 @@ export class ConnectionStep< const $leftPad = access(this.paginationParams(), "__skipOver"); const $offset = access(this.paginationParams(), "offset", 0); const $index = access($indexed, "index") as Step; - return lambda([$leftPad, $offset, $index], encodeNumericCursor); + return lambda( + [$leftPad, $offset, $index], + encodeNumericCursor, + ) as Step; } } @@ -1245,9 +1248,16 @@ export function itemsOrStep< : $step; } -function encodeNumericCursor(index: number | readonly number[]): string { - const cursor = - typeof index === "number" ? index : index.reduce((memo, n) => memo + n, 0); +function encodeNumericCursor( + index: number | readonly (number | null | undefined)[], +): string | null { + if (typeof index === "number") { + return Buffer.from(String(index), "utf8").toString("base64"); + } + if (index.some((n) => n == null)) { + return null; + } + const cursor = index.reduce((memo, n) => memo + n!, 0); return Buffer.from(String(cursor), "utf8").toString("base64"); } function decodeNumericCursor(cursor: string): number { From a8194e1c23d191bcf3a0aab69accbc16c747f5b1 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 06/14] Have __TrapStep copy connection behaviors from dependent step --- grafast/grafast/src/steps/__flag.ts | 93 ++++++++++++++++++----------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index 416278b5c8..ac6388fe1d 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -22,7 +22,11 @@ import type { import { isListCapableStep, Step } from "../step.ts"; import { sudo } from "../utils.ts"; import type { __ItemStep } from "./__item.ts"; -import type { StepWithItems } from "./connection.ts"; +import type { + ConnectionOptimizedStep, + PaginationFeatures, + StepWithItems, +} from "./connection.ts"; import { itemsOrStep } from "./connection.ts"; // PUBLIC FLAGS @@ -149,33 +153,47 @@ export class __FlagStep } else { this.addDependency({ step, acceptFlags, onReject, dataOnly }); } - if (isListCapableStep(step)) { - this.listItem = this._listItem; + + if ("paginationSupport" in step) { + this.paginationSupport = step.paginationSupport as any; + } + + for (const method of [ + "applyPagination", + "parseCursor", + "nodeForItem", + "edgeForItem", + "listItem", + "cursorForItem", + ] as const) { + if ( + method in step && + typeof (step as unknown as StepWithMethods)[method] === "function" + ) { + this[method] = ($step) => { + const $dep = this.dependencies[0] as StepWithMethods; + return $dep[method]($step); + }; + } } + if ( "connectionClone" in step && typeof step.connectionClone === "function" ) { - const $connectionStep = step as any; - Object.assign(this, { - connectionClone: $connectionStep.connectionClone.bind($connectionStep), - ...(typeof $connectionStep.parseCursor === "function" - ? { parseCursor: $connectionStep.parseCursor.bind($connectionStep) } - : null), - ...(typeof $connectionStep.nodeForItem === "function" - ? { nodeForItem: $connectionStep.nodeForItem.bind($connectionStep) } - : null), - ...(typeof $connectionStep.edgeForItem === "function" - ? { edgeForItem: $connectionStep.edgeForItem.bind($connectionStep) } - : null), - ...(typeof $connectionStep.cursorForItem === "function" - ? { - cursorForItem: - $connectionStep.cursorForItem.bind($connectionStep), - } - : null), - }); + this.connectionClone = (...args: any[]) => { + const $dep = this.dependencies[0] as StepWithMethods; + if (args.length === 0) { + return this.copyFlags( + $dep.connectionClone(), + ) as ConnectionOptimizedStep; + } else { + // Cannot reliably optimize, just use the underlying method without flags + return $dep.connectionClone(...args); + } + }; } + sudo(this).implicitSideEffectStep = null; this.layerPlan.latestSideEffectStep = null; // Can't be `this`, because __FlagStep can be optimized away. } @@ -206,21 +224,11 @@ export class __FlagStep // re-wrap, avoid creating more steps and just return ourself. return this; } - const $if = this.ifDep != null ? this.getDepOptions(0).step : undefined; + const $if = + this.ifDep != null ? this.getDepOptions(this.ifDep).step : undefined; return new __FlagStep($items, { ...this.baseOptions, if: $if }); } - listItem?: ($item: __ItemStep) => Step; - /** - * Copied over `this.listItem` if the dependent step is a list capable step. - * Does **NOT** copy flagging over to the derived step, since `$item` already - * handles that. - */ - private _listItem($item: __ItemStep) { - const $dep = this.dependencies[0]; - return isListCapableStep($dep) ? $dep.listItem($item) : $item; - } - /** Return inlining instructions if we can be inlined. @internal */ inline( options: Omit, @@ -342,6 +350,21 @@ export class __FlagStep return details.indexMap(() => val); } } + + private copyFlags($step: TStep) { + const $if = + this.ifDep != null ? this.getDepOptions(this.ifDep).step : undefined; + return new __FlagStep($step, { ...this.baseOptions, if: $if }); + } + + paginationSupport?: PaginationFeatures; + applyPagination?($params: Step): void; + connectionClone?(...args: any[]): ConnectionOptimizedStep; + parseCursor?($cursor: Step): Step; + nodeForItem?($item: Step): Step; + edgeForItem?($item: Step): Step; + listItem?($item: Step): Step; + cursorForItem?($item: Step): Step; } /** @@ -414,3 +437,5 @@ export function trap( return new __FlagStep(step, { acceptFlags, onReject, dataOnly }); } }; +type StepWithMethods = Step & + Required>; From 5fcc9b624b2360f798151d6d3a19e0d1b0d29a81 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 07/14] Undo totalCount wrapping --- grafast/dataplan-pg/src/examples/exampleSchema.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/grafast/dataplan-pg/src/examples/exampleSchema.ts b/grafast/dataplan-pg/src/examples/exampleSchema.ts index cdbf78e8cc..40b00fe44f 100644 --- a/grafast/dataplan-pg/src/examples/exampleSchema.ts +++ b/grafast/dataplan-pg/src/examples/exampleSchema.ts @@ -2190,17 +2190,12 @@ export function makeExampleSchema( totalCount: { type: new GraphQLNonNull(GraphQLInt), plan: EXPORTABLE( - (TYPES, lambda, sql, trap, TRAP_INHIBITED) => ($connection) => { - const $count = $connection + (TYPES, sql) => ($connection) => + $connection .cloneSubplanWithoutPagination("aggregate") .single() - .select(sql`count(*)`, TYPES.bigint, false); - const $trappedCount = trap($count, TRAP_INHIBITED, { - valueForInhibited: "NULL", - }); - return lambda($trappedCount, (count) => count ?? 0); - }, - [TYPES, lambda, sql, trap, TRAP_INHIBITED], + .select(sql`count(*)`, TYPES.bigint, false), + [TYPES, sql], ), }, }, From 90588249b22bab81f585ef61aa310a119d81c5de Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 08/14] More connection behavior cloning --- grafast/grafast/src/steps/__flag.ts | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index ac6388fe1d..34a287d75b 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -15,6 +15,7 @@ import type { AddDependencyOptions, DataFromStep, ExecutionDetails, + ExecutionDetailsStream, ExecutionEntryFlags, GrafastResultsList, Maybe, @@ -23,6 +24,7 @@ import { isListCapableStep, Step } from "../step.ts"; import { sudo } from "../utils.ts"; import type { __ItemStep } from "./__item.ts"; import type { + ConnectionHandlingStep, ConnectionOptimizedStep, PaginationFeatures, StepWithItems, @@ -165,14 +167,21 @@ export class __FlagStep "edgeForItem", "listItem", "cursorForItem", + "setFirst", + "setLast", + "setOffset", + "setBefore", + "setAfter", + "setNeedsHasMore", + "addStreamDetails", ] as const) { if ( method in step && - typeof (step as unknown as StepWithMethods)[method] === "function" + typeof (step as unknown as AllTheMethodsStep)[method] === "function" ) { - this[method] = ($step) => { - const $dep = this.dependencies[0] as StepWithMethods; - return $dep[method]($step); + this[method] = (...args: [...any[]]) => { + const $dep = this.dependencies[0] as AllTheMethodsStep; + return ($dep[method] as Function)(...args); }; } } @@ -182,7 +191,7 @@ export class __FlagStep typeof step.connectionClone === "function" ) { this.connectionClone = (...args: any[]) => { - const $dep = this.dependencies[0] as StepWithMethods; + const $dep = this.dependencies[0] as AllTheMethodsStep; if (args.length === 0) { return this.copyFlags( $dep.connectionClone(), @@ -365,6 +374,13 @@ export class __FlagStep edgeForItem?($item: Step): Step; listItem?($item: Step): Step; cursorForItem?($item: Step): Step; + setFirst?($first: Step): void; + setLast?($last: Step): void; + setOffset?($offset: Step): void; + setBefore?($before: Step): void; + setAfter?($after: Step): void; + setNeedsHasMore?(): void; + addStreamDetails?($streamDetails: Step | null): void; } /** @@ -437,5 +453,8 @@ export function trap( return new __FlagStep(step, { acceptFlags, onReject, dataOnly }); } }; -type StepWithMethods = Step & - Required>; + +/** This type is not real, it's just so we don't have to do so much casting. @internal */ +type AllTheMethodsStep = Step & + Required> & + Required>; From dc4227bd49421916bb51df4a67e0034c5b7bf175 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 09/14] Revert a bunch of changes --- grafast/grafast/src/steps/__flag.ts | 83 +++---------------------- grafast/grafast/src/steps/connection.ts | 18 ++---- 2 files changed, 13 insertions(+), 88 deletions(-) diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index 34a287d75b..78055f2cea 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -155,54 +155,9 @@ export class __FlagStep } else { this.addDependency({ step, acceptFlags, onReject, dataOnly }); } - - if ("paginationSupport" in step) { - this.paginationSupport = step.paginationSupport as any; - } - - for (const method of [ - "applyPagination", - "parseCursor", - "nodeForItem", - "edgeForItem", - "listItem", - "cursorForItem", - "setFirst", - "setLast", - "setOffset", - "setBefore", - "setAfter", - "setNeedsHasMore", - "addStreamDetails", - ] as const) { - if ( - method in step && - typeof (step as unknown as AllTheMethodsStep)[method] === "function" - ) { - this[method] = (...args: [...any[]]) => { - const $dep = this.dependencies[0] as AllTheMethodsStep; - return ($dep[method] as Function)(...args); - }; - } + if (isListCapableStep(step)) { + this.listItem = this._listItem; } - - if ( - "connectionClone" in step && - typeof step.connectionClone === "function" - ) { - this.connectionClone = (...args: any[]) => { - const $dep = this.dependencies[0] as AllTheMethodsStep; - if (args.length === 0) { - return this.copyFlags( - $dep.connectionClone(), - ) as ConnectionOptimizedStep; - } else { - // Cannot reliably optimize, just use the underlying method without flags - return $dep.connectionClone(...args); - } - }; - } - sudo(this).implicitSideEffectStep = null; this.layerPlan.latestSideEffectStep = null; // Can't be `this`, because __FlagStep can be optimized away. } @@ -221,6 +176,13 @@ export class __FlagStep return this.getDepOptions(0).step; } + listItem?: ($item: __ItemStep) => Step; + // Copied over listItem if the dependent step is a list capable step + _listItem($item: __ItemStep) { + const $dep = this.dependencies[0]; + return isListCapableStep($dep) ? $dep.listItem($item) : $item; + } + /** * Makes `__FlagStep` compatible with `ConnectionStep`; importantly, this * copies our flagging over to the derived step. @@ -359,28 +321,6 @@ export class __FlagStep return details.indexMap(() => val); } } - - private copyFlags($step: TStep) { - const $if = - this.ifDep != null ? this.getDepOptions(this.ifDep).step : undefined; - return new __FlagStep($step, { ...this.baseOptions, if: $if }); - } - - paginationSupport?: PaginationFeatures; - applyPagination?($params: Step): void; - connectionClone?(...args: any[]): ConnectionOptimizedStep; - parseCursor?($cursor: Step): Step; - nodeForItem?($item: Step): Step; - edgeForItem?($item: Step): Step; - listItem?($item: Step): Step; - cursorForItem?($item: Step): Step; - setFirst?($first: Step): void; - setLast?($last: Step): void; - setOffset?($offset: Step): void; - setBefore?($before: Step): void; - setAfter?($after: Step): void; - setNeedsHasMore?(): void; - addStreamDetails?($streamDetails: Step | null): void; } /** @@ -453,8 +393,3 @@ export function trap( return new __FlagStep(step, { acceptFlags, onReject, dataOnly }); } }; - -/** This type is not real, it's just so we don't have to do so much casting. @internal */ -type AllTheMethodsStep = Step & - Required> & - Required>; diff --git a/grafast/grafast/src/steps/connection.ts b/grafast/grafast/src/steps/connection.ts index dafbefeb9b..8201699033 100644 --- a/grafast/grafast/src/steps/connection.ts +++ b/grafast/grafast/src/steps/connection.ts @@ -762,10 +762,7 @@ export class ConnectionStep< const $leftPad = access(this.paginationParams(), "__skipOver"); const $offset = access(this.paginationParams(), "offset", 0); const $index = access($indexed, "index") as Step; - return lambda( - [$leftPad, $offset, $index], - encodeNumericCursor, - ) as Step; + return lambda([$leftPad, $offset, $index], encodeNumericCursor); } } @@ -1248,16 +1245,9 @@ export function itemsOrStep< : $step; } -function encodeNumericCursor( - index: number | readonly (number | null | undefined)[], -): string | null { - if (typeof index === "number") { - return Buffer.from(String(index), "utf8").toString("base64"); - } - if (index.some((n) => n == null)) { - return null; - } - const cursor = index.reduce((memo, n) => memo + n!, 0); +function encodeNumericCursor(index: number | readonly number[]): string { + const cursor = + typeof index === "number" ? index : index.reduce((memo, n) => memo + n, 0); return Buffer.from(String(cursor), "utf8").toString("base64"); } function decodeNumericCursor(cursor: string): number { From 7464d9338c83844313031c3aff9e8f9f852fcd1c Mon Sep 17 00:00:00 2001 From: BenjAIe Date: Fri, 7 Aug 2026 13:55:26 +0100 Subject: [PATCH 10/14] Forbid trap() in connection --- grafast/grafast/src/steps/connection.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grafast/grafast/src/steps/connection.ts b/grafast/grafast/src/steps/connection.ts index 8201699033..5c94a9f7f1 100644 --- a/grafast/grafast/src/steps/connection.ts +++ b/grafast/grafast/src/steps/connection.ts @@ -26,6 +26,7 @@ import { each } from "./each.ts"; import { first } from "./first.ts"; import { lambda } from "./lambda.ts"; import { last } from "./last.ts"; +import { __FlagStep } from "./__flag.ts"; /** * Indicates which features are supported for pagination; support for `limit` @@ -1205,6 +1206,11 @@ export function connection< TCursorValue, TCollectionStep > { + if (step instanceof __FlagStep) { + throw new Error( + "connection() cannot be used with trap() or inhibit...() at this time.", + ); + } if (typeof params === "function" || params?.nodePlan || params?.cursorPlan) { throw new Error( `connection() was completely overhauled during the beta; this usage is no longer supported. Usage is much more straightforward now.`, From 051480644d9240e04bda1e80a283019ba6c14d20 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 11/14] Expect errors --- .../queries/trap/trapped-empty-pg-select.test.graphql | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql index dc98ddcf2b..6f1bb8c0e9 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.test.graphql @@ -1,9 +1,7 @@ -## expect(errors).toBeFalsy() -## expect(data.emptyConnection.pageInfo.startCursor).toBeNull() -## expect(data.emptyConnection.pageInfo.endCursor).toBeNull() -## expect(data.emptyConnection.totalCount).toBe(0) -## expect(data.connection.pageInfo.hasNextPage).toBe(true) -## expect(data.connection.totalCount).toBe(2) +## expect(errors).toHaveLength(2) +## expect(data.emptyList).toEqual([]) +## expect(data.list).toEqual([{ id: "ca70ca70-0000-0000-0000-a11ce000ca70" }]) +#> dontLogErrors: true { emptyList: trappedMessages(ids: []) { id From 3be0b58e1e277f9898fe0279f02c7b1c872c8838 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:03:23 +0100 Subject: [PATCH 12/14] Update snapshots with planning error --- .../trapped-empty-pg-select.deopt.mermaid | 215 +++--------------- .../trap/trapped-empty-pg-select.deopt.sql | 25 -- .../trap/trapped-empty-pg-select.json5 | 35 +-- .../trap/trapped-empty-pg-select.mermaid | 215 +++--------------- .../queries/trap/trapped-empty-pg-select.sql | 25 -- 5 files changed, 62 insertions(+), 453 deletions(-) diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid index 43ee3ea364..5d71c6ae45 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.mermaid @@ -8,52 +8,17 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 29, 125, 7, 12, 15, 22, 28, 30, 86, 87
2: 8, 16, 23, 31
3: 9, 17, 24, 32, 57, 64
4: 26, 34, 37, 39, 59, 66
ᐳ: 58, 60, 61, 65, 67, 68
5: 27, 35, 38, 40, 62, 69
6: 45, 48, 63, 70
ᐳ: 85, 88, 89, 91, 94, 95, 97, 99, 100, 102, 104, 105
7: 90, 96, 101, 106"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 27, 86, 87, 45, 63, 90, 101

ROOT Connectionᐸ26ᐳ[27]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 35, 86, 87, 48, 70, 96, 106

ROOT Connectionᐸ34ᐳ[35]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ38ᐳ[41]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ40ᐳ[43]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{3}ᐸmessagesᐳ[42]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 44

ROOT PgSelectSingle{4}ᐸmessagesᐳ[44]"):::bucket - Bucket11("Bucket 11 (listItem)
Deps: 86, 87

ROOT __Item{11}ᐸ45ᐳ[71]
1:
ᐳ: 75, 109, 76, 110
2: Lambda[111]
ᐳ: Edge[72]"):::bucket - Bucket12("Bucket 12 (listItem)
Deps: 86, 87

ROOT __Item{12}ᐸ48ᐳ[73]
1:
ᐳ: 77, 114, 78, 115
2: Lambda[116]
ᐳ: Edge[74]"):::bucket - Bucket13("Bucket 13 (nullableBoundary)
Deps: 72, 76, 111

ROOT Edge{11}[72]"):::bucket - Bucket14("Bucket 14 (nullableBoundary)
Deps: 74, 78, 116

ROOT Edge{12}[74]"):::bucket - Bucket15("Bucket 15 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket - Bucket16("Bucket 16 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket - Bucket17("Bucket 17 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket - Bucket18("Bucket 18 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket + Bucket0("Bucket 0 (root)

1: Error[27], Error[34]
ᐳ: 6, 10, 11, 14, 7, 12, 15
2: InhibitIf[8], InhibitIf[16]
3: PgSelect[9], PgSelect[17]
4: PgSelectRows[35], PgSelectRows[37]
5: __Flag[36], __Flag[38]"):::bucket + Bucket1("Bucket 1 (listItem)

ROOT __Item{1}ᐸ36ᐳ[39]"):::bucket + Bucket2("Bucket 2 (listItem)

ROOT __Item{2}ᐸ38ᐳ[41]"):::bucket + Bucket3("Bucket 3 (nullableBoundary)
Deps: 40

ROOT PgSelectSingle{1}ᐸmessagesᐳ[40]"):::bucket + Bucket4("Bucket 4 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{2}ᐸmessagesᐳ[42]"):::bucket end - Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 - Bucket1 --> Bucket11 - Bucket2 --> Bucket12 - Bucket3 --> Bucket5 - Bucket4 --> Bucket6 - Bucket11 --> Bucket13 & Bucket15 - Bucket12 --> Bucket14 & Bucket16 - Bucket13 --> Bucket17 - Bucket14 --> Bucket18 + Bucket0 --> Bucket1 & Bucket2 + Bucket1 --> Bucket3 + Bucket2 --> Bucket4 %% plan dependencies - Connection27[["Connection[27∈0] ➊
ᐸ26ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan - ConnectionParams28{{"ConnectionParams[28∈0] ➊
More deps:
- Constantᐸ1ᐳ[125]"}}:::plan - __Flag26[["__Flag[26∈0] ➊
ᐸ24, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams28 & __Flag26 --> Connection27 - Connection35[["Connection[35∈0] ➊
ᐸ34ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan - __Flag34[["__Flag[34∈0] ➊
ᐸ32, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams28 & __Flag34 --> Connection35 - List89{{"List[89∈0] ➊
ᐸ86,87,88ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access88{{"Access[88∈0] ➊
ᐸ85.indexᐳ"}}:::plan - Access88 --> List89 - List95{{"List[95∈0] ➊
ᐸ86,87,94ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access94{{"Access[94∈0] ➊
ᐸ91.indexᐳ"}}:::plan - Access94 --> List95 - List100{{"List[100∈0] ➊
ᐸ86,87,99ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access99{{"Access[99∈0] ➊
ᐸ97.indexᐳ"}}:::plan - Access99 --> List100 - List105{{"List[105∈0] ➊
ᐸ86,87,104ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access104{{"Access[104∈0] ➊
ᐸ102.indexᐳ"}}:::plan - Access104 --> List105 InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan Lambda7 --> InhibitIf8 @@ -68,161 +33,41 @@ graph TD Lambda15 --> InhibitIf16 PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan Object12 & InhibitIf16 --> PgSelect17 - InhibitIf23[["InhibitIf[23∈0] ➊
ᐸ20, if(22)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan - Lambda22{{"Lambda[22∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan - Lambda22 --> InhibitIf23 - PgSelect24[["PgSelect[24∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf23 --> PgSelect24 - InhibitIf31[["InhibitIf[31∈0] ➊
ᐸ29, if(30)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"]]:::plan - Lambda30{{"Lambda[30∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"}}:::plan - Lambda30 --> InhibitIf31 - PgSelect32[["PgSelect[32∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf31 --> PgSelect32 - PgSelect57[["PgSelect[57∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan - Object12 & InhibitIf23 --> PgSelect57 - PgSelect64[["PgSelect[64∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan - Object12 & InhibitIf31 --> PgSelect64 __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - PgSelect24 -->|"trapError;trapInhibited"| __Flag26 - PgSelect32 -->|"trapError;trapInhibited"| __Flag34 + PgSelectRows35[["PgSelectRows[35∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows35 + __Flag36[["__Flag[36∈0] ➊
ᐸ35, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows35 -->|"trapError;trapInhibited"| __Flag36 PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan - PgSelect9 --> PgSelectRows37 + PgSelect17 --> PgSelectRows37 __Flag38[["__Flag[38∈0] ➊
ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38 - PgSelectRows39[["PgSelectRows[39∈0] ➊"]]:::plan - PgSelect17 --> PgSelectRows39 - __Flag40[["__Flag[40∈0] ➊
ᐸ39, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows39 -->|"trapError;trapInhibited"| __Flag40 - ConnectionItems45[["ConnectionItems[45∈0] ➊
Dependents: 3
More deps:
- Connection[27]"]]:::plan - ConnectionItems48[["ConnectionItems[48∈0] ➊
Dependents: 3
More deps:
- Connection[35]"]]:::plan - First58{{"First[58∈0] ➊"}}:::plan - PgSelectRows59[["PgSelectRows[59∈0] ➊"]]:::plan - PgSelectRows59 --> First58 - PgSelect57 --> PgSelectRows59 - PgSelectSingle60{{"PgSelectSingle[60∈0] ➊
ᐸmessagesᐳ"}}:::plan - First58 --> PgSelectSingle60 - PgClassExpression61{{"PgClassExpression[61∈0] ➊
ᐸcount(*)ᐳ"}}:::plan - PgSelectSingle60 --> PgClassExpression61 - __Flag62[["__Flag[62∈0] ➊
ᐸ61, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgClassExpression61 -->|"trapError;trapInhibited"| __Flag62 - Lambda63[["Lambda[63∈0] ➊"]]:::unbatchedplan - __Flag62 --> Lambda63 - First65{{"First[65∈0] ➊"}}:::plan - PgSelectRows66[["PgSelectRows[66∈0] ➊"]]:::plan - PgSelectRows66 --> First65 - PgSelect64 --> PgSelectRows66 - PgSelectSingle67{{"PgSelectSingle[67∈0] ➊
ᐸmessagesᐳ"}}:::plan - First65 --> PgSelectSingle67 - PgClassExpression68{{"PgClassExpression[68∈0] ➊
ᐸcount(*)ᐳ"}}:::plan - PgSelectSingle67 --> PgClassExpression68 - __Flag69[["__Flag[69∈0] ➊
ᐸ68, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgClassExpression68 -->|"trapError;trapInhibited"| __Flag69 - Lambda70[["Lambda[70∈0] ➊"]]:::unbatchedplan - __Flag69 --> Lambda70 - First85{{"First[85∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan - Access86{{"Access[86∈0] ➊
ᐸ28.__skipOverᐳ
Dependents: 6"}}:::plan - ConnectionParams28 --> Access86 - Access87{{"Access[87∈0] ➊
ᐸ28.offsetᐳ
Dependents: 6"}}:::plan - ConnectionParams28 --> Access87 - First85 --> Access88 - Lambda90[["Lambda[90∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List89 --> Lambda90 - First91{{"First[91∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan - First91 --> Access94 - Lambda96[["Lambda[96∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List95 --> Lambda96 - Last97{{"Last[97∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan - Last97 --> Access99 - Lambda101[["Lambda[101∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List100 --> Lambda101 - Last102{{"Last[102∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan - Last102 --> Access104 - Lambda106[["Lambda[106∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List105 --> Lambda106 - PageInfo55{{"PageInfo[55∈1] ➊
More deps:
- Connection[27]"}}:::plan - Access81{{"Access[81∈1] ➊
ᐸ27.hasNextPageᐳ
More deps:
- Connection[27]"}}:::plan - Access83{{"Access[83∈1] ➊
ᐸ27.hasPreviousPageᐳ
More deps:
- Connection[27]"}}:::plan - PageInfo56{{"PageInfo[56∈2] ➊
More deps:
- Connection[35]"}}:::plan - Access82{{"Access[82∈2] ➊
ᐸ35.hasNextPageᐳ
More deps:
- Connection[35]"}}:::plan - Access84{{"Access[84∈2] ➊
ᐸ35.hasPreviousPageᐳ
More deps:
- Connection[35]"}}:::plan - __Item41[/"__Item[41∈3]
ᐸ38ᐳ"\]:::itemplan + Error27[["Error[27∈0] ➊"]]:::unbatchedplan + Error34[["Error[34∈0] ➊"]]:::unbatchedplan + __Item39[/"__Item[39∈1]
ᐸ36ᐳ"\]:::itemplan + __Flag36 ==> __Item39 + PgSelectSingle40{{"PgSelectSingle[40∈1]
ᐸmessagesᐳ"}}:::plan + __Item39 --> PgSelectSingle40 + __Item41[/"__Item[41∈2]
ᐸ38ᐳ"\]:::itemplan __Flag38 ==> __Item41 - PgSelectSingle42{{"PgSelectSingle[42∈3]
ᐸmessagesᐳ"}}:::plan + PgSelectSingle42{{"PgSelectSingle[42∈2]
ᐸmessagesᐳ"}}:::plan __Item41 --> PgSelectSingle42 - __Item43[/"__Item[43∈4]
ᐸ40ᐳ"\]:::itemplan - __Flag40 ==> __Item43 - PgSelectSingle44{{"PgSelectSingle[44∈4]
ᐸmessagesᐳ"}}:::plan - __Item43 --> PgSelectSingle44 - PgClassExpression79{{"PgClassExpression[79∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle42 --> PgClassExpression79 - PgClassExpression80{{"PgClassExpression[80∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle44 --> PgClassExpression80 - List110{{"List[110∈11]
ᐸ86,87,109ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access109{{"Access[109∈11]
ᐸ71.indexᐳ"}}:::plan - Access109 --> List110 - Edge72{{"Edge[72∈11]"}}:::plan - __Item71[/"__Item[71∈11]
ᐸ45ᐳ
More deps:
- ConnectionItems[45]"\]:::itemplan - Lambda111[["Lambda[111∈11]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - __Item71 & Lambda111 --> Edge72 - Access75{{"Access[75∈11]
ᐸ71.itemᐳ"}}:::plan - __Item71 --> Access75 - PgSelectSingle76{{"PgSelectSingle[76∈11]
ᐸmessagesᐳ"}}:::plan - Access75 --> PgSelectSingle76 - __Item71 --> Access109 - List110 --> Lambda111 - List115{{"List[115∈12]
ᐸ86,87,114ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access114{{"Access[114∈12]
ᐸ73.indexᐳ"}}:::plan - Access114 --> List115 - Edge74{{"Edge[74∈12]"}}:::plan - __Item73[/"__Item[73∈12]
ᐸ48ᐳ
More deps:
- ConnectionItems[48]"\]:::itemplan - Lambda116[["Lambda[116∈12]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - __Item73 & Lambda116 --> Edge74 - Access77{{"Access[77∈12]
ᐸ73.itemᐳ"}}:::plan - __Item73 --> Access77 - PgSelectSingle78{{"PgSelectSingle[78∈12]
ᐸmessagesᐳ"}}:::plan - Access77 --> PgSelectSingle78 - __Item73 --> Access114 - List115 --> Lambda116 - PgClassExpression117{{"PgClassExpression[117∈15]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle76 --> PgClassExpression117 - PgClassExpression118{{"PgClassExpression[118∈16]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle78 --> PgClassExpression118 - PgClassExpression123{{"PgClassExpression[123∈17]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgClassExpression117 o--o PgClassExpression123 - PgClassExpression124{{"PgClassExpression[124∈18]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgClassExpression118 o--o PgClassExpression124 + PgClassExpression43{{"PgClassExpression[43∈3]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle40 --> PgClassExpression43 + PgClassExpression44{{"PgClassExpression[44∈4]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle42 --> PgClassExpression44 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda22,InhibitIf23,PgSelect24,__Flag26,Connection27,ConnectionParams28,Lambda30,InhibitIf31,PgSelect32,__Flag34,Connection35,PgSelectRows37,__Flag38,PgSelectRows39,__Flag40,ConnectionItems45,ConnectionItems48,PgSelect57,First58,PgSelectRows59,PgSelectSingle60,PgClassExpression61,__Flag62,Lambda63,PgSelect64,First65,PgSelectRows66,PgSelectSingle67,PgClassExpression68,__Flag69,Lambda70,First85,Access86,Access87,Access88,List89,Lambda90,First91,Access94,List95,Lambda96,Last97,Access99,List100,Lambda101,Last102,Access104,List105,Lambda106 bucket0 + 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,PageInfo55,Access81,Access83 bucket1 + class Bucket1,__Item39,PgSelectSingle40 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo56,Access82,Access84 bucket2 + class Bucket2,__Item41,PgSelectSingle42 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item41,PgSelectSingle42 bucket3 + class Bucket3,PgClassExpression43 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item43,PgSelectSingle44 bucket4 - classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression79 bucket5 - classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression80 bucket6 - classDef bucket11 stroke:#00ffff - class Bucket11,__Item71,Edge72,Access75,PgSelectSingle76,Access109,List110,Lambda111 bucket11 - classDef bucket12 stroke:#4169e1 - class Bucket12,__Item73,Edge74,Access77,PgSelectSingle78,Access114,List115,Lambda116 bucket12 - classDef bucket13 stroke:#3cb371 - class Bucket13 bucket13 - classDef bucket14 stroke:#a52a2a - class Bucket14 bucket14 - classDef bucket15 stroke:#ff00ff - class Bucket15,PgClassExpression117 bucket15 - classDef bucket16 stroke:#f5deb3 - class Bucket16,PgClassExpression118 bucket16 - classDef bucket17 stroke:#696969 - class Bucket17,PgClassExpression123 bucket17 - classDef bucket18 stroke:#00bfff - class Bucket18,PgClassExpression124 bucket18 + class Bucket4,PgClassExpression44 bucket4 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql index ca0de6402d..528042e4f0 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.deopt.sql @@ -8,28 +8,3 @@ where true /* authorization checks */ ) order by __messages__."id" asc; - -select - __messages__."id" as "0" -from app_public.messages as __messages__ -where - ( - true /* connection */ - ) and ( - __messages__.id = any($1::"uuid"[]) - ) and ( - true /* authorization checks */ - ) -order by __messages__."id" asc; - -select - (count(*))::text as "0" -from app_public.messages as __messages__ -where - ( - true /* connection */ - ) and ( - __messages__.id = any($1::"uuid"[]) - ) and ( - true /* authorization checks */ - ); diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 index 59121d1658..4747756426 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.json5 @@ -5,37 +5,6 @@ id: "ca70ca70-0000-0000-0000-a11ce000ca70", }, ], - emptyConnection: { - edges: [], - nodes: [], - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: null, - endCursor: null, - }, - totalCount: 0, - }, - connection: { - edges: [ - { - cursor: "MA==", - node: { - id: "ca70ca70-0000-0000-0000-a11ce000ca70", - }, - }, - ], - nodes: [ - { - id: "ca70ca70-0000-0000-0000-a11ce000ca70", - }, - ], - pageInfo: { - hasNextPage: true, - hasPreviousPage: false, - startCursor: "MA==", - endCursor: "MA==", - }, - totalCount: 2, - }, + emptyConnection: null, + connection: null, } diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid index 43ee3ea364..5d71c6ae45 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.mermaid @@ -8,52 +8,17 @@ graph TD 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)

1:
ᐳ: 6, 10, 11, 14, 20, 29, 125, 7, 12, 15, 22, 28, 30, 86, 87
2: 8, 16, 23, 31
3: 9, 17, 24, 32, 57, 64
4: 26, 34, 37, 39, 59, 66
ᐳ: 58, 60, 61, 65, 67, 68
5: 27, 35, 38, 40, 62, 69
6: 45, 48, 63, 70
ᐳ: 85, 88, 89, 91, 94, 95, 97, 99, 100, 102, 104, 105
7: 90, 96, 101, 106"):::bucket - Bucket1("Bucket 1 (nullableBoundary)
Deps: 27, 86, 87, 45, 63, 90, 101

ROOT Connectionᐸ26ᐳ[27]"):::bucket - Bucket2("Bucket 2 (nullableBoundary)
Deps: 35, 86, 87, 48, 70, 96, 106

ROOT Connectionᐸ34ᐳ[35]"):::bucket - Bucket3("Bucket 3 (listItem)

ROOT __Item{3}ᐸ38ᐳ[41]"):::bucket - Bucket4("Bucket 4 (listItem)

ROOT __Item{4}ᐸ40ᐳ[43]"):::bucket - Bucket5("Bucket 5 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{3}ᐸmessagesᐳ[42]"):::bucket - Bucket6("Bucket 6 (nullableBoundary)
Deps: 44

ROOT PgSelectSingle{4}ᐸmessagesᐳ[44]"):::bucket - Bucket11("Bucket 11 (listItem)
Deps: 86, 87

ROOT __Item{11}ᐸ45ᐳ[71]
1:
ᐳ: 75, 109, 76, 110
2: Lambda[111]
ᐳ: Edge[72]"):::bucket - Bucket12("Bucket 12 (listItem)
Deps: 86, 87

ROOT __Item{12}ᐸ48ᐳ[73]
1:
ᐳ: 77, 114, 78, 115
2: Lambda[116]
ᐳ: Edge[74]"):::bucket - Bucket13("Bucket 13 (nullableBoundary)
Deps: 72, 76, 111

ROOT Edge{11}[72]"):::bucket - Bucket14("Bucket 14 (nullableBoundary)
Deps: 74, 78, 116

ROOT Edge{12}[74]"):::bucket - Bucket15("Bucket 15 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket - Bucket16("Bucket 16 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket - Bucket17("Bucket 17 (nullableBoundary)
Deps: 76

ROOT PgSelectSingle{11}ᐸmessagesᐳ[76]"):::bucket - Bucket18("Bucket 18 (nullableBoundary)
Deps: 78

ROOT PgSelectSingle{12}ᐸmessagesᐳ[78]"):::bucket + Bucket0("Bucket 0 (root)

1: Error[27], Error[34]
ᐳ: 6, 10, 11, 14, 7, 12, 15
2: InhibitIf[8], InhibitIf[16]
3: PgSelect[9], PgSelect[17]
4: PgSelectRows[35], PgSelectRows[37]
5: __Flag[36], __Flag[38]"):::bucket + Bucket1("Bucket 1 (listItem)

ROOT __Item{1}ᐸ36ᐳ[39]"):::bucket + Bucket2("Bucket 2 (listItem)

ROOT __Item{2}ᐸ38ᐳ[41]"):::bucket + Bucket3("Bucket 3 (nullableBoundary)
Deps: 40

ROOT PgSelectSingle{1}ᐸmessagesᐳ[40]"):::bucket + Bucket4("Bucket 4 (nullableBoundary)
Deps: 42

ROOT PgSelectSingle{2}ᐸmessagesᐳ[42]"):::bucket end - Bucket0 --> Bucket1 & Bucket2 & Bucket3 & Bucket4 - Bucket1 --> Bucket11 - Bucket2 --> Bucket12 - Bucket3 --> Bucket5 - Bucket4 --> Bucket6 - Bucket11 --> Bucket13 & Bucket15 - Bucket12 --> Bucket14 & Bucket16 - Bucket13 --> Bucket17 - Bucket14 --> Bucket18 + Bucket0 --> Bucket1 & Bucket2 + Bucket1 --> Bucket3 + Bucket2 --> Bucket4 %% plan dependencies - Connection27[["Connection[27∈0] ➊
ᐸ26ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan - ConnectionParams28{{"ConnectionParams[28∈0] ➊
More deps:
- Constantᐸ1ᐳ[125]"}}:::plan - __Flag26[["__Flag[26∈0] ➊
ᐸ24, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams28 & __Flag26 --> Connection27 - Connection35[["Connection[35∈0] ➊
ᐸ34ᐳ
Dependents: 4
More deps:
- Constantᐸ1ᐳ[125]"]]:::plan - __Flag34[["__Flag[34∈0] ➊
ᐸ32, trapInhibited, onReject: INHIBITᐳ"]]:::plan - ConnectionParams28 & __Flag34 --> Connection35 - List89{{"List[89∈0] ➊
ᐸ86,87,88ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access88{{"Access[88∈0] ➊
ᐸ85.indexᐳ"}}:::plan - Access88 --> List89 - List95{{"List[95∈0] ➊
ᐸ86,87,94ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access94{{"Access[94∈0] ➊
ᐸ91.indexᐳ"}}:::plan - Access94 --> List95 - List100{{"List[100∈0] ➊
ᐸ86,87,99ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access99{{"Access[99∈0] ➊
ᐸ97.indexᐳ"}}:::plan - Access99 --> List100 - List105{{"List[105∈0] ➊
ᐸ86,87,104ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access104{{"Access[104∈0] ➊
ᐸ102.indexᐳ"}}:::plan - Access104 --> List105 InhibitIf8[["InhibitIf[8∈0] ➊
ᐸ6, if(7)ᐳ
More deps:
- Constantᐸ[]ᐳ[6]"]]:::plan Lambda7{{"Lambda[7∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[6]"}}:::plan Lambda7 --> InhibitIf8 @@ -68,161 +33,41 @@ graph TD Lambda15 --> InhibitIf16 PgSelect17[["PgSelect[17∈0] ➊
ᐸmessagesᐳ"]]:::plan Object12 & InhibitIf16 --> PgSelect17 - InhibitIf23[["InhibitIf[23∈0] ➊
ᐸ20, if(22)ᐳ
More deps:
- Constantᐸ[]ᐳ[20]"]]:::plan - Lambda22{{"Lambda[22∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[]ᐳ[20]"}}:::plan - Lambda22 --> InhibitIf23 - PgSelect24[["PgSelect[24∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf23 --> PgSelect24 - InhibitIf31[["InhibitIf[31∈0] ➊
ᐸ29, if(30)ᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"]]:::plan - Lambda30{{"Lambda[30∈0] ➊
ᐸisEmptyᐳ
More deps:
- Constantᐸ[ 'ca70ca70-0000-0000-0000-a11ce000ca70', 'ca70ca70-0000-000ᐳ[29]"}}:::plan - Lambda30 --> InhibitIf31 - PgSelect32[["PgSelect[32∈0] ➊
ᐸmessagesᐳ"]]:::plan - Object12 & InhibitIf31 --> PgSelect32 - PgSelect57[["PgSelect[57∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan - Object12 & InhibitIf23 --> PgSelect57 - PgSelect64[["PgSelect[64∈0] ➊
ᐸmessages(aggregate)ᐳ"]]:::plan - Object12 & InhibitIf31 --> PgSelect64 __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan __Value2 --> Access10 __Value2 --> Access11 - PgSelect24 -->|"trapError;trapInhibited"| __Flag26 - PgSelect32 -->|"trapError;trapInhibited"| __Flag34 + PgSelectRows35[["PgSelectRows[35∈0] ➊"]]:::plan + PgSelect9 --> PgSelectRows35 + __Flag36[["__Flag[36∈0] ➊
ᐸ35, trapInhibited, onReject: INHIBITᐳ"]]:::plan + PgSelectRows35 -->|"trapError;trapInhibited"| __Flag36 PgSelectRows37[["PgSelectRows[37∈0] ➊"]]:::plan - PgSelect9 --> PgSelectRows37 + PgSelect17 --> PgSelectRows37 __Flag38[["__Flag[38∈0] ➊
ᐸ37, trapInhibited, onReject: INHIBITᐳ"]]:::plan PgSelectRows37 -->|"trapError;trapInhibited"| __Flag38 - PgSelectRows39[["PgSelectRows[39∈0] ➊"]]:::plan - PgSelect17 --> PgSelectRows39 - __Flag40[["__Flag[40∈0] ➊
ᐸ39, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgSelectRows39 -->|"trapError;trapInhibited"| __Flag40 - ConnectionItems45[["ConnectionItems[45∈0] ➊
Dependents: 3
More deps:
- Connection[27]"]]:::plan - ConnectionItems48[["ConnectionItems[48∈0] ➊
Dependents: 3
More deps:
- Connection[35]"]]:::plan - First58{{"First[58∈0] ➊"}}:::plan - PgSelectRows59[["PgSelectRows[59∈0] ➊"]]:::plan - PgSelectRows59 --> First58 - PgSelect57 --> PgSelectRows59 - PgSelectSingle60{{"PgSelectSingle[60∈0] ➊
ᐸmessagesᐳ"}}:::plan - First58 --> PgSelectSingle60 - PgClassExpression61{{"PgClassExpression[61∈0] ➊
ᐸcount(*)ᐳ"}}:::plan - PgSelectSingle60 --> PgClassExpression61 - __Flag62[["__Flag[62∈0] ➊
ᐸ61, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgClassExpression61 -->|"trapError;trapInhibited"| __Flag62 - Lambda63[["Lambda[63∈0] ➊"]]:::unbatchedplan - __Flag62 --> Lambda63 - First65{{"First[65∈0] ➊"}}:::plan - PgSelectRows66[["PgSelectRows[66∈0] ➊"]]:::plan - PgSelectRows66 --> First65 - PgSelect64 --> PgSelectRows66 - PgSelectSingle67{{"PgSelectSingle[67∈0] ➊
ᐸmessagesᐳ"}}:::plan - First65 --> PgSelectSingle67 - PgClassExpression68{{"PgClassExpression[68∈0] ➊
ᐸcount(*)ᐳ"}}:::plan - PgSelectSingle67 --> PgClassExpression68 - __Flag69[["__Flag[69∈0] ➊
ᐸ68, trapInhibited, onReject: INHIBITᐳ"]]:::plan - PgClassExpression68 -->|"trapError;trapInhibited"| __Flag69 - Lambda70[["Lambda[70∈0] ➊"]]:::unbatchedplan - __Flag69 --> Lambda70 - First85{{"First[85∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan - Access86{{"Access[86∈0] ➊
ᐸ28.__skipOverᐳ
Dependents: 6"}}:::plan - ConnectionParams28 --> Access86 - Access87{{"Access[87∈0] ➊
ᐸ28.offsetᐳ
Dependents: 6"}}:::plan - ConnectionParams28 --> Access87 - First85 --> Access88 - Lambda90[["Lambda[90∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List89 --> Lambda90 - First91{{"First[91∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan - First91 --> Access94 - Lambda96[["Lambda[96∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List95 --> Lambda96 - Last97{{"Last[97∈0] ➊
More deps:
- ConnectionItems[45]"}}:::plan - Last97 --> Access99 - Lambda101[["Lambda[101∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List100 --> Lambda101 - Last102{{"Last[102∈0] ➊
More deps:
- ConnectionItems[48]"}}:::plan - Last102 --> Access104 - Lambda106[["Lambda[106∈0] ➊
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - List105 --> Lambda106 - PageInfo55{{"PageInfo[55∈1] ➊
More deps:
- Connection[27]"}}:::plan - Access81{{"Access[81∈1] ➊
ᐸ27.hasNextPageᐳ
More deps:
- Connection[27]"}}:::plan - Access83{{"Access[83∈1] ➊
ᐸ27.hasPreviousPageᐳ
More deps:
- Connection[27]"}}:::plan - PageInfo56{{"PageInfo[56∈2] ➊
More deps:
- Connection[35]"}}:::plan - Access82{{"Access[82∈2] ➊
ᐸ35.hasNextPageᐳ
More deps:
- Connection[35]"}}:::plan - Access84{{"Access[84∈2] ➊
ᐸ35.hasPreviousPageᐳ
More deps:
- Connection[35]"}}:::plan - __Item41[/"__Item[41∈3]
ᐸ38ᐳ"\]:::itemplan + Error27[["Error[27∈0] ➊"]]:::unbatchedplan + Error34[["Error[34∈0] ➊"]]:::unbatchedplan + __Item39[/"__Item[39∈1]
ᐸ36ᐳ"\]:::itemplan + __Flag36 ==> __Item39 + PgSelectSingle40{{"PgSelectSingle[40∈1]
ᐸmessagesᐳ"}}:::plan + __Item39 --> PgSelectSingle40 + __Item41[/"__Item[41∈2]
ᐸ38ᐳ"\]:::itemplan __Flag38 ==> __Item41 - PgSelectSingle42{{"PgSelectSingle[42∈3]
ᐸmessagesᐳ"}}:::plan + PgSelectSingle42{{"PgSelectSingle[42∈2]
ᐸmessagesᐳ"}}:::plan __Item41 --> PgSelectSingle42 - __Item43[/"__Item[43∈4]
ᐸ40ᐳ"\]:::itemplan - __Flag40 ==> __Item43 - PgSelectSingle44{{"PgSelectSingle[44∈4]
ᐸmessagesᐳ"}}:::plan - __Item43 --> PgSelectSingle44 - PgClassExpression79{{"PgClassExpression[79∈5]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle42 --> PgClassExpression79 - PgClassExpression80{{"PgClassExpression[80∈6]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle44 --> PgClassExpression80 - List110{{"List[110∈11]
ᐸ86,87,109ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access109{{"Access[109∈11]
ᐸ71.indexᐳ"}}:::plan - Access109 --> List110 - Edge72{{"Edge[72∈11]"}}:::plan - __Item71[/"__Item[71∈11]
ᐸ45ᐳ
More deps:
- ConnectionItems[45]"\]:::itemplan - Lambda111[["Lambda[111∈11]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - __Item71 & Lambda111 --> Edge72 - Access75{{"Access[75∈11]
ᐸ71.itemᐳ"}}:::plan - __Item71 --> Access75 - PgSelectSingle76{{"PgSelectSingle[76∈11]
ᐸmessagesᐳ"}}:::plan - Access75 --> PgSelectSingle76 - __Item71 --> Access109 - List110 --> Lambda111 - List115{{"List[115∈12]
ᐸ86,87,114ᐳ
More deps:
- Access[86]
- Access[87]"}}:::plan - Access114{{"Access[114∈12]
ᐸ73.indexᐳ"}}:::plan - Access114 --> List115 - Edge74{{"Edge[74∈12]"}}:::plan - __Item73[/"__Item[73∈12]
ᐸ48ᐳ
More deps:
- ConnectionItems[48]"\]:::itemplan - Lambda116[["Lambda[116∈12]
ᐸencodeNumericCursorᐳ"]]:::unbatchedplan - __Item73 & Lambda116 --> Edge74 - Access77{{"Access[77∈12]
ᐸ73.itemᐳ"}}:::plan - __Item73 --> Access77 - PgSelectSingle78{{"PgSelectSingle[78∈12]
ᐸmessagesᐳ"}}:::plan - Access77 --> PgSelectSingle78 - __Item73 --> Access114 - List115 --> Lambda116 - PgClassExpression117{{"PgClassExpression[117∈15]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle76 --> PgClassExpression117 - PgClassExpression118{{"PgClassExpression[118∈16]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgSelectSingle78 --> PgClassExpression118 - PgClassExpression123{{"PgClassExpression[123∈17]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgClassExpression117 o--o PgClassExpression123 - PgClassExpression124{{"PgClassExpression[124∈18]
ᐸ__messages__.”id”ᐳ"}}:::plan - PgClassExpression118 o--o PgClassExpression124 + PgClassExpression43{{"PgClassExpression[43∈3]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle40 --> PgClassExpression43 + PgClassExpression44{{"PgClassExpression[44∈4]
ᐸ__messages__.”id”ᐳ"}}:::plan + PgSelectSingle42 --> PgClassExpression44 %% define steps classDef bucket0 stroke:#696969 - class Bucket0,__Value2,Lambda7,InhibitIf8,PgSelect9,Access10,Access11,Object12,Lambda15,InhibitIf16,PgSelect17,Lambda22,InhibitIf23,PgSelect24,__Flag26,Connection27,ConnectionParams28,Lambda30,InhibitIf31,PgSelect32,__Flag34,Connection35,PgSelectRows37,__Flag38,PgSelectRows39,__Flag40,ConnectionItems45,ConnectionItems48,PgSelect57,First58,PgSelectRows59,PgSelectSingle60,PgClassExpression61,__Flag62,Lambda63,PgSelect64,First65,PgSelectRows66,PgSelectSingle67,PgClassExpression68,__Flag69,Lambda70,First85,Access86,Access87,Access88,List89,Lambda90,First91,Access94,List95,Lambda96,Last97,Access99,List100,Lambda101,Last102,Access104,List105,Lambda106 bucket0 + 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,PageInfo55,Access81,Access83 bucket1 + class Bucket1,__Item39,PgSelectSingle40 bucket1 classDef bucket2 stroke:#7f007f - class Bucket2,PageInfo56,Access82,Access84 bucket2 + class Bucket2,__Item41,PgSelectSingle42 bucket2 classDef bucket3 stroke:#ffa500 - class Bucket3,__Item41,PgSelectSingle42 bucket3 + class Bucket3,PgClassExpression43 bucket3 classDef bucket4 stroke:#0000ff - class Bucket4,__Item43,PgSelectSingle44 bucket4 - classDef bucket5 stroke:#7fff00 - class Bucket5,PgClassExpression79 bucket5 - classDef bucket6 stroke:#ff1493 - class Bucket6,PgClassExpression80 bucket6 - classDef bucket11 stroke:#00ffff - class Bucket11,__Item71,Edge72,Access75,PgSelectSingle76,Access109,List110,Lambda111 bucket11 - classDef bucket12 stroke:#4169e1 - class Bucket12,__Item73,Edge74,Access77,PgSelectSingle78,Access114,List115,Lambda116 bucket12 - classDef bucket13 stroke:#3cb371 - class Bucket13 bucket13 - classDef bucket14 stroke:#a52a2a - class Bucket14 bucket14 - classDef bucket15 stroke:#ff00ff - class Bucket15,PgClassExpression117 bucket15 - classDef bucket16 stroke:#f5deb3 - class Bucket16,PgClassExpression118 bucket16 - classDef bucket17 stroke:#696969 - class Bucket17,PgClassExpression123 bucket17 - classDef bucket18 stroke:#00bfff - class Bucket18,PgClassExpression124 bucket18 + class Bucket4,PgClassExpression44 bucket4 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql index ca0de6402d..528042e4f0 100644 --- a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.sql @@ -8,28 +8,3 @@ where true /* authorization checks */ ) order by __messages__."id" asc; - -select - __messages__."id" as "0" -from app_public.messages as __messages__ -where - ( - true /* connection */ - ) and ( - __messages__.id = any($1::"uuid"[]) - ) and ( - true /* authorization checks */ - ) -order by __messages__."id" asc; - -select - (count(*))::text as "0" -from app_public.messages as __messages__ -where - ( - true /* connection */ - ) and ( - __messages__.id = any($1::"uuid"[]) - ) and ( - true /* authorization checks */ - ); From a2ac757862c28380fc0b15e658f741cb78a5d753 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:17:40 +0100 Subject: [PATCH 13/14] Lint --- grafast/grafast/src/steps/__flag.ts | 8 +------- grafast/grafast/src/steps/connection.ts | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/grafast/grafast/src/steps/__flag.ts b/grafast/grafast/src/steps/__flag.ts index 78055f2cea..809c346d29 100644 --- a/grafast/grafast/src/steps/__flag.ts +++ b/grafast/grafast/src/steps/__flag.ts @@ -15,7 +15,6 @@ import type { AddDependencyOptions, DataFromStep, ExecutionDetails, - ExecutionDetailsStream, ExecutionEntryFlags, GrafastResultsList, Maybe, @@ -23,12 +22,7 @@ import type { import { isListCapableStep, Step } from "../step.ts"; import { sudo } from "../utils.ts"; import type { __ItemStep } from "./__item.ts"; -import type { - ConnectionHandlingStep, - ConnectionOptimizedStep, - PaginationFeatures, - StepWithItems, -} from "./connection.ts"; +import type { StepWithItems } from "./connection.ts"; import { itemsOrStep } from "./connection.ts"; // PUBLIC FLAGS diff --git a/grafast/grafast/src/steps/connection.ts b/grafast/grafast/src/steps/connection.ts index 5c94a9f7f1..f6ae208554 100644 --- a/grafast/grafast/src/steps/connection.ts +++ b/grafast/grafast/src/steps/connection.ts @@ -20,13 +20,13 @@ import { maybeArraysMatch, terminateIterable, } from "../utils.ts"; +import { __FlagStep } from "./__flag.ts"; import { access } from "./access.ts"; import { constant, ConstantStep } from "./constant.ts"; import { each } from "./each.ts"; import { first } from "./first.ts"; import { lambda } from "./lambda.ts"; import { last } from "./last.ts"; -import { __FlagStep } from "./__flag.ts"; /** * Indicates which features are supported for pagination; support for `limit` From 3101d994d6393821ba5bfa1f94d7133cd2cfd99a Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 7 Aug 2026 14:20:37 +0100 Subject: [PATCH 14/14] Add missing error snapshot --- .../trap/trapped-empty-pg-select.errors.json5 | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.errors.json5 diff --git a/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.errors.json5 b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.errors.json5 new file mode 100644 index 0000000000..869c06f83d --- /dev/null +++ b/grafast/dataplan-pg/__tests__/queries/trap/trapped-empty-pg-select.errors.json5 @@ -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', + ], + }, +] \ No newline at end of file