From b21bb996796b487d7f369f7e162a0d3096ffaae4 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 9 Nov 2023 14:19:05 +0000 Subject: [PATCH 1/8] Add note about nullable variables with default values --- spec/Section 6 -- Execution.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index 8efd401a4..9d3fcaee5 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -645,6 +645,13 @@ Note: Variable values are not coerced because they are expected to be coerced before executing the operation in {CoerceVariableValues()}, and valid operations must only allow usage of variables of appropriate types. +Note: When a default value exists for a variable definition, the type of the +variable is allowed to be nullable even if it is used in a non-nullable +position, see +[Allowing Optional Variables When Default Values Exist](#sec-All-Variable-Usages-Are-Allowed.Allowing-Optional-Variables-When-Default-Values-Exist) +in Validation. If the value for a variable is explicitly {null} and is used in a +non-nullable position, a _field error_ will be raised. + ### Value Resolution While nearly all of GraphQL execution can be described generically, ultimately From a91cdba0dfedd0c13655f03cc3ac3910da388686 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 9 Nov 2023 15:07:33 +0000 Subject: [PATCH 2/8] Add variables to table --- spec/Section 3 -- Type System.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index e52519fb7..74f598399 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1773,18 +1773,18 @@ constructing the list. Following are examples of input coercion with various list types and values: -| Expected Type | Provided Value | Coerced Value | -| ------------- | ---------------- | --------------------------- | -| `[Int]` | `[1, 2, 3]` | `[1, 2, 3]` | -| `[Int]` | `[1, "b", true]` | Error: Incorrect item value | -| `[Int]` | `1` | `[1]` | -| `[Int]` | `null` | `null` | -| `[[Int]]` | `[[1], [2, 3]]` | `[[1], [2, 3]]` | -| `[[Int]]` | `[1, 2, 3]` | `[[1], [2], [3]]` | -| `[[Int]]` | `[1, null, 3]` | `[[1], null, [3]]` | -| `[[Int]]` | `[[1], ["b"]]` | Error: Incorrect item value | -| `[[Int]]` | `1` | `[[1]]` | -| `[[Int]]` | `null` | `null` | +| Expected Type | Literal Value | Variable Values | Coerced Value | +| ------------- | ---------------- | --------------- | --------------------------- | +| `[Int]` | `[1, 2, 3]` | `{}` | `[1, 2, 3]` | +| `[Int]` | `[1, "b", true]` | `{}` | Error: Incorrect item value | +| `[Int]` | `1` | `{}` | `[1]` | +| `[Int]` | `null` | `{}` | `null` | +| `[[Int]]` | `[[1], [2, 3]]` | `{}` | `[[1], [2, 3]]` | +| `[[Int]]` | `[1, 2, 3]` | `{}` | `[[1], [2], [3]]` | +| `[[Int]]` | `[1, null, 3]` | `{}` | `[[1], null, [3]]` | +| `[[Int]]` | `[[1], ["b"]]` | `{}` | Error: Incorrect item value | +| `[[Int]]` | `1` | `{}` | `[[1]]` | +| `[[Int]]` | `null` | `{}` | `null` | ## Non-Null From 4ca2023992ed5bb2aef2c58d4f532859fb580acd Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 9 Nov 2023 16:59:23 +0000 Subject: [PATCH 3/8] Algorithm for coercing list values --- spec/Section 3 -- Type System.md | 73 ++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 74f598399..c147257e2 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1771,20 +1771,69 @@ This allows inputs which accept one or many arguments (sometimes referred to as single value, a client can just pass that value directly rather than constructing the list. +The result of coercion of a value {value} to a list type {listType} is +{CoerceListValue(value, listType)}. + +CoerceListValue(value, listType): + +- If {value} is {null}, return {null}. +- Let {itemType} be the inner type of {listType}. +- Let {coercedList} be an empty list. +- If {value} is a list: + - For each {itemValue} in {value}: + - Let {coercedItemValue} be {CoerceListItemValue(itemValue, itemType)}. + - Append {coercedItemValue} to {coercedList}. +- Otherwise: + - Let {coercedItemValue} be {CoerceListItemValue(value, itemType)}. + - Append {coercedItemValue} to {coercedList}. +- Return {coercedList}. + +CoerceListItemValue(itemValue, itemType): + +- If {itemValue} is {null}, return {null}. +- Otherwise, if {itemValue} is a Variable: + - Let {runtimeValue} be the runtime value of that variable, or {null} if no + runtime value is provided. + - If {runtimeValue} is {null} and {itemType} is a non-null type, a _field + error_ must be raised. + - Return {runtimeValue}. +- Otherwise, return the result of coercing {itemValue} according to the input + coercion rules for {itemType}. + Following are examples of input coercion with various list types and values: -| Expected Type | Literal Value | Variable Values | Coerced Value | -| ------------- | ---------------- | --------------- | --------------------------- | -| `[Int]` | `[1, 2, 3]` | `{}` | `[1, 2, 3]` | -| `[Int]` | `[1, "b", true]` | `{}` | Error: Incorrect item value | -| `[Int]` | `1` | `{}` | `[1]` | -| `[Int]` | `null` | `{}` | `null` | -| `[[Int]]` | `[[1], [2, 3]]` | `{}` | `[[1], [2, 3]]` | -| `[[Int]]` | `[1, 2, 3]` | `{}` | `[[1], [2], [3]]` | -| `[[Int]]` | `[1, null, 3]` | `{}` | `[[1], null, [3]]` | -| `[[Int]]` | `[[1], ["b"]]` | `{}` | Error: Incorrect item value | -| `[[Int]]` | `1` | `{}` | `[[1]]` | -| `[[Int]]` | `null` | `{}` | `null` | +| Expected Type | Literal Value | Variable Values | Coerced Value | +| ------------- | ---------------- | --------------- | ---------------------------- | +| `[Int]` | `[1, 2, 3]` | `{}` | `[1, 2, 3]` | +| `[Int]` | `[1, null]` | `{}` | `[1, null]` | +| `[Int]` | `[1, "b", true]` | `{}` | Error: Incorrect item value | +| `[Int]` | `1` | `{}` | `[1]` | +| `[Int]` | `null` | `{}` | `null` | +| `[Int]` | `[1, $b]` | `{}` | `[1, null]` | +| `[Int]` | `[1, $b]` | `{"b": 2}` | `[1, 2]` | +| `[Int]` | `[1, $b]` | `{"b": null}` | `[1, null]` | +| `[Int]!` | `[null]` | `{}` | `[null]` | +| `[Int]!` | `null` | `{}` | Error: Must be non-null | +| `[Int!]` | `[1, 2, 3]` | `{}` | `[1, 2, 3]` | +| `[Int!]` | `[1, null]` | `{}` | Error: Item must be non-null | +| `[Int!]` | `[1, "b", true]` | `{}` | Error: Incorrect item value | +| `[Int!]` | `1` | `{}` | `[1]` | +| `[Int!]` | `null` | `{}` | `null` | +| `[Int!]` | `[1, $b]` | `{}` | Error: Item must be non-null | +| `[Int!]` | `[1, $b]` | `{"b": 2}` | `[1, 2]` | +| `[Int!]` | `[1, $b]` | `{"b": null}` | Error: Item must be non-null | +| `[[Int]]` | `[[1], [2, 3]]` | `{}` | `[[1], [2, 3]]` | +| `[[Int]]` | `[1, 2, 3]` | `{}` | `[[1], [2], [3]]` | +| `[[Int]]` | `[1, null, 3]` | `{}` | `[[1], null, [3]]` | +| `[[Int]]` | `[[1], ["b"]]` | `{}` | Error: Incorrect item value | +| `[[Int]]` | `1` | `{}` | `[[1]]` | +| `[[Int]]` | `null` | `{}` | `null` | +| `[[Int]]` | `[1, [$b]]` | `{}` | `[[1],[null]]` | +| `[[Int]]` | `[1, [$b]]` | `{"b": null}` | `[[1],[null]]` | +| `[[Int]]` | `[1, [$b]]` | `{"b": 2}` | `[[1],[2]]` | +| `[[Int]]` | `[1, $b]` | `{"b": [2]}` | `[[1],[2]]` | +| `[[Int]]` | `[1, $b]` | `{"b": 2}` | `[[1],[2]]` | +| `[[Int]]` | `[1, $b]` | `{"b": null}` | `[[1],null]` | ## Non-Null From ec3d50a664f02a3218c81bb8c3202c91765fa952 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 9 Nov 2023 17:14:38 +0000 Subject: [PATCH 4/8] Move note and clarify algorithm --- spec/Section 3 -- Type System.md | 20 ++++++++++++++++---- spec/Section 6 -- Execution.md | 7 ------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index c147257e2..2a1f783fd 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1792,14 +1792,26 @@ CoerceListItemValue(itemValue, itemType): - If {itemValue} is {null}, return {null}. - Otherwise, if {itemValue} is a Variable: - - Let {runtimeValue} be the runtime value of that variable, or {null} if no - runtime value is provided. - - If {runtimeValue} is {null} and {itemType} is a non-null type, a _field + - If the variable provides a runtime value: + - Let {coercedItemValue} be the runtime value of the variable. + - Otherwise, if the variable definition provides a default value: + - Let {coercedItemValue} be this default value. + - Otherwise: + - Let {coercedItemValue} be {null}. + - If {coercedItemValue} is {null} and {itemType} is a non-null type, a _field error_ must be raised. - - Return {runtimeValue}. + - Return {coercedItemValue}. - Otherwise, return the result of coercing {itemValue} according to the input coercion rules for {itemType}. +Note: When a default value exists for a variable definition, the type of the +variable is allowed to be nullable even if it is used in a non-nullable +position, see +[Allowing Optional Variables When Default Values Exist](#sec-All-Variable-Usages-Are-Allowed.Allowing-Optional-Variables-When-Default-Values-Exist) +in Validation. If the value for such a variable is explicitly {null} and is used +as the value for a list item of non-nullable type then a _field error_ will be +raised. + Following are examples of input coercion with various list types and values: | Expected Type | Literal Value | Variable Values | Coerced Value | diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index 9d3fcaee5..8efd401a4 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -645,13 +645,6 @@ Note: Variable values are not coerced because they are expected to be coerced before executing the operation in {CoerceVariableValues()}, and valid operations must only allow usage of variables of appropriate types. -Note: When a default value exists for a variable definition, the type of the -variable is allowed to be nullable even if it is used in a non-nullable -position, see -[Allowing Optional Variables When Default Values Exist](#sec-All-Variable-Usages-Are-Allowed.Allowing-Optional-Variables-When-Default-Values-Exist) -in Validation. If the value for a variable is explicitly {null} and is used in a -non-nullable position, a _field error_ will be raised. - ### Value Resolution While nearly all of GraphQL execution can be described generically, ultimately From 6aed5a9653a92ada8849fdf6e289623997977436 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Mon, 13 Nov 2023 09:26:27 +0000 Subject: [PATCH 5/8] Add another example --- spec/Section 3 -- Type System.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 2a1f783fd..7e9663946 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1836,6 +1836,7 @@ Following are examples of input coercion with various list types and values: | `[Int!]` | `[1, $b]` | `{"b": null}` | Error: Item must be non-null | | `[[Int]]` | `[[1], [2, 3]]` | `{}` | `[[1], [2, 3]]` | | `[[Int]]` | `[1, 2, 3]` | `{}` | `[[1], [2], [3]]` | +| `[[Int]]` | `[1, [2], 3]` | `{}` | `[[1], [2], [3]]` | | `[[Int]]` | `[1, null, 3]` | `{}` | `[[1], null, [3]]` | | `[[Int]]` | `[[1], ["b"]]` | `{}` | Error: Incorrect item value | | `[[Int]]` | `1` | `{}` | `[[1]]` | From fba35d54523692c5b112c8fc3c773f50ef402b43 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 28 Nov 2024 18:14:59 +0000 Subject: [PATCH 6/8] Fix bug in null handling --- spec/Section 3 -- Type System.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 7e9663946..7c07ae64d 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1790,8 +1790,7 @@ CoerceListValue(value, listType): CoerceListItemValue(itemValue, itemType): -- If {itemValue} is {null}, return {null}. -- Otherwise, if {itemValue} is a Variable: +- If {itemValue} is a Variable: - If the variable provides a runtime value: - Let {coercedItemValue} be the runtime value of the variable. - Otherwise, if the variable definition provides a default value: From 8eaa45ba7bff01359afc2e3b038a1842a0c110e7 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 23 Jan 2025 17:40:44 +0000 Subject: [PATCH 7/8] Reflect that variable values should already be coerced, and thus applying their defaults is redundant. --- spec/Section 3 -- Type System.md | 29 +++++++++++++++-------------- spec/Section 6 -- Execution.md | 3 +++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 7c07ae64d..b9f3f74a6 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1638,9 +1638,9 @@ defined by the input object type and for which a value exists. The resulting map is constructed with the following rules: - If no value is provided for a defined input object field and that field - definition provides a default value, the default value should be used. If no + definition provides a default value, the default value must be used. If no default value is provided and the input object field's type is non-null, an - error should be raised. Otherwise, if the field is not required, then no entry + error must be raised. Otherwise, if the field is not required, then no entry is added to the coerced unordered map. - If the value {null} was provided for an input object field, and the field's @@ -1652,12 +1652,17 @@ is constructed with the following rules: coerced unordered map is given the result of coercing that value according to the input coercion rules for the type of that field. -- If a variable is provided for an input object field, the runtime value of that - variable must be used. If the runtime value is {null} and the field type is - non-null, a _field error_ must be raised. If no runtime value is provided, the - variable definition's default value should be used. If the variable definition - does not provide a default value, the input object field definition's default - value should be used. +- If a variable is provided for an input object field: + + - If the _coerced runtime value_ of that variable exists then it must be used. + If the coerced runtime value is {null} and the field type is non-null, a + _field error_ must be raised. + + - If the _coerced runtime value_ of that variable does not exist then the + input object field definition's default value must be used. If no default + value is provided and the input object field's type is non-null, an error + must be raised. Otherwise, if the field is not required, then no entry is + added to the coerced unordered map. Following are examples of input coercion for an input object type with a `String` field `a` and a required (non-null) `Int!` field `b`: @@ -1791,12 +1796,8 @@ CoerceListValue(value, listType): CoerceListItemValue(itemValue, itemType): - If {itemValue} is a Variable: - - If the variable provides a runtime value: - - Let {coercedItemValue} be the runtime value of the variable. - - Otherwise, if the variable definition provides a default value: - - Let {coercedItemValue} be this default value. - - Otherwise: - - Let {coercedItemValue} be {null}. + - Let {coercedItemValue} be the _coerced runtime value_ of that variable, or + {null} if no such value exists. - If {coercedItemValue} is {null} and {itemType} is a non-null type, a _field error_ must be raised. - Return {coercedItemValue}. diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index 8efd401a4..a51939a41 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -81,6 +81,9 @@ need to be coerced using the input coercion rules of variable's declared type. If a _request error_ is encountered during input coercion of variable values, then the operation fails without execution. +:: The _coerced runtime value_ of a variable is the entry matching the +variable's name in the result of {CoerceVariableValues()}, if any. + CoerceVariableValues(schema, operation, variableValues): - Let {coercedValues} be an empty unordered Map. From a045f1493352f05570d39b8aa372ef0c5723961d Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 23 Jan 2025 17:46:39 +0000 Subject: [PATCH 8/8] 'Including null' (copied from section 6) --- spec/Section 3 -- Type System.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index b9f3f74a6..4be232544 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1654,9 +1654,9 @@ is constructed with the following rules: - If a variable is provided for an input object field: - - If the _coerced runtime value_ of that variable exists then it must be used. - If the coerced runtime value is {null} and the field type is non-null, a - _field error_ must be raised. + - If the _coerced runtime value_ of that variable exists (including {null}) + then it must be used. If the coerced runtime value is {null} and the field + type is non-null, a _field error_ must be raised. - If the _coerced runtime value_ of that variable does not exist then the input object field definition's default value must be used. If no default