From 2fc66dc530c407dad94a005d2dc50bbd4e6484a6 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 19 Nov 2025 23:09:56 +0100 Subject: [PATCH 01/30] Cleanup constants and helpers --- .../Text/Json/JsonConstants.cs | 81 ------------------- .../Fusion.Execution/Text/Json/JsonHelpers.cs | 22 ----- .../src/Json/HotChocolate.Text.Json.csproj | 4 - 3 files changed, 107 deletions(-) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs index 7fd0b4379d4..71dafe31735 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs @@ -13,48 +13,22 @@ internal static class JsonConstants public const byte LineFeed = (byte)'\n'; public const byte Tab = (byte)'\t'; public const byte Comma = (byte)','; - public const byte KeyValueSeparator = (byte)':'; public const byte Quote = (byte)'"'; public const byte BackSlash = (byte)'\\'; public const byte Slash = (byte)'/'; public const byte BackSpace = (byte)'\b'; public const byte FormFeed = (byte)'\f'; - public const byte Asterisk = (byte)'*'; public const byte Colon = (byte)':'; - public const byte Period = (byte)'.'; - public const byte Plus = (byte)'+'; - public const byte Hyphen = (byte)'-'; - public const byte UtcOffsetToken = (byte)'Z'; - public const byte TimePrefix = (byte)'T'; - public const byte NewLineLineFeed = (byte)'\n'; - // \u2028 and \u2029 are considered respectively line and paragraph separators - // UTF-8 representation for them is E2, 80, A8/A9 - public const byte StartingByteOfNonStandardSeparator = 0xE2; - public static ReadOnlySpan Data => "data"u8; public static ReadOnlySpan Errors => "errors"u8; public static ReadOnlySpan Extensions => "extensions"u8; - public static ReadOnlySpan Utf8Bom => [0xEF, 0xBB, 0xBF]; public static ReadOnlySpan TrueValue => "true"u8; public static ReadOnlySpan FalseValue => "false"u8; public static ReadOnlySpan NullValue => "null"u8; - public static ReadOnlySpan NaNValue => "NaN"u8; - public static ReadOnlySpan PositiveInfinityValue => "Infinity"u8; - public static ReadOnlySpan NegativeInfinityValue => "-Infinity"u8; - public const int MaximumFloatingPointConstantLength = 9; - - // Used to search for the end of a number - public static ReadOnlySpan Delimiters => ",}] \n\r\t/"u8; - - // Explicitly skipping ReverseSolidus since that is handled separately - public static ReadOnlySpan EscapableChars => "\"nrt/ubf"u8; - - public const int RemoveFlagsBitMask = 0x7FFFFFFF; - // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. // For example: '+' becomes '\u0043' // Escaping surrogate pairs (represented by 3 or 4 utf-8 bytes) would expand to 12 bytes (which is still <= 6x). @@ -66,65 +40,10 @@ internal static class JsonConstants // All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes. public const int MaxExpansionFactorWhileTranscoding = 3; - // When transcoding from UTF8 -> UTF16, the byte count threshold where we rent from the array pool before performing a normal alloc. - public const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = -#if NET - 1024 * 1024 * 1024; // ArrayPool limit increased in .NET 6 -#else - 1024 * 1024; -#endif - - // The maximum number of characters allowed when writing raw UTF-16 JSON. This is the maximum length that we can guarantee can - // be safely transcoded to UTF-8 and fit within an integer-length span, given the max expansion factor of a single character (3). - public const int MaxUtf16RawValueLength = int.MaxValue / MaxExpansionFactorWhileTranscoding; - - public const int MaxEscapedTokenSize = 1_000_000_000; // Max size for already escaped value. - public const int MaxUnescapedTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 bytes - public const int MaxCharacterTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 characters - - public const int MaximumFormatBooleanLength = 5; - public const int MaximumFormatInt64Length = 20; // 19 + sign (i.e. -9223372036854775808) - public const int MaximumFormatUInt32Length = 10; // i.e. 4294967295 - public const int MaximumFormatUInt64Length = 20; // i.e. 18446744073709551615 - public const int MaximumFormatDoubleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof. - public const int MaximumFormatSingleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof. - public const int MaximumFormatDecimalLength = 31; // default (i.e. 'G') - public const int MaximumFormatGuidLength = 36; // default (i.e. 'D'), 8 + 4 + 4 + 4 + 12 + 4 for the hyphens (e.g. 094ffa0a-0442-494d-b452-04003fa755cc) - public const int MaximumEscapedGuidLength = MaxExpansionFactorWhileEscaping * MaximumFormatGuidLength; - public const int MaximumFormatDateTimeLength = 27; // StandardFormat 'O', e.g. 2017-06-12T05:30:45.7680000 - public const int MaximumFormatDateTimeOffsetLength = 33; // StandardFormat 'O', e.g. 2017-06-12T05:30:45.7680000-07:00 - public const int MaxDateTimeUtcOffsetHours = 14; // The UTC offset portion of a TimeSpan or DateTime can be no more than 14 hours and no less than -14 hours. - public const int DateTimeNumFractionDigits = 7; // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. - public const int MaxDateTimeFraction = 9_999_999; // The largest fraction expressible by TimeSpan and DateTime formats - public const int DateTimeParseNumFractionDigits = 16; // The maximum number of fraction digits the Json DateTime parser allows - public const int MaximumDateTimeOffsetParseLength = MaximumFormatDateTimeOffsetLength - + (DateTimeParseNumFractionDigits - DateTimeNumFractionDigits); // Like StandardFormat 'O' for DateTimeOffset, but allowing 9 additional (up to 16) fraction digits. - public const int MinimumDateTimeParseLength = 10; // YYYY-MM-DD - public const int MaximumEscapedDateTimeOffsetParseLength = MaxExpansionFactorWhileEscaping * MaximumDateTimeOffsetParseLength; - - public const int MaximumLiteralLength = 5; // Must be able to fit null, true, & false. - - // Encoding Helpers - public const char HighSurrogateStart = '\ud800'; - public const char HighSurrogateEnd = '\udbff'; - public const char LowSurrogateStart = '\udc00'; - public const char LowSurrogateEnd = '\udfff'; - public const int UnicodePlane01StartValue = 0x10000; public const int HighSurrogateStartValue = 0xD800; public const int HighSurrogateEndValue = 0xDBFF; public const int LowSurrogateStartValue = 0xDC00; public const int LowSurrogateEndValue = 0xDFFF; public const int BitShiftBy10 = 0x400; - - // The maximum number of parameters a constructor can have where it can be considered - // for a path on deserialization where we don't box the constructor arguments. - public const int UnboxedParameterCountThreshold = 4; - - // Two space characters is the default indentation. - public const char DefaultIndentCharacter = ' '; - public const char TabIndentCharacter = '\t'; - public const int DefaultIndentSize = 2; - public const int MinimumIndentSize = 0; - public const int MaximumIndentSize = 127; // If this value is changed, the impact on the options masking used in the JsonWriterOptions struct must be checked carefully. } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs index 6de1d7c5347..f8d9b5a8b12 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs @@ -4,13 +4,6 @@ namespace HotChocolate.Fusion.Text.Json; internal static class JsonHelpers { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidDateTimeOffsetParseLength(int length) - => IsInRangeInclusive( - length, - JsonConstants.MinimumDateTimeParseLength, - JsonConstants.MaximumEscapedDateTimeOffsetParseLength); - /// /// Returns if is between /// and , inclusive. @@ -18,19 +11,4 @@ public static bool IsValidDateTimeOffsetParseLength(int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRangeInclusive(uint value, uint lowerBound, uint upperBound) => (value - lowerBound) <= (upperBound - lowerBound); - - /// - /// Returns if is between - /// and , inclusive. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) - => (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidUnescapedDateTimeOffsetParseLength(int length) - => IsInRangeInclusive( - length, - JsonConstants.MinimumDateTimeParseLength, - JsonConstants.MaximumDateTimeOffsetParseLength); } diff --git a/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj b/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj index c66c31fec8c..3366abcbd96 100644 --- a/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj +++ b/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj @@ -6,8 +6,4 @@ preview - - - - From e133a9b8436dadf123e94858cf8d1c285a25f750 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 20 Nov 2025 08:54:55 +0100 Subject: [PATCH 02/30] Copied metadb to the core --- .../Core/src/Types/HotChocolate.Types.csproj | 38 +++++ .../src/Types/Text/Json/ElementTokenType.cs | 21 +++ .../Types/Text/Json/ResultDocument.DbRow.cs | 157 ++++++++++++++++++ .../Json/CompositeResultDocument.DbRow.cs | 52 ++++-- .../Text/Json/JsonConstants.cs | 4 + .../Fusion.Execution/Text/Json/JsonHelpers.cs | 4 + 6 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index c97a2503d2a..efa4a1d2e5a 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -67,6 +67,40 @@ + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonHelpers.cs + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonConstants.cs + + @@ -152,4 +186,8 @@ + + + + diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs b/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs new file mode 100644 index 00000000000..095f8521b55 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs @@ -0,0 +1,21 @@ +namespace HotChocolate.Text.Json; + +internal enum ElementTokenType : byte +{ + None = 0, + StartObject = 1, + EndObject = 2, + StartArray = 3, + EndArray = 4, + PropertyName = 5, + // Retained for compatibility, we do not actually need this. + Comment = 6, + String = 7, + Number = 8, + True = 9, + False = 10, + Null = 11, + // A reference in case a property or array element point + // to an array or an object + Reference = 12 +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs new file mode 100644 index 00000000000..701c7734e40 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs @@ -0,0 +1,157 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + [StructLayout(LayoutKind.Sequential)] + internal readonly struct DbRow + { + public const int Size = 20; + public const int UnknownSize = -1; + + // 27 bits for location + 2 bits OpRefType + 3 reserved bits + private readonly int _locationAndOpRefType; + + // Sign bit for HasComplexChildren + 31 bits for size/length + private readonly int _sizeOrLengthUnion; + + // 4 bits TokenType + 27 bits NumberOfRows + 1 reserved bit + private readonly int _numberOfRowsTypeAndReserved; + + // 15 bits SourceDocumentId + 17 bits (high 17 bits of ParentRow) + private readonly int _sourceAndParentHigh; + + // 15 bits OperationReferenceId + 6 bits Flags + 11 bits (low bits of ParentRow) + private readonly int _selectionSetFlagsAndParentLow; + + public DbRow( + ElementTokenType tokenType, + int location, + int sizeOrLength = 0, + int sourceDocumentId = 0, + int parentRow = 0, + int operationReferenceId = 0, + OperationReferenceType operationReferenceType = OperationReferenceType.None, + int numberOfRows = 0, + ElementFlags flags = ElementFlags.None) + { + Debug.Assert((byte)tokenType < 16); + Debug.Assert(location is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert(sizeOrLength >= UnknownSize); + Debug.Assert(sourceDocumentId is >= 0 and <= 0x7FFF); // 15 bits + Debug.Assert(parentRow is >= 0 and <= 0x0FFFFFFF); // 28 bits + Debug.Assert(operationReferenceId is >= 0 and <= 0x7FFF); // 15 bits + Debug.Assert(numberOfRows is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert((byte)flags <= 63); // 6 bits (0x3F) + Debug.Assert((byte)operationReferenceType <= 3); // 2 bits + Debug.Assert(Unsafe.SizeOf() == Size); + + _locationAndOpRefType = location | ((int)operationReferenceType << 27); + _sizeOrLengthUnion = sizeOrLength; + _numberOfRowsTypeAndReserved = ((int)tokenType << 28) | (numberOfRows & 0x07FFFFFF); + _sourceAndParentHigh = sourceDocumentId | ((parentRow >> 11) << 15); + _selectionSetFlagsAndParentLow = operationReferenceId | ((int)flags << 15) | ((parentRow & 0x7FF) << 21); + } + + /// + /// Element token type (includes Reference for composition). + /// + /// + /// 4 bits = possible values + /// + public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28); + + /// + /// Operation reference type indicating the type of GraphQL operation element. + /// + /// + /// 2 bits = 4 possible values + /// + public OperationReferenceType OperationReferenceType + => (OperationReferenceType)((_locationAndOpRefType >> 27) & 0x03); + + /// + /// Byte offset in source data OR metaDb row index for references. + /// + /// + /// 2 bits = 4 possible values + /// + public int Location => _locationAndOpRefType & 0x07FFFFFF; + + /// + /// Length of data in JSON payload, number of elements if array or number of properties in an object. + /// + /// + /// 27 bits = 134M limit + /// + public int SizeOrLength => _sizeOrLengthUnion & int.MaxValue; + + /// + /// String/PropertyName: Unescaping required. + /// + public bool HasComplexChildren => _sizeOrLengthUnion < 0; + + /// + /// Specifies if a size for the item has ben set. + /// + public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize; + + /// + /// Number of metadb rows this element spans. + /// + /// + /// 27 bits = 134M rows + /// + public int NumberOfRows => _numberOfRowsTypeAndReserved & 0x07FFFFFF; + + /// + /// Index of parent element in metadb for navigation and null propagation. + /// + /// + /// 28 bits = 268M rows + /// + public int ParentRow + => ((int)((uint)_sourceAndParentHigh >> 15) << 11) | ((_selectionSetFlagsAndParentLow >> 21) & 0x7FF); + + /// + /// Reference to GraphQL selection set or selection metadata. + /// 15 bits = 32K selections + /// + public int OperationReferenceId => _selectionSetFlagsAndParentLow & 0x7FFF; + + /// + /// Element metadata flags. + /// + /// + /// 6 bits = 64 combinations + /// + public ElementFlags Flags => (ElementFlags)((_selectionSetFlagsAndParentLow >> 15) & 0x3F); + + /// + /// True for primitive JSON values (strings, numbers, booleans, null). + /// + public bool IsSimpleValue => TokenType is >= ElementTokenType.PropertyName and <= ElementTokenType.Null; + } + + internal enum OperationReferenceType : byte + { + None = 0, + SelectionSet = 1, + Selection = 2 + } + + [Flags] + internal enum ElementFlags : byte + { + None = 0, + Invalidated = 1, + SourceResult = 2, + IsNullable = 4, + IsRoot = 8, + IsInternal = 16, + IsExcluded = 32 + } +} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs index 08fdf7eeff7..42785ef9614 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs @@ -57,67 +57,87 @@ public DbRow( } /// - /// Byte offset in source data OR metaDb row index for references. - /// 27 bits = 134M limit (increased from 26 bits / 67M limit) + /// Element token type (includes Reference for composition). /// - public int Location => _locationAndOpRefType & 0x07FFFFFF; + /// + /// 4 bits = possible values + /// + public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28); /// /// Operation reference type indicating the type of GraphQL operation element. - /// 2 bits = 4 possible values /// + /// + /// 2 bits = 4 possible values + /// public OperationReferenceType OperationReferenceType => (OperationReferenceType)((_locationAndOpRefType >> 27) & 0x03); + /// + /// Byte offset in source data OR metaDb row index for references + /// + /// + /// 27 bits = 134M limit + /// + public int Location => _locationAndOpRefType & 0x07FFFFFF; + /// /// Length of data in JSON payload, number of elements if array or number of properties in an object. - /// 31 bits = 2GB limit /// + /// + /// 31 bits = 2GB limit + /// public int SizeOrLength => _sizeOrLengthUnion & int.MaxValue; /// /// String/PropertyName: Unescaping required. - /// Array: Contains complex children. /// public bool HasComplexChildren => _sizeOrLengthUnion < 0; - public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize; - /// - /// Element token type (includes Reference for composition). - /// 4 bits = 16 types + /// Specifies if a size for the item has ben set. /// - public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28); + public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize; /// /// Number of metadb rows this element spans. - /// 27 bits = 134M rows /// + /// + /// 27 bits = 134M rows + /// public int NumberOfRows => _numberOfRowsTypeAndReserved & 0x07FFFFFF; /// /// Which source JSON document contains the data. - /// 15 bits = 32K documents /// + /// + /// 15 bits = 32K documents + /// public int SourceDocumentId => _sourceAndParentHigh & 0x7FFF; /// /// Index of parent element in metadb for navigation and null propagation. - /// 28 bits = 268M rows (reconstructed from high+low bits) /// + /// + /// 28 bits = 268M rows + /// public int ParentRow => ((int)((uint)_sourceAndParentHigh >> 15) << 11) | ((_selectionSetFlagsAndParentLow >> 21) & 0x7FF); /// /// Reference to GraphQL selection set or selection metadata. - /// 15 bits = 32K selections /// + /// + /// 15 bits = 32K selections + /// public int OperationReferenceId => _selectionSetFlagsAndParentLow & 0x7FFF; /// /// Element metadata flags. - /// 6 bits = 64 combinations /// + /// + /// 6 bits = 64 combinations + /// public ElementFlags Flags => (ElementFlags)((_selectionSetFlagsAndParentLow >> 15) & 0x3F); /// diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs index 71dafe31735..108aa42d801 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs @@ -1,4 +1,8 @@ +#if Fusion namespace HotChocolate.Fusion.Text.Json; +#else +namespace HotChocolate.Text.Json; +#endif internal static class JsonConstants { diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs index f8d9b5a8b12..e824b996bab 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs @@ -1,6 +1,10 @@ using System.Runtime.CompilerServices; +#if Fusion namespace HotChocolate.Fusion.Text.Json; +#else +namespace HotChocolate.Text.Json; +#endif internal static class JsonHelpers { From baac8d853206507d02d9985e9e3b2e1288ee60e2 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 20 Nov 2025 09:17:13 +0100 Subject: [PATCH 03/30] Added metabd --- .../Core/src/Types/HotChocolate.Types.csproj | 6 +- .../Json/CompositeResultDocument.MetaDb.cs | 383 ++++++++++++++++++ .../Types/Text/Json/ResultDocument.Cursor.cs | 171 ++++++++ .../Text/Json/JsonConstants.cs | 2 +- .../Fusion.Execution/Text/Json/JsonHelpers.cs | 2 +- .../Text/Json/MetaDbEventSource.cs | 6 + 6 files changed, 565 insertions(+), 5 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index efa4a1d2e5a..ebf81309726 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -67,7 +67,7 @@ - + Text\Json\JsonConstants.cs @@ -77,8 +77,8 @@ Text\Json\JsonHelpers.cs - - Text\Json\JsonConstants.cs + + Text\Json\MetaDbEventSource.cs diff --git a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs new file mode 100644 index 00000000000..5f6561ac0ec --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs @@ -0,0 +1,383 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static HotChocolate.Text.Json.MetaDbEventSource; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal struct MetaDb : IDisposable + { + private const int TokenTypeOffset = 8; + private static readonly ArrayPool s_arrayPool = ArrayPool.Shared; + + private byte[][] _chunks; + private Cursor _next; + private bool _disposed; + + internal static MetaDb CreateForEstimatedRows(int estimatedRows) + { + var chunksNeeded = Math.Max(4, (estimatedRows / Cursor.RowsPerChunk) + 1); + var chunks = s_arrayPool.Rent(chunksNeeded); + var log = Log; + + log.MetaDbCreated(2, estimatedRows, 1); + + // Rent the first chunk now to avoid branching on first append + chunks[0] = MetaDbMemory.Rent(); + log.ChunkAllocated(2, 0); + + for (var i = 1; i < chunks.Length; i++) + { + chunks[i] = []; + } + + return new MetaDb + { + _chunks = chunks, + _next = Cursor.Zero + }; + } + + public Cursor NextCursor => _next; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Cursor Append( + ElementTokenType tokenType, + int location = 0, + int sizeOrLength = 0, + int sourceDocumentId = 0, + int parentRow = 0, + int operationReferenceId = 0, + OperationReferenceType operationReferenceType = OperationReferenceType.None, + int numberOfRows = 0, + ElementFlags flags = ElementFlags.None) + { + var log = Log; + var next = _next; + var chunkIndex = next.Chunk; + var byteOffset = next.ByteOffset; + + var chunks = _chunks.AsSpan(); + var chunksLength = chunks.Length; + + if (byteOffset + DbRow.Size > Cursor.ChunkBytes) + { + chunkIndex++; + byteOffset = 0; + next = Cursor.FromByteOffset(chunkIndex, byteOffset); + } + + // make sure we have enough space for the chunk referenced by the chunkIndex. + if (chunkIndex >= chunksLength) + { + // if we do not have enough space we will double the size we have for + // chunks of memory. + var nextChunksLength = chunksLength * 2; + var newChunks = s_arrayPool.Rent(nextChunksLength); + log.ChunksExpanded(2, chunksLength, nextChunksLength); + + // copy chunks to new buffer + Array.Copy(_chunks, newChunks, chunksLength); + + for (var i = chunksLength; i < nextChunksLength; i++) + { + newChunks[i] = []; + } + + // clear and return old chunks buffer + chunks.Clear(); + s_arrayPool.Return(_chunks); + + // assign new chunks buffer + _chunks = newChunks; + chunks = newChunks.AsSpan(); + } + + var chunk = chunks[chunkIndex]; + + // if the chunk is empty we did not yet rent any memory for it + if (chunk.Length == 0) + { + chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + log.ChunkAllocated(2, chunkIndex); + } + + var row = new DbRow( + tokenType, + location, + sizeOrLength, + sourceDocumentId, + parentRow, + operationReferenceId, + operationReferenceType, + numberOfRows, + flags); + + ref var dest = ref MemoryMarshal.GetArrayDataReference(chunk); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, byteOffset), row); + + // Advance write head by one row + _next = next + 1; + return next; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Replace( + Cursor cursor, + ElementTokenType tokenType, + int location = 0, + int sizeOrLength = 0, + int sourceDocumentId = 0, + int parentRow = 0, + int operationReferenceId = 0, + OperationReferenceType operationReferenceType = OperationReferenceType.None, + int numberOfRows = 0, + ElementFlags flags = ElementFlags.None) + { + AssertValidCursor(cursor); + + var row = new DbRow( + tokenType, + location, + sizeOrLength, + sourceDocumentId, + parentRow, + operationReferenceId, + operationReferenceType, + numberOfRows, + flags); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + MemoryMarshal.Write(span, in row); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal DbRow Get(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + return MemoryMarshal.Read(span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal (Cursor, ElementTokenType) GetStartCursor(Cursor cursor) + { + AssertValidCursor(cursor); + + var chunks = _chunks.AsSpan(); + var span = chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + var union = MemoryMarshal.Read(span[TokenTypeOffset..]); + var tokenType = (ElementTokenType)(union >> 28); + + if (tokenType is ElementTokenType.Reference) + { + var index = MemoryMarshal.Read(span) & 0x07FFFFFF; + cursor = Cursor.FromIndex(index); + span = chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); + union = MemoryMarshal.Read(span); + tokenType = (ElementTokenType)(union >> 28); + } + + return (cursor, tokenType); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetLocation(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + var locationAndOpRefType = MemoryMarshal.Read(span); + return locationAndOpRefType & 0x07FFFFFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Cursor GetLocationCursor(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + var locationAndOpRefType = MemoryMarshal.Read(span); + return Cursor.FromIndex(locationAndOpRefType & 0x07FFFFFF); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetParent(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + var sourceAndParentHigh = MemoryMarshal.Read(span[12..]); + var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]); + + return (sourceAndParentHigh >>> 15 << 11) + | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Cursor GetParentCursor(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); + + var sourceAndParentHigh = MemoryMarshal.Read(span[12..]); + var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]); + + var index = (sourceAndParentHigh >>> 15 << 11) + | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF); + + return Cursor.FromIndex(index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetNumberOfRows(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); + + var value = MemoryMarshal.Read(span); + return value & 0x0FFFFFFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ElementFlags GetFlags(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); + + var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span); + return (ElementFlags)((selectionSetFlagsAndParentLow >> 15) & 0x3F); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetFlags(Cursor cursor, ElementFlags flags) + { + AssertValidCursor(cursor); + Debug.Assert((byte)flags <= 63, "Flags value exceeds 6-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); + var currentValue = MemoryMarshal.Read(fieldSpan); + + var clearedValue = currentValue & 0xFFE07FFF; // ~(0x3F << 15) + var newValue = (int)(clearedValue | (uint)((int)flags << 15)); + + MemoryMarshal.Write(fieldSpan, newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetSizeOrLength(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); + var value = MemoryMarshal.Read(span); + + return value & int.MaxValue; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) + { + AssertValidCursor(cursor); + Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= int.MaxValue, "SizeOrLength value exceeds 31-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); + var currentValue = MemoryMarshal.Read(fieldSpan); + + // Keep only the sign bit (HasComplexChildren) + var clearedValue = currentValue & unchecked((int)0x80000000); + var newValue = clearedValue | (sizeOrLength & int.MaxValue); + + MemoryMarshal.Write(fieldSpan, newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetNumberOfRows(Cursor cursor, int numberOfRows) + { + AssertValidCursor(cursor); + Debug.Assert(numberOfRows >= 0 && numberOfRows <= 0x0FFFFFFF, "NumberOfRows value exceeds 28-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); + var currentValue = MemoryMarshal.Read(fieldSpan); + + // Keep only the top 4 bits (token type) + var clearedValue = currentValue & unchecked((int)0xF0000000); + var newValue = clearedValue | (numberOfRows & 0x0FFFFFFF); + + MemoryMarshal.Write(fieldSpan, newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ElementTokenType GetElementTokenType(Cursor cursor, bool resolveReferences = true) + { + AssertValidCursor(cursor); + + var union = MemoryMarshal.Read(_chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset)); + var tokenType = (ElementTokenType)(union >> 28); + + if (resolveReferences && tokenType == ElementTokenType.Reference) + { + var idx = GetLocation(cursor); + var resolved = Cursor.FromIndex(idx); + union = MemoryMarshal.Read(_chunks[resolved.Chunk].AsSpan(resolved.ByteOffset + TokenTypeOffset)); + tokenType = (ElementTokenType)(union >> 28); + } + + return tokenType; + } + + [Conditional("DEBUG")] + private void AssertValidCursor(Cursor cursor) + { + Debug.Assert(cursor.Chunk >= 0, "Negative chunk"); + Debug.Assert(cursor.Chunk < _chunks.Length, "Chunk index out of bounds"); + Debug.Assert(_chunks[cursor.Chunk].Length > 0, "Accessing unallocated chunk"); + + var maxExclusive = _next.Chunk * Cursor.RowsPerChunk + _next.Row; + var absoluteIndex = (cursor.Chunk * Cursor.RowsPerChunk) + cursor.Row; + + Debug.Assert(absoluteIndex >= 0 && absoluteIndex < maxExclusive, + $"Cursor points to row {absoluteIndex}, but only {maxExclusive} rows are valid."); + Debug.Assert(cursor.ByteOffset + DbRow.Size <= MetaDbMemory.BufferSize, "Cursor byte offset out of bounds"); + } + + public void Dispose() + { + if (!_disposed) + { + var cursor = _next; + var chunksLength = cursor.Chunk + 1; + var chunks = _chunks.AsSpan(0, chunksLength); + Log.MetaDbDisposed(2, chunksLength, cursor.Row); + + foreach (var chunk in chunks) + { + if (chunk.Length == 0) + { + break; + } + + MetaDbMemory.Return(chunk); + } + + chunks.Clear(); + s_arrayPool.Return(_chunks); + + _chunks = []; + _disposed = true; + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs new file mode 100644 index 00000000000..4e8ee11677b --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs @@ -0,0 +1,171 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + /// + /// Comparable MetaDb cursor (chunk, row) + /// + [StructLayout(LayoutKind.Sequential, Size = 4)] + internal readonly struct Cursor : IEquatable, IComparable + { + public const int ChunkBytes = 1 << 17; + public const int RowsPerChunk = ChunkBytes / DbRow.Size; + public const int MaxChunks = 4096; + + private const int RowBits = 14; + private const int ChunkBits = 12; + private const int ChunkShift = RowBits; + + private const uint RowMask = (1u << RowBits) - 1u; + private const uint ChunkMask = (1u << ChunkBits) - 1u; + + private readonly uint _value; + + public static readonly Cursor Zero = From(0, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor(uint value) => _value = value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor From(int chunkIndex, int rowWithinChunk) + { + Debug.Assert((uint)chunkIndex < MaxChunks); + Debug.Assert((uint)rowWithinChunk < RowsPerChunk); + return new Cursor(((uint)chunkIndex << ChunkShift) | (uint)rowWithinChunk); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor FromIndex(int rowIndex) + { + Debug.Assert(rowIndex >= 0); + var chunk = rowIndex / RowsPerChunk; + var row = rowIndex - (chunk * RowsPerChunk); + return From(chunk, row); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor FromByteOffset(int chunkIndex, int byteOffset) + { + Debug.Assert(byteOffset % DbRow.Size == 0, "byteOffset must be row-aligned."); + return From(chunkIndex, byteOffset / DbRow.Size); + } + + public uint Value => _value; + + public int Chunk + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (int)((_value >> ChunkShift) & ChunkMask); + } + + public int Row + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (int)(_value & RowMask); + } + + public int ByteOffset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Row * DbRow.Size; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cursor AddRows(int delta) + { + if (delta == 0) + { + return this; + } + + var row = Row + delta; + var chunk = Chunk; + + if (row >= RowsPerChunk) + { + var carry = row / RowsPerChunk; + row -= carry * RowsPerChunk; + chunk += carry; + } + else if (row < 0) + { + var borrow = (-row + RowsPerChunk - 1) / RowsPerChunk; + row += borrow * RowsPerChunk; + chunk -= borrow; + } + + if (chunk < 0) + { + Debug.Fail("Cursor underflow"); + chunk = 0; + row = 0; + } + else if (chunk >= MaxChunks) + { + Debug.Fail("Cursor overflow"); + chunk = MaxChunks - 1; + row = RowsPerChunk - 1; + } + + return From(chunk, row); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cursor WithChunk(int chunk) => From(chunk, Row); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cursor WithRow(int row) => From(Chunk, row); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ToIndex() => (Chunk * RowsPerChunk) + Row; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ToTotalBytes() => (Chunk * ChunkBytes) + ByteOffset; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Cursor other) => _value == other._value; + + public override bool Equals(object? obj) => obj is Cursor c && Equals(c); + + public override int GetHashCode() => (int)_value; + + public override string ToString() => $"chunk={Chunk}, row={Row} (0x{_value:X8})"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(Cursor other) => _value.CompareTo(other._value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Cursor a, Cursor b) => a._value == b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Cursor a, Cursor b) => a._value != b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <(Cursor a, Cursor b) => a._value < b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >(Cursor a, Cursor b) => a._value > b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator <=(Cursor a, Cursor b) => a._value <= b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator >=(Cursor a, Cursor b) => a._value >= b._value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor operator +(Cursor x, int delta) => x.AddRows(delta); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor operator -(Cursor x, int delta) => x.AddRows(-delta); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor operator ++(Cursor x) => x.AddRows(1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Cursor operator --(Cursor x) => x.AddRows(-1); + } +} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs index 108aa42d801..9201cc9fc5d 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs @@ -1,4 +1,4 @@ -#if Fusion +#if FUSION namespace HotChocolate.Fusion.Text.Json; #else namespace HotChocolate.Text.Json; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs index e824b996bab..4a281fc2966 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -#if Fusion +#if FUSION namespace HotChocolate.Fusion.Text.Json; #else namespace HotChocolate.Text.Json; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs index 8135fd7c4f7..9394f2d213d 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs @@ -1,8 +1,14 @@ using System.Diagnostics.Tracing; +#if FUSION namespace HotChocolate.Fusion.Text.Json; [EventSource(Name = "HotChocolate-Fusion-MetaDb")] +#else +namespace HotChocolate.Text.Json; + +[EventSource(Name = "HotChocolate-MetaDb")] +#endif internal sealed class MetaDbEventSource : EventSource { public static readonly MetaDbEventSource Log = new(); From 269436beda8f2b6bd2191fcdb743975ba409f7e1 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 26 Nov 2025 19:47:43 +0100 Subject: [PATCH 04/30] Merge Types Fetching and Execution --- src/All.slnx | 2 - .../CookieCrumble.HotChocolate.csproj | 1 - .../HotChocolate.Adapters.Mcp.csproj | 1 - .../src/Transport.Http/GraphQLHttpResponse.cs | 8 +- .../JsonLines/JsonLinesReader.cs | 3 +- .../src/Transport.Http/Sse/SseEventParser.cs | 2 +- .../src/Transport.Http/Sse/SseReader.cs | 7 +- .../src/Caching/HotChocolate.Caching.csproj | 1 - src/HotChocolate/Core/HotChocolate.Core.slnx | 2 - .../HotChocolate.Authorization.csproj | 2 +- .../Core/src/Core/HotChocolate.Core.csproj | 2 - .../HotChocolate.Execution.Projections.csproj | 2 +- .../Execution/HotChocolate.Execution.csproj | 247 -------- .../src/Fetching/HotChocolate.Fetching.csproj | 40 -- .../HotChocolate.Types.Errors.csproj | 2 +- .../Types.Json/HotChocolate.Types.Json.csproj | 1 - .../HotChocolate.Types.Mutations.csproj | 2 +- ...HotChocolate.Types.OffsetPagination.csproj | 1 - .../HotChocolate.Types.Queries.csproj | 2 +- .../Execution/Caching/DefaultDocumentCache.cs | 0 .../Caching/DefaultPreparedOperationCache.cs | 0 .../Caching/IPreparedOperationCache.cs | 0 .../Configuration/ConfigurationContext.cs | 0 .../ConfigureRequestExecutorSetup.cs | 0 .../DefaultRequestExecutorBuilder.cs | 0 .../IConfigureRequestExecutorSetup.cs | 0 .../Execution/Configuration/ITypeModule.cs | 0 ...OnConfigureRequestExecutorOptionsAction.cs | 0 .../OnConfigureSchemaBuilderAction.cs | 0 .../OnConfigureSchemaServices.cs | 0 .../OnRequestExecutorCreatedAction.cs | 0 .../OnRequestExecutorEvictedAction.cs | 0 .../Configuration/RequestExecutorSetup.cs | 0 .../RootServiceProviderAccessor.cs | 0 .../DefaultRequestContextAccessor.cs | 0 .../Execution/DefaultRequestExecutor.cs | 0 .../DeferredWorkStateOwnerFactory.cs | 0 .../DependencyInjection/Factories/IFactory.cs | 0 .../Factories/OperationContextFactory.cs | 0 .../Factories/OperationContextOwnerFactory.cs | 0 .../Factories/PooledServiceFactory.cs | 0 ...ternalSchemaServiceCollectionExtensions.cs | 0 .../InternalServiceCollectionExtensions.cs | 0 ...RequestExecutorBuilderExtensions.Caches.cs | 0 ...uestExecutorBuilderExtensions.Composite.cs | 0 ...estExecutorBuilderExtensions.DataLoader.cs | 0 ...stExecutorBuilderExtensions.ErrorFilter.cs | 0 ...equestExecutorBuilderExtensions.Hashing.cs | 0 ...tExecutorBuilderExtensions.IdSerializer.cs | 0 ...stExecutorBuilderExtensions.InputParser.cs | 0 ...ecutorBuilderExtensions.Instrumentation.cs | 0 ...uestExecutorBuilderExtensions.Optimizer.cs | 0 ...questExecutorBuilderExtensions.Services.cs | 0 ...cutorBuilderExtensions.TransactionScope.cs | 0 ...xecutorBuilderExtensions.TypeConversion.cs | 0 ...ExecutorBuilderExtensions.TypeDiscovery.cs | 0 ...estExecutorBuilderExtensions.UseRequest.cs | 0 ...estExecutorBuilderExtensions.Validation.cs | 0 .../RequestExecutorBuilderExtensions.cs | 0 ...uestExecutorServiceCollectionExtensions.cs | 0 ...equestExecutorServiceProviderExtensions.cs | 0 ...estExecutorBuilderExtensions.Convention.cs | 0 ...questExecutorBuilderExtensions.Document.cs | 0 ...estExecutorBuilderExtensions.Middleware.cs | 0 ...RequestExecutorBuilderExtensions.Paging.cs | 0 ...aRequestExecutorBuilderExtensions.Relay.cs | 0 ...uestExecutorBuilderExtensions.Resolvers.cs | 0 ...ecutorBuilderExtensions.TypeInterceptor.cs | 0 ...stExecutorBuilderExtensions.TypeModules.cs | 0 ...aRequestExecutorBuilderExtensions.Types.cs | 0 .../SchemaRequestExecutorBuilderExtensions.cs | 0 .../src/{ => Types}/Execution/ErrorHelper.cs | 0 .../Errors/FuncErrorFilterWrapper.cs | 0 ...xecutionObjectFieldDescriptorExtensions.cs | 0 .../ExecutionRequestExecutorExtensions.cs | 0 .../Extensions/ExecutionResultExtensions.cs | 0 .../Extensions/ExecutionSchemaExtensions.cs | 0 ...colateExecutionRequestContextExtensions.cs | 0 .../Extensions/OperationContextExtensions.cs | 0 .../Execution/IRequestContextAccessor.cs | 0 .../Execution/IRequestContextEnricher.cs | 0 .../AggregateExecutionDiagnosticEvents.cs | 0 .../ExecutionDiagnosticEventListener.cs | 0 .../IExecutionDiagnosticEventListener.cs | 0 .../IExecutionDiagnosticEvents.cs | 0 .../NoopExecutionDiagnosticEvents.cs | 0 .../Internal/ArgumentCoercionHelper.cs | 0 .../Internal/MiddlewareContextMarshal.cs | 0 .../Execution/Internal/SchemaFileExporter.cs | 0 .../{ => Types}/Execution/NeedsFormatting.cs | 0 .../Options/IErrorHandlerOptionsAccessor.cs | 0 .../IPersistedOperationOptionsAccessor.cs | 0 .../IRequestExecutorOptionsAccessor.cs | 0 .../Options/IRequestTimeoutOptionsAccessor.cs | 0 .../Options/NodeIdSerializerOptions.cs | 0 .../Options/RequestExecutorOptions.cs | 0 .../Execution/Options/RequestParserOptions.cs | 0 .../Execution/Options/ResultBufferOptions.cs | 0 .../Execution/Options/TracingPreference.cs | 0 .../Pipeline/OperationCacheMiddleware.cs | 0 .../Pipeline/OperationExecutionMiddleware.cs | 0 .../Execution/Pipeline/OperationInfo.cs | 0 .../Pipeline/OperationResolverMiddleware.cs | 0 .../OperationVariableCoercionMiddleware.cs | 0 .../Execution/Pipeline/PipelineTools.cs | 0 .../Execution/Pipeline/Pipelines.md | 0 .../Pipeline/RequestClassMiddlewareFactory.cs | 0 .../Execution/Pipeline/TimeoutMiddleware.cs | 0 .../Processing/ArgumentNonNullValidator.cs | 0 .../Processing/AsyncManualResetEvent.cs | 0 .../Execution/Processing/DefaultActivator.cs | 0 .../Processing/DefaultTransactionScope.cs | 0 .../DefaultTransactionScopeHandler.cs | 0 .../Processing/DeferredExecutionTask.cs | 0 .../Processing/DeferredExecutionTaskResult.cs | 0 .../Execution/Processing/DeferredFragment.cs | 0 .../Execution/Processing/DeferredStream.cs | 0 .../Processing/DeferredWorkScheduler.cs | 0 .../Execution/Processing/DeferredWorkState.cs | 0 .../Processing/DeferredWorkStateOwner.cs | 0 .../Processing/EmptySelectionCollection.cs | 0 .../Execution/Processing/Fragment.cs | 0 .../Processing/IOperationCompilerOptimizer.cs | 0 .../Processing/IOperationOptimizer.cs | 0 .../Processing/ISelectionSetOptimizer.cs | 0 .../Execution/Processing/ISubscription.cs | 0 .../Execution/Processing/ITaskStatistics.cs | 0 .../Execution/Processing/ITransactionScope.cs | 0 .../Processing/ITransactionScopeHandler.cs | 0 .../Execution/Processing/IncludeCondition.cs | 0 .../Processing/MiddlewareContext.Arguments.cs | 0 .../Processing/MiddlewareContext.Global.cs | 0 .../Processing/MiddlewareContext.Pooling.cs | 0 .../Processing/MiddlewareContext.Pure.cs | 0 .../Processing/MiddlewareContext.Selection.cs | 0 .../Processing/MiddlewareContext.State.cs | 0 .../Processing/NoOpTransactionScope.cs | 0 .../Processing/NoOpTransactionScopeHandler.cs | 0 .../Processing/NoopBatchDispatcher.cs | 0 .../Execution/Processing/Operation.cs | 0 .../OperationCompiler.ArgumentValues.cs | 0 .../OperationCompiler.BacklogItem.cs | 0 .../OperationCompiler.CompilerContext.cs | 0 .../OperationCompiler.Optimizers.cs | 0 .../OperationCompiler.SelectionSetInfo.cs | 0 .../Execution/Processing/OperationCompiler.cs | 0 .../Processing/OperationCompilerMetrics.cs | 0 .../OperationCompilerOptimizerHelper.cs | 0 .../Processing/OperationCompilerOptimizers.cs | 0 .../Processing/OperationCompilerPool.cs | 0 .../Processing/OperationContext.Execution.cs | 0 .../OperationContext.IExecutionTaskContext.cs | 0 .../Processing/OperationContext.Operation.cs | 0 .../Processing/OperationContext.Pooling.cs | 0 .../Processing/OperationContext.Services.cs | 0 .../Processing/OperationContext.Utilities.cs | 0 .../Execution/Processing/OperationContext.cs | 0 .../Processing/OperationContextOwner.cs | 0 .../Processing/OperationOptimizerContext.cs | 0 .../Execution/Processing/OperationPrinter.cs | 0 .../Processing/OperationResolverHelper.cs | 0 .../Execution/Processing/PathHelper.cs | 0 .../Execution/Processing/QueryExecutor.cs | 0 .../Execution/Processing/Result/ListResult.cs | 0 .../Processing/Result/ListResultPool.cs | 0 .../Processing/Result/ObjectFieldResult.cs | 0 .../Processing/Result/ObjectResult.cs | 0 .../Result/ObjectResultExtensions.cs | 0 .../Processing/Result/ObjectResultPool.cs | 0 .../Processing/Result/ResultBucket.cs | 0 .../Result/ResultBuilder.NonNullHandling.cs | 0 .../Result/ResultBuilder.ObjectResult.cs | 0 .../Result/ResultBuilder.Pooling.cs | 0 .../Processing/Result/ResultBuilder.cs | 0 .../Execution/Processing/Result/ResultData.cs | 0 .../Processing/Result/ResultMemoryOwner.cs | 0 .../Execution/Processing/Result/ResultPool.cs | 0 .../Processing/Result/ResultPoolDefaults.cs | 0 .../Execution/Processing/RootValueResolver.cs | 0 .../Execution/Processing/Selection.cs | 0 .../Processing/SelectionCollection.cs | 0 .../Processing/SelectionIncludeCondition.cs | 0 .../Processing/SelectionInclusionKind.cs | 0 .../Execution/Processing/SelectionPath.cs | 0 .../Execution/Processing/SelectionSet.cs | 0 .../SelectionSetOptimizerContext.cs | 0 .../Execution/Processing/SelectionVariants.cs | 0 .../SubscriptionExecutor.Subscription.cs | 0 .../Processing/SubscriptionExecutor.cs | 0 .../Processing/Tasks/ExecutionTaskPool.cs | 0 .../Tasks/ExecutionTaskPoolPolicy.cs | 0 .../Tasks/ResolverTask.CompleteValue.cs | 0 .../Processing/Tasks/ResolverTask.Execute.cs | 0 .../Processing/Tasks/ResolverTask.Pooling.cs | 0 .../Processing/Tasks/ResolverTask.cs | 0 .../Processing/Tasks/ResolverTaskFactory.cs | 0 .../Tasks/ResolverTaskPoolPolicy.cs | 0 .../Processing/Tasks/StreamHelper.cs | 0 .../Processing/ValueCompletion.Leaf.cs | 0 .../Processing/ValueCompletion.List.cs | 0 .../Processing/ValueCompletion.Object.cs | 0 .../Execution/Processing/ValueCompletion.cs | 0 .../Processing/ValueCompletionContext.cs | 0 .../Processing/VariableCoercionHelper.cs | 0 .../Execution/Processing/VariableRewriter.cs | 0 .../Processing/VariableValueCollection.cs | 0 .../Processing/VariableValueOrLiteral.cs | 0 .../Execution/Processing/WorkQueue.cs | 0 .../Processing/WorkScheduler.Execute.cs | 0 .../Processing/WorkScheduler.Pooling.cs | 0 .../Execution/Processing/WorkScheduler.cs | 0 .../RequestExecutorManager.Events.cs | 0 .../Execution/RequestExecutorManager.Hooks.cs | 0 .../RequestExecutorManager.Warmup.cs | 0 .../Execution/RequestExecutorManager.cs | 0 .../Requirements/FieldRequirementsMetadata.cs | 0 .../Execution/Requirements/PropertyNode.cs | 0 .../Requirements/PropertyTreeBuilder.cs | 0 .../RequirementsTypeInterceptor.cs | 0 .../Execution/Requirements/TypeContainer.cs | 0 .../Execution/Requirements/TypeNode.cs | 0 .../src/{ => Types}/Execution/SchemaName.cs | 0 .../src/{ => Types}/Execution/ThrowHelper.cs | 0 .../src/{ => Types}/Execution/Timestamp.cs | 0 .../Fetching/AdHocBatchDataLoader.cs | 0 .../Fetching/AdHocCacheDataLoader.cs | 0 .../Fetching/AdHocGroupedDataLoader.cs | 0 .../Fetching/AsyncAutoResetEvent.cs | 0 .../Attributes/UseDataLoaderAttribute.cs | 0 .../Fetching/BatchDispatchEventArgs.cs | 0 .../Fetching/BatchDispatchEventType.cs | 0 .../BatchDispatcher.ExecutorSession.cs | 0 .../BatchDispatcher.IBatchScheduler.cs | 0 .../{ => Types}/Fetching/BatchDispatcher.cs | 0 .../Fetching/BatchDispatcherOptions.cs | 0 .../Fetching/BatchDispatcherResult.cs | 0 .../Fetching/DataLoaderDefaults.cs | 0 .../DataLoaderParameterExpressionBuilder.cs | 0 .../DataLoaderRootFieldTypeInterceptor.cs | 0 .../Fetching/DataLoaderScopeHolder.cs | 0 .../Fetching/ExecutionDataLoaderScope.cs | 0 .../ExecutionDataLoaderScopeFactory.cs | 0 .../DataLoaderResolverContextExtensions.cs | 0 .../DataLoaderServiceProviderExtensions.cs | 0 .../Extensions/GetDataLoaderAttribute.cs | 0 .../ObjectFieldDataLoaderExtensions.cs | 0 .../src/{ => Types}/Fetching/FetchBatch.cs | 0 .../src/{ => Types}/Fetching/FetchCache.cs | 0 .../src/{ => Types}/Fetching/FetchGroup.cs | 0 .../{ => Types}/Fetching/IBatchDispatcher.cs | 0 .../Fetching/IDataLoaderScopeFactory.cs | 0 .../Fetching/RegisterDataLoaderException.cs | 0 .../Fetching/SkipDataLoaderCacheAttribute.cs | 0 .../Fetching/Utilities/ThrowHelper.cs | 0 .../Core/src/Types/HotChocolate.Types.csproj | 58 +- .../Internal/IParameterExpressionBuilder.cs | 2 +- .../Properties/FetchingResources.Designer.cs | 30 +- .../Properties/FetchingResources.resx | 0 .../Properties/Resources.Designer.cs | 0 .../Properties/Resources.resx | 0 ...ent.MetaDb.cs => ResultDocument.MetaDb.cs} | 11 +- .../src/Types/Text/Json/ResultDocument.cs | 598 ++++++++++++++++++ .../HotChocolate.Execution.Tests.csproj | 2 +- .../HotChocolate.Fetching.Tests.csproj | 1 - .../HotChocolate.Types.NodaTime.Tests.csproj | 1 - .../HotChocolate.Types.Scalars.Tests.csproj | 1 - .../HotChocolate.CostAnalysis.csproj | 1 - .../Data/src/Data/HotChocolate.Data.csproj | 2 +- .../HotChocolate.Diagnostics.csproj | 2 +- .../HotChocolate.Fusion.Execution.csproj | 1 - .../Json/CompositeResultDocument.DbRow.cs | 2 +- .../Json/CompositeResultDocument.MetaDb.cs | 11 +- .../Fusion.Execution/Text/Json/JsonMemory.cs | 54 -- .../Text/Json/MetaDbMemory.cs | 17 - .../Text/Json/SourceResultDocument.MetaDb.cs | 9 +- .../Text/Json/SourceResultDocument.Parse.cs | 5 +- .../Text/Json/SourceResultDocument.cs | 3 +- .../Text/Json/SourceResultDocumentBuilder.cs | 6 +- .../src/Data/HotChocolate.Data.MongoDb.csproj | 2 +- .../Types/HotChocolate.Types.MongoDb.csproj | 1 - .../Types/HotChocolate.Types.Spatial.csproj | 1 - .../Utilities.Buffers}/FixedSizeArrayPool.cs | 19 +- .../FixedSizeArrayPoolEventSource.cs | 4 +- .../FixedSizeArrayPoolKinds.cs | 6 + .../HotChocolate.Utilities.Buffers.csproj | 5 + .../src/Utilities.Buffers/JsonMemory.cs | 102 +++ .../JsonMemoryEventSource.cs | 47 ++ .../src/Utilities.Buffers/JsonMemoryKind.cs | 7 + .../Properties/BuffersResources.Designer.cs | 6 + .../Properties/BuffersResources.resx | 3 + 290 files changed, 889 insertions(+), 459 deletions(-) delete mode 100644 src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj delete mode 100644 src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj rename src/HotChocolate/Core/src/{ => Types}/Execution/Caching/DefaultDocumentCache.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Caching/DefaultPreparedOperationCache.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Caching/IPreparedOperationCache.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/ConfigurationContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/ConfigureRequestExecutorSetup.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/DefaultRequestExecutorBuilder.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/IConfigureRequestExecutorSetup.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/ITypeModule.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/OnConfigureSchemaBuilderAction.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/OnConfigureSchemaServices.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/OnRequestExecutorCreatedAction.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/OnRequestExecutorEvictedAction.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/RequestExecutorSetup.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Configuration/RootServiceProviderAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DefaultRequestContextAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DefaultRequestExecutor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/Factories/IFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/Factories/OperationContextFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/Factories/PooledServiceFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/ErrorHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Errors/FuncErrorFilterWrapper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/ExecutionRequestExecutorExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/ExecutionResultExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/ExecutionSchemaExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Extensions/OperationContextExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/IRequestContextAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/IRequestContextEnricher.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Instrumentation/IExecutionDiagnosticEvents.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Internal/ArgumentCoercionHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Internal/MiddlewareContextMarshal.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Internal/SchemaFileExporter.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/NeedsFormatting.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/IErrorHandlerOptionsAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/IPersistedOperationOptionsAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/IRequestExecutorOptionsAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/IRequestTimeoutOptionsAccessor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/NodeIdSerializerOptions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/RequestExecutorOptions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/RequestParserOptions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/ResultBufferOptions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Options/TracingPreference.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/OperationCacheMiddleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/OperationExecutionMiddleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/OperationInfo.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/OperationResolverMiddleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/OperationVariableCoercionMiddleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/PipelineTools.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/Pipelines.md (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/RequestClassMiddlewareFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Pipeline/TimeoutMiddleware.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ArgumentNonNullValidator.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/AsyncManualResetEvent.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DefaultActivator.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DefaultTransactionScope.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DefaultTransactionScopeHandler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredExecutionTask.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredExecutionTaskResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredFragment.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredStream.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredWorkScheduler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredWorkState.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/DeferredWorkStateOwner.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/EmptySelectionCollection.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Fragment.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/IOperationCompilerOptimizer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/IOperationOptimizer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ISelectionSetOptimizer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ISubscription.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ITaskStatistics.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ITransactionScope.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ITransactionScopeHandler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/IncludeCondition.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.Arguments.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.Global.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.Pooling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.Pure.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.Selection.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/MiddlewareContext.State.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/NoOpTransactionScope.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/NoOpTransactionScopeHandler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/NoopBatchDispatcher.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Operation.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.ArgumentValues.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.BacklogItem.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.CompilerContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.Optimizers.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.SelectionSetInfo.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompiler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompilerMetrics.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompilerOptimizerHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompilerOptimizers.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationCompilerPool.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.Execution.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.IExecutionTaskContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.Operation.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.Pooling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.Services.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.Utilities.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationContextOwner.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationOptimizerContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationPrinter.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/OperationResolverHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/PathHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/QueryExecutor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ListResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ListResultPool.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ObjectFieldResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ObjectResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ObjectResultExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ObjectResultPool.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultBucket.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultBuilder.ObjectResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultBuilder.Pooling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultBuilder.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultData.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultMemoryOwner.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultPool.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Result/ResultPoolDefaults.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/RootValueResolver.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Selection.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionCollection.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionIncludeCondition.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionInclusionKind.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionPath.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionSet.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionSetOptimizerContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SelectionVariants.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SubscriptionExecutor.Subscription.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/SubscriptionExecutor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ExecutionTaskPool.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTask.Execute.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTask.Pooling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTask.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTaskFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/Tasks/StreamHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ValueCompletion.Leaf.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ValueCompletion.List.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ValueCompletion.Object.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ValueCompletion.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/ValueCompletionContext.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/VariableCoercionHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/VariableRewriter.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/VariableValueCollection.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/VariableValueOrLiteral.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/WorkQueue.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/WorkScheduler.Execute.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/WorkScheduler.Pooling.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Processing/WorkScheduler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/RequestExecutorManager.Events.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/RequestExecutorManager.Hooks.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/RequestExecutorManager.Warmup.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/RequestExecutorManager.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/FieldRequirementsMetadata.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/PropertyNode.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/PropertyTreeBuilder.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/RequirementsTypeInterceptor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/TypeContainer.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Requirements/TypeNode.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/SchemaName.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/ThrowHelper.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Execution/Timestamp.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/AdHocBatchDataLoader.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/AdHocCacheDataLoader.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/AdHocGroupedDataLoader.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/AsyncAutoResetEvent.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Attributes/UseDataLoaderAttribute.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatchEventArgs.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatchEventType.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatcher.ExecutorSession.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatcher.IBatchScheduler.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatcher.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatcherOptions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/BatchDispatcherResult.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/DataLoaderDefaults.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/DataLoaderParameterExpressionBuilder.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/DataLoaderRootFieldTypeInterceptor.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/DataLoaderScopeHolder.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/ExecutionDataLoaderScope.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/ExecutionDataLoaderScopeFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Extensions/DataLoaderResolverContextExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Extensions/GetDataLoaderAttribute.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/FetchBatch.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/FetchCache.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/FetchGroup.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/IBatchDispatcher.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/IDataLoaderScopeFactory.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/RegisterDataLoaderException.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/SkipDataLoaderCacheAttribute.cs (100%) rename src/HotChocolate/Core/src/{ => Types}/Fetching/Utilities/ThrowHelper.cs (100%) rename src/HotChocolate/Core/src/{Fetching => Types}/Properties/FetchingResources.Designer.cs (96%) rename src/HotChocolate/Core/src/{Fetching => Types}/Properties/FetchingResources.resx (100%) rename src/HotChocolate/Core/src/{Execution => Types}/Properties/Resources.Designer.cs (100%) rename src/HotChocolate/Core/src/{Execution => Types}/Properties/Resources.resx (100%) rename src/HotChocolate/Core/src/Types/Text/Json/{CompositeResultDocument.MetaDb.cs => ResultDocument.MetaDb.cs} (96%) create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs delete mode 100644 src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs delete mode 100644 src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs rename src/HotChocolate/{Fusion-vnext/src/Fusion.Execution/Buffers => Utilities/src/Utilities.Buffers}/FixedSizeArrayPool.cs (90%) rename src/HotChocolate/{Fusion-vnext/src/Fusion.Execution/Buffers => Utilities/src/Utilities.Buffers}/FixedSizeArrayPoolEventSource.cs (95%) create mode 100644 src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs create mode 100644 src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs create mode 100644 src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs create mode 100644 src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs diff --git a/src/All.slnx b/src/All.slnx index 5620c0e8200..d6a71a909b4 100644 --- a/src/All.slnx +++ b/src/All.slnx @@ -90,9 +90,7 @@ - - diff --git a/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj b/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj index d177e47f486..a674bb77188 100644 --- a/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj +++ b/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj @@ -10,7 +10,6 @@ - diff --git a/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj b/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj index 119b51ea708..6d167c021a3 100644 --- a/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj +++ b/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs index 4c57bac7179..2281101d5b2 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs @@ -1,3 +1,5 @@ + +using HotChocolate.Buffers; #if FUSION using System.Buffers; using System.Net; @@ -160,7 +162,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS #if FUSION // we try and read the first chunk into a single chunk. var reader = PipeReader.Create(stream, s_options); - var currentChunk = JsonMemory.Rent(); + var currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); var currentChunkPosition = 0; var chunkIndex = 0; var chunks = ArrayPool.Shared.Rent(64); @@ -215,7 +217,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS } chunks[chunkIndex++] = currentChunk; - currentChunk = JsonMemory.Rent(); + currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); currentChunkPosition = 0; } } @@ -253,7 +255,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS } chunks[chunkIndex++] = currentChunk; - currentChunk = JsonMemory.Rent(); + currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); currentChunkPosition = 0; } } diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs index b8bfef41735..f9896ac4442 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; +using HotChocolate.Buffers; using HotChocolate.Fusion.Text.Json; #else using System.Buffers; @@ -103,7 +104,7 @@ private static SourceResultDocument ParseDocument(ReadOnlySequence lineBuf // Ceiling division to make sure we end up with the right amount of chunks. var chunksNeeded = (requiredSize + JsonMemory.BufferSize - 1) / JsonMemory.BufferSize; - var chunks = JsonMemory.RentRange(chunksNeeded); + var chunks = JsonMemory.RentRange(JsonMemoryKind.Json, chunksNeeded); var chunkIndex = 0; var chunkPosition = 0; diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs index 47d0ba8e6a3..2866a300500 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs @@ -2,7 +2,7 @@ using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; -using HotChocolate.Fusion.Text.Json; +using HotChocolate.Buffers; #else using System.Runtime.CompilerServices; diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs index f7de7d41d38..3348a1e1100 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; +using HotChocolate.Buffers; using HotChocolate.Fusion.Text.Json; #else using System.Buffers; @@ -107,7 +108,7 @@ public async IAsyncEnumerator GetAsyncEnumerator( case SseEventType.Complete: reader.AdvanceTo(buffer.GetPosition(1, position.Value)); #if FUSION - JsonMemory.Return(eventBuffers); + JsonMemory.Return(JsonMemoryKind.Json, eventBuffers); eventBuffers.Clear(); #endif yield break; @@ -162,7 +163,7 @@ public async IAsyncEnumerator GetAsyncEnumerator( await reader.CompleteAsync().ConfigureAwait(false); #if FUSION // we return whatever is in here. - JsonMemory.Return(eventBuffers); + JsonMemory.Return(JsonMemoryKind.Json, eventBuffers); #endif } } @@ -232,7 +233,7 @@ private static void WriteBytesToChunks(List chunks, ref int currentPosit if (chunks.Count == 0 || currentPosition >= JsonMemory.BufferSize) { currentPosition = 0; - chunks.Add(JsonMemory.Rent()); + chunks.Add(JsonMemory.Rent(JsonMemoryKind.Json)); } var currentChunk = chunks[^1]; diff --git a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj index 19568782fb1..76d0623f5d7 100644 --- a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj +++ b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj @@ -18,7 +18,6 @@ - diff --git a/src/HotChocolate/Core/HotChocolate.Core.slnx b/src/HotChocolate/Core/HotChocolate.Core.slnx index 874b0c44d4f..2f8095bec3b 100644 --- a/src/HotChocolate/Core/HotChocolate.Core.slnx +++ b/src/HotChocolate/Core/HotChocolate.Core.slnx @@ -7,9 +7,7 @@ - - diff --git a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj index 36369fa8d90..c4fea1db81e 100644 --- a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj +++ b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj index 8c5f1883691..21d8f857b7b 100644 --- a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj +++ b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj @@ -18,8 +18,6 @@ - - diff --git a/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj b/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj index 74997ec1546..8e5a544034f 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj +++ b/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj deleted file mode 100644 index a30275088fc..00000000000 --- a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj +++ /dev/null @@ -1,247 +0,0 @@ - - - - true - - - - HotChocolate.Execution - HotChocolate.Execution - HotChocolate.Execution - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationCompiler.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - ResolverTask.cs - - - WorkScheduler.cs - - - WorkScheduler.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - ValueCompletion.cs - - - ValueCompletion.cs - - - ValueCompletion.cs - - - ResultBuilder.cs - - - ResultBuilder.cs - - - ResultBuilder.cs - - - ResolverTask.cs - - - ResolverTask.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationContext.cs - - - JsonResultFormatter.cs - - - SubscriptionExecutor.cs - - - RequestExecutorResolver.cs - - - ValueCompletion.cs - - - BatchExecutor.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorManager.cs - - - RequestExecutorManager.cs - - - RequestExecutorManager.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - diff --git a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj b/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj deleted file mode 100644 index 27229d59adc..00000000000 --- a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - HotChocolate.Fetching - HotChocolate.Fetching - HotChocolate.Fetching - HC8001;$(NoWarn) - - - - - - - - - - - - - - ResXFileCodeGenerator - FetchingResources.Designer.cs - - - - - - True - True - FetchingResources.resx - - - BatchDispatcher.cs - - - BatchDispatcher.cs - - - - diff --git a/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj b/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj index 04de55ffd98..4bdc4169e1f 100644 --- a/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj +++ b/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj b/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj index a2ff17d8626..cafc753595e 100644 --- a/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj +++ b/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj @@ -13,7 +13,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj b/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj index 8faf45d5e11..bb59ec657e4 100644 --- a/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj +++ b/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj index 3561ed6ead9..489e7d6cf91 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj @@ -13,7 +13,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj index 1a42342fada..2ec4deeeec7 100644 --- a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj +++ b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultDocumentCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/DefaultDocumentCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Caching/DefaultPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/DefaultPreparedOperationCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Caching/IPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/IPreparedOperationCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigurationContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigurationContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ConfigureRequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigureRequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ConfigureRequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigureRequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/DefaultRequestExecutorBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/DefaultRequestExecutorBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/IConfigureRequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/IConfigureRequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/IConfigureRequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/IConfigureRequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ITypeModule.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ITypeModule.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ITypeModule.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ITypeModule.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaBuilderAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaBuilderAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaBuilderAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaBuilderAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaServices.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaServices.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaServices.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaServices.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorCreatedAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorCreatedAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorCreatedAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorCreatedAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorEvictedAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorEvictedAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorEvictedAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorEvictedAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/RequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/RequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/RequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/RequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/RootServiceProviderAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/RootServiceProviderAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/RootServiceProviderAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/RootServiceProviderAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/DefaultRequestContextAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/DefaultRequestContextAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DefaultRequestContextAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/DefaultRequestContextAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/DefaultRequestExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/DefaultRequestExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DefaultRequestExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/DefaultRequestExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/IFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/IFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/IFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/IFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/PooledServiceFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/PooledServiceFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/PooledServiceFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/PooledServiceFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/ErrorHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Errors/FuncErrorFilterWrapper.cs b/src/HotChocolate/Core/src/Types/Execution/Errors/FuncErrorFilterWrapper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Errors/FuncErrorFilterWrapper.cs rename to src/HotChocolate/Core/src/Types/Execution/Errors/FuncErrorFilterWrapper.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionRequestExecutorExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionRequestExecutorExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionRequestExecutorExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionRequestExecutorExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionResultExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionResultExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionSchemaExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionSchemaExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionSchemaExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionSchemaExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/OperationContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/OperationContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/IRequestContextAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/IRequestContextAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/IRequestContextAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/IRequestContextAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/IRequestContextEnricher.cs b/src/HotChocolate/Core/src/Types/Execution/IRequestContextEnricher.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/IRequestContextEnricher.cs rename to src/HotChocolate/Core/src/Types/Execution/IRequestContextEnricher.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/ArgumentCoercionHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/ArgumentCoercionHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/MiddlewareContextMarshal.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/MiddlewareContextMarshal.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/MiddlewareContextMarshal.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/MiddlewareContextMarshal.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/SchemaFileExporter.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/SchemaFileExporter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/SchemaFileExporter.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/SchemaFileExporter.cs diff --git a/src/HotChocolate/Core/src/Execution/NeedsFormatting.cs b/src/HotChocolate/Core/src/Types/Execution/NeedsFormatting.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/NeedsFormatting.cs rename to src/HotChocolate/Core/src/Types/Execution/NeedsFormatting.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IErrorHandlerOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IErrorHandlerOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IErrorHandlerOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IErrorHandlerOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IPersistedOperationOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IPersistedOperationOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IRequestExecutorOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IRequestExecutorOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IRequestExecutorOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IRequestExecutorOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IRequestTimeoutOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IRequestTimeoutOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IRequestTimeoutOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IRequestTimeoutOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/NodeIdSerializerOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/NodeIdSerializerOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/NodeIdSerializerOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/NodeIdSerializerOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/RequestExecutorOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/RequestExecutorOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestParserOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/RequestParserOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/RequestParserOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/RequestParserOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/ResultBufferOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/ResultBufferOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/ResultBufferOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/ResultBufferOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/TracingPreference.cs b/src/HotChocolate/Core/src/Types/Execution/Options/TracingPreference.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/TracingPreference.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/TracingPreference.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationCacheMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationCacheMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationInfo.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationResolverMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationResolverMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationVariableCoercionMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationVariableCoercionMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationVariableCoercionMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationVariableCoercionMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/PipelineTools.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/PipelineTools.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/PipelineTools.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/PipelineTools.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/Pipelines.md b/src/HotChocolate/Core/src/Types/Execution/Pipeline/Pipelines.md similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/Pipelines.md rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/Pipelines.md diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/RequestClassMiddlewareFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/RequestClassMiddlewareFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/RequestClassMiddlewareFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/RequestClassMiddlewareFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/TimeoutMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/TimeoutMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/TimeoutMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/TimeoutMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ArgumentNonNullValidator.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentNonNullValidator.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ArgumentNonNullValidator.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentNonNullValidator.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/AsyncManualResetEvent.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/AsyncManualResetEvent.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/AsyncManualResetEvent.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/AsyncManualResetEvent.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultActivator.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultActivator.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultActivator.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultActivator.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTaskResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTaskResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredFragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredFragment.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredStream.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredStream.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkScheduler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredWorkScheduler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkState.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredWorkState.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkStateOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DeferredWorkStateOwner.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/EmptySelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/EmptySelectionCollection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/EmptySelectionCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/EmptySelectionCollection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Fragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Fragment.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/IOperationCompilerOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperationCompilerOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/IOperationCompilerOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/IOperationCompilerOptimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/IOperationOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperationOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/IOperationOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/IOperationOptimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ISelectionSetOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSetOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ISelectionSetOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSetOptimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ISubscription.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISubscription.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ISubscription.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ISubscription.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITaskStatistics.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITaskStatistics.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITaskStatistics.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITaskStatistics.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/IncludeCondition.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/IncludeCondition.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Arguments.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Arguments.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Selection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.State.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.State.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.State.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.State.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoopBatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoopBatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoopBatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoopBatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Operation.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.ArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.ArgumentValues.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.ArgumentValues.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.ArgumentValues.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.BacklogItem.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.BacklogItem.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.CompilerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompilerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.CompilerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompilerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.Optimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.Optimizers.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.SelectionSetInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.SelectionSetInfo.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerMetrics.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerMetrics.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerMetrics.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerMetrics.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizerHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizerHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizers.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Execution.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Execution.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.IExecutionTaskContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.IExecutionTaskContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.IExecutionTaskContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.IExecutionTaskContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Operation.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Services.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Services.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Services.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Services.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Utilities.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Utilities.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Utilities.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Utilities.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContextOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContextOwner.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationOptimizerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationPrinter.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationPrinter.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationResolverHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationResolverHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationResolverHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationResolverHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/PathHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/PathHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/QueryExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/QueryExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/QueryExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/QueryExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ListResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ListResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ListResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ListResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectFieldResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectFieldResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectFieldResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectFieldResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBucket.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBucket.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBucket.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBucket.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.ObjectResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.ObjectResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.ObjectResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.ObjectResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultData.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultData.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultData.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultData.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultMemoryOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultMemoryOwner.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultMemoryOwner.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultMemoryOwner.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultPoolDefaults.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPoolDefaults.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultPoolDefaults.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPoolDefaults.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/RootValueResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/RootValueResolver.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Selection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionIncludeCondition.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionIncludeCondition.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionIncludeCondition.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionIncludeCondition.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionInclusionKind.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionInclusionKind.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionInclusionKind.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionInclusionKind.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionPath.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionPath.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionPath.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionPath.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionSet.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionSetOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionSetOptimizerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionVariants.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionVariants.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.Subscription.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.Subscription.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/StreamHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/StreamHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Leaf.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Leaf.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.List.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.List.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Object.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Object.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletionContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletionContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletionContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletionContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableCoercionHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableCoercionHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableCoercionHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableCoercionHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableRewriter.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableRewriter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableRewriter.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableRewriter.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableValueCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueCollection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableValueCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueCollection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableValueOrLiteral.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableValueOrLiteral.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkQueue.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkQueue.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkQueue.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkQueue.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Execute.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Execute.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Execute.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Events.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Events.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Events.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Events.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Hooks.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Hooks.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Hooks.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Hooks.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Warmup.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Warmup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Warmup.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Warmup.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/FieldRequirementsMetadata.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/FieldRequirementsMetadata.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/FieldRequirementsMetadata.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/FieldRequirementsMetadata.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/PropertyNode.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyNode.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/PropertyNode.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyNode.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/PropertyTreeBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyTreeBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/PropertyTreeBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyTreeBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/RequirementsTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/RequirementsTypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/RequirementsTypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/RequirementsTypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/TypeContainer.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/TypeContainer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/TypeContainer.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/TypeContainer.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/TypeNode.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/TypeNode.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/TypeNode.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/TypeNode.cs diff --git a/src/HotChocolate/Core/src/Execution/SchemaName.cs b/src/HotChocolate/Core/src/Types/Execution/SchemaName.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/SchemaName.cs rename to src/HotChocolate/Core/src/Types/Execution/SchemaName.cs diff --git a/src/HotChocolate/Core/src/Execution/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/ThrowHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Timestamp.cs b/src/HotChocolate/Core/src/Types/Execution/Timestamp.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Timestamp.cs rename to src/HotChocolate/Core/src/Types/Execution/Timestamp.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocBatchDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocBatchDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocBatchDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocBatchDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocCacheDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocCacheDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocCacheDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocCacheDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocGroupedDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocGroupedDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocGroupedDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocGroupedDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AsyncAutoResetEvent.cs b/src/HotChocolate/Core/src/Types/Fetching/AsyncAutoResetEvent.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AsyncAutoResetEvent.cs rename to src/HotChocolate/Core/src/Types/Fetching/AsyncAutoResetEvent.cs diff --git a/src/HotChocolate/Core/src/Fetching/Attributes/UseDataLoaderAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/Attributes/UseDataLoaderAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Attributes/UseDataLoaderAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/Attributes/UseDataLoaderAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatchEventArgs.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventArgs.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatchEventArgs.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventArgs.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatchEventType.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventType.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatchEventType.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventType.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.ExecutorSession.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.ExecutorSession.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.ExecutorSession.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.ExecutorSession.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.IBatchScheduler.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.IBatchScheduler.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.IBatchScheduler.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.IBatchScheduler.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcherOptions.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcherOptions.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherOptions.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcherResult.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcherResult.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderDefaults.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderDefaults.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderDefaults.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderDefaults.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderParameterExpressionBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderParameterExpressionBuilder.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderParameterExpressionBuilder.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderRootFieldTypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderRootFieldTypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderScopeHolder.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderScopeHolder.cs diff --git a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs rename to src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs diff --git a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScopeFactory.cs b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScopeFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScopeFactory.cs rename to src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScopeFactory.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderResolverContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/GetDataLoaderAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/GetDataLoaderAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/GetDataLoaderAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/GetDataLoaderAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchBatch.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchBatch.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchBatch.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchBatch.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchCache.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchCache.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchCache.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchGroup.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchGroup.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchGroup.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchGroup.cs diff --git a/src/HotChocolate/Core/src/Fetching/IBatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Fetching/IBatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/IBatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Fetching/IBatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Fetching/IDataLoaderScopeFactory.cs b/src/HotChocolate/Core/src/Types/Fetching/IDataLoaderScopeFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/IDataLoaderScopeFactory.cs rename to src/HotChocolate/Core/src/Types/Fetching/IDataLoaderScopeFactory.cs diff --git a/src/HotChocolate/Core/src/Fetching/RegisterDataLoaderException.cs b/src/HotChocolate/Core/src/Types/Fetching/RegisterDataLoaderException.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/RegisterDataLoaderException.cs rename to src/HotChocolate/Core/src/Types/Fetching/RegisterDataLoaderException.cs diff --git a/src/HotChocolate/Core/src/Fetching/SkipDataLoaderCacheAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/SkipDataLoaderCacheAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/SkipDataLoaderCacheAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/SkipDataLoaderCacheAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/Utilities/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Utilities/ThrowHelper.cs rename to src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index ebf81309726..bf2bed99fa2 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -34,7 +34,6 @@ - @@ -54,13 +53,26 @@ + + + + + + + + + + + + + @@ -81,24 +93,16 @@ Text\Json\MetaDbEventSource.cs - - Text\Json\JsonConstants.cs - - - - Text\Json\JsonConstants.cs - - - - Text\Json\JsonConstants.cs + + ResultDocument.cs - - Text\Json\JsonConstants.cs + + ResultDocument.cs - - Text\Json\JsonConstants.cs + + ResultDocument.cs @@ -169,25 +173,33 @@ DescriptorContext.cs - - True - True - TypeResources.resx - InterfaceType.cs + + True + True + TypeResources.resx + + ResXFileCodeGenerator TypeResources.Designer.cs - - - + + True + True + Resources.resx + + + + ResXFileCodeGenerator + Resources.Designer.cs + diff --git a/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs index 432f3ce243b..8c0e873c448 100644 --- a/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Internal; /// -/// This interface represents an expression builder to resolver resolver parameter values. +/// This interface represents an expression builder to resolver parameter values. /// public interface IParameterExpressionBuilder { diff --git a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs similarity index 96% rename from src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs rename to src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs index ce30a8ad324..83d091f7900 100644 --- a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs @@ -9,21 +9,21 @@ namespace HotChocolate.Fetching.Properties { using System; - - + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class FetchingResources { - + private static System.Resources.ResourceManager resourceMan; - + private static System.Globalization.CultureInfo resourceCulture; - + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal FetchingResources() { } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Resources.ResourceManager ResourceManager { get { @@ -34,7 +34,7 @@ internal static System.Resources.ResourceManager ResourceManager { return resourceMan; } } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Globalization.CultureInfo Culture { get { @@ -44,49 +44,49 @@ internal static System.Globalization.CultureInfo Culture { resourceCulture = value; } } - + internal static string DataLoaderRegistry_KeyNullOrEmpty { get { return ResourceManager.GetString("DataLoaderRegistry_KeyNullOrEmpty", resourceCulture); } } - + internal static string DefaultDataLoaderRegistry_GetOrRegister { get { return ResourceManager.GetString("DefaultDataLoaderRegistry_GetOrRegister", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_RegistryIsNull { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_RegistryIsNull", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_UnableToRegister { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_UnableToRegister", resourceCulture); } } - + internal static string ThrowHelper_DataLoader_InvalidType { get { return ResourceManager.GetString("ThrowHelper_DataLoader_InvalidType", resourceCulture); } } - + internal static string BatchDispatcherResult_NoExceptions { get { return ResourceManager.GetString("BatchDispatcherResult_NoExceptions", resourceCulture); diff --git a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.resx b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.resx rename to src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx diff --git a/src/HotChocolate/Core/src/Execution/Properties/Resources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Properties/Resources.Designer.cs rename to src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs diff --git a/src/HotChocolate/Core/src/Execution/Properties/Resources.resx b/src/HotChocolate/Core/src/Types/Properties/Resources.resx similarity index 100% rename from src/HotChocolate/Core/src/Execution/Properties/Resources.resx rename to src/HotChocolate/Core/src/Types/Properties/Resources.resx diff --git a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs similarity index 96% rename from src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs rename to src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs index 5f6561ac0ec..46af3d2c6b3 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using HotChocolate.Buffers; using static HotChocolate.Text.Json.MetaDbEventSource; namespace HotChocolate.Text.Json; @@ -26,7 +27,7 @@ internal static MetaDb CreateForEstimatedRows(int estimatedRows) log.MetaDbCreated(2, estimatedRows, 1); // Rent the first chunk now to avoid branching on first append - chunks[0] = MetaDbMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, 0); for (var i = 1; i < chunks.Length; i++) @@ -101,7 +102,7 @@ internal Cursor Append( // if the chunk is empty we did not yet rent any memory for it if (chunk.Length == 0) { - chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + chunk = chunks[chunkIndex] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, chunkIndex); } @@ -291,7 +292,7 @@ internal int GetSizeOrLength(Cursor cursor) internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) { AssertValidCursor(cursor); - Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= int.MaxValue, "SizeOrLength value exceeds 31-bit limit"); + Debug.Assert(sizeOrLength >= 0, "SizeOrLength value exceeds 31-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); var currentValue = MemoryMarshal.Read(fieldSpan); @@ -350,7 +351,7 @@ private void AssertValidCursor(Cursor cursor) Debug.Assert(absoluteIndex >= 0 && absoluteIndex < maxExclusive, $"Cursor points to row {absoluteIndex}, but only {maxExclusive} rows are valid."); - Debug.Assert(cursor.ByteOffset + DbRow.Size <= MetaDbMemory.BufferSize, "Cursor byte offset out of bounds"); + Debug.Assert(cursor.ByteOffset + DbRow.Size <= JsonMemory.BufferSize, "Cursor byte offset out of bounds"); } public void Dispose() @@ -369,7 +370,7 @@ public void Dispose() break; } - MetaDbMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); } chunks.Clear(); diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs new file mode 100644 index 00000000000..ab09873c494 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -0,0 +1,598 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using HotChocolate.Execution.Processing; +using HotChocolate.Types; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument : IDisposable +{ + private static readonly Encoding s_utf8Encoding = Encoding.UTF8; + private readonly IOperation _operation; + private readonly ulong _includeFlags; + private List? _errors; + private Dictionary? _extensions; + internal MetaDb _metaDb; + private bool _disposed; + + public ResultDocument(IOperation operation, ulong includeFlags) + { + _metaDb = MetaDb.CreateForEstimatedRows(Cursor.RowsPerChunk * 8); + _operation = operation; + _includeFlags = includeFlags; + + Data = CreateObject(Cursor.Zero, operation.RootSelectionSet); + } + + public CompositeResultElement Data { get; } + + public List? Errors + { + get => _errors; + internal set => _errors = value; + } + + public Dictionary? Extensions + { + get => _extensions; + internal set => _extensions = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ElementTokenType GetElementTokenType(Cursor cursor) + => _metaDb.GetElementTokenType(cursor); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal IOperation GetOperation() + => _operation; + + internal ISelectionSet? GetSelectionSet(Cursor cursor) + { + var row = _metaDb.Get(cursor); + + if (row.OperationReferenceType is not OperationReferenceType.SelectionSet) + { + return null; + } + + return _operation.GetSelectionSetById(row.OperationReferenceId); + } + + internal ISelection? GetSelection(Cursor cursor) + { + if (cursor == Cursor.Zero) + { + return null; + } + + // If the cursor points at a value, step back to the PropertyName row. + var row = _metaDb.Get(cursor); + + if (row.TokenType is not ElementTokenType.PropertyName) + { + cursor = cursor.AddRows(-1); + row = _metaDb.Get(cursor); + + if (row.TokenType is not ElementTokenType.PropertyName) + { + return null; + } + } + + if (row.OperationReferenceType is not OperationReferenceType.Selection) + { + return null; + } + + return _operation.GetSelectionById(row.OperationReferenceId); + } + + internal CompositeResultElement GetArrayIndexElement(Cursor current, int arrayIndex) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var (start, tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartArray, tokenType); + + var len = _metaDb.GetNumberOfRows(start); + + if ((uint)arrayIndex >= (uint)len) + { + throw new IndexOutOfRangeException(); + } + + // first element is at +1 after StartArray + return new CompositeResultElement(this, start.AddRows(arrayIndex + 1)); + } + + internal int GetArrayLength(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (current, var tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartArray, tokenType); + + return _metaDb.GetSizeOrLength(current); + } + + internal int GetPropertyCount(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (current, var tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + return _metaDb.GetSizeOrLength(current); + } + + internal Path CreatePath(Cursor current) + { + // Stop at root via IsRoot flag. + if ((_metaDb.GetFlags(current) & ElementFlags.IsRoot) == ElementFlags.IsRoot) + { + return Path.Root; + } + + Span chain = stackalloc Cursor[64]; + var c = current; + var written = 0; + + do + { + chain[written++] = c; + + var parentIndex = _metaDb.GetParent(c); + if (parentIndex <= 0) + { + break; + } + + c = Cursor.FromIndex(parentIndex); + + if (written >= 64) + { + throw new InvalidOperationException("The path is to deep."); + } + } while (true); + + var path = Path.Root; + var parentTokenType = ElementTokenType.StartObject; + + chain = chain[..written]; + + for (var i = chain.Length - 1; i >= 0; i--) + { + c = chain[i]; + var tokenType = _metaDb.GetElementTokenType(c, resolveReferences: false); + + if (tokenType == ElementTokenType.PropertyName) + { + path = path.Append(GetSelection(c)!.ResponseName); + i--; // skip over the actual value + } + else if (chain.Length - 1 > i) + { + var parentCursor = chain[i + 1]; + + if (parentTokenType is ElementTokenType.StartArray) + { + // arrayIndex = abs(child) - (abs(parent) + 1) + var absChild = c.Chunk * Cursor.RowsPerChunk + c.Row; + var absParent = parentCursor.Chunk * Cursor.RowsPerChunk + parentCursor.Row; + var arrayIndex = absChild - (absParent + 1); + path = path.Append(arrayIndex); + } + } + + parentTokenType = tokenType; + } + + return path; + } + + internal CompositeResultElement GetParent(Cursor current) + { + // The null cursor represents the data object, which is the utmost root. + // If we have reached that we simply return an undefined element + if (current == Cursor.Zero) + { + return default; + } + + var parent = _metaDb.GetParentCursor(current); + + // if the parent element is a property name then we must get the parent of that, + // as property name and value represent the same element. + if (_metaDb.GetElementTokenType(parent) is ElementTokenType.PropertyName) + { + parent = _metaDb.GetParentCursor(parent); + } + + // if we have not yet reached the root and the element type of the parent is an object or an array + // then we need to get still the parent of this row as we want to get the logical parent + // which is the value level of the property or the element in an array. + if (parent != Cursor.Zero + && _metaDb.GetElementTokenType(parent) is ElementTokenType.StartObject or ElementTokenType.StartArray) + { + parent = _metaDb.GetParentCursor(parent); + + // in this case the parent must be a reference, otherwise we would have + // found an inconsistency in the database. + Debug.Assert(_metaDb.GetElementTokenType(parent, resolveReferences: false) == ElementTokenType.Reference); + } + + return new CompositeResultElement(this, parent); + } + + internal bool IsInvalidated(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current, resolveReferences: false); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + } + } + + return false; + } + + internal bool IsNullOrInvalidated(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.Null) + { + return true; + } + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + } + } + + return false; + } + + internal bool IsInternalProperty(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The flag sits on the property row (one before value) + var propertyCursor = current.AddRows(-1); + var flags = _metaDb.GetFlags(propertyCursor); + return (flags & ElementFlags.IsInternal) == ElementFlags.IsInternal; + } + + internal void Invalidate(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current, resolveReferences: false); + + if (tokenType is ElementTokenType.None) + { + return; + } + + if (tokenType is ElementTokenType.StartArray) + { + return; + } + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + _metaDb.SetFlags(current, flags | ElementFlags.Invalidated); + return; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + _metaDb.SetFlags(current, flags | ElementFlags.Invalidated); + } + + return; + } + + Debug.Fail("Only objects can be invalidated."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteRawValueTo(IBufferWriter writer, DbRow row, int indentLevel, bool indented) + { + if ((row.Flags & ElementFlags.SourceResult) == ElementFlags.SourceResult) + { + var document = _sources[row.SourceDocumentId]; + + if (row.TokenType is ElementTokenType.StartObject or ElementTokenType.StartArray) + { + // Reconstruct the source cursor from stored Location (Chunk) and SizeOrLength (Row) + var sourceCursor = SourceResultDocument.Cursor.From(row.Location, row.SizeOrLength); + var formatter = new SourceResultDocument.RawJsonFormatter(document, writer, indentLevel, indented); + formatter.WriteValue(sourceCursor); + return; + } + + // For simple values, write directly using location and size + document.WriteRawValueTo(writer, row.Location, row.SizeOrLength); + return; + } + + throw new NotSupportedException(); + } + + private ReadOnlySpan ReadRawValue(DbRow row) + { + if (row.TokenType == ElementTokenType.Null) + { + return JsonConstants.NullValue; + } + + if (row.TokenType == ElementTokenType.True) + { + return JsonConstants.TrueValue; + } + + if (row.TokenType == ElementTokenType.False) + { + return JsonConstants.FalseValue; + } + + if (row.TokenType == ElementTokenType.PropertyName) + { + return _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName; + } + + if ((row.Flags & ElementFlags.SourceResult) == ElementFlags.SourceResult) + { + var document = _sources[row.SourceDocumentId]; + return document.ReadRawValue(row.Location, row.SizeOrLength); + } + + throw new NotSupportedException(); + } + + internal CompositeResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) + { + var startObjectCursor = WriteStartObject(parent, selectionSet.Id); + + var selectionCount = 0; + foreach (var selection in selectionSet.Selections) + { + WriteEmptyProperty(startObjectCursor, selection); + selectionCount++; + } + + WriteEndObject(startObjectCursor, selectionCount); + + return new CompositeResultElement(this, startObjectCursor); + } + + internal CompositeResultElement CreateArray(Cursor parent, int length) + { + var cursor = WriteStartArray(parent, length); + + for (var i = 0; i < length; i++) + { + WriteEmptyValue(cursor); + } + + WriteEndArray(); + + return new CompositeResultElement(this, cursor); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignCompositeValue(CompositeResultElement target, CompositeResultElement value) + { + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Reference, + location: value.Cursor.ToIndex(), + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignSourceValue(CompositeResultElement target, SourceResultElement source) + { + var value = source.GetValuePointer(); + var parent = source._parent; + + if (parent.Id == -1) + { + Debug.Assert(!_sources.Contains(parent), "The source document is marked as unbound but is already registered."); + parent.Id = _sources.Count; + _sources.Add(parent); + } + + Debug.Assert(_sources.Contains(parent), "Expected the source document of the source element to be registered."); + + var tokenType = source.TokenType.ToElementTokenType(); + + if (tokenType is ElementTokenType.StartObject or ElementTokenType.StartArray) + { + var sourceCursor = source._cursor; + + _metaDb.Replace( + cursor: target.Cursor, + tokenType: source.TokenType.ToElementTokenType(), + location: sourceCursor.Chunk, + sizeOrLength: sourceCursor.Row, + sourceDocumentId: parent.Id, + parentRow: _metaDb.GetParent(target.Cursor), + flags: ElementFlags.SourceResult); + return; + } + + _metaDb.Replace( + cursor: target.Cursor, + tokenType: source.TokenType.ToElementTokenType(), + location: value.Location, + sizeOrLength: value.Size, + sourceDocumentId: parent.Id, + parentRow: _metaDb.GetParent(target.Cursor), + flags: ElementFlags.SourceResult); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignNullValue(CompositeResultElement target) + { + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Null, + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor WriteStartObject(Cursor parent, int selectionSetId = 0) + { + var flags = ElementFlags.None; + var parentRow = ToIndex(parent); + + if (parentRow < 0) + { + parentRow = 0; + flags = ElementFlags.IsRoot; + } + + return _metaDb.Append( + ElementTokenType.StartObject, + parentRow: parentRow, + operationReferenceId: selectionSetId, + operationReferenceType: OperationReferenceType.SelectionSet, + flags: flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEndObject(Cursor startObjectCursor, int length) + { + _metaDb.Append(ElementTokenType.EndObject); + + _metaDb.SetNumberOfRows(startObjectCursor, (length * 2) + 1); + _metaDb.SetSizeOrLength(startObjectCursor, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor WriteStartArray(Cursor parent, int length = 0) + { + var flags = ElementFlags.None; + var parentRow = ToIndex(parent); + + if (parentRow < 0) + { + parentRow = 0; + flags = ElementFlags.IsRoot; + } + + return _metaDb.Append( + ElementTokenType.StartArray, + sizeOrLength: length, + parentRow: parentRow, + numberOfRows: length + 1, + flags: flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEndArray() => _metaDb.Append(ElementTokenType.EndArray); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyProperty(Cursor parent, ISelection selection) + { + var flags = ElementFlags.None; + + if (selection.IsInternal) + { + flags = ElementFlags.IsInternal; + } + + if (!selection.IsIncluded(_includeFlags)) + { + flags |= ElementFlags.IsExcluded; + } + + if (selection.Type.Kind is not TypeKind.NonNull) + { + flags |= ElementFlags.IsNullable; + } + + var prop = _metaDb.Append( + ElementTokenType.PropertyName, + parentRow: ToIndex(parent), + operationReferenceId: selection.Id, + operationReferenceType: OperationReferenceType.Selection, + flags: flags); + + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(prop)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyValue(Cursor parent) + { + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(parent)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ToIndex(Cursor c) => (c.Chunk * Cursor.RowsPerChunk) + c.Row; + + private static void CheckExpectedType(ElementTokenType expected, ElementTokenType actual) + { + if (expected != actual) + { + throw new ArgumentOutOfRangeException($"Expected {expected} but found {actual}."); + } + } + + public void Dispose() + { + if (!_disposed) + { + _metaDb.Dispose(); + _disposed = true; + } + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj b/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj index 17b7c9c5d0d..0f46058c97a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj +++ b/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj b/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj index cd72005885c..e7cb45365b6 100644 --- a/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj +++ b/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj @@ -8,7 +8,6 @@ - diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj index d7cdb3cafe4..f773538a6ff 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj b/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj index b314284c42c..2d157d98a23 100644 --- a/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj index c3bd3025c3f..a6baf2a2152 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index c6d4fbb16bf..f5f21fbc643 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj index 886bfa3e70d..49885604251 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj index 1ff85c15c78..784fba4a269 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj @@ -90,7 +90,6 @@ Transport\Http\Sse\SseReader.cs - SourceResultElement.cs diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs index 42785ef9614..0a84bde497f 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs @@ -15,7 +15,7 @@ internal readonly struct DbRow // 27 bits for location + 2 bits OpRefType + 3 reserved bits private readonly int _locationAndOpRefType; - // Sign bit for HasComplexChildren + 31 bits for size/length + // A Sign bit for HasComplexChildren + 31 bits for size/length private readonly int _sizeOrLengthUnion; // 4 bits TokenType + 27 bits NumberOfRows + 1 reserved bit diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs index 5d2d0d0d9c8..bda078eb5bf 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using HotChocolate.Buffers; using static HotChocolate.Fusion.Text.Json.MetaDbEventSource; namespace HotChocolate.Fusion.Text.Json; @@ -26,7 +27,7 @@ internal static MetaDb CreateForEstimatedRows(int estimatedRows) log.MetaDbCreated(2, estimatedRows, 1); // Rent the first chunk now to avoid branching on first append - chunks[0] = MetaDbMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, 0); for (var i = 1; i < chunks.Length; i++) @@ -101,7 +102,7 @@ internal Cursor Append( // if the chunk is empty we did not yet rent any memory for it if (chunk.Length == 0) { - chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + chunk = chunks[chunkIndex] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, chunkIndex); } @@ -291,7 +292,7 @@ internal int GetSizeOrLength(Cursor cursor) internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) { AssertValidCursor(cursor); - Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= int.MaxValue, "SizeOrLength value exceeds 31-bit limit"); + Debug.Assert(sizeOrLength >= 0, "SizeOrLength value exceeds 31-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); var currentValue = MemoryMarshal.Read(fieldSpan); @@ -350,7 +351,7 @@ private void AssertValidCursor(Cursor cursor) Debug.Assert(absoluteIndex >= 0 && absoluteIndex < maxExclusive, $"Cursor points to row {absoluteIndex}, but only {maxExclusive} rows are valid."); - Debug.Assert(cursor.ByteOffset + DbRow.Size <= MetaDbMemory.BufferSize, "Cursor byte offset out of bounds"); + Debug.Assert(cursor.ByteOffset + DbRow.Size <= JsonMemory.BufferSize, "Cursor byte offset out of bounds"); } public void Dispose() @@ -369,7 +370,7 @@ public void Dispose() break; } - MetaDbMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); } chunks.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs deleted file mode 100644 index e11f24de5ff..00000000000 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Buffers; -using System.Runtime.InteropServices; -using HotChocolate.Fusion.Buffers; - -namespace HotChocolate.Fusion.Text.Json; - -/// -/// Manages the memory for storing JSON data. -/// -public static class JsonMemory -{ - public const int BufferSize = 1 << 17; - - private static readonly FixedSizeArrayPool s_pool = new(1, BufferSize, 128 * 6, preAllocate: true); - private static readonly ArrayPool s_chunkPool = ArrayPool.Shared; - - public static byte[] Rent() - => s_pool.Rent(); - - public static byte[][] RentRange(int requiredChunks) - { - var chunks = s_chunkPool.Rent(requiredChunks); - - for (var i = 0; i < requiredChunks; i++) - { - chunks[i] = s_pool.Rent(); - } - - return chunks; - } - - public static void Return(byte[] chunk) - => s_pool.Return(chunk); - - public static void Return(List chunks) - { - ArgumentNullException.ThrowIfNull(chunks); - - foreach (var chunk in CollectionsMarshal.AsSpan(chunks)) - { - s_pool.Return(chunk); - } - } - - public static void Return(byte[][] chunks, int usedChunks) - { - ArgumentNullException.ThrowIfNull(chunks); - - foreach (var chunk in chunks.AsSpan(0, usedChunks)) - { - s_pool.Return(chunk); - } - } -} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs deleted file mode 100644 index 20c4232e4dd..00000000000 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using HotChocolate.Fusion.Buffers; - -namespace HotChocolate.Fusion.Text.Json; - -public static class MetaDbMemory -{ - public const int BufferSize = 1 << 17; - public const int RowsPerChunk = 6552; - - private static readonly FixedSizeArrayPool s_pool = new(2, BufferSize, 128 * 6, preAllocate: true); - - public static byte[] Rent() - => s_pool.Rent(); - - public static void Return(byte[] chunk) - => s_pool.Return(chunk); -} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs index 0e5b4815f52..08fc5aadc6e 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Json; +using HotChocolate.Buffers; using static HotChocolate.Fusion.Text.Json.MetaDbEventSource; namespace HotChocolate.Fusion.Text.Json; @@ -23,7 +24,7 @@ internal struct MetaDb : IDisposable static MetaDb() { Debug.Assert( - MetaDbMemory.BufferSize >= Cursor.ChunkBytes, + JsonMemory.BufferSize >= Cursor.ChunkBytes, "MetaDb.BufferSize must match Cursor.ChunkBytes for index math to align."); } @@ -36,7 +37,7 @@ internal static MetaDb CreateForEstimatedRows(int estimatedRows) log.MetaDbCreated(1, estimatedRows, 1); // Rent the first chunk now to avoid branching on first append - chunks[0] = MetaDbMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(1, 0); for (var i = 1; i < chunks.Length; i++) @@ -101,7 +102,7 @@ internal Cursor Append( // if the chunk is empty we did not yet rent any memory for it if (chunk.Length == 0) { - chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + chunk = chunks[chunkIndex] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(1, chunkIndex); } @@ -215,7 +216,7 @@ public void Dispose() break; } - MetaDbMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); } chunks.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs index f7af9525d73..6c3fbf84af7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text.Json; +using HotChocolate.Buffers; namespace HotChocolate.Fusion.Text.Json; @@ -62,7 +63,7 @@ internal static SourceResultDocument ParseSingleSegment( { foreach (var chunk in dataChunksSpan) { - JsonMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Json, chunk); } dataChunksSpan.Clear(); @@ -121,7 +122,7 @@ internal static SourceResultDocument ParseMultipleSegments( { foreach (var chunk in dataChunksSpan) { - JsonMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Json, chunk); } dataChunksSpan.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs index 31bce68fb06..b51dc541331 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; +using HotChocolate.Buffers; namespace HotChocolate.Fusion.Text.Json; @@ -228,7 +229,7 @@ public void Dispose() { if (_pooledMemory) { - JsonMemory.Return(_dataChunks, _usedChunks); + JsonMemory.Return(JsonMemoryKind.Json, _dataChunks, _usedChunks); if (_dataChunks.Length > 1) { diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs index 2c5c1b28a13..57a69863033 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs @@ -116,7 +116,7 @@ public SourceResultDocument Build() } // Rent the first chunk - chunks[0] = JsonMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Json); var currentChunkIndex = 0; var currentChunkOffset = 0; @@ -132,7 +132,7 @@ public SourceResultDocument Build() } catch { - JsonMemory.Return(chunks, currentChunkIndex + 1); + JsonMemory.Return(JsonMemoryKind.Json, chunks, currentChunkIndex + 1); ArrayPool.Shared.Return(chunks); throw; } @@ -334,7 +334,7 @@ private static void EnsureChunkCapacity( { Debug.Fail("foo"); - chunks[currentChunkIndex] = JsonMemory.Rent(); + chunks[currentChunkIndex] = JsonMemory.Rent(JsonMemoryKind.Json); } } diff --git a/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj b/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj index 35ad9d7e70c..5a8c1cfd60f 100644 --- a/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj +++ b/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj b/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj index def8e832c74..3be34f6d613 100644 --- a/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj +++ b/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj @@ -9,7 +9,6 @@ - diff --git a/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj b/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj index 505cf7f2374..52a2228e685 100644 --- a/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj +++ b/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj @@ -12,7 +12,6 @@ - diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs similarity index 90% rename from src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs rename to src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs index 46b3c20f5a1..a8e4df61cba 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs @@ -1,8 +1,8 @@ using System.Diagnostics; -using static HotChocolate.Fusion.Buffers.FixedSizeArrayPoolEventSource; -using static HotChocolate.Fusion.Properties.FusionExecutionResources; +using static HotChocolate.Buffers.FixedSizeArrayPoolEventSource; +using static HotChocolate.Buffers.Properties.BuffersResources; -namespace HotChocolate.Fusion.Buffers; +namespace HotChocolate.Buffers; internal sealed class FixedSizeArrayPool { @@ -45,7 +45,14 @@ public byte[] Rent() public void Return(byte[] array) { +#if NET8_0_OR_GREATER ArgumentNullException.ThrowIfNull(array); +#else + if(array is null) + { + throw new ArgumentNullException(nameof(array)); + } +#endif if (array.Length != _arraySize) { @@ -149,7 +156,11 @@ internal Bucket(int poolId, int bufferLength, int numberOfBuffers, bool preAlloc internal bool Return(byte[] array) { - Debug.Assert(array.Length == _bufferLength); + // if the returned array has not the expected size we will reject it without throwing an error. + if (array.Length != _bufferLength) + { + return false; + } var returned = false; var lockTaken = false; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs similarity index 95% rename from src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs rename to src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs index 0f550556dd3..d219a9e4e2b 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs @@ -1,8 +1,8 @@ using System.Diagnostics.Tracing; -namespace HotChocolate.Fusion.Buffers; +namespace HotChocolate.Buffers; -[EventSource(Name = "HotChocolate-Fusion-FixedSizeArrayPool")] +[EventSource(Name = "HotChocolate-Buffers-FixedSizeArrayPool")] internal sealed class FixedSizeArrayPoolEventSource : EventSource { public static readonly FixedSizeArrayPoolEventSource Log = new(); diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs new file mode 100644 index 00000000000..2f28bf87fb9 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs @@ -0,0 +1,6 @@ +namespace HotChocolate.Buffers; + +internal static class FixedSizeArrayPoolKinds +{ + public const int JsonMemory = 1; +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj index c8dbaec6ad7..d6931ad087f 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj @@ -16,6 +16,11 @@ true + + + + + diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs new file mode 100644 index 00000000000..9b21a6fd4d5 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs @@ -0,0 +1,102 @@ +using System.Buffers; +#if NET8_0_OR_GREATER +using System.Runtime.InteropServices; +#endif +using static HotChocolate.Buffers.JsonMemoryEventSource; + +namespace HotChocolate.Buffers; + +/// +/// Manages the memory for storing JSON data. +/// +internal static class JsonMemory +{ + public const int BufferSize = 1 << 17; + + private static FixedSizeArrayPool s_pool = new(FixedSizeArrayPoolKinds.JsonMemory, BufferSize, 128); + private static readonly ArrayPool s_chunkPool = ArrayPool.Shared; + + public static void Reconfigure(Func factory) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(factory); +#else + if(factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } +#endif + + s_pool = factory() ?? throw new InvalidOperationException("The factory must create a valid pool."); + Log.ReconfiguredPool(); + } + + public static byte[] Rent(JsonMemoryKind kind) + { + var buffer = s_pool.Rent(); + Log.BufferRented(kind, bufferCount: 1); + return buffer; + } + + public static byte[][] RentRange(JsonMemoryKind kind, int requiredChunks) + { + var chunks = s_chunkPool.Rent(requiredChunks); + + for (var i = 0; i < requiredChunks; i++) + { + chunks[i] = s_pool.Rent(); + } + + Log.BufferReturned(kind, requiredChunks); + return chunks; + } + + public static void Return(JsonMemoryKind kind, byte[] chunk) + { + s_pool.Return(chunk); + Log.BufferReturned(kind, 1); + } + + public static void Return(JsonMemoryKind kind, byte[][] chunks, int usedChunks) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(chunks); +#else + if (chunks is null) + { + throw new ArgumentNullException(nameof(chunks)); + } +#endif + + foreach (var chunk in chunks.AsSpan(0, usedChunks)) + { + s_pool.Return(chunk); + } + + Log.BufferReturned(kind, usedChunks); + } + + public static void Return(JsonMemoryKind kind, List chunks) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(chunks); + + foreach (var chunk in CollectionsMarshal.AsSpan(chunks)) + { + s_pool.Return(chunk); + } +#else + if (chunks is null) + { + throw new ArgumentNullException(nameof(chunks)); + } + + foreach (var chunk in chunks) + { + s_pool.Return(chunk); + } +#endif + + Log.BufferReturned(kind, chunks.Count); + } +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs new file mode 100644 index 00000000000..58799402b72 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.Tracing; + +namespace HotChocolate.Buffers; + +[EventSource(Name = "HotChocolate-Buffers-JsonMemory")] +internal sealed class JsonMemoryEventSource : EventSource +{ + public static readonly JsonMemoryEventSource Log = new(); + + private JsonMemoryEventSource() { } + + [Event( + eventId: 1, + Level = EventLevel.Informational, + Message = "Pool reconfigured.")] + public void ReconfiguredPool() + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(1); + } + } + + [Event( + eventId: 2, + Level = EventLevel.Verbose, + Message = "Buffer rented (Kind={0}, BufferCount={1})")] + public void BufferRented(JsonMemoryKind kind, int bufferCount) + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(2, kind, bufferCount); + } + } + + [Event( + eventId: 3, + Level = EventLevel.Verbose, + Message = "Buffer returned (Kind={0}, BufferCount={1})")] + public void BufferReturned(JsonMemoryKind kind, int bufferCount) + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(3, kind, bufferCount); + } + } +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs new file mode 100644 index 00000000000..d4016d2f620 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs @@ -0,0 +1,7 @@ +namespace HotChocolate.Buffers; + +internal enum JsonMemoryKind +{ + Metadata = 1, + Json = 2 +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs index 6388029e0e7..a02fc93a0b6 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs @@ -50,5 +50,11 @@ internal static string ArrayWriter_Advance_BufferOverflow { return ResourceManager.GetString("ArrayWriter_Advance_BufferOverflow", resourceCulture); } } + + internal static string FixedSizeArrayPool_Return_InvalidArraySize { + get { + return ResourceManager.GetString("FixedSizeArrayPool_Return_InvalidArraySize", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx index 5449972ccdc..05fdeb25e4c 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx @@ -21,4 +21,7 @@ Cannot advance past the end of the buffer. + + Buffer size {0} does not match expected chunk size {1} + From 2505534f880a8b0ef58361e463836ab21469c980 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 26 Nov 2025 19:53:44 +0100 Subject: [PATCH 05/30] Deprectaion notice --- .../hotchocolate/v16/migrating/migrate-from-15-to-16.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md index 68d903f035e..ef350da0e9d 100644 --- a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md +++ b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md @@ -278,6 +278,12 @@ If you need the old behavior, use can still use the non-generic `ID`-attribute a Previously the `TryConfigure` or `OnConfigure` methods carried a non-nullable parameter of the member the descriptor attribute was annotated to. With the new source generator we moved away from pure reflection based APIs. This means that when you use the source generator +## Merged Assemblies HotChocolate.Types, HotChocolate.Execution, HotChocolate.Fetching + +With Hot Chocolate 16 we introduced a lot more abstractions, meaning we pulled out abstractions of the type system or the execution into separate libraries. But at the same time we simplified the implementation of the type system and the execution by moving the implementations of HotChocolate.Execution and HotChocolate.Fetching into HotChocolate.Types. This allowed us to simplify the implementation and make it more efficient. + +So, if you were referencing HotChocolate.Execution or HotChocolate.Fetching directly make sure to remove references to these libraries and replace them with HotChocolate.Types. + # Deprecations Things that will continue to function this release, but we encourage you to move away from. From a7a87ca0815e5ae81a25cb40d15b003e3184c324 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 27 Nov 2025 10:25:25 +0100 Subject: [PATCH 06/30] wip --- src/All.slnx | 1 - src/HotChocolate/Core/HotChocolate.Core.slnx | 1 - .../src/Types.Shared/WellKnownDirectives.cs | 13 - .../Core/src/Types.Shared/WellKnownTypes.cs | 20 - .../Processing/IOptionalSelection.cs | 2 +- .../Types/Execution/Processing/Operation.cs | 10 + .../Core/src/Types/HotChocolate.Types.csproj | 44 +- .../Properties/TextJsonResources.Designer.cs | 90 +++ .../Types/Properties/TextJsonResources.resx | 42 ++ .../Core/src/Types/SchemaBuilder.Setup.cs | 1 - .../Core/src/Types/SchemaPrinter.cs | 1 - .../Text/Json/CompositeResultProperty.cs | 123 +++ .../Types/Text/Json/ResultDocument.Text.cs | 205 +++++ .../Json/ResultDocument.TryGetProperty.cs | 251 ++++++ .../Text/Json/ResultDocument.TryGetValue.cs | 226 ++++++ .../Types/Text/Json/ResultDocument.WriteTo.cs | 313 ++++++++ .../src/Types/Text/Json/ResultDocument.cs | 60 +- .../Json/ResultElement.ArrayEnumerator.cs | 100 +++ .../Json/ResultElement.ObjectEnumerator.cs | 127 ++++ .../Core/src/Types/Text/Json/ResultElement.cs | 713 ++++++++++++++++++ .../Text/Json/JsonReaderHelper.cs | 7 + .../Fusion.Execution/Text/Json/ThrowHelper.cs | 7 + .../src/Primitives/Types}/BuiltInTypes.cs | 42 +- .../Types/IntrospectionTypeNames.cs | 15 + ...otChocolate.Utilities.Introspection.csproj | 2 +- .../IntrospectionClient.cs | 1 + .../IntrospectionFormatter.cs | 6 +- 27 files changed, 2325 insertions(+), 98 deletions(-) delete mode 100644 src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs delete mode 100644 src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs create mode 100644 src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs create mode 100644 src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs create mode 100644 src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs rename src/HotChocolate/{Core/src/Types.Shared => Primitives/src/Primitives/Types}/BuiltInTypes.cs (60%) create mode 100644 src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs diff --git a/src/All.slnx b/src/All.slnx index d6a71a909b4..a0dedb768b2 100644 --- a/src/All.slnx +++ b/src/All.slnx @@ -110,7 +110,6 @@ - diff --git a/src/HotChocolate/Core/HotChocolate.Core.slnx b/src/HotChocolate/Core/HotChocolate.Core.slnx index 2f8095bec3b..64c576bc86a 100644 --- a/src/HotChocolate/Core/HotChocolate.Core.slnx +++ b/src/HotChocolate/Core/HotChocolate.Core.slnx @@ -27,7 +27,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs b/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs deleted file mode 100644 index 62a1fecea18..00000000000 --- a/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace HotChocolate.Utilities.Introspection; - -internal static class WellKnownDirectives -{ - public const string Skip = "skip"; - public const string Include = "include"; - public const string Defer = "defer"; - public const string Stream = "stream"; - public const string Deprecated = "deprecated"; - public const string SpecifiedBy = "specifiedBy"; - public const string DeprecationReasonArgument = "reason"; - public const string OneOf = "oneOf"; -} diff --git a/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs b/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs deleted file mode 100644 index 3749d2bfa4f..00000000000 --- a/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace HotChocolate.Utilities.Introspection; - -internal static class WellKnownTypes -{ -#pragma warning disable IDE1006, InconsistentNaming - public const string __Directive = "__Directive"; - public const string __DirectiveLocation = "__DirectiveLocation"; - public const string __EnumValue = "__EnumValue"; - public const string __Field = "__Field"; - public const string __InputValue = "__InputValue"; - public const string __Schema = "__Schema"; - public const string __Type = "__Type"; - public const string __TypeKind = "__TypeKind"; -#pragma warning restore IDE1006, InconsistentNaming - public const string String = "String"; - public const string Boolean = "Boolean"; - public const string Float = "Float"; - public const string ID = "ID"; - public const string Int = "Int"; -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs index 8345292c0d3..fb6839101d4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs @@ -27,5 +27,5 @@ public interface IOptionalSelection /// /// True, if this selection shall be included into the request execution. /// - bool IsIncluded(long includeFlags, bool allowInternals = false); + bool IsIncluded(ulong includeFlags, bool allowInternals = false); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index df2bf92e50e..0c2e3384b3f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -200,6 +200,16 @@ internal void Seal( } } + public SelectionSet GetSelectionSetById(int selectionSetId) + { + + } + + public Selection GetSelectionById(int selectionSetId) + { + + } + public IEnumerator GetEnumerator() { foreach (var selectionVariant in _selectionVariants) diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index bf2bed99fa2..ae9e6fe3046 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -77,7 +77,6 @@ - @@ -89,10 +88,18 @@ Text\Json\JsonHelpers.cs + + Text\Json\JsonReaderHelper.cs + + Text\Json\MetaDbEventSource.cs + + Text\Json\ThrowHelper.cs + + ResultDocument.cs @@ -104,6 +111,36 @@ ResultDocument.cs + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultElement.cs + + + + ResultElement.cs + + + + True + True + TextJsonResources.resx + @@ -200,6 +237,11 @@ ResXFileCodeGenerator Resources.Designer.cs + + + ResXFileCodeGenerator + TextJsonResources.Designer.cs + diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs new file mode 100644 index 00000000000..47f922f9119 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HotChocolate.Properties { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TextJsonResources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TextJsonResources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Properties.TextJsonResources", typeof(TextJsonResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string CompositeResultElement_GetBoolean_JsonElementHasWrongType { + get { + return ResourceManager.GetString("CompositeResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); + } + } + + internal static string SourceResultElement_GetBoolean_JsonElementHasWrongType { + get { + return ResourceManager.GetString("SourceResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); + } + } + + internal static string Rethrowable { + get { + return ResourceManager.GetString("Rethrowable", resourceCulture); + } + } + + internal static string JsonReaderHelper_TranscodeHelper_CannotTranscodeInvalidUtf8 { + get { + return ResourceManager.GetString("JsonReaderHelper_TranscodeHelper_CannotTranscodeInvalidUtf8", resourceCulture); + } + } + + internal static string ThrowHelper_ReadInvalidUTF16 { + get { + return ResourceManager.GetString("ThrowHelper_ReadInvalidUTF16", resourceCulture); + } + } + + internal static string ThrowHelper_ReadIncompleteUTF16 { + get { + return ResourceManager.GetString("ThrowHelper_ReadIncompleteUTF16", resourceCulture); + } + } + + internal static string FixedSizeArrayPool_Return_InvalidArraySize { + get { + return ResourceManager.GetString("FixedSizeArrayPool_Return_InvalidArraySize", resourceCulture); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx new file mode 100644 index 00000000000..9375281edf6 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx @@ -0,0 +1,42 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The requested operation requires an element of type '{0}', but the target element has type '{1}'. + + + The requested operation requires an element of type '{0}', but the target element has type '{1}'. + + + HotChocolate.Text.Json.Rethrowable + + + Cannot transcode invalid UTF-8 JSON text to UTF-16 string. + + + Cannot read invalid UTF-16 JSON text as string. Invalid surrogate value: '{0}'. + + + Cannot read incomplete UTF-16 JSON text as string with missing low surrogate. + + + Buffer size {0} does not match expected chunk size {1} + + diff --git a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs index c274967f213..de9f72c32a7 100644 --- a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs +++ b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs @@ -12,7 +12,6 @@ using HotChocolate.Types.Pagination; using HotChocolate.Types.Relay; using HotChocolate.Utilities; -using HotChocolate.Utilities.Introspection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace HotChocolate; diff --git a/src/HotChocolate/Core/src/Types/SchemaPrinter.cs b/src/HotChocolate/Core/src/Types/SchemaPrinter.cs index c0e50daa958..4e4449efb4d 100644 --- a/src/HotChocolate/Core/src/Types/SchemaPrinter.cs +++ b/src/HotChocolate/Core/src/Types/SchemaPrinter.cs @@ -4,7 +4,6 @@ using HotChocolate.Language.Utilities; using HotChocolate.Types; using HotChocolate.Utilities; -using HotChocolate.Utilities.Introspection; namespace HotChocolate; diff --git a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs new file mode 100644 index 00000000000..d34e5576f8c --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs @@ -0,0 +1,123 @@ +using System.Diagnostics; +using System.Text.Json; +using HotChocolate.Fusion.Execution.Nodes; + +namespace HotChocolate.Fusion.Text.Json; + +/// +/// Represents a single property for a JSON object. +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public readonly struct CompositeResultProperty +{ + internal CompositeResultProperty(CompositeResultElement value) + { + Value = value; + } + + /// + /// The value of this property. + /// + public CompositeResultElement Value { get; } + + /// + /// The name of this property. + /// This allocates a new string instance for each call. + /// + public string Name => Value.GetPropertyName(); + + public Selection? Selection => Value.Selection; + + public Selection GetRequiredSelection() => Value.AssertSelection(); + + /// + /// Compares to the name of this property. + /// + /// The text to compare against. + /// + /// if the name of this property matches , + /// otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(string? text) + { + return NameEquals(text.AsSpan()); + } + + /// + /// Compares the text represented by to the name of this property. + /// + /// The UTF-8 encoded text to compare against. + /// + /// if the name of this property has the same UTF-8 encoding as + /// , otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(ReadOnlySpan utf8Text) + { + return Value.TextEqualsHelper(utf8Text, isPropertyName: true, shouldUnescape: true); + } + + /// + /// Compares to the name of this property. + /// + /// The text to compare against. + /// + /// if the name of this property matches , + /// otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(ReadOnlySpan text) + { + return Value.TextEqualsHelper(text, isPropertyName: true); + } + + internal bool EscapedNameEquals(ReadOnlySpan utf8Text) + { + return Value.TextEqualsHelper(utf8Text, isPropertyName: true, shouldUnescape: false); + } + + internal ReadOnlySpan NameSpan => Value.GetPropertyNameRaw(); + + /// + /// Provides a representation of the property for + /// debugging purposes. + /// + /// + /// A string containing the un-interpreted value of the property, beginning + /// at the declaring open-quote and ending at the last character that is part of + /// the value. + /// + public override string ToString() + { + return Value.GetPropertyRawText(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay + => Value.ValueKind == JsonValueKind.Undefined ? "" : $"\"{ToString()}\""; + + public void Deconstruct(out Selection selection, out CompositeResultElement value) + { + selection = GetRequiredSelection(); + value = Value; + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs new file mode 100644 index 00000000000..510e4dc3d01 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs @@ -0,0 +1,205 @@ +using System.Buffers; +using System.Diagnostics; +using System.Text.Unicode; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal string? GetString(Cursor cursor, ElementTokenType expectedType) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + var tokenType = row.TokenType; + + if (tokenType == ElementTokenType.Null) + { + return null; + } + + CheckExpectedType(expectedType, tokenType); + + var segment = ReadRawValue(row); + + if (tokenType is ElementTokenType.String) + { + segment = segment[1..^1]; + } + + return row.HasComplexChildren + ? JsonReaderHelper.GetUnescapedString(segment) + : JsonReaderHelper.TranscodeHelper(segment); + } + + internal string GetRequiredString(Cursor cursor, ElementTokenType expectedType) + { + var value = GetString(cursor, expectedType); + + if (value is null) + { + throw new InvalidOperationException("The element value is null."); + } + + return value; + } + + internal string GetNameOfPropertyValue(Cursor valueCursor) + { + // The property name is one row before the property value + return GetString(valueCursor + (-1), ElementTokenType.PropertyName)!; + } + + internal ReadOnlySpan GetPropertyNameRaw(Cursor valueCursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The property name is stored one row before the value + var nameCursor = valueCursor + (-1); + var row = _metaDb.Get(nameCursor); + Debug.Assert(row.TokenType is ElementTokenType.PropertyName); + + return ReadRawValue(row); + } + + internal string GetRawValueAsString(Cursor cursor) + { + var segment = GetRawValue(cursor, includeQuotes: true); + return JsonReaderHelper.TranscodeHelper(segment); + } + + internal ReadOnlySpan GetRawValue(Cursor cursor, bool includeQuotes) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + + if (row.IsSimpleValue) + { + if (!includeQuotes && row.TokenType == ElementTokenType.String) + { + // Skip opening/closing quotes + return ReadRawValue(row)[1..^1]; + } + + return ReadRawValue(row); + } + + // TODO: this is more complex with the new design, we gonna tackle this later. + // var endCursor = GetEndCursor(cursor, includeEndElement: false); + // var start = row.Location; + // var endRow = _metaDb.Get(endCursor); + // return _utf8Json.Slice(start, endRow.Location - start + endRow.SizeOrLength); + throw new NotImplementedException(); + } + + internal string GetPropertyRawValueAsString(Cursor valueCursor) + { + var segment = GetPropertyRawValue(valueCursor); + return JsonReaderHelper.TranscodeHelper(segment); + } + + private ReadOnlySpan GetPropertyRawValue(Cursor valueCursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The property name is stored one row before the value + Debug.Assert(_metaDb.GetElementTokenType(valueCursor - 1) == ElementTokenType.PropertyName); + + var row = _metaDb.Get(valueCursor); + + if (row.IsSimpleValue) + { + return ReadRawValue(row); + } + + // var endCursor = GetEndCursor(valueCursor, includeEndElement: false); + // var endRow = _metaDb.Get(endCursor); + // return _utf8Json.Slice(start, end - start); + throw new NotSupportedException("Properties are expected to be simple values."); + } + + internal bool TextEquals(Cursor cursor, ReadOnlySpan otherText, bool isPropertyName) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + byte[]? otherUtf8TextArray = null; + + var length = checked(otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); + var otherUtf8Text = length <= JsonConstants.StackallocByteThreshold + ? stackalloc byte[JsonConstants.StackallocByteThreshold] + : (otherUtf8TextArray = ArrayPool.Shared.Rent(length)); + + var status = Utf8.FromUtf16( + otherText, otherUtf8Text, out var charsRead, out var written, + replaceInvalidSequences: false, isFinalBlock: true); + + Debug.Assert(status is OperationStatus.Done or + OperationStatus.DestinationTooSmall or + OperationStatus.InvalidData); + Debug.Assert(charsRead == otherText.Length || status is not OperationStatus.Done); + + bool result; + if (status == OperationStatus.InvalidData) + { + result = false; + } + else + { + Debug.Assert(status == OperationStatus.Done); + result = TextEquals(cursor, otherUtf8Text[..written], isPropertyName, shouldUnescape: true); + } + + if (otherUtf8TextArray != null) + { + otherUtf8Text[..written].Clear(); + ArrayPool.Shared.Return(otherUtf8TextArray); + } + + return result; + } + + internal bool TextEquals(Cursor cursor, ReadOnlySpan otherUtf8Text, bool isPropertyName, bool shouldUnescape) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var matchCursor = isPropertyName ? cursor + (-1) : cursor; + var row = _metaDb.Get(matchCursor); + + CheckExpectedType( + isPropertyName ? ElementTokenType.PropertyName : ElementTokenType.String, + row.TokenType); + + var segment = ReadRawValue(row); + + if (!isPropertyName) + { + segment = segment[1..^1]; + } + + if (otherUtf8Text.Length > segment.Length || (!shouldUnescape && otherUtf8Text.Length != segment.Length)) + { + return false; + } + + if (row.HasComplexChildren && shouldUnescape) + { + if (otherUtf8Text.Length < segment.Length / JsonConstants.MaxExpansionFactorWhileEscaping) + { + return false; + } + + var idx = segment.IndexOf(JsonConstants.BackSlash); + Debug.Assert(idx != -1); + + if (!otherUtf8Text.StartsWith(segment[..idx])) + { + return false; + } + + return JsonReaderHelper.UnescapeAndCompare(segment[idx..], otherUtf8Text[idx..]); + } + + return segment.SequenceEqual(otherUtf8Text); + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs new file mode 100644 index 00000000000..9fc7679a472 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs @@ -0,0 +1,251 @@ +using System.Buffers; +using System.Diagnostics; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal bool TryGetNamedPropertyValue( + Cursor startCursor, + string propertyName, + out CompositeResultElement value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (startCursor, var tokenType) = _metaDb.GetStartCursor(startCursor); + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + var numberOfRows = _metaDb.GetNumberOfRows(startCursor); + + // Only one row means it was EndObject. + if (numberOfRows == 1) + { + value = default; + return false; + } + + var row = _metaDb.Get(startCursor); + if (row.OperationReferenceType is OperationReferenceType.SelectionSet) + { + var selectionSet = _operation.GetSelectionSetById(row.OperationReferenceId); + if (selectionSet.TryGetSelection(propertyName, out var selection)) + { + var propertyIndex = selection.Id - selectionSet.Id - 1; + var propertyRowIndex = (propertyIndex * 2) + 1; + var propertyCursor = startCursor + propertyRowIndex; + Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); + Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); + value = new CompositeResultElement(this, propertyCursor + 1); + return true; + } + } + + var maxBytes = s_utf8Encoding.GetMaxByteCount(propertyName.Length); + var endCursor = startCursor + (numberOfRows - 1); + + if (maxBytes < JsonConstants.StackallocByteThreshold) + { + Span utf8Name = stackalloc byte[JsonConstants.StackallocByteThreshold]; + var len = s_utf8Encoding.GetBytes(propertyName, utf8Name); + utf8Name = utf8Name[..len]; + + return TryGetNamedPropertyValue( + startCursor + 1, + endCursor, + utf8Name, + out value); + } + + // Unescaping the property name will make the string shorter (or the same) + // So the first viable candidate is one whose length in bytes matches, or + // exceeds, our length in chars. + // + // The maximal escaping seems to be 6 -> 1 ("\u0030" => "0"), but just transcode + // and switch once one viable long property is found. + + var minBytes = propertyName.Length; + var candidate = endCursor; + + while (candidate > startCursor) + { + var passed = candidate; + + row = _metaDb.Get(candidate); + Debug.Assert(row.TokenType != ElementTokenType.PropertyName); + + candidate--; + row = _metaDb.Get(candidate); + Debug.Assert(row.TokenType == ElementTokenType.PropertyName); + + if (row.SizeOrLength >= minBytes) + { + var tmpUtf8 = ArrayPool.Shared.Rent(maxBytes); + Span utf8Name = default; + + try + { + var len = s_utf8Encoding.GetBytes(propertyName, tmpUtf8); + utf8Name = tmpUtf8.AsSpan(0, len); + + return TryGetNamedPropertyValue( + startCursor, + passed + 1, + utf8Name, + out value); + } + finally + { + // While property names aren't usually a secret, they also usually + // aren't long enough to end up in the rented buffer transcode path. + // + // On the basis that this is user data, go ahead and clear it. + utf8Name.Clear(); + ArrayPool.Shared.Return(tmpUtf8); + } + } + + // Move to the previous value + candidate--; + } + + // None of the property names were within the range that the UTF-8 encoding would have been. + value = default; + return false; + } + + internal bool TryGetNamedPropertyValue( + Cursor startCursor, + ReadOnlySpan propertyName, + out CompositeResultElement value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (startCursor, var tokenType) = _metaDb.GetStartCursor(startCursor); + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + var numberOfRows = _metaDb.GetNumberOfRows(startCursor); + + // Only one row means it was EndObject. + if (numberOfRows == 1) + { + value = default; + return false; + } + + var row = _metaDb.Get(startCursor); + if (row.OperationReferenceType is OperationReferenceType.SelectionSet) + { + var selectionSet = _operation.GetSelectionSetById(row.OperationReferenceId); + if (selectionSet.TryGetSelection(propertyName, out var selection)) + { + var propertyIndex = selection.Id - selectionSet.Id - 1; + var propertyRowIndex = (propertyIndex * 2) + 1; + var propertyCursor = startCursor + propertyRowIndex; + Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); + Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); + value = new CompositeResultElement(this, propertyCursor + 1); + return true; + } + } + + var endCursor = startCursor + (numberOfRows - 1); + + return TryGetNamedPropertyValue( + startCursor + 1, + endCursor, + propertyName, + out value); + } + + private bool TryGetNamedPropertyValue( + Cursor startCursor, + Cursor endCursor, + ReadOnlySpan propertyName, + out CompositeResultElement value) + { + Span utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold]; + var cursor = endCursor; + + while (cursor > startCursor) + { + var row = _metaDb.Get(cursor); + Debug.Assert(row.TokenType != ElementTokenType.PropertyName); + cursor--; + + row = _metaDb.Get(cursor); + Debug.Assert(row.TokenType == ElementTokenType.PropertyName); + var currentPropertyName = ReadRawValue(row); + + if (row.HasComplexChildren) + { + // An escaped property name will be longer than an unescaped candidate, so only unescape + // when the lengths are compatible. + if (currentPropertyName.Length > propertyName.Length) + { + var idx = currentPropertyName.IndexOf(JsonConstants.BackSlash); + Debug.Assert(idx >= 0); + + // If everything up to where the property name has a backslash matches, keep going. + if (propertyName.Length > idx + && currentPropertyName[..idx].SequenceEqual(propertyName[..idx])) + { + var remaining = currentPropertyName.Length - idx; + var written = 0; + byte[]? rented = null; + + try + { + var utf8Unescaped = remaining <= utf8UnescapedStack.Length + ? utf8UnescapedStack + : (rented = ArrayPool.Shared.Rent(remaining)); + + // Only unescape the part we haven't processed. + JsonReaderHelper.Unescape(currentPropertyName[idx..], utf8Unescaped, 0, out written); + + // If the unescaped remainder matches the input remainder, it's a match. + if (utf8Unescaped[..written].SequenceEqual(propertyName[idx..])) + { + // If the property name is a match, the answer is the next element. + value = new CompositeResultElement(this, cursor + 1); + return true; + } + } + finally + { + if (rented != null) + { + rented.AsSpan(0, written).Clear(); + ArrayPool.Shared.Return(rented); + } + } + } + } + } + else if (currentPropertyName.SequenceEqual(propertyName)) + { + // If the property name is a match, the answer is the next element. + value = new CompositeResultElement(this, cursor + 1); + return true; + } + + // Move to the previous value + cursor--; + } + + value = default; + return false; + } + + internal Cursor GetStartCursor(Cursor cursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + (cursor, _) = _metaDb.GetStartCursor(cursor); + return cursor; + } + + internal Cursor GetEndCursor(Cursor cursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + return cursor + _metaDb.GetNumberOfRows(cursor); + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs new file mode 100644 index 00000000000..2794eb02500 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs @@ -0,0 +1,226 @@ +using System.Buffers.Text; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal bool TryGetValue(Cursor cursor, out sbyte value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out sbyte tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out byte value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out byte tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out short value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out short tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out ushort value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out ushort tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out int value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out int tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out uint value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out uint tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out long value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out long tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out ulong value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out ulong tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out double value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out double tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out float value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out float tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out decimal value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out decimal tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs new file mode 100644 index 00000000000..c5c86bd324a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs @@ -0,0 +1,313 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text.Json; +using HotChocolate.Execution; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument : IRawJsonFormatter +{ + public void WriteTo(IBufferWriter writer, bool indented = false) + { + var formatter = new RawJsonFormatter(this, writer, indented); + formatter.Write(); + } + + internal ref struct RawJsonFormatter(ResultDocument document, IBufferWriter writer, bool indented) + { + private int _indentLevel = 0; + + public void Write() + { + WriteByte(JsonConstants.OpenBrace); + + if (indented) + { + WriteNewLine(); + _indentLevel++; + } + + if (document._errors?.Count > 0) + { + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Errors); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + var options = new JsonWriterOptions { Indented = indented }; + using var jsonWriter = new Utf8JsonWriter(writer, options); + JsonValueFormatter.WriteErrors( + jsonWriter, + document._errors, + new JsonSerializerOptions(JsonSerializerDefaults.Web), + default); + jsonWriter.Flush(); + + WriteByte(JsonConstants.Comma); + } + + // Write "data": + var root = Cursor.Zero; + var row = document._metaDb.Get(root); + + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Data); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + if (row.TokenType is ElementTokenType.Null + || (ElementFlags.Invalidated & row.Flags) == ElementFlags.Invalidated) + { + writer.Write(JsonConstants.NullValue); + } + else + { + WriteObject(root, row); + } + + if (document.Extensions?.Count > 0) + { + WriteByte(JsonConstants.Comma); + + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Extensions); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + var options = new JsonWriterOptions { Indented = indented }; + using var jsonWriter = new Utf8JsonWriter(writer, options); + JsonValueFormatter.WriteDictionary( + jsonWriter, + document.Extensions, + new JsonSerializerOptions(JsonSerializerDefaults.Web), + default); + jsonWriter.Flush(); + } + + if (indented) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBrace); + } + + public void WriteValue(Cursor cursor, DbRow row) + { + var tokenType = row.TokenType; + + // Inline reference resolution + if (tokenType is ElementTokenType.Reference) + { + cursor = document._metaDb.GetLocationCursor(cursor); + row = document._metaDb.Get(cursor); + tokenType = row.TokenType; + } + + Debug.Assert(tokenType is not ElementTokenType.Reference); + Debug.Assert(tokenType is not ElementTokenType.EndObject); + Debug.Assert(tokenType is not ElementTokenType.EndArray); + + switch (tokenType) + { + case ElementTokenType.StartObject + when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + WriteObject(cursor, row); + break; + + case ElementTokenType.StartArray + when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + WriteArray(cursor, row); + break; + + case ElementTokenType.None: + case ElementTokenType.Null: + writer.Write(JsonConstants.NullValue); + break; + + case ElementTokenType.True: + writer.Write(JsonConstants.TrueValue); + break; + + case ElementTokenType.False: + writer.Write(JsonConstants.FalseValue); + break; + + default: + document.WriteRawValueTo(writer, row, _indentLevel, indented); + break; + } + } + + private void WriteObject(Cursor start, DbRow startRow) + { + Debug.Assert(startRow.TokenType is ElementTokenType.StartObject); + + var current = start + 1; + var end = start + startRow.NumberOfRows; + + WriteByte(JsonConstants.OpenBrace); + + if (indented && current < end) + { + _indentLevel++; + } + + var first = true; + while (current < end) + { + var row = document._metaDb.Get(current); + Debug.Assert(row.TokenType is ElementTokenType.PropertyName); + + if ((ElementFlags.IsInternal & row.Flags) == ElementFlags.IsInternal + || (ElementFlags.IsExcluded & row.Flags) == ElementFlags.IsExcluded) + { + // skip name+value + current += 2; + continue; + } + + if (!first) + { + WriteByte(JsonConstants.Comma); + } + first = false; + + if (indented) + { + WriteNewLine(); + WriteIndent(); + } + + // property name (quoted) + WriteByte(JsonConstants.Quote); + writer.Write(document.ReadRawValue(row)); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + // property value + current++; + row = document._metaDb.Get(current); + WriteValue(current, row); + + // next property (move past value) + current++; + } + + if (indented && !first) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBrace); + } + + private void WriteArray(Cursor start, DbRow startRow) + { + Debug.Assert(startRow.TokenType is ElementTokenType.StartArray); + + var current = start + 1; + var end = start + startRow.NumberOfRows; + + WriteByte(JsonConstants.OpenBracket); + + if (indented && current < end) + { + _indentLevel++; + } + + var first = true; + while (current < end) + { + if (!first) + { + WriteByte(JsonConstants.Comma); + } + first = false; + + if (indented) + { + WriteNewLine(); + WriteIndent(); + } + + var row = document._metaDb.Get(current); + WriteValue(current, row); + + current++; + } + + if (indented && end > start + 1) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBracket); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteNewLine() => WriteByte(JsonConstants.NewLineLineFeed); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteIndent() + { + var indentSize = _indentLevel * 2; + if (indentSize > 0) + { + var span = writer.GetSpan(indentSize); + span[..indentSize].Fill(JsonConstants.Space); + writer.Advance(indentSize); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteByte(byte value) + { + var span = writer.GetSpan(1); + span[0] = value; + writer.Advance(1); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs index ab09873c494..c34fedfd4ca 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -10,14 +10,13 @@ namespace HotChocolate.Text.Json; public sealed partial class ResultDocument : IDisposable { private static readonly Encoding s_utf8Encoding = Encoding.UTF8; - private readonly IOperation _operation; + private readonly Operation _operation; private readonly ulong _includeFlags; private List? _errors; - private Dictionary? _extensions; internal MetaDb _metaDb; private bool _disposed; - public ResultDocument(IOperation operation, ulong includeFlags) + internal ResultDocument(Operation operation, ulong includeFlags) { _metaDb = MetaDb.CreateForEstimatedRows(Cursor.RowsPerChunk * 8); _operation = operation; @@ -26,41 +25,35 @@ public ResultDocument(IOperation operation, ulong includeFlags) Data = CreateObject(Cursor.Zero, operation.RootSelectionSet); } - public CompositeResultElement Data { get; } + public ResultElement Data { get; } + // we need extra methods to add stuff public List? Errors { get => _errors; internal set => _errors = value; } - public Dictionary? Extensions - { - get => _extensions; - internal set => _extensions = value; - } + public Dictionary? Extensions { get; set; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ElementTokenType GetElementTokenType(Cursor cursor) => _metaDb.GetElementTokenType(cursor); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal IOperation GetOperation() + internal Operation GetOperation() => _operation; - internal ISelectionSet? GetSelectionSet(Cursor cursor) + internal SelectionSet? GetSelectionSet(Cursor cursor) { var row = _metaDb.Get(cursor); - if (row.OperationReferenceType is not OperationReferenceType.SelectionSet) - { - return null; - } - - return _operation.GetSelectionSetById(row.OperationReferenceId); + return row.OperationReferenceType is OperationReferenceType.SelectionSet + ? _operation.GetSelectionSetById(row.OperationReferenceId) + : null; } - internal ISelection? GetSelection(Cursor cursor) + internal Selection? GetSelection(Cursor cursor) { if (cursor == Cursor.Zero) { @@ -81,15 +74,12 @@ internal IOperation GetOperation() } } - if (row.OperationReferenceType is not OperationReferenceType.Selection) - { - return null; - } - - return _operation.GetSelectionById(row.OperationReferenceId); + return row.OperationReferenceType is OperationReferenceType.Selection + ? _operation.GetSelectionById(row.OperationReferenceId) + : null; } - internal CompositeResultElement GetArrayIndexElement(Cursor current, int arrayIndex) + internal ResultElement GetArrayIndexElement(Cursor current, int arrayIndex) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -105,7 +95,7 @@ internal CompositeResultElement GetArrayIndexElement(Cursor current, int arrayIn } // first element is at +1 after StartArray - return new CompositeResultElement(this, start.AddRows(arrayIndex + 1)); + return new ResultElement(this, start.AddRows(arrayIndex + 1)); } internal int GetArrayLength(Cursor current) @@ -195,7 +185,7 @@ internal Path CreatePath(Cursor current) return path; } - internal CompositeResultElement GetParent(Cursor current) + internal ResultElement GetParent(Cursor current) { // The null cursor represents the data object, which is the utmost root. // If we have reached that we simply return an undefined element @@ -226,7 +216,7 @@ internal CompositeResultElement GetParent(Cursor current) Debug.Assert(_metaDb.GetElementTokenType(parent, resolveReferences: false) == ElementTokenType.Reference); } - return new CompositeResultElement(this, parent); + return new ResultElement(this, parent); } internal bool IsInvalidated(Cursor current) @@ -393,7 +383,7 @@ private ReadOnlySpan ReadRawValue(DbRow row) throw new NotSupportedException(); } - internal CompositeResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) + internal ResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) { var startObjectCursor = WriteStartObject(parent, selectionSet.Id); @@ -406,10 +396,10 @@ internal CompositeResultElement CreateObject(Cursor parent, ISelectionSet select WriteEndObject(startObjectCursor, selectionCount); - return new CompositeResultElement(this, startObjectCursor); + return new ResultElement(this, startObjectCursor); } - internal CompositeResultElement CreateArray(Cursor parent, int length) + internal ResultElement CreateArray(Cursor parent, int length) { var cursor = WriteStartArray(parent, length); @@ -420,11 +410,11 @@ internal CompositeResultElement CreateArray(Cursor parent, int length) WriteEndArray(); - return new CompositeResultElement(this, cursor); + return new ResultElement(this, cursor); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AssignCompositeValue(CompositeResultElement target, CompositeResultElement value) + internal void AssignCompositeValue(ResultElement target, ResultElement value) { _metaDb.Replace( cursor: target.Cursor, @@ -434,7 +424,7 @@ internal void AssignCompositeValue(CompositeResultElement target, CompositeResul } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AssignSourceValue(CompositeResultElement target, SourceResultElement source) + internal void AssignSourceValue(ResultElement target, ResultElement source) { var value = source.GetValuePointer(); var parent = source._parent; @@ -476,7 +466,7 @@ internal void AssignSourceValue(CompositeResultElement target, SourceResultEleme } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AssignNullValue(CompositeResultElement target) + internal void AssignNullValue(ResultElement target) { _metaDb.Replace( cursor: target.Cursor, diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs new file mode 100644 index 00000000000..b7a04e9ec98 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Diagnostics; + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement +{ + /// + /// An enumerable and enumerator for the contents of a JSON array. + /// + [DebuggerDisplay("{Current,nq}")] + public struct ArrayEnumerator : IEnumerable, IEnumerator + { + private readonly ResultDocument _document; + private readonly ResultDocument.Cursor _start; + private readonly ResultDocument.Cursor _end; + private ResultDocument.Cursor _cursor; + + internal ArrayEnumerator(ResultElement target) + { + _document = target._parent; + (_start, var tokenType) = _document._metaDb.GetStartCursor(target._cursor); + Debug.Assert(tokenType is ElementTokenType.StartArray); + _end = _start + _document._metaDb.GetNumberOfRows(_start); + _cursor = _start; + } + + /// + public ResultElement Current + { + get + { + var cursor = _cursor; + if (cursor == _start || cursor >= _end) + { + return default; + } + + return new ResultElement(_document, cursor); + } + } + + /// + object IEnumerator.Current => Current; + + /// + /// Returns an enumerator that iterates through the array. + /// + public ArrayEnumerator GetEnumerator() + { + var enumerator = this; + enumerator._cursor = enumerator._start; + return enumerator; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + public bool MoveNext() + { + var start = _start; + var end = _end; + + if (_cursor == start) + { + var first = start + 1; + if (first < end) + { + _cursor = first; + return true; + } + + _cursor = end; + return false; + } + + var next = _cursor + 1; + if (next < end) + { + _cursor = next; + return true; + } + + _cursor = end; + return false; + } + + /// + public void Reset() => _cursor = _start; + + /// + public void Dispose() => _cursor = _end; + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs new file mode 100644 index 00000000000..58d8d5d163b --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs @@ -0,0 +1,127 @@ +using System.Collections; +using System.Diagnostics; +using static HotChocolate.Fusion.Text.Json.CompositeResultDocument; + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement +{ + /// + /// An enumerable and enumerator for the properties of a JSON object. + /// + [DebuggerDisplay("{Current,nq}")] + public struct ObjectEnumerator : IEnumerable, IEnumerator + { + private readonly CompositeResultDocument _document; + private readonly Cursor _start; + private readonly Cursor _end; + private Cursor _cursor; + + internal ObjectEnumerator(CompositeResultElement target) + { + _document = target._parent; + (_start, var tokenType) = _document._metaDb.GetStartCursor(target._cursor); + Debug.Assert(tokenType is ElementTokenType.StartObject); + _end = _start + _document._metaDb.GetNumberOfRows(_start); + _cursor = _start; + } + + /// + public CompositeResultProperty Current + { + get + { + if (_cursor == _start || _cursor >= _end) + { + return default; + } + + return new CompositeResultProperty(new CompositeResultElement(_document, _cursor + 1)); + } + } + + /// + /// Returns an enumerator that iterates the properties of an object. + /// + /// + /// An value that can be used to iterate + /// through the object. + /// + /// + /// The enumerator will enumerate the properties in the order they are + /// declared, and when an object has multiple definitions of a single + /// property they will all individually be returned (each in the order + /// they appear in the content). + /// + public ObjectEnumerator GetEnumerator() + { + var enumerator = this; + enumerator._cursor = enumerator._start; + return enumerator; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + public void Dispose() + { + _cursor = _end; + } + + /// + public void Reset() + { + _cursor = _start; + } + + /// + object IEnumerator.Current => Current; + + /// + public bool MoveNext() + { + while (MoveNextInternal()) + { + var flags = _document._metaDb.GetFlags(_cursor); + if ((ElementFlags.IsExcluded & flags) is not ElementFlags.IsExcluded) + { + return true; + } + } + + return false; + } + + private bool MoveNextInternal() + { + if (_cursor == _start) + { + var first = _start + 1; + if (first < _end) + { + _cursor = first; + return true; + } + + _cursor = _end; + return false; + } + + var next = _cursor += 2; + if (next < _end) + { + _cursor = next; + return true; + } + + _cursor = _end; + return false; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs new file mode 100644 index 00000000000..94b91ddb9bf --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs @@ -0,0 +1,713 @@ +using System.Buffers; +using System.Diagnostics; +using System.Text.Json; +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using HotChocolate.Types; +using static HotChocolate.Properties.TextJsonResources; + +#pragma warning disable CS1574, CS1584, CS1581, CS1580 + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement : IRawJsonFormatter +{ + private readonly ResultDocument _parent; + private readonly ResultDocument.Cursor _cursor; + + internal ResultElement(ResultDocument parent, ResultDocument.Cursor cursor) + { + // parent is usually not null, but the Current property + // on the enumerators (when initialized as `default`) can + // get here with a null. + _parent = parent; + _cursor = cursor; + } + + public void WriteTo(IBufferWriter writer, bool indented = false) + { + var formatter = new ResultDocument.RawJsonFormatter(_parent, writer, indented); + + var row = _parent._metaDb.Get(_cursor); + formatter.WriteValue(_cursor, row); + } + + /// + /// Gets the internal meta-db cursor. + /// + internal ResultDocument.Cursor Cursor => _cursor; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private ElementTokenType TokenType => _parent?.GetElementTokenType(_cursor) ?? ElementTokenType.None; + + /// + /// The that the value is. + /// + /// + /// The parent has been disposed. + /// + public JsonValueKind ValueKind => TokenType.ToValueKind(); + + /// + /// Get the value at a specified index when the current value is a + /// . + /// + /// + /// This value's is not . + /// + /// + /// is not in the range [0, ()). + /// + /// + /// The parent has been disposed. + /// + public ResultElement this[int index] + { + get + { + CheckValidInstance(); + + return _parent.GetArrayIndexElement(_cursor, index); + } + } + + public IOperation Operation + { + get + { + CheckValidInstance(); + + return _parent.GetOperation(); + } + } + + public ISelectionSet? SelectionSet + { + get + { + CheckValidInstance(); + + return _parent.GetSelectionSet(_cursor); + } + } + + public ISelection? Selection + { + get + { + CheckValidInstance(); + + if (_cursor == ResultDocument.Cursor.Zero) + { + return null; + } + + // note: the selection is stored on the property not on the value. + return _parent.GetSelection(_cursor - 1); + } + } + + public IType? Type + { + get + { + if (_cursor == ResultDocument.Cursor.Zero) + { + return null; + } + + var selection = Selection; + + if (selection is not null) + { + return selection.Type; + } + + var type = Parent.Type; + + if (type?.IsListType() == true) + { + return type.ElementType(); + } + + return null; + } + } + + public bool IsInvalidated + { + get + { + CheckValidInstance(); + + return _parent.IsInvalidated(_cursor); + } + } + + public bool IsNullOrInvalidated + { + get + { + if (_parent is null) + { + return true; + } + + return _parent.IsNullOrInvalidated(_cursor); + } + } + + public Path Path + { + get + { + CheckValidInstance(); + + return _parent.CreatePath(_cursor); + } + } + + public ResultElement Parent + { + get + { + CheckValidInstance(); + + return _parent.GetParent(_cursor); + } + } + + public bool IsNullable + { + get + { + CheckValidInstance(); + + if (_cursor == ResultDocument.Cursor.Zero) + { + return false; + } + + return Type?.IsNullableType() ?? true; + } + } + + public bool IsInternal + { + get + { + CheckValidInstance(); + + return _parent.IsInternalProperty(_cursor); + } + } + + public ISelectionSet AssertSelectionSet() + { + var selectionSet = SelectionSet; + + if (selectionSet is null) + { + throw new InvalidOperationException("The selection set is null.") { Source = Rethrowable }; + } + + return selectionSet; + } + + public ISelection AssertSelection() + { + var selection = Selection; + + if (selection is null) + { + throw new InvalidOperationException("The selection set is null.") { Source = Rethrowable }; + } + + return selection; + } + + public IType AssertType() + { + var type = Type; + + if (type is null) + { + throw new InvalidOperationException("The type is null.") { Source = Rethrowable }; + } + + return type; + } + + public void Invalidate() + { + CheckValidInstance(); + + _parent.Invalidate(_cursor); + } + + /// + /// Get the number of values contained within the current array value. + /// + public int GetArrayLength() + { + CheckValidInstance(); + + return _parent.GetArrayLength(_cursor); + } + + /// + /// Get the number of properties contained within the current object value. + /// + public int GetPropertyCount() + { + CheckValidInstance(); + + return _parent.GetPropertyCount(_cursor); + } + + public ResultElement GetProperty(string propertyName) + { + ArgumentNullException.ThrowIfNull(propertyName); + + if (TryGetProperty(propertyName, out var property)) + { + return property; + } + + throw new KeyNotFoundException(); + } + + public ResultElement GetProperty(ReadOnlySpan utf8PropertyName) + { + if (TryGetProperty(utf8PropertyName, out var property)) + { + return property; + } + + throw new KeyNotFoundException(); + } + + public bool TryGetProperty(string propertyName, out ResultElement value) + { + ArgumentNullException.ThrowIfNull(propertyName); + + return _parent.TryGetNamedPropertyValue(_cursor, propertyName, out value); + } + + public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out ResultElement value) + { + CheckValidInstance(); + + return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value); + } + + public bool GetBoolean() + { + var type = TokenType; + + return type switch + { + ElementTokenType.True => true, + ElementTokenType.False => false, + _ => ThrowJsonElementWrongTypeException(type) + }; + + static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) + { + throw new InvalidOperationException(string.Format( + ResultElement_GetBoolean_JsonElementHasWrongType, + nameof(Boolean), + actualType.ToValueKind())) + { + Source = Rethrowable + }; + } + } + + public string? GetString() + { + CheckValidInstance(); + + return _parent.GetString(_cursor, ElementTokenType.String); + } + + public string AssertString() + { + CheckValidInstance(); + + return _parent.GetRequiredString(_cursor, ElementTokenType.String); + } + + public bool TryGetSByte(out sbyte value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public sbyte GetSByte() => TryGetSByte(out var value) ? value : throw new FormatException(); + + public bool TryGetByte(out byte value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public byte GetByte() + { + if (TryGetByte(out var value)) + { + return value; + } + + throw new FormatException(); + } + + public bool TryGetInt16(out short value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public short GetInt16() + { + if (TryGetInt16(out var value)) + { + return value; + } + + throw new FormatException(); + } + + public bool TryGetUInt16(out ushort value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public ushort GetUInt16() + { + if (TryGetUInt16(out var value)) + { + return value; + } + + throw new FormatException(); + } + + public bool TryGetInt32(out int value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public int GetInt32() + { + if (!TryGetInt32(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetUInt32(out uint value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public uint GetUInt32() + { + if (!TryGetUInt32(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetInt64(out long value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public long GetInt64() + { + if (!TryGetInt64(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetUInt64(out ulong value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public ulong GetUInt64() + { + if (!TryGetUInt64(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetDouble(out double value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public double GetDouble() + { + if (!TryGetDouble(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetSingle(out float value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public float GetSingle() + { + if (!TryGetSingle(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + public bool TryGetDecimal(out decimal value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + public decimal GetDecimal() + { + if (!TryGetDecimal(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + internal string GetPropertyName() + { + CheckValidInstance(); + + return _parent.GetNameOfPropertyValue(_cursor); + } + + internal ReadOnlySpan GetPropertyNameRaw() + { + CheckValidInstance(); + + return _parent.GetPropertyNameRaw(_cursor); + } + + public string GetRawText() + { + CheckValidInstance(); + + return _parent.GetRawValueAsString(_cursor); + } + + internal ReadOnlySpan GetRawValue(bool includeQuotes = true) + { + CheckValidInstance(); + + return _parent.GetRawValue(_cursor, includeQuotes: true); + } + + public bool ValueEquals(string? text) + { + if (TokenType == ElementTokenType.Null) + { + return text == null; + } + + return TextEqualsHelper(text.AsSpan(), isPropertyName: false); + } + + public bool ValueEquals(ReadOnlySpan utf8Text) + { + if (TokenType == ElementTokenType.Null) + { +#pragma warning disable CA2265 + return utf8Text[..0] == default; +#pragma warning restore CA2265 + } + + return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); + } + + public bool ValueEquals(ReadOnlySpan text) + { + if (TokenType == ElementTokenType.Null) + { +#pragma warning disable CA2265 + return text[..0] == default; +#pragma warning restore CA2265 + } + + return TextEqualsHelper(text, isPropertyName: false); + } + + internal bool TextEqualsHelper(ReadOnlySpan utf8Text, bool isPropertyName, bool shouldUnescape) + { + CheckValidInstance(); + + return _parent.TextEquals(_cursor, utf8Text, isPropertyName, shouldUnescape); + } + + internal bool TextEqualsHelper(ReadOnlySpan text, bool isPropertyName) + { + CheckValidInstance(); + + return _parent.TextEquals(_cursor, text, isPropertyName); + } + + internal string GetPropertyRawText() + { + CheckValidInstance(); + + return _parent.GetPropertyRawValueAsString(_cursor); + } + + public ArrayEnumerator EnumerateArray() + { + CheckValidInstance(); + + var tokenType = TokenType; + + if (tokenType != ElementTokenType.StartArray) + { + throw new InvalidOperationException(string.Format( + "The requested operation requires an element of type '{0}', but the target element has type '{1}'.", + ElementTokenType.StartArray, + tokenType)) + { + Source = Rethrowable + }; + } + + return new ArrayEnumerator(this); + } + + public ObjectEnumerator EnumerateObject() + { + CheckValidInstance(); + + var tokenType = TokenType; + + if (tokenType is not ElementTokenType.StartObject) + { + throw new InvalidOperationException(string.Format( + "The requested operation requires an element of type '{0}', but the target element has type '{1}'.", + ElementTokenType.StartObject, + tokenType)); + } + + return new ObjectEnumerator(this); + } + + internal void SetObjectValue(SelectionSet selectionSet) + { + CheckValidInstance(); + + ArgumentNullException.ThrowIfNull(selectionSet); + + var obj = _parent.CreateObject(_cursor, selectionSet: selectionSet); + _parent.AssignCompositeValue(this, obj); + } + + internal void SetArrayValue(int length) + { + CheckValidInstance(); + + ArgumentOutOfRangeException.ThrowIfNegative(length); + + var arr = _parent.CreateArray(_cursor, length); + _parent.AssignCompositeValue(this, arr); + } + + internal void SetLeafValue(SourceResultElement source) + { + CheckValidInstance(); + + _parent.AssignSourceValue(this, source); + } + + internal void SetNullValue() + { + CheckValidInstance(); + + _parent.AssignNullValue(this); + } + + public override string ToString() + { + switch (TokenType) + { + case ElementTokenType.None: + case ElementTokenType.Null: + return string.Empty; + + case ElementTokenType.True: + return bool.TrueString; + + case ElementTokenType.False: + return bool.FalseString; + + case ElementTokenType.Number: + case ElementTokenType.StartArray: + case ElementTokenType.StartObject: + Debug.Assert(_parent != null); + return _parent.GetRawValueAsString(_cursor); + + case ElementTokenType.String: + return GetString()!; + + case ElementTokenType.Comment: + case ElementTokenType.EndArray: + case ElementTokenType.EndObject: + default: + Debug.Fail($"No handler for {nameof(JsonTokenType)}.{TokenType}"); + return string.Empty; + } + } + + private void CheckValidInstance() + { + if (_parent == null) + { + throw new InvalidOperationException(); + } + } +} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs index 8f3d1a55222..100366d0af1 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs @@ -4,9 +4,16 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; + +#if FUSION using static HotChocolate.Fusion.Properties.FusionExecutionResources; namespace HotChocolate.Fusion.Text.Json; +#else +using static HotChocolate.Properties.TypeResources; + +namespace HotChocolate.Text.Json; +#endif internal static class JsonReaderHelper { diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs index 992c9511d9d..5461a82bcee 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs @@ -1,7 +1,14 @@ using System.Diagnostics.CodeAnalysis; + +#if FUSION using static HotChocolate.Fusion.Properties.FusionExecutionResources; namespace HotChocolate.Fusion.Text.Json; +#else +using static HotChocolate.Properties.TextJsonResources; + +namespace HotChocolate.Text.Json; +#endif internal static class ThrowHelper { diff --git a/src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs b/src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs similarity index 60% rename from src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs rename to src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs index ba792c03e00..16d5e401aca 100644 --- a/src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs +++ b/src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs @@ -1,35 +1,35 @@ using HotChocolate.Language; -namespace HotChocolate.Utilities.Introspection; +namespace HotChocolate.Types; public static class BuiltInTypes { private static readonly HashSet s_typeNames = [ - WellKnownTypes.__Directive, - WellKnownTypes.__DirectiveLocation, - WellKnownTypes.__EnumValue, - WellKnownTypes.__Field, - WellKnownTypes.__InputValue, - WellKnownTypes.__Schema, - WellKnownTypes.__Type, - WellKnownTypes.__TypeKind, - WellKnownTypes.String, - WellKnownTypes.Boolean, - WellKnownTypes.Float, - WellKnownTypes.ID, - WellKnownTypes.Int + IntrospectionTypeNames.__Directive, + IntrospectionTypeNames.__DirectiveLocation, + IntrospectionTypeNames.__EnumValue, + IntrospectionTypeNames.__Field, + IntrospectionTypeNames.__InputValue, + IntrospectionTypeNames.__Schema, + IntrospectionTypeNames.__Type, + IntrospectionTypeNames.__TypeKind, + SpecScalarNames.String.Name, + SpecScalarNames.Boolean.Name, + SpecScalarNames.Float.Name, + SpecScalarNames.ID.Name, + SpecScalarNames.Int.Name ]; private static readonly HashSet s_directiveNames = [ - WellKnownDirectives.Skip, - WellKnownDirectives.Include, - WellKnownDirectives.Deprecated, - WellKnownDirectives.Defer, - WellKnownDirectives.Stream, - WellKnownDirectives.SpecifiedBy, - WellKnownDirectives.OneOf + DirectiveNames.Skip.Name, + DirectiveNames.Include.Name, + DirectiveNames.Deprecated.Name, + DirectiveNames.Defer.Name, + DirectiveNames.Stream.Name, + DirectiveNames.SpecifiedBy.Name, + DirectiveNames.OneOf.Name ]; public static bool IsBuiltInType(string name) diff --git a/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs b/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs new file mode 100644 index 00000000000..9057e806224 --- /dev/null +++ b/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs @@ -0,0 +1,15 @@ +namespace HotChocolate.Types; + +public static class IntrospectionTypeNames +{ + // ReSharper disable InconsistentNaming + public const string __Directive = nameof(__Directive); + public const string __DirectiveLocation = nameof(__DirectiveLocation); + public const string __EnumValue = nameof(__EnumValue); + public const string __Field = nameof(__Field); + public const string __InputValue = nameof(__InputValue); + public const string __Schema = nameof(__Schema); + public const string __Type = nameof(__Type); + public const string __TypeKind = nameof(__TypeKind); + // ReSharper restore InconsistentNaming +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj b/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj index a014d417a25..bef5c3c11aa 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj @@ -24,8 +24,8 @@ - + diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs index afe9cdbf646..b223c7cf5be 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs @@ -2,6 +2,7 @@ using System.Text.Json; using HotChocolate.Language; using HotChocolate.Transport.Http; +using HotChocolate.Types; using static HotChocolate.Utilities.Introspection.CapabilityInspector; using static HotChocolate.Utilities.Introspection.IntrospectionQueryHelper; diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs index 16443d7a052..0748992eabd 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs @@ -1,5 +1,7 @@ using HotChocolate.Language; +using HotChocolate.Types; using HotChocolate.Utilities.Introspection.Properties; +using DirectiveLocation = HotChocolate.Language.DirectiveLocation; namespace HotChocolate.Utilities.Introspection; @@ -333,10 +335,10 @@ private static IReadOnlyList CreateDeprecatedDirective( { new DirectiveNode ( - WellKnownDirectives.Deprecated, + DirectiveNames.Deprecated.Name, new ArgumentNode ( - WellKnownDirectives.DeprecationReasonArgument, + DirectiveNames.Deprecated.Arguments.Reason, new StringValueNode(deprecationReason) ) ) From 3fd40c3ba40fe33fba16a012b74da788cf58187b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 27 Nov 2025 15:30:50 +0100 Subject: [PATCH 07/30] wip --- .../Types/Execution/Processing/Fragment.cs | 10 +- .../Types/Execution/Processing/ISelection.cs | 5 + .../Types/Execution/Processing/Selection.cs | 17 +- .../Properties/TextJsonResources.Designer.cs | 16 +- .../Types/Properties/TextJsonResources.resx | 12 +- .../Types/Text/Json/ResultDocument.DbRow.cs | 63 +-- .../Types/Text/Json/ResultDocument.MetaDb.cs | 53 +- .../Json/ResultDocument.TryGetProperty.cs | 14 +- .../Types/Text/Json/ResultDocument.WriteTo.cs | 2 +- .../src/Types/Text/Json/ResultDocument.cs | 360 +++++++++--- .../Json/ResultElement.ObjectEnumerator.cs | 14 +- .../Core/src/Types/Text/Json/ResultElement.cs | 526 +++++++++++++++++- ...iteResultProperty.cs => ResultProperty.cs} | 18 +- .../Text/Json/CompositeResultElement.cs | 302 +++++++++- .../Text/Json/CompositeResultProperty.cs | 4 +- 15 files changed, 1193 insertions(+), 223 deletions(-) rename src/HotChocolate/Core/src/Types/Text/Json/{CompositeResultProperty.cs => ResultProperty.cs} (89%) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs index 713b9cb28df..3e3d2a1c42c 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs @@ -5,8 +5,8 @@ namespace HotChocolate.Execution.Processing; internal sealed class Fragment : IFragment { - private readonly long _includeCondition; - private readonly long _deferIfCondition; + private readonly ulong _includeCondition; + private readonly ulong _deferIfCondition; public Fragment( int id, @@ -14,8 +14,8 @@ public Fragment( ISyntaxNode syntaxNode, IReadOnlyList directives, ISelectionSet selectionSet, - long includeCondition, - long deferIfCondition, + ulong includeCondition, + ulong deferIfCondition, bool isInternal = false) { Id = id; @@ -45,7 +45,7 @@ public Fragment( public string? GetLabel(IVariableValueCollection variables) => Directives.GetDeferDirective(variables)?.Label; - public bool IsIncluded(long includeFlags, bool allowInternals = false) + public bool IsIncluded(ulong includeFlags, bool allowInternals = false) => (includeFlags & _includeCondition) == _includeCondition && (_deferIfCondition is 0 || (includeFlags & _deferIfCondition) != _deferIfCondition) && (!IsInternal || allowInternals); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs index d4880429f14..f6979a4a84f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs @@ -19,6 +19,11 @@ public interface ISelection : IOptionalSelection /// string ResponseName { get; } + /// + /// Gets the UTF-8 encoded name this field will have in the response map. + /// + byte[] Utf8ResponseName { get; } + /// /// Gets the field that was selected. /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index 309faace72b..1ed864d2455 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Text; using HotChocolate.Execution.Properties; using HotChocolate.Language; using HotChocolate.Resolvers; @@ -12,7 +13,7 @@ namespace HotChocolate.Execution.Processing; public class Selection : ISelection { private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; - private long[] _includeConditions; + private ulong[] _includeConditions; private long _streamIfCondition; private Flags _flags; private FieldNode _syntaxNode; @@ -26,7 +27,7 @@ public Selection( FieldNode syntaxNode, string responseName, ArgumentMap? arguments = null, - long[]? includeConditions = null, + ulong[]? includeConditions = null, bool isInternal = false, bool isParallelExecutable = true, FieldDelegate? resolverPipeline = null, @@ -121,6 +122,10 @@ protected Selection(Selection selection) /// public string ResponseName { get; } + /// + public byte[] Utf8ResponseName => _utf8ResponseName ??= Encoding.UTF8.GetBytes(ResponseName); + private byte[]? _utf8ResponseName; + /// public FieldDelegate? ResolverPipeline { get; private set; } @@ -147,9 +152,9 @@ public bool HasStreamDirective(long includeFlags) public bool IsConditional => _includeConditions.Length > 0 || (_flags & Flags.Internal) == Flags.Internal; - internal ReadOnlySpan IncludeConditions => _includeConditions; + internal ReadOnlySpan IncludeConditions => _includeConditions; - public bool IsIncluded(long includeFlags, bool allowInternals = false) + public bool IsIncluded(ulong includeFlags, bool allowInternals = false) { // in most case we do not have any include condition, // so we can take the easy way out here if we do not have any flags. @@ -190,7 +195,7 @@ public bool IsIncluded(long includeFlags, bool allowInternals = false) public override string ToString() => _syntaxNode.ToString(); - internal void AddSelection(FieldNode selectionSyntax, long includeCondition = 0) + internal void AddSelection(FieldNode selectionSyntax, ulong includeCondition = 0) { if ((_flags & Flags.Sealed) == Flags.Sealed) { @@ -412,7 +417,7 @@ public Sealed( FieldNode syntaxNode, string responseName, ArgumentMap? arguments = null, - long[]? includeConditions = null, + ulong[]? includeConditions = null, bool isInternal = false, bool isParallelExecutable = true, FieldDelegate? resolverPipeline = null, diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs index 47f922f9119..872b64ad013 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs @@ -45,21 +45,21 @@ internal static System.Globalization.CultureInfo Culture { } } - internal static string CompositeResultElement_GetBoolean_JsonElementHasWrongType { + internal static string ResultElement_GetBoolean_JsonElementHasWrongType { get { - return ResourceManager.GetString("CompositeResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); + return ResourceManager.GetString("ResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); } } - internal static string SourceResultElement_GetBoolean_JsonElementHasWrongType { + internal static string Rethrowable { get { - return ResourceManager.GetString("SourceResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); + return ResourceManager.GetString("Rethrowable", resourceCulture); } } - internal static string Rethrowable { + internal static string ResultElement_SetObjectValue_NotObjectType { get { - return ResourceManager.GetString("Rethrowable", resourceCulture); + return ResourceManager.GetString("ResultElement_SetObjectValue_NotObjectType", resourceCulture); } } @@ -81,9 +81,9 @@ internal static string ThrowHelper_ReadIncompleteUTF16 { } } - internal static string FixedSizeArrayPool_Return_InvalidArraySize { + internal static string ResultElement_SetArrayValue_NotListType { get { - return ResourceManager.GetString("FixedSizeArrayPool_Return_InvalidArraySize", resourceCulture); + return ResourceManager.GetString("ResultElement_SetArrayValue_NotListType", resourceCulture); } } } diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx index 9375281edf6..e98294406cd 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx @@ -18,15 +18,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The requested operation requires an element of type '{0}', but the target element has type '{1}'. - - + The requested operation requires an element of type '{0}', but the target element has type '{1}'. HotChocolate.Text.Json.Rethrowable + + The element is not a composite type. + Cannot transcode invalid UTF-8 JSON text to UTF-16 string. @@ -36,7 +36,7 @@ Cannot read incomplete UTF-16 JSON text as string with missing low surrogate. - - Buffer size {0} does not match expected chunk size {1} + + The location type `{0}` is not a list. diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs index 701c7734e40..5e613d3f224 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs @@ -15,23 +15,22 @@ internal readonly struct DbRow // 27 bits for location + 2 bits OpRefType + 3 reserved bits private readonly int _locationAndOpRefType; - // Sign bit for HasComplexChildren + 31 bits for size/length + // 27 bits for size/length + sign bit for HasComplexChildren + 4 reserved bits private readonly int _sizeOrLengthUnion; - // 4 bits TokenType + 27 bits NumberOfRows + 1 reserved bit - private readonly int _numberOfRowsTypeAndReserved; + // 27 bits NumberOfRows + 1 reserved bit + 4 bits TokenType + private readonly int _tokenTypeAndNumberOfRows; - // 15 bits SourceDocumentId + 17 bits (high 17 bits of ParentRow) - private readonly int _sourceAndParentHigh; + // 27 bits ParentRow + 5 reserved bits + private readonly int _parentRow; - // 15 bits OperationReferenceId + 6 bits Flags + 11 bits (low bits of ParentRow) - private readonly int _selectionSetFlagsAndParentLow; + // 15 bits OperationReferenceId + 8 bits Flags + 9 reserved bits + private readonly int _opRefIdAndFlags; public DbRow( ElementTokenType tokenType, int location, int sizeOrLength = 0, - int sourceDocumentId = 0, int parentRow = 0, int operationReferenceId = 0, OperationReferenceType operationReferenceType = OperationReferenceType.None, @@ -40,20 +39,19 @@ public DbRow( { Debug.Assert((byte)tokenType < 16); Debug.Assert(location is >= 0 and <= 0x07FFFFFF); // 27 bits - Debug.Assert(sizeOrLength >= UnknownSize); - Debug.Assert(sourceDocumentId is >= 0 and <= 0x7FFF); // 15 bits - Debug.Assert(parentRow is >= 0 and <= 0x0FFFFFFF); // 28 bits + Debug.Assert(sizeOrLength == UnknownSize || sizeOrLength is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert(parentRow is >= 0 and <= 0x07FFFFFF); // 27 bits Debug.Assert(operationReferenceId is >= 0 and <= 0x7FFF); // 15 bits Debug.Assert(numberOfRows is >= 0 and <= 0x07FFFFFF); // 27 bits - Debug.Assert((byte)flags <= 63); // 6 bits (0x3F) + Debug.Assert((byte)flags <= 255); // 8 bits (0xFF) Debug.Assert((byte)operationReferenceType <= 3); // 2 bits Debug.Assert(Unsafe.SizeOf() == Size); _locationAndOpRefType = location | ((int)operationReferenceType << 27); _sizeOrLengthUnion = sizeOrLength; - _numberOfRowsTypeAndReserved = ((int)tokenType << 28) | (numberOfRows & 0x07FFFFFF); - _sourceAndParentHigh = sourceDocumentId | ((parentRow >> 11) << 15); - _selectionSetFlagsAndParentLow = operationReferenceId | ((int)flags << 15) | ((parentRow & 0x7FF) << 21); + _tokenTypeAndNumberOfRows = ((int)tokenType << 28) | (numberOfRows & 0x07FFFFFF); + _parentRow = parentRow; + _opRefIdAndFlags = operationReferenceId | ((int)flags << 15); } /// @@ -62,7 +60,7 @@ public DbRow( /// /// 4 bits = possible values /// - public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28); + public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_tokenTypeAndNumberOfRows) >> 28); /// /// Operation reference type indicating the type of GraphQL operation element. @@ -77,7 +75,7 @@ public OperationReferenceType OperationReferenceType /// Byte offset in source data OR metaDb row index for references. /// /// - /// 2 bits = 4 possible values + /// 27 bits = 134M limit /// public int Location => _locationAndOpRefType & 0x07FFFFFF; @@ -87,7 +85,7 @@ public OperationReferenceType OperationReferenceType /// /// 27 bits = 134M limit /// - public int SizeOrLength => _sizeOrLengthUnion & int.MaxValue; + public int SizeOrLength => _sizeOrLengthUnion & 0x07FFFFFF; /// /// String/PropertyName: Unescaping required. @@ -105,30 +103,32 @@ public OperationReferenceType OperationReferenceType /// /// 27 bits = 134M rows /// - public int NumberOfRows => _numberOfRowsTypeAndReserved & 0x07FFFFFF; + public int NumberOfRows => _tokenTypeAndNumberOfRows & 0x07FFFFFF; /// /// Index of parent element in metadb for navigation and null propagation. /// /// - /// 28 bits = 268M rows + /// 27 bits = 134M rows /// - public int ParentRow - => ((int)((uint)_sourceAndParentHigh >> 15) << 11) | ((_selectionSetFlagsAndParentLow >> 21) & 0x7FF); + public int ParentRow => _parentRow & 0x07FFFFFF; /// /// Reference to GraphQL selection set or selection metadata. /// 15 bits = 32K selections /// - public int OperationReferenceId => _selectionSetFlagsAndParentLow & 0x7FFF; + /// + /// 15 bits = 32K selections + /// + public int OperationReferenceId => _opRefIdAndFlags & 0x7FFF; /// /// Element metadata flags. /// /// - /// 6 bits = 64 combinations + /// 8 bits = 256 combinations /// - public ElementFlags Flags => (ElementFlags)((_selectionSetFlagsAndParentLow >> 15) & 0x3F); + public ElementFlags Flags => (ElementFlags)((_opRefIdAndFlags >> 15) & 0xFF); /// /// True for primitive JSON values (strings, numbers, booleans, null). @@ -147,11 +147,12 @@ internal enum OperationReferenceType : byte internal enum ElementFlags : byte { None = 0, - Invalidated = 1, - SourceResult = 2, - IsNullable = 4, - IsRoot = 8, - IsInternal = 16, - IsExcluded = 32 + IsRoot = 1, + IsObject = 2, + IsList = 4, + IsInternal = 8, + IsExcluded = 16, + IsNullable = 32, + IsInvalidated = 64 } } diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs index 46af3d2c6b3..af81d3cc647 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs @@ -49,7 +49,6 @@ internal Cursor Append( ElementTokenType tokenType, int location = 0, int sizeOrLength = 0, - int sourceDocumentId = 0, int parentRow = 0, int operationReferenceId = 0, OperationReferenceType operationReferenceType = OperationReferenceType.None, @@ -110,7 +109,6 @@ internal Cursor Append( tokenType, location, sizeOrLength, - sourceDocumentId, parentRow, operationReferenceId, operationReferenceType, @@ -131,7 +129,6 @@ internal void Replace( ElementTokenType tokenType, int location = 0, int sizeOrLength = 0, - int sourceDocumentId = 0, int parentRow = 0, int operationReferenceId = 0, OperationReferenceType operationReferenceType = OperationReferenceType.None, @@ -144,7 +141,6 @@ internal void Replace( tokenType, location, sizeOrLength, - sourceDocumentId, parentRow, operationReferenceId, operationReferenceType, @@ -215,13 +211,8 @@ internal int GetParent(Cursor cursor) { AssertValidCursor(cursor); - var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); - - var sourceAndParentHigh = MemoryMarshal.Read(span[12..]); - var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]); - - return (sourceAndParentHigh >>> 15 << 11) - | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF); + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 12); + return MemoryMarshal.Read(span) & 0x07FFFFFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -229,14 +220,8 @@ internal Cursor GetParentCursor(Cursor cursor) { AssertValidCursor(cursor); - var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset); - - var sourceAndParentHigh = MemoryMarshal.Read(span[12..]); - var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]); - - var index = (sourceAndParentHigh >>> 15 << 11) - | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF); - + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 12); + var index = MemoryMarshal.Read(span) & 0x07FFFFFF; return Cursor.FromIndex(index); } @@ -248,7 +233,7 @@ internal int GetNumberOfRows(Cursor cursor) var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); var value = MemoryMarshal.Read(span); - return value & 0x0FFFFFFF; + return value & 0x07FFFFFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -258,21 +243,21 @@ internal ElementFlags GetFlags(Cursor cursor) var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); - var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span); - return (ElementFlags)((selectionSetFlagsAndParentLow >> 15) & 0x3F); + var value = MemoryMarshal.Read(span); + return (ElementFlags)((value >> 15) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void SetFlags(Cursor cursor, ElementFlags flags) { AssertValidCursor(cursor); - Debug.Assert((byte)flags <= 63, "Flags value exceeds 6-bit limit"); + Debug.Assert((byte)flags <= 255, "Flags value exceeds 8-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); var currentValue = MemoryMarshal.Read(fieldSpan); - var clearedValue = currentValue & 0xFFE07FFF; // ~(0x3F << 15) - var newValue = (int)(clearedValue | (uint)((int)flags << 15)); + var clearedValue = currentValue & unchecked((int)0xFF807FFF); // ~(0xFF << 15) + var newValue = clearedValue | ((int)flags << 15); MemoryMarshal.Write(fieldSpan, newValue); } @@ -285,21 +270,21 @@ internal int GetSizeOrLength(Cursor cursor) var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); var value = MemoryMarshal.Read(span); - return value & int.MaxValue; + return value & 0x07FFFFFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) { AssertValidCursor(cursor); - Debug.Assert(sizeOrLength >= 0, "SizeOrLength value exceeds 31-bit limit"); + Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= 0x07FFFFFF, "SizeOrLength value exceeds 27-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); var currentValue = MemoryMarshal.Read(fieldSpan); - // Keep only the sign bit (HasComplexChildren) - var clearedValue = currentValue & unchecked((int)0x80000000); - var newValue = clearedValue | (sizeOrLength & int.MaxValue); + // Keep only the sign bit (HasComplexChildren) + 4 reserved bits + var clearedValue = currentValue & unchecked((int)0xF8000000); + var newValue = clearedValue | (sizeOrLength & 0x07FFFFFF); MemoryMarshal.Write(fieldSpan, newValue); } @@ -308,14 +293,14 @@ internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) internal void SetNumberOfRows(Cursor cursor, int numberOfRows) { AssertValidCursor(cursor); - Debug.Assert(numberOfRows >= 0 && numberOfRows <= 0x0FFFFFFF, "NumberOfRows value exceeds 28-bit limit"); + Debug.Assert(numberOfRows >= 0 && numberOfRows <= 0x07FFFFFF, "NumberOfRows value exceeds 27-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); var currentValue = MemoryMarshal.Read(fieldSpan); - // Keep only the top 4 bits (token type) - var clearedValue = currentValue & unchecked((int)0xF0000000); - var newValue = clearedValue | (numberOfRows & 0x0FFFFFFF); + // Keep only the top 5 bits (4 bits token type + 1 reserved) + var clearedValue = currentValue & unchecked((int)0xF8000000); + var newValue = clearedValue | (numberOfRows & 0x07FFFFFF); MemoryMarshal.Write(fieldSpan, newValue); } diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs index 9fc7679a472..01b41ac815e 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs @@ -8,7 +8,7 @@ public sealed partial class ResultDocument internal bool TryGetNamedPropertyValue( Cursor startCursor, string propertyName, - out CompositeResultElement value) + out ResultElement value) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -35,7 +35,7 @@ internal bool TryGetNamedPropertyValue( var propertyCursor = startCursor + propertyRowIndex; Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); - value = new CompositeResultElement(this, propertyCursor + 1); + value = new ResultElement(this, propertyCursor + 1); return true; } } @@ -116,7 +116,7 @@ internal bool TryGetNamedPropertyValue( internal bool TryGetNamedPropertyValue( Cursor startCursor, ReadOnlySpan propertyName, - out CompositeResultElement value) + out ResultElement value) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -143,7 +143,7 @@ internal bool TryGetNamedPropertyValue( var propertyCursor = startCursor + propertyRowIndex; Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); - value = new CompositeResultElement(this, propertyCursor + 1); + value = new ResultElement(this, propertyCursor + 1); return true; } } @@ -161,7 +161,7 @@ private bool TryGetNamedPropertyValue( Cursor startCursor, Cursor endCursor, ReadOnlySpan propertyName, - out CompositeResultElement value) + out ResultElement value) { Span utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold]; var cursor = endCursor; @@ -206,7 +206,7 @@ private bool TryGetNamedPropertyValue( if (utf8Unescaped[..written].SequenceEqual(propertyName[idx..])) { // If the property name is a match, the answer is the next element. - value = new CompositeResultElement(this, cursor + 1); + value = new ResultElement(this, cursor + 1); return true; } } @@ -224,7 +224,7 @@ private bool TryGetNamedPropertyValue( else if (currentPropertyName.SequenceEqual(propertyName)) { // If the property name is a match, the answer is the next element. - value = new CompositeResultElement(this, cursor + 1); + value = new ResultElement(this, cursor + 1); return true; } diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs index c5c86bd324a..869b8c26d26 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs @@ -77,7 +77,7 @@ public void Write() } if (row.TokenType is ElementTokenType.Null - || (ElementFlags.Invalidated & row.Flags) == ElementFlags.Invalidated) + || (ElementFlags.IsInvalidated & row.Flags) == ElementFlags.IsInvalidated) { writer.Write(JsonConstants.NullValue); } diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs index c34fedfd4ca..780e99ee305 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; +using HotChocolate.Buffers; using HotChocolate.Execution.Processing; using HotChocolate.Types; @@ -12,13 +13,17 @@ public sealed partial class ResultDocument : IDisposable private static readonly Encoding s_utf8Encoding = Encoding.UTF8; private readonly Operation _operation; private readonly ulong _includeFlags; - private List? _errors; internal MetaDb _metaDb; + private int _nextDataIndex; + private int _rentedDataSize; + private readonly List _data = []; + private readonly object _dataChunkLock = new(); + private List? _errors; private bool _disposed; internal ResultDocument(Operation operation, ulong includeFlags) { - _metaDb = MetaDb.CreateForEstimatedRows(Cursor.RowsPerChunk * 8); + _metaDb = MetaDb.CreateForEstimatedRows(Cursor.RowsPerChunk); _operation = operation; _includeFlags = includeFlags; @@ -228,7 +233,7 @@ internal bool IsInvalidated(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; } if (tokenType is ElementTokenType.Reference) @@ -239,7 +244,7 @@ internal bool IsInvalidated(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; } } @@ -260,7 +265,7 @@ internal bool IsNullOrInvalidated(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; } if (tokenType is ElementTokenType.Reference) @@ -271,7 +276,7 @@ internal bool IsNullOrInvalidated(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - return (flags & ElementFlags.Invalidated) == ElementFlags.Invalidated; + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; } } @@ -307,7 +312,7 @@ internal void Invalidate(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - _metaDb.SetFlags(current, flags | ElementFlags.Invalidated); + _metaDb.SetFlags(current, flags | ElementFlags.IsInvalidated); return; } @@ -319,7 +324,7 @@ internal void Invalidate(Cursor current) if (tokenType is ElementTokenType.StartObject) { var flags = _metaDb.GetFlags(current); - _metaDb.SetFlags(current, flags | ElementFlags.Invalidated); + _metaDb.SetFlags(current, flags | ElementFlags.IsInvalidated); } return; @@ -329,58 +334,117 @@ internal void Invalidate(Cursor current) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteRawValueTo(IBufferWriter writer, DbRow row, int indentLevel, bool indented) + private void WriteRawValueTo(IBufferWriter writer, DbRow row) { - if ((row.Flags & ElementFlags.SourceResult) == ElementFlags.SourceResult) + switch (row.TokenType) { - var document = _sources[row.SourceDocumentId]; + case ElementTokenType.Null: + WriteToBuffer(writer, JsonConstants.NullValue); + return; - if (row.TokenType is ElementTokenType.StartObject or ElementTokenType.StartArray) - { - // Reconstruct the source cursor from stored Location (Chunk) and SizeOrLength (Row) - var sourceCursor = SourceResultDocument.Cursor.From(row.Location, row.SizeOrLength); - var formatter = new SourceResultDocument.RawJsonFormatter(document, writer, indentLevel, indented); - formatter.WriteValue(sourceCursor); + case ElementTokenType.True: + WriteToBuffer(writer, JsonConstants.TrueValue); return; - } - // For simple values, write directly using location and size - document.WriteRawValueTo(writer, row.Location, row.SizeOrLength); - return; + case ElementTokenType.False: + WriteToBuffer(writer, JsonConstants.FalseValue); + return; + + case ElementTokenType.PropertyName: + WriteToBuffer(writer, _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName); + return; + + case ElementTokenType.String: + case ElementTokenType.Number: + WriteLocalDataTo(writer, row.Location, row.SizeOrLength); + return; + + default: + throw new NotSupportedException(); } + } - throw new NotSupportedException(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteToBuffer(IBufferWriter writer, ReadOnlySpan data) + { + var span = writer.GetSpan(data.Length); + data.CopyTo(span); + writer.Advance(data.Length); } - private ReadOnlySpan ReadRawValue(DbRow row) + /// + /// Writes local data to the buffer, handling chunk boundaries. + /// + private void WriteLocalDataTo(IBufferWriter writer, int location, int size) { - if (row.TokenType == ElementTokenType.Null) - { - return JsonConstants.NullValue; - } + var remaining = size; + var currentPos = location; - if (row.TokenType == ElementTokenType.True) + while (remaining > 0) { - return JsonConstants.TrueValue; - } + var chunkIndex = currentPos / JsonMemory.BufferSize; + var offset = currentPos % JsonMemory.BufferSize; + var availableInChunk = JsonMemory.BufferSize - offset; + var toWrite = Math.Min(remaining, availableInChunk); - if (row.TokenType == ElementTokenType.False) - { - return JsonConstants.FalseValue; + var source = _data[chunkIndex].AsSpan(offset, toWrite); + var dest = writer.GetSpan(toWrite); + source.CopyTo(dest); + writer.Advance(toWrite); + + currentPos += toWrite; + remaining -= toWrite; } + } - if (row.TokenType == ElementTokenType.PropertyName) + private ReadOnlySpan ReadRawValue(DbRow row) + { + switch (row.TokenType) { - return _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName; + case ElementTokenType.Null: + return JsonConstants.NullValue; + + case ElementTokenType.True: + return JsonConstants.TrueValue; + + case ElementTokenType.False: + return JsonConstants.FalseValue; + + case ElementTokenType.PropertyName: + return _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName; + + case ElementTokenType.String: + case ElementTokenType.Number: + return ReadLocalData(row.Location, row.SizeOrLength); + + default: + throw new NotSupportedException(); } + } + + /// + /// Reads local data from the data chunks. + /// + /// + /// This method only supports data that fits within a single chunk. + /// Data that spans chunk boundaries should use instead. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan ReadLocalData(int location, int size) + { + var chunkIndex = location / JsonMemory.BufferSize; + var offset = location % JsonMemory.BufferSize; - if ((row.Flags & ElementFlags.SourceResult) == ElementFlags.SourceResult) + // Fast path: data fits in a single chunk + if (offset + size <= JsonMemory.BufferSize) { - var document = _sources[row.SourceDocumentId]; - return document.ReadRawValue(row.Location, row.SizeOrLength); + return _data[chunkIndex].AsSpan(offset, size); } - throw new NotSupportedException(); + // Data spans chunk boundaries - this should be rare for typical JSON values + throw new NotSupportedException( + "Reading data that spans chunk boundaries as a span is not supported. " + + "Use WriteLocalDataTo for writing to an IBufferWriter instead."); } internal ResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) @@ -399,6 +463,20 @@ internal ResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) return new ResultElement(this, startObjectCursor); } + internal ResultElement CreateObject(Cursor parent, int propertyCount) + { + var startObjectCursor = WriteStartObject(parent, isSelectionSet: false); + + for (var i = 0; i < propertyCount; i++) + { + WriteEmptyProperty(startObjectCursor); + } + + WriteEndObject(startObjectCursor, propertyCount); + + return new ResultElement(this, startObjectCursor); + } + internal ResultElement CreateArray(Cursor parent, int length) { var cursor = WriteStartArray(parent, length); @@ -414,7 +492,7 @@ internal ResultElement CreateArray(Cursor parent, int length) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AssignCompositeValue(ResultElement target, ResultElement value) + internal void AssignObjectOrArray(ResultElement target, ResultElement value) { _metaDb.Replace( cursor: target.Cursor, @@ -424,45 +502,48 @@ internal void AssignCompositeValue(ResultElement target, ResultElement value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AssignSourceValue(ResultElement target, ResultElement source) + internal void AssignStringValue(ResultElement target, ReadOnlySpan value) { - var value = source.GetValuePointer(); - var parent = source._parent; - - if (parent.Id == -1) - { - Debug.Assert(!_sources.Contains(parent), "The source document is marked as unbound but is already registered."); - parent.Id = _sources.Count; - _sources.Add(parent); - } + var totalSize = value.Length + 2; + var position = ClaimDataSpace(totalSize); + WriteData(position, value, withQuotes: true); - Debug.Assert(_sources.Contains(parent), "Expected the source document of the source element to be registered."); + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.String, + location: position, + sizeOrLength: totalSize, + parentRow: _metaDb.GetParent(target.Cursor)); + } - var tokenType = source.TokenType.ToElementTokenType(); + internal void AssignPropertyName(ResultElement target, ReadOnlySpan propertyName) + { + var totalSize = propertyName.Length + 2; + var position = ClaimDataSpace(totalSize); + WriteData(position, propertyName, withQuotes: true); + } - if (tokenType is ElementTokenType.StartObject or ElementTokenType.StartArray) - { - var sourceCursor = source._cursor; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignNumberValue(ResultElement target, ReadOnlySpan value) + { + var position = ClaimDataSpace(value.Length); + WriteData(position, value); - _metaDb.Replace( - cursor: target.Cursor, - tokenType: source.TokenType.ToElementTokenType(), - location: sourceCursor.Chunk, - sizeOrLength: sourceCursor.Row, - sourceDocumentId: parent.Id, - parentRow: _metaDb.GetParent(target.Cursor), - flags: ElementFlags.SourceResult); - return; - } + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Number, + location: position, + sizeOrLength: value.Length, + parentRow: _metaDb.GetParent(target.Cursor)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignBooleanValue(ResultElement target, bool value) + { _metaDb.Replace( cursor: target.Cursor, - tokenType: source.TokenType.ToElementTokenType(), - location: value.Location, - sizeOrLength: value.Size, - sourceDocumentId: parent.Id, - parentRow: _metaDb.GetParent(target.Cursor), - flags: ElementFlags.SourceResult); + tokenType: value ? ElementTokenType.True : ElementTokenType.False, + parentRow: _metaDb.GetParent(target.Cursor)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -475,7 +556,110 @@ internal void AssignNullValue(ResultElement target) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Cursor WriteStartObject(Cursor parent, int selectionSetId = 0) + private int ClaimDataSpace(int size) + { + // Atomically claim space + var endPosition = Interlocked.Add(ref _nextDataIndex, size); + var startPosition = endPosition - size; + + // Fast path: we check if we already have enough rented memory + // if so we can directly return and write the data without locking. + if (endPosition <= Volatile.Read(ref _rentedDataSize)) + { + return startPosition; + } + + // Slow path: we need to rent more chunks so in this case + // we will need to do a proper synchronization. + EnsureDataCapacity(endPosition); + return startPosition; + } + + private void EnsureDataCapacity(int requiredSize) + { + lock (_dataChunkLock) + { + // Double-check after acquiring lock + var currentSize = _rentedDataSize; + if (requiredSize <= currentSize) + { + return; + } + + // Rent chunks until we have enough + while (currentSize < requiredSize) + { + _data.Add(JsonMemory.Rent(JsonMemoryKind.Json)); + currentSize += JsonMemory.BufferSize; + } + + // Publish new size (volatile write) + Volatile.Write(ref _rentedDataSize, currentSize); + } + } + + /// + /// Writes data to the data chunks, handling chunk boundaries. + /// + /// The position to start writing at. + /// The data to write. + /// If true, wraps the data with JSON quotes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteData(int position, ReadOnlySpan data, bool withQuotes = false) + { + if (withQuotes) + { + WriteByte(position, JsonConstants.Quote); + WriteDataCore(position + 1, data); + WriteByte(position + 1 + data.Length, JsonConstants.Quote); + } + else + { + WriteDataCore(position, data); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteByte(int position, byte value) + { + var chunkIndex = position / JsonMemory.BufferSize; + var offset = position % JsonMemory.BufferSize; + _data[chunkIndex][offset] = value; + } + + private void WriteDataCore(int position, ReadOnlySpan data) + { + var chunkIndex = position / JsonMemory.BufferSize; + var offset = position % JsonMemory.BufferSize; + var availableInChunk = JsonMemory.BufferSize - offset; + + // Fast path: we can write all the data into single chunk + if (data.Length <= availableInChunk) + { + data.CopyTo(_data[chunkIndex].AsSpan(offset, data.Length)); + return; + } + + // Slow path: data spans multiple chunks so we need to loop + var remaining = data; + var currentPos = position; + + while (remaining.Length > 0) + { + chunkIndex = currentPos / JsonMemory.BufferSize; + offset = currentPos % JsonMemory.BufferSize; + availableInChunk = JsonMemory.BufferSize - offset; + var toWrite = Math.Min(remaining.Length, availableInChunk); + + remaining[..toWrite].CopyTo(_data[chunkIndex].AsSpan(offset, toWrite)); + + currentPos += toWrite; + remaining = remaining[toWrite..]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor WriteStartObject(Cursor parent, int selectionSetId = 0, bool isSelectionSet = true) { var flags = ElementFlags.None; var parentRow = ToIndex(parent); @@ -490,7 +674,9 @@ private Cursor WriteStartObject(Cursor parent, int selectionSetId = 0) ElementTokenType.StartObject, parentRow: parentRow, operationReferenceId: selectionSetId, - operationReferenceType: OperationReferenceType.SelectionSet, + operationReferenceType: isSelectionSet + ? OperationReferenceType.SelectionSet + : OperationReferenceType.None, flags: flags); } @@ -546,6 +732,16 @@ private void WriteEmptyProperty(Cursor parent, ISelection selection) flags |= ElementFlags.IsNullable; } + if (selection.Type.IsListType()) + { + flags |= ElementFlags.IsList; + } + + if (selection.Type.IsCompositeType()) + { + flags |= ElementFlags.IsObject; + } + var prop = _metaDb.Append( ElementTokenType.PropertyName, parentRow: ToIndex(parent), @@ -558,6 +754,18 @@ private void WriteEmptyProperty(Cursor parent, ISelection selection) parentRow: ToIndex(prop)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyProperty(Cursor parent) + { + var prop = _metaDb.Append( + ElementTokenType.PropertyName, + parentRow: ToIndex(parent)); + + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(prop)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteEmptyValue(Cursor parent) { @@ -582,6 +790,12 @@ public void Dispose() if (!_disposed) { _metaDb.Dispose(); + + if (_data.Count > 0) + { + JsonMemory.Return(JsonMemoryKind.Json, _data); + } + _disposed = true; } } diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs index 58d8d5d163b..90725204ee3 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Diagnostics; -using static HotChocolate.Fusion.Text.Json.CompositeResultDocument; +using static HotChocolate.Text.Json.ResultDocument; namespace HotChocolate.Text.Json; @@ -10,14 +10,14 @@ public readonly partial struct ResultElement /// An enumerable and enumerator for the properties of a JSON object. /// [DebuggerDisplay("{Current,nq}")] - public struct ObjectEnumerator : IEnumerable, IEnumerator + public struct ObjectEnumerator : IEnumerable, IEnumerator { - private readonly CompositeResultDocument _document; + private readonly ResultDocument _document; private readonly Cursor _start; private readonly Cursor _end; private Cursor _cursor; - internal ObjectEnumerator(CompositeResultElement target) + internal ObjectEnumerator(ResultElement target) { _document = target._parent; (_start, var tokenType) = _document._metaDb.GetStartCursor(target._cursor); @@ -27,7 +27,7 @@ internal ObjectEnumerator(CompositeResultElement target) } /// - public CompositeResultProperty Current + public ResultProperty Current { get { @@ -36,7 +36,7 @@ public CompositeResultProperty Current return default; } - return new CompositeResultProperty(new CompositeResultElement(_document, _cursor + 1)); + return new ResultProperty(new ResultElement(_document, _cursor + 1)); } } @@ -65,7 +65,7 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs index 94b91ddb9bf..5b363b8bb8a 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs @@ -1,5 +1,7 @@ using System.Buffers; +using System.Buffers.Text; using System.Diagnostics; +using System.Text; using System.Text.Json; using HotChocolate.Execution; using HotChocolate.Execution.Processing; @@ -24,10 +26,16 @@ internal ResultElement(ResultDocument parent, ResultDocument.Cursor cursor) _cursor = cursor; } + /// + /// Writes this element as JSON to the specified buffer writer. + /// + /// The buffer writer to write to. + /// + /// true to write indented JSON; otherwise, false. + /// public void WriteTo(IBufferWriter writer, bool indented = false) { var formatter = new ResultDocument.RawJsonFormatter(_parent, writer, indented); - var row = _parent._metaDb.Get(_cursor); formatter.WriteValue(_cursor, row); } @@ -41,25 +49,19 @@ public void WriteTo(IBufferWriter writer, bool indented = false) private ElementTokenType TokenType => _parent?.GetElementTokenType(_cursor) ?? ElementTokenType.None; /// - /// The that the value is. + /// Gets the of this element. /// - /// - /// The parent has been disposed. - /// public JsonValueKind ValueKind => TokenType.ToValueKind(); /// - /// Get the value at a specified index when the current value is a - /// . + /// Gets the element at the specified index when the current element is an array. /// + /// The zero-based index of the element to get. /// - /// This value's is not . + /// This element's is not . /// /// - /// is not in the range [0, ()). - /// - /// - /// The parent has been disposed. + /// is not in the range [0, ()). /// public ResultElement this[int index] { @@ -71,6 +73,9 @@ public ResultElement this[int index] } } + /// + /// Gets the operation this element belongs to. + /// public IOperation Operation { get @@ -81,6 +86,10 @@ public IOperation Operation } } + /// + /// Gets the if this element represents the data of a selection set; + /// otherwise, null. + /// public ISelectionSet? SelectionSet { get @@ -91,6 +100,10 @@ public ISelectionSet? SelectionSet } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public ISelection? Selection { get @@ -107,6 +120,10 @@ public ISelection? Selection } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public IType? Type { get @@ -134,6 +151,9 @@ public IType? Type } } + /// + /// Gets a value indicating whether this element has been invalidated during null propagation. + /// public bool IsInvalidated { get @@ -144,6 +164,9 @@ public bool IsInvalidated } } + /// + /// Gets a value indicating whether this element is either null or was invalidated during null propagation. + /// public bool IsNullOrInvalidated { get @@ -157,6 +180,9 @@ public bool IsNullOrInvalidated } } + /// + /// Gets the path to this element within the result document. + /// public Path Path { get @@ -167,6 +193,9 @@ public Path Path } } + /// + /// Gets the parent element that contains this element. + /// public ResultElement Parent { get @@ -177,6 +206,9 @@ public ResultElement Parent } } + /// + /// Gets a value indicating whether this element is nullable according to the GraphQL type system. + /// public bool IsNullable { get @@ -192,6 +224,10 @@ public bool IsNullable } } + /// + /// Gets a value indicating whether this element represents internal data + /// that is required for processing and must not be written to the GraphQL response. + /// public bool IsInternal { get @@ -202,6 +238,15 @@ public bool IsInternal } } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a selection set. + /// public ISelectionSet AssertSelectionSet() { var selectionSet = SelectionSet; @@ -214,6 +259,15 @@ public ISelectionSet AssertSelectionSet() return selectionSet; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public ISelection AssertSelection() { var selection = Selection; @@ -226,6 +280,15 @@ public ISelection AssertSelection() return selection; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public IType AssertType() { var type = Type; @@ -238,6 +301,10 @@ public IType AssertType() return type; } + /// + /// Marks this element as invalidated, which occurs during null propagation + /// when a non-nullable field returns null. + /// public void Invalidate() { CheckValidInstance(); @@ -246,8 +313,11 @@ public void Invalidate() } /// - /// Get the number of values contained within the current array value. + /// Gets the number of elements contained within the current array element. /// + /// + /// The number of elements in the array. + /// public int GetArrayLength() { CheckValidInstance(); @@ -256,8 +326,11 @@ public int GetArrayLength() } /// - /// Get the number of properties contained within the current object value. + /// Gets the number of properties contained within the current object element. /// + /// + /// The number of properties in the object. + /// public int GetPropertyCount() { CheckValidInstance(); @@ -265,6 +338,14 @@ public int GetPropertyCount() return _parent.GetPropertyCount(_cursor); } + /// + /// Gets a property by name when the current element is an object. + /// + /// The name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public ResultElement GetProperty(string propertyName) { ArgumentNullException.ThrowIfNull(propertyName); @@ -277,6 +358,14 @@ public ResultElement GetProperty(string propertyName) throw new KeyNotFoundException(); } + /// + /// Gets a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public ResultElement GetProperty(ReadOnlySpan utf8PropertyName) { if (TryGetProperty(utf8PropertyName, out var property)) @@ -287,6 +376,16 @@ public ResultElement GetProperty(ReadOnlySpan utf8PropertyName) throw new KeyNotFoundException(); } + /// + /// Attempts to get a property by name when the current element is an object. + /// + /// The name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(string propertyName, out ResultElement value) { ArgumentNullException.ThrowIfNull(propertyName); @@ -294,6 +393,16 @@ public bool TryGetProperty(string propertyName, out ResultElement value) return _parent.TryGetNamedPropertyValue(_cursor, propertyName, out value); } + /// + /// Attempts to get a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out ResultElement value) { CheckValidInstance(); @@ -301,6 +410,13 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out ResultElemen return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value); } + /// + /// Gets the value as a . + /// + /// The boolean value. + /// + /// This element's is not or . + /// public bool GetBoolean() { var type = TokenType; @@ -324,6 +440,10 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) } } + /// + /// Gets the value as a . + /// + /// The string value, or null if this element is a JSON null. public string? GetString() { CheckValidInstance(); @@ -331,6 +451,13 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) return _parent.GetString(_cursor, ElementTokenType.String); } + /// + /// Gets the value as a non-null . + /// + /// The string value. + /// + /// This element is a JSON null. + /// public string AssertString() { CheckValidInstance(); @@ -338,6 +465,11 @@ public string AssertString() return _parent.GetRequiredString(_cursor, ElementTokenType.String); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSByte(out sbyte value) { CheckValidInstance(); @@ -345,8 +477,18 @@ public bool TryGetSByte(out sbyte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public sbyte GetSByte() => TryGetSByte(out var value) ? value : throw new FormatException(); + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetByte(out byte value) { CheckValidInstance(); @@ -354,6 +496,11 @@ public bool TryGetByte(out byte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public byte GetByte() { if (TryGetByte(out var value)) @@ -364,6 +511,11 @@ public byte GetByte() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt16(out short value) { CheckValidInstance(); @@ -371,6 +523,11 @@ public bool TryGetInt16(out short value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public short GetInt16() { if (TryGetInt16(out var value)) @@ -381,6 +538,11 @@ public short GetInt16() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt16(out ushort value) { CheckValidInstance(); @@ -388,6 +550,11 @@ public bool TryGetUInt16(out ushort value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ushort GetUInt16() { if (TryGetUInt16(out var value)) @@ -398,6 +565,11 @@ public ushort GetUInt16() throw new FormatException(); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt32(out int value) { CheckValidInstance(); @@ -405,6 +577,11 @@ public bool TryGetInt32(out int value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public int GetInt32() { if (!TryGetInt32(out var value)) @@ -415,6 +592,11 @@ public int GetInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt32(out uint value) { CheckValidInstance(); @@ -422,6 +604,11 @@ public bool TryGetUInt32(out uint value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public uint GetUInt32() { if (!TryGetUInt32(out var value)) @@ -432,6 +619,11 @@ public uint GetUInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt64(out long value) { CheckValidInstance(); @@ -439,6 +631,11 @@ public bool TryGetInt64(out long value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public long GetInt64() { if (!TryGetInt64(out var value)) @@ -449,6 +646,11 @@ public long GetInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt64(out ulong value) { CheckValidInstance(); @@ -456,6 +658,11 @@ public bool TryGetUInt64(out ulong value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ulong GetUInt64() { if (!TryGetUInt64(out var value)) @@ -466,6 +673,11 @@ public ulong GetUInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDouble(out double value) { CheckValidInstance(); @@ -473,6 +685,11 @@ public bool TryGetDouble(out double value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public double GetDouble() { if (!TryGetDouble(out var value)) @@ -483,6 +700,11 @@ public double GetDouble() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSingle(out float value) { CheckValidInstance(); @@ -490,6 +712,11 @@ public bool TryGetSingle(out float value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public float GetSingle() { if (!TryGetSingle(out var value)) @@ -500,6 +727,11 @@ public float GetSingle() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDecimal(out decimal value) { CheckValidInstance(); @@ -507,6 +739,11 @@ public bool TryGetDecimal(out decimal value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public decimal GetDecimal() { if (!TryGetDecimal(out var value)) @@ -531,6 +768,10 @@ internal ReadOnlySpan GetPropertyNameRaw() return _parent.GetPropertyNameRaw(_cursor); } + /// + /// Gets the raw JSON text representing this element. + /// + /// The raw JSON text. public string GetRawText() { CheckValidInstance(); @@ -545,6 +786,13 @@ internal ReadOnlySpan GetRawValue(bool includeQuotes = true) return _parent.GetRawValue(_cursor, includeQuotes: true); } + /// + /// Compares the text of this element to the specified string. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(string? text) { if (TokenType == ElementTokenType.Null) @@ -555,6 +803,13 @@ public bool ValueEquals(string? text) return TextEqualsHelper(text.AsSpan(), isPropertyName: false); } + /// + /// Compares the text of this element to the specified UTF-8 encoded text. + /// + /// The UTF-8 encoded text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan utf8Text) { if (TokenType == ElementTokenType.Null) @@ -567,6 +822,13 @@ public bool ValueEquals(ReadOnlySpan utf8Text) return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); } + /// + /// Compares the text of this element to the specified character span. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan text) { if (TokenType == ElementTokenType.Null) @@ -600,6 +862,13 @@ internal string GetPropertyRawText() return _parent.GetPropertyRawValueAsString(_cursor); } + /// + /// Gets an enumerator to enumerate the elements of this array. + /// + /// An enumerator for the array elements. + /// + /// This element's is not . + /// public ArrayEnumerator EnumerateArray() { CheckValidInstance(); @@ -620,6 +889,13 @@ public ArrayEnumerator EnumerateArray() return new ArrayEnumerator(this); } + /// + /// Gets an enumerator to enumerate the properties of this object. + /// + /// An enumerator for the object properties. + /// + /// This element's is not . + /// public ObjectEnumerator EnumerateObject() { CheckValidInstance(); @@ -643,34 +919,242 @@ internal void SetObjectValue(SelectionSet selectionSet) ArgumentNullException.ThrowIfNull(selectionSet); + if (Type is { } type && !type.IsObjectType()) + { + throw new InvalidOperationException( + string.Format(ResultElement_SetObjectValue_NotObjectType, type)); + } + var obj = _parent.CreateObject(_cursor, selectionSet: selectionSet); - _parent.AssignCompositeValue(this, obj); + _parent.AssignObjectOrArray(this, obj); + } + + public void SetObjectValue(int propertyCount) + { + CheckValidInstance(); + + ArgumentOutOfRangeException.ThrowIfLessThan(propertyCount, 0); + + var obj = _parent.CreateObject(_cursor, propertyCount); + _parent.AssignObjectOrArray(this, obj); + } + + public void SetPropertyName(ReadOnlySpan propertyName) + { + CheckValidInstance(); + + if (propertyName.Length == 0) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + var requiredBytes = Encoding.UTF8.GetByteCount(propertyName); + byte[]? rented = null; + var buffer = JsonConstants.StackallocByteThreshold <= requiredBytes + ? stackalloc byte[propertyName.Length] + : (rented = ArrayPool.Shared.Rent(requiredBytes)); + + try + { + var usedBytes = Encoding.UTF8.GetBytes(propertyName, buffer); + _parent.AssignPropertyName(this, buffer[..usedBytes]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + } } - internal void SetArrayValue(int length) + public void SetPropertyName(ReadOnlySpan propertyName) + { + CheckValidInstance(); + + if (propertyName.Length == 0) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + _parent.AssignPropertyName(this, propertyName); + } + + public void SetArrayValue(int length) { CheckValidInstance(); ArgumentOutOfRangeException.ThrowIfNegative(length); + if (Type is { } type && !type.IsListType()) + { + throw new InvalidOperationException( + string.Format(ResultElement_SetArrayValue_NotListType, type)); + } + var arr = _parent.CreateArray(_cursor, length); - _parent.AssignCompositeValue(this, arr); + _parent.AssignObjectOrArray(this, arr); } - internal void SetLeafValue(SourceResultElement source) + public void SetNullValue() { CheckValidInstance(); - _parent.AssignSourceValue(this, source); + _parent.AssignNullValue(this); } - internal void SetNullValue() + public void SetBooleanValue(bool value) { CheckValidInstance(); - _parent.AssignNullValue(this); + _parent.AssignBooleanValue(this, value); + } + + public void SetStringValue(ReadOnlySpan value) + { + CheckValidInstance(); + + _parent.AssignStringValue(this, value); + } + + public void SetStringValue(ReadOnlySpan value) + { + CheckValidInstance(); + + // If we have an empty string, we can directly assign it. + if (value.Length == 0) + { + _parent.AssignStringValue(this, []); + return; + } + + var requiredBytes = Encoding.UTF8.GetByteCount(value); + byte[]? rented = null; + var buffer = JsonConstants.StackallocByteThreshold <= requiredBytes + ? stackalloc byte[value.Length] + : (rented = ArrayPool.Shared.Rent(requiredBytes)); + + try + { + var usedBytes = Encoding.UTF8.GetBytes(value, buffer); + _parent.AssignStringValue(this, buffer[..usedBytes]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + } + } + + public void SetNumberValue(ReadOnlySpan value) + { + CheckValidInstance(); + + _parent.AssignNumberValue(this, value); + } + + public void SetNumberValue(sbyte value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[4]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(byte value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[3]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(short value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[6]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(ushort value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[5]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(int value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[11]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(uint value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[10]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(long value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[20]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(ulong value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[20]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(float value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[16]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(double value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[24]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(decimal value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[31]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); } + /// public override string ToString() { switch (TokenType) diff --git a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs similarity index 89% rename from src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs rename to src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs index d34e5576f8c..695bc20aa08 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultProperty.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs @@ -1,16 +1,16 @@ using System.Diagnostics; using System.Text.Json; -using HotChocolate.Fusion.Execution.Nodes; +using HotChocolate.Execution.Processing; -namespace HotChocolate.Fusion.Text.Json; +namespace HotChocolate.Text.Json; /// /// Represents a single property for a JSON object. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] -public readonly struct CompositeResultProperty +public readonly struct ResultProperty { - internal CompositeResultProperty(CompositeResultElement value) + internal ResultProperty(ResultElement value) { Value = value; } @@ -18,7 +18,7 @@ internal CompositeResultProperty(CompositeResultElement value) /// /// The value of this property. /// - public CompositeResultElement Value { get; } + public ResultElement Value { get; } /// /// The name of this property. @@ -26,9 +26,9 @@ internal CompositeResultProperty(CompositeResultElement value) /// public string Name => Value.GetPropertyName(); - public Selection? Selection => Value.Selection; + public ISelection? Selection => Value.Selection; - public Selection GetRequiredSelection() => Value.AssertSelection(); + public ISelection AssertSelection() => Value.AssertSelection(); /// /// Compares to the name of this property. @@ -115,9 +115,9 @@ public override string ToString() private string DebuggerDisplay => Value.ValueKind == JsonValueKind.Undefined ? "" : $"\"{ToString()}\""; - public void Deconstruct(out Selection selection, out CompositeResultElement value) + public void Deconstruct(out ISelection selection, out ResultElement value) { - selection = GetRequiredSelection(); + selection = AssertSelection(); value = Value; } } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs index 551df5e0349..a72d6ac3b94 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs @@ -24,6 +24,13 @@ internal CompositeResultElement(CompositeResultDocument parent, CompositeResultD _cursor = cursor; } + /// + /// Writes this element as JSON to the specified buffer writer. + /// + /// The buffer writer to write to. + /// + /// true to write indented JSON; otherwise, false. + /// public void WriteTo(IBufferWriter writer, bool indented = false) { var formatter = new CompositeResultDocument.RawJsonFormatter(_parent, writer, indented); @@ -41,25 +48,19 @@ public void WriteTo(IBufferWriter writer, bool indented = false) private ElementTokenType TokenType => _parent?.GetElementTokenType(_cursor) ?? ElementTokenType.None; /// - /// The that the value is. + /// Gets the of this element. /// - /// - /// The parent has been disposed. - /// public JsonValueKind ValueKind => TokenType.ToValueKind(); /// - /// Get the value at a specified index when the current value is a - /// . + /// Gets the element at the specified index when the current element is an array. /// + /// The zero-based index of the element to get. /// - /// This value's is not . + /// This element's is not . /// /// - /// is not in the range [0, ()). - /// - /// - /// The parent has been disposed. + /// is not in the range [0, ()). /// public CompositeResultElement this[int index] { @@ -71,6 +72,9 @@ public CompositeResultElement this[int index] } } + /// + /// Gets the operation this element belongs to. + /// public Operation Operation { get @@ -81,6 +85,10 @@ public Operation Operation } } + /// + /// Gets the if this element represents the data of a selection set; + /// otherwise, null. + /// public SelectionSet? SelectionSet { get @@ -91,6 +99,10 @@ public SelectionSet? SelectionSet } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public Selection? Selection { get @@ -107,6 +119,10 @@ public Selection? Selection } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public IType? Type { get @@ -134,6 +150,9 @@ public IType? Type } } + /// + /// Gets a value indicating whether this element has been invalidated during null propagation. + /// public bool IsInvalidated { get @@ -144,6 +163,9 @@ public bool IsInvalidated } } + /// + /// Gets a value indicating whether this element is either null or was invalidated during null propagation. + /// public bool IsNullOrInvalidated { get @@ -157,6 +179,9 @@ public bool IsNullOrInvalidated } } + /// + /// Gets the path to this element within the result document. + /// public Path Path { get @@ -167,6 +192,9 @@ public Path Path } } + /// + /// Gets the parent element that contains this element. + /// public CompositeResultElement Parent { get @@ -177,6 +205,9 @@ public CompositeResultElement Parent } } + /// + /// Gets a value indicating whether this element is nullable according to the GraphQL type system. + /// public bool IsNullable { get @@ -192,6 +223,10 @@ public bool IsNullable } } + /// + /// Gets a value indicating whether this element represents internal data + /// that is required for processing and must not be written to the GraphQL response. + /// public bool IsInternal { get @@ -202,6 +237,15 @@ public bool IsInternal } } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a selection set. + /// public SelectionSet AssertSelectionSet() { var selectionSet = SelectionSet; @@ -214,6 +258,15 @@ public SelectionSet AssertSelectionSet() return selectionSet; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public Selection AssertSelection() { var selection = Selection; @@ -226,6 +279,15 @@ public Selection AssertSelection() return selection; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public IType AssertType() { var type = Type; @@ -238,6 +300,10 @@ public IType AssertType() return type; } + /// + /// Marks this element as invalidated, which occurs during null propagation + /// when a non-nullable field returns null. + /// public void Invalidate() { CheckValidInstance(); @@ -246,8 +312,11 @@ public void Invalidate() } /// - /// Get the number of values contained within the current array value. + /// Gets the number of elements contained within the current array element. /// + /// + /// The number of elements in the array. + /// public int GetArrayLength() { CheckValidInstance(); @@ -256,8 +325,11 @@ public int GetArrayLength() } /// - /// Get the number of properties contained within the current object value. + /// Gets the number of properties contained within the current object element. /// + /// + /// The number of properties in the object. + /// public int GetPropertyCount() { CheckValidInstance(); @@ -265,6 +337,14 @@ public int GetPropertyCount() return _parent.GetPropertyCount(_cursor); } + /// + /// Gets a property by name when the current element is an object. + /// + /// The name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public CompositeResultElement GetProperty(string propertyName) { ArgumentNullException.ThrowIfNull(propertyName); @@ -277,6 +357,14 @@ public CompositeResultElement GetProperty(string propertyName) throw new KeyNotFoundException(); } + /// + /// Gets a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public CompositeResultElement GetProperty(ReadOnlySpan utf8PropertyName) { if (TryGetProperty(utf8PropertyName, out var property)) @@ -287,6 +375,16 @@ public CompositeResultElement GetProperty(ReadOnlySpan utf8PropertyName) throw new KeyNotFoundException(); } + /// + /// Attempts to get a property by name when the current element is an object. + /// + /// The name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(string propertyName, out CompositeResultElement value) { ArgumentNullException.ThrowIfNull(propertyName); @@ -294,6 +392,16 @@ public bool TryGetProperty(string propertyName, out CompositeResultElement value return _parent.TryGetNamedPropertyValue(_cursor, propertyName, out value); } + /// + /// Attempts to get a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out CompositeResultElement value) { CheckValidInstance(); @@ -301,6 +409,13 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out CompositeRes return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value); } + /// + /// Gets the value as a . + /// + /// The boolean value. + /// + /// This element's is not or . + /// public bool GetBoolean() { var type = TokenType; @@ -324,6 +439,10 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) } } + /// + /// Gets the value as a . + /// + /// The string value, or null if this element is a JSON null. public string? GetString() { CheckValidInstance(); @@ -331,6 +450,13 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) return _parent.GetString(_cursor, ElementTokenType.String); } + /// + /// Gets the value as a non-null . + /// + /// The string value. + /// + /// This element is a JSON null. + /// public string AssertString() { CheckValidInstance(); @@ -338,6 +464,11 @@ public string AssertString() return _parent.GetRequiredString(_cursor, ElementTokenType.String); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSByte(out sbyte value) { CheckValidInstance(); @@ -345,8 +476,18 @@ public bool TryGetSByte(out sbyte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public sbyte GetSByte() => TryGetSByte(out var value) ? value : throw new FormatException(); + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetByte(out byte value) { CheckValidInstance(); @@ -354,6 +495,11 @@ public bool TryGetByte(out byte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public byte GetByte() { if (TryGetByte(out var value)) @@ -364,6 +510,11 @@ public byte GetByte() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt16(out short value) { CheckValidInstance(); @@ -371,6 +522,11 @@ public bool TryGetInt16(out short value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public short GetInt16() { if (TryGetInt16(out var value)) @@ -381,6 +537,11 @@ public short GetInt16() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt16(out ushort value) { CheckValidInstance(); @@ -388,6 +549,11 @@ public bool TryGetUInt16(out ushort value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ushort GetUInt16() { if (TryGetUInt16(out var value)) @@ -398,6 +564,11 @@ public ushort GetUInt16() throw new FormatException(); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt32(out int value) { CheckValidInstance(); @@ -405,6 +576,11 @@ public bool TryGetInt32(out int value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public int GetInt32() { if (!TryGetInt32(out var value)) @@ -415,6 +591,11 @@ public int GetInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt32(out uint value) { CheckValidInstance(); @@ -422,6 +603,11 @@ public bool TryGetUInt32(out uint value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public uint GetUInt32() { if (!TryGetUInt32(out var value)) @@ -432,6 +618,11 @@ public uint GetUInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt64(out long value) { CheckValidInstance(); @@ -439,6 +630,11 @@ public bool TryGetInt64(out long value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public long GetInt64() { if (!TryGetInt64(out var value)) @@ -449,6 +645,11 @@ public long GetInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt64(out ulong value) { CheckValidInstance(); @@ -456,6 +657,11 @@ public bool TryGetUInt64(out ulong value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ulong GetUInt64() { if (!TryGetUInt64(out var value)) @@ -466,6 +672,11 @@ public ulong GetUInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDouble(out double value) { CheckValidInstance(); @@ -473,6 +684,11 @@ public bool TryGetDouble(out double value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public double GetDouble() { if (!TryGetDouble(out var value)) @@ -483,6 +699,11 @@ public double GetDouble() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSingle(out float value) { CheckValidInstance(); @@ -490,6 +711,11 @@ public bool TryGetSingle(out float value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public float GetSingle() { if (!TryGetSingle(out var value)) @@ -500,6 +726,11 @@ public float GetSingle() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDecimal(out decimal value) { CheckValidInstance(); @@ -507,6 +738,11 @@ public bool TryGetDecimal(out decimal value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public decimal GetDecimal() { if (!TryGetDecimal(out var value)) @@ -531,6 +767,10 @@ internal ReadOnlySpan GetPropertyNameRaw() return _parent.GetPropertyNameRaw(_cursor); } + /// + /// Gets the raw JSON text representing this element. + /// + /// The raw JSON text. public string GetRawText() { CheckValidInstance(); @@ -545,6 +785,13 @@ internal ReadOnlySpan GetRawValue(bool includeQuotes = true) return _parent.GetRawValue(_cursor, includeQuotes: true); } + /// + /// Compares the text of this element to the specified string. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(string? text) { if (TokenType == ElementTokenType.Null) @@ -555,6 +802,13 @@ public bool ValueEquals(string? text) return TextEqualsHelper(text.AsSpan(), isPropertyName: false); } + /// + /// Compares the text of this element to the specified UTF-8 encoded text. + /// + /// The UTF-8 encoded text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan utf8Text) { if (TokenType == ElementTokenType.Null) @@ -567,6 +821,13 @@ public bool ValueEquals(ReadOnlySpan utf8Text) return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); } + /// + /// Compares the text of this element to the specified character span. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan text) { if (TokenType == ElementTokenType.Null) @@ -600,6 +861,13 @@ internal string GetPropertyRawText() return _parent.GetPropertyRawValueAsString(_cursor); } + /// + /// Gets an enumerator to enumerate the elements of this array. + /// + /// An enumerator for the array elements. + /// + /// This element's is not . + /// public ArrayEnumerator EnumerateArray() { CheckValidInstance(); @@ -620,6 +888,13 @@ public ArrayEnumerator EnumerateArray() return new ArrayEnumerator(this); } + /// + /// Gets an enumerator to enumerate the properties of this object. + /// + /// An enumerator for the object properties. + /// + /// This element's is not . + /// public ObjectEnumerator EnumerateObject() { CheckValidInstance(); @@ -671,6 +946,7 @@ internal void SetNullValue() _parent.AssignNullValue(this); } + /// public override string ToString() { switch (TokenType) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs index d34e5576f8c..e61eab856d8 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs @@ -28,7 +28,7 @@ internal CompositeResultProperty(CompositeResultElement value) public Selection? Selection => Value.Selection; - public Selection GetRequiredSelection() => Value.AssertSelection(); + public Selection AssertSelection() => Value.AssertSelection(); /// /// Compares to the name of this property. @@ -117,7 +117,7 @@ private string DebuggerDisplay public void Deconstruct(out Selection selection, out CompositeResultElement value) { - selection = GetRequiredSelection(); + selection = AssertSelection(); value = Value; } } From 971333fb830b529475f640d5c9892189aae936a0 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 27 Nov 2025 22:52:04 +0100 Subject: [PATCH 08/30] wip --- .../Types/Execution/Processing/IOperation.cs | 2 +- .../Types/Execution/Processing/ISelection.cs | 2 +- .../Execution/Processing/ISelectionSet.cs | 30 ++++ .../Types/Execution/Processing/Operation.cs | 4 + .../OperationCompiler.SelectionSetInfo.cs | 6 +- .../Execution/Processing/OperationCompiler.cs | 14 +- .../Processing/OperationContext.Operation.cs | 2 +- .../Types/Execution/Processing/Selection.cs | 6 +- .../Processing/SelectionCollection.cs | 12 +- .../Execution/Processing/SelectionLookup.cs | 156 ++++++++++++++++++ .../Execution/Processing/SelectionSet.cs | 16 +- .../Text/Json/JsonReaderHelper.cs | 2 +- 12 files changed, 229 insertions(+), 23 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs index 9ad9097c0c2..29635565407 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs @@ -104,7 +104,7 @@ public interface IOperation : IHasReadOnlyContextData, IEnumerable /// Returns the include flags for the specified variable values. /// - long CreateIncludeFlags(IVariableValueCollection variables); + ulong CreateIncludeFlags(IVariableValueCollection variables); bool TryGetState(out TState? state); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs index f6979a4a84f..5eba9ad5385 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs @@ -109,5 +109,5 @@ public interface ISelection : IOptionalSelection /// /// Returns if this selection is annotated with the stream directive. /// - bool HasStreamDirective(long includeFlags); + bool HasStreamDirective(ulong includeFlags); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs index df45aea7ef5..6ef00358cc4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace HotChocolate.Execution.Processing; /// @@ -31,4 +33,32 @@ public interface ISelectionSet /// Gets the declaring operation. /// IOperation DeclaringOperation { get; } + + /// + /// Tries to resolve a selection by name. + /// + /// + /// The selection response name. + /// + /// + /// The resolved selection. + /// + /// + /// Returns true if the selection was successfully resolved. + /// + bool TryGetSelection(string responseName, [NotNullWhen(true)] out ISelection? selection); + + /// + /// Tries to resolve a selection by name. + /// + /// + /// The selection response name. + /// + /// + /// The resolved selection. + /// + /// + /// Returns true if the selection was successfully resolved. + /// + bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out ISelection? selection); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 0c2e3384b3f..ed5b4c63bf0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -9,7 +9,11 @@ namespace HotChocolate.Execution.Processing; internal sealed class Operation : IOperation { +#if NET9_0_OR_GREATER + private readonly Lock _writeLock = new(); +#else private readonly object _writeLock = new(); +#endif private SelectionVariants[] _selectionVariants = []; private IncludeCondition[] _includeConditions = []; private ImmutableDictionary _contextData = diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs index a6363e4c195..7c46db01ff7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs @@ -4,10 +4,12 @@ namespace HotChocolate.Execution.Processing; public sealed partial class OperationCompiler { - internal readonly struct SelectionSetInfo(SelectionSetNode selectionSet, long includeCondition) + internal readonly struct SelectionSetInfo( + SelectionSetNode selectionSet, + ulong includeCondition) { public SelectionSetNode SelectionSet { get; } = selectionSet; - public long IncludeCondition { get; } = includeCondition; + public ulong IncludeCondition { get; } = includeCondition; } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 02446a02fbd..174bc3ca348 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -370,7 +370,7 @@ private void CollectFields(CompilerContext context) private void CollectFields( CompilerContext context, SelectionSetNode selectionSet, - long includeCondition) + ulong includeCondition) { for (var j = 0; j < selectionSet.Selections.Count; j++) { @@ -411,7 +411,7 @@ private void ResolveFields( private void ResolveField( CompilerContext context, FieldNode selection, - long includeCondition) + ulong includeCondition) { includeCondition = GetSelectionIncludeCondition(selection, includeCondition); @@ -514,7 +514,7 @@ private void ResolveFragment( NamedTypeNode? typeCondition, SelectionSetNode selectionSet, IReadOnlyList directives, - long includeCondition) + ulong includeCondition) { if (typeCondition is null || (context.Schema.Types.TryGetType(typeCondition, out IType? typeCon) @@ -528,7 +528,7 @@ private void ResolveFragment( var nullValue = NullValueNode.Default; var ifValue = deferDirective?.GetArgumentValue(DirectiveNames.Defer.Arguments.If) ?? nullValue; - long ifConditionFlags = 0; + ulong ifConditionFlags = 0; if (ifValue.Kind is not SyntaxKind.NullValue) { @@ -649,9 +649,9 @@ private SelectionVariants GetOrCreateSelectionVariants(int selectionSetId) return variants; } - private long GetSelectionIncludeCondition( + private ulong GetSelectionIncludeCondition( ISelectionNode selectionSyntax, - long parentIncludeCondition) + ulong parentIncludeCondition) { var condition = IncludeCondition.FromSelection(selectionSyntax); @@ -697,7 +697,7 @@ private long GetSelectionIncludeCondition( private long GetSelectionIncludeCondition( IncludeCondition condition, - long parentIncludeCondition) + ulong parentIncludeCondition) { var pos = Array.IndexOf(_includeConditions, condition); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs index 8239f55055b..0005bf324d2 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs @@ -44,7 +44,7 @@ public IVariableValueCollection Variables /// /// Gets the include flags for the current request. /// - public long IncludeFlags { get; private set; } + public ulong IncludeFlags { get; private set; } /// /// Gets the value representing the instance of the diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index 1ed864d2455..9470b054e68 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -14,7 +14,7 @@ public class Selection : ISelection { private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; private ulong[] _includeConditions; - private long _streamIfCondition; + private ulong _streamIfCondition; private Flags _flags; private FieldNode _syntaxNode; private FieldNode[] _syntaxNodes; @@ -136,7 +136,7 @@ protected Selection(Selection selection) public ArgumentMap Arguments { get; } /// - public bool HasStreamDirective(long includeFlags) + public bool HasStreamDirective(ulong includeFlags) => (_flags & Flags.Stream) == Flags.Stream && (_streamIfCondition is 0 || (includeFlags & _streamIfCondition) != _streamIfCondition); @@ -313,7 +313,7 @@ internal void SetSelectionSetId(int selectionSetId) SelectionSetId = selectionSetId; } - internal void MarkAsStream(long ifCondition) + internal void MarkAsStream(ulong ifCondition) { if ((_flags & Flags.Sealed) == Flags.Sealed) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs index 58fb04ac373..6a7370dfe04 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs @@ -14,7 +14,7 @@ internal sealed class SelectionCollection( Schema schema, IOperation operation, ISelection[] selections, - long includeFlags) + ulong includeFlags) : ISelectionCollection { private readonly Schema _schema = schema ?? throw new ArgumentNullException(nameof(schema)); @@ -119,7 +119,7 @@ public bool IsSelected(string fieldName) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName) @@ -199,7 +199,7 @@ public bool IsSelected(string fieldName1, string fieldName2) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName1, @@ -284,7 +284,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName1, @@ -360,7 +360,7 @@ public bool IsSelected(ISet fieldNames) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, ISet fieldNames) @@ -481,7 +481,7 @@ private bool CollectSelections( private static void CollectFields( ReadOnlySpan fieldNames, - long includeFlags, + ulong includeFlags, ref ISelection[] buffer, ISelectionSet selectionSet, int index, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs new file mode 100644 index 00000000000..3986a6aab5a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs @@ -0,0 +1,156 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace HotChocolate.Execution.Processing; + +internal sealed class SelectionLookup +{ + private readonly Entry[] _table; + private readonly int _seed; + private readonly int _mask; + + private SelectionLookup(Entry[] table, int seed, int mask) + { + _table = table; + _seed = seed; + _mask = mask; + } + + public static SelectionLookup Create(SelectionSet selectionSet) + { + var selections = selectionSet.Selections; + var tableSize = NextPowerOfTwo(Math.Max(selections.Count * 2, 4)); + var mask = tableSize - 1; + var table = new Entry[tableSize]; + + // We try multiple seeds to find one with minimal clustering + var bestSeed = 0; + var bestMaxProbeLength = int.MaxValue; + + for (var seed = 0; seed < 100; seed++) + { + Array.Clear(table); + var maxProbeLength = 0; + + foreach (var selection in selections) + { + var hashCode = ComputeHash(selection.Utf8ResponseName, seed); + var index = hashCode & mask; + var probeLength = 0; + + // Linear probe to find empty slot + while (table[index].Selection != null) + { + index = (index + 1) & mask; + probeLength++; + } + + table[index] = new Entry(hashCode, selection); + maxProbeLength = Math.Max(maxProbeLength, probeLength); + } + + // Track best seed + if (maxProbeLength < bestMaxProbeLength) + { + bestMaxProbeLength = maxProbeLength; + bestSeed = seed; + + // If we found excellent distribution, stop early + if (maxProbeLength <= 2) + { + break; + } + } + } + + // Rebuild table with best seed + Array.Clear(table); + foreach (var selection in selections) + { + var hashCode = ComputeHash(selection.Utf8ResponseName, bestSeed); + var index = hashCode & mask; + + while (table[index].Selection != null) + { + index = (index + 1) & mask; + } + + table[index] = new Entry(hashCode, selection); + } + + return new SelectionLookup(table, bestSeed, mask); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetSelection(ReadOnlySpan name, [NotNullWhen(true)] out ISelection? selection) + { + var table = _table.AsSpan(); + + if (table.Length == 0) + { + selection = null!; + return false; + } + + var hashCode = ComputeHash(name, _seed); + var index = hashCode & _mask; + + while (true) + { + ref var entry = ref table[index]; + + // if we hit an empty slot, then there is no selection with the specified name. + if (entry.Selection is null) + { + selection = null; + return false; + } + + if (entry.HashCode == hashCode && name.SequenceEqual(entry.Selection.Utf8ResponseName)) + { + selection = entry.Selection; + return true; + } + + // we had a hash collision need to find the next slot. + index = (index + 1) & _mask; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ComputeHash(ReadOnlySpan bytes, int seed) + { + var hash = (uint)seed; + + foreach (var b in bytes) + { + hash = hash * 31 + b; + } + + return (int)(hash & 0x7FFFFFFF); + } + + private static int NextPowerOfTwo(int n) + { + if (n <= 0) + { + return 1; + } + + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + + return n; + } + + private readonly struct Entry(int hashCode, ISelection selection) + { + public readonly int HashCode = hashCode; + public readonly ISelection? Selection = selection; + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index cd38b9f6388..88223788ca3 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -1,3 +1,5 @@ +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using HotChocolate.Types; @@ -13,6 +15,8 @@ internal sealed class SelectionSet : ISelectionSet { private static readonly Fragment[] s_empty = []; private readonly Selection[] _selections; + private readonly FrozenDictionary _responseNameLookup; + private readonly SelectionLookup _utf8ResponseNameLookup; private readonly Fragment[] _fragments; private Flags _flags; @@ -30,7 +34,7 @@ internal sealed class SelectionSet : ISelectionSet /// some of the execution. /// /// - /// Defines if this list needs post processing for skip and include. + /// Defines if this list needs post-processing for skip and include. /// public SelectionSet( int id, @@ -40,6 +44,8 @@ public SelectionSet( { Id = id; _selections = selections; + _responseNameLookup = _selections.ToFrozenDictionary(t => t.ResponseName, ISelection (t) => t); + _utf8ResponseNameLookup = SelectionLookup.Create(this); _fragments = fragments ?? s_empty; _flags = isConditional ? Flags.Conditional : Flags.None; } @@ -59,6 +65,14 @@ public SelectionSet( /// public IOperation DeclaringOperation { get; private set; } = null!; + /// + public bool TryGetSelection(string responseName, [NotNullWhen(true)] out ISelection? selection) + => _responseNameLookup.TryGetValue(responseName, out selection); + + /// + public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out ISelection? selection) + => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); + /// /// Completes the selection set without sealing it. /// diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs index 100366d0af1..edbbcac3f24 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs @@ -10,7 +10,7 @@ namespace HotChocolate.Fusion.Text.Json; #else -using static HotChocolate.Properties.TypeResources; +using static HotChocolate.Properties.TextJsonResources; namespace HotChocolate.Text.Json; #endif From 0388821aaee84e6c6dc34ddc5fc5ffa8979f6bba Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 28 Nov 2025 11:34:39 +0100 Subject: [PATCH 09/30] wip --- .../ISelectionSet.cs | 5 + .../Types/Execution/Processing/ArgumentMap.cs | 91 +- .../Processing/CreateFieldPipeline.cs | 10 + .../Processing/DeferredExecutionTask.cs | 85 -- .../Processing/DeferredExecutionTaskResult.cs | 20 - .../Execution/Processing/DeferredFragment.cs | 98 -- .../Execution/Processing/DeferredStream.cs | 158 --- .../Processing/DeferredWorkScheduler.cs | 178 --- .../Execution/Processing/DeferredWorkState.cs | 215 ---- .../Types/Execution/Processing/Fragment.cs | 52 - .../Types/Execution/Processing/IFragment.cs | 46 - .../Types/Execution/Processing/IOperation.cs | 194 --- .../Processing/IOptionalSelection.cs | 31 - .../Types/Execution/Processing/ISelection.cs | 113 -- .../Execution/Processing/ISelectionSet.cs | 64 - .../Execution/Processing/IncludeCondition.cs | 173 --- .../Types/Execution/Processing/Operation.cs | 353 +++--- .../OperationCompiler.CoerceArgumentValues.cs | 122 ++ .../Execution/Processing/OperationCompiler.cs | 1074 +++++++---------- .../Processing/OperationFeatureCollection.cs | 113 ++ .../Types/Execution/Processing/Selection.cs | 365 ++---- .../Execution/Processing/SelectionSet.cs | 108 +- .../Execution/Processing/SelectionVariants.cs | 194 --- ...s => _OperationCompiler.ArgumentValues.cs} | 5 - .../Processing/_OperationCompiler.cs | 787 ++++++++++++ .../Core/src/Types/HotChocolate.Types.csproj | 2 + .../Nodes/IncludeConditionCollection.cs | 9 +- .../Execution/Nodes/SelectionSet.cs | 19 +- 28 files changed, 1903 insertions(+), 2781 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs rename src/HotChocolate/Core/src/Types/Execution/Processing/{OperationCompiler.ArgumentValues.cs => _OperationCompiler.ArgumentValues.cs} (98%) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs diff --git a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs index 7bd3aeef3d3..a64c48f961a 100644 --- a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs +++ b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs @@ -2,6 +2,11 @@ namespace HotChocolate.Execution; +/// +/// A selection set is primarily composed of field selections. +/// When needed a selection set can preserve fragments so that the execution engine +/// can branch the processing of these fragments. +/// public interface ISelectionSet { /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs index e48ca10171d..354e3bdfd73 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs @@ -1,24 +1,25 @@ using System.Collections; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using HotChocolate.Resolvers; namespace HotChocolate.Execution.Processing; /// -/// Represents the map of argument values that can be accessed on the . +/// Represents a read-only map of argument values for a field selection in a GraphQL query. +/// This map provides efficient access to coerced argument values and tracks coercion errors. /// -public sealed class ArgumentMap - : IReadOnlyDictionary - , IEnumerable +public sealed class ArgumentMap : IReadOnlyDictionary { - private readonly Dictionary _arguments; - private readonly bool _isFinal; + private readonly FrozenDictionary _arguments; private readonly bool _hasErrors; internal ArgumentMap(Dictionary arguments) { - _arguments = arguments; - _isFinal = true; + _arguments = arguments.ToFrozenDictionary(StringComparer.Ordinal); + IsFullyCoercedNoErrors = true; if (_arguments.Count > 0) { @@ -26,7 +27,7 @@ internal ArgumentMap(Dictionary arguments) { if (!argument.IsFullyCoerced) { - _isFinal = false; + IsFullyCoercedNoErrors = false; } if (argument.HasError) @@ -38,49 +39,79 @@ internal ArgumentMap(Dictionary arguments) } /// - /// Gets an empty argument map. + /// Gets an empty argument map with no arguments. /// public static ArgumentMap Empty { get; } = new([]); /// - /// This indexer allows to access the - /// by the argument . + /// Gets the for the specified argument name. /// /// /// The argument name. /// + /// + /// The associated with the specified name. + /// + /// + /// Thrown when the specified is not found. + /// public ArgumentValue this[string name] => _arguments[name]; /// - /// Specifies if the argument map is fully coerced and has no errors. + /// Gets a value indicating whether all arguments in this map are + /// fully coerced and no errors occurred during coercion. /// - public bool IsFullyCoercedNoErrors => _isFinal && !_hasErrors; + /// + /// true if all arguments are fully coerced without errors; otherwise, false. + /// + public bool IsFullyCoercedNoErrors => field && !_hasErrors; /// - /// Specifies if this argument map has errors. + /// Gets a value indicating whether any argument in this map has coercion errors. /// + /// + /// true if at least one argument has errors; otherwise, false. + /// public bool HasErrors => _hasErrors; /// - /// The argument count. + /// Gets the number of arguments in this map. /// + /// + /// The total count of arguments. + /// public int Count => _arguments.Count; + /// + /// Gets an immutable array containing all argument names in this map. + /// + /// + /// An of argument names. + /// + public ImmutableArray ArgumentNames => _arguments.Keys; + IEnumerable IReadOnlyDictionary.Keys => _arguments.Keys; + /// + /// Gets an immutable array containing all argument values in this map. + /// + /// + /// An of instances. + /// + public ImmutableArray Values => _arguments.Values; + IEnumerable IReadOnlyDictionary.Values => _arguments.Values; /// - /// This method allows to check if an argument value with the specified - /// argument exists. + /// Determines whether this map contains an argument with the specified name. /// /// - /// The argument name. + /// The argument name to check. /// /// - /// true if the argument exists; otherwise, false. + /// true if an argument with the specified exists; otherwise, false. /// public bool ContainsName(string name) => _arguments.ContainsKey(name); @@ -88,17 +119,17 @@ bool IReadOnlyDictionary.ContainsKey(string key) => ContainsName(key); /// - /// Tries to get an by its . + /// Attempts to retrieve the associated with the specified argument name. /// /// - /// The argument name. + /// The argument name to locate. /// /// - /// The argument value. + /// When this method returns, contains the associated with the specified + /// , if found; otherwise, null. /// /// - /// true if an argument value with the specified - /// was retrieved; otherwise, false. + /// true if an argument with the specified was found; otherwise, false. /// public bool TryGetValue(string name, [NotNullWhen(true)] out ArgumentValue? value) => _arguments.TryGetValue(name, out value); @@ -108,14 +139,8 @@ bool IReadOnlyDictionary.TryGetValue( out ArgumentValue value) => TryGetValue(key, out value!); - /// - /// Gets an enumerator for the argument values. - /// - public IEnumerator GetEnumerator() - => _arguments.Values.GetEnumerator(); - - IEnumerator> - IEnumerable>.GetEnumerator() + /// + public IEnumerator> GetEnumerator() => _arguments.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs new file mode 100644 index 00000000000..6d2647936b3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs @@ -0,0 +1,10 @@ +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +internal delegate FieldDelegate CreateFieldPipeline( + Schema schema, + ObjectField field, + FieldNode selection); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs deleted file mode 100644 index f4de9856a12..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTask.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Utilities; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deprioritized part of the query that will be executed after -/// the main execution has finished. -/// -internal abstract class DeferredExecutionTask -{ - /// - /// Initializes a new instance of . - /// - protected DeferredExecutionTask(IImmutableDictionary scopedContextData) - { - ScopedContextData = scopedContextData; - } - - /// - /// Gets the preserved scoped context from the parent resolver. - /// - public IImmutableDictionary ScopedContextData { get; } - - /// - /// Starts executing the deferred execution task. - /// - /// - /// The operation context owner. - /// - /// - /// The internal result identifier. - /// - /// - /// The internal identifier of the object that the result will be patched into. - /// - public void Begin(OperationContextOwner operationContextOwner, uint resultId, uint patchId) - { - // retrieve the task on which this task depends upon. We do this to ensure that the result - // of this task is not delivered before the parent result is delivered. - uint parentResultId = 0; - if (ScopedContextData.TryGetValue(DeferredResultId, out var value) - && value is uint id) - { - parentResultId = id; - } - - var capturedContext = ExecutionContext.Capture(); - if (capturedContext is null) - { - ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId).FireAndForget(); - } - else - { - var execute = () => - ExecutionContext.Run( - capturedContext, - _ => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), - null); - execute.FireAndForget(); - } - } - - /// - /// The task execution logic. - /// - /// - /// The operation context owner. - /// - /// - /// The internal result identifier. - /// - /// - /// The parent result identifier. - /// - /// - /// The internal identifier of the object that the result will be patched into. - /// - protected abstract Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs deleted file mode 100644 index 23fad4d1ea5..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredExecutionTaskResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace HotChocolate.Execution.Processing; - -internal readonly struct DeferredExecutionTaskResult -{ - public DeferredExecutionTaskResult( - uint taskId, - uint parentTaskId, - IOperationResult? result = null) - { - TaskId = taskId; - ParentTaskId = parentTaskId; - Result = result; - } - - public uint TaskId { get; } - - public uint ParentTaskId { get; } - - public IOperationResult? Result { get; } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs deleted file mode 100644 index 90faf750543..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredFragment.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Immutable; -using static HotChocolate.Execution.Processing.Tasks.ResolverTaskFactory; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deprioritized fragment of the query that will be executed after -/// the main execution has finished. -/// -internal sealed class DeferredFragment : DeferredExecutionTask -{ - /// - /// Initializes a new instance of . - /// - public DeferredFragment( - IFragment fragment, - string? label, - Path path, - object? parent, - IImmutableDictionary scopedContextData) - : base(scopedContextData) - { - Fragment = fragment; - Label = label; - Path = path; - Parent = parent; - } - - /// - /// Gets the deferred fragment. - /// - public IFragment Fragment { get; } - - /// - /// If this argument label has a value other than null, it will be passed - /// on to the result of this defer directive. This label is intended to - /// give client applications a way to identify to which fragment a deferred - /// result belongs to. - /// - public string? Label { get; } - - /// - /// Gets the result path into which this deferred fragment shall be patched. - /// - public Path Path { get; } - - /// - /// Gets the parent / source value. - /// - public object? Parent { get; } - - protected override async Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId) - { - try - { - var operationContext = operationContextOwner.OperationContext; - var parentResult = operationContext.Result.RentObject(Fragment.SelectionSet.Selections.Count); - - parentResult.PatchPath = Path; - - EnqueueResolverTasks( - operationContext, - Fragment.SelectionSet, - Parent, - Path, - // for the execution of this task we set the deferred task ID so that - // child deferrals can lookup their dependency to this task. - ScopedContextData.SetItem(DeferredResultId, resultId), - parentResult); - - // start executing the deferred fragment. - await operationContext.Scheduler.ExecuteAsync().ConfigureAwait(false); - - // we create the result but will not create the final result object yet. - // We will leave the final creation to the deferred work scheduler so that the - // has next property can be correctly set. - var result = - operationContext - .SetLabel(Label) - .SetPath(Path) - .SetData(parentResult) - .SetPatchId(patchId) - .BuildResult(); - - // complete the task and provide the result - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - finally - { - operationContextOwner.Dispose(); - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs deleted file mode 100644 index f5d6b220101..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredStream.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Execution.Processing.Tasks; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents the work to executed the deferred elements of a stream. -/// -internal sealed class DeferredStream : DeferredExecutionTask -{ - private StreamExecutionTask? _task; - - /// - /// Initializes a new instance of . - /// - public DeferredStream( - ISelection selection, - string? label, - Path path, - object? parent, - int index, - IAsyncEnumerator enumerator, - IImmutableDictionary scopedContextData) - : base(scopedContextData) - { - Selection = selection; - Label = label; - Path = path; - Parent = parent; - Index = index; - Enumerator = enumerator; - } - - /// - /// Gets the selection of the streamed field. - /// - public ISelection Selection { get; } - - /// - /// If this argument label has a value other than null, it will be passed - /// on to the result of this defer directive. This label is intended to - /// give client applications a way to identify to which fragment a deferred - /// result belongs to. - /// - public string? Label { get; } - - /// - /// Gets the result path into which this deferred fragment shall be patched. - /// - public Path Path { get; } - - /// - /// Gets the index of the last element. - /// - public int Index { get; private set; } - - /// - /// Gets the parent / source value. - /// - public object? Parent { get; } - - /// - /// Gets the enumerator to retrieve the payloads of the stream. - /// - public IAsyncEnumerator Enumerator { get; } - - protected override async Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId) - { - var operationContext = operationContextOwner.OperationContext; - - try - { - _task ??= new StreamExecutionTask(this); - _task.Reset(operationContext, resultId); - operationContext.Scheduler.Register(_task); - await operationContext.Scheduler.ExecuteAsync().ConfigureAwait(false); - - // if there is no child task, then there is no more data, so we can complete. - if (_task.ChildTask is null) - { - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId)); - return; - } - - var item = _task.ChildTask.ParentResult[0].Value!; - - var result = operationContext - .SetLabel(Label) - .SetPath(Path.Append(Index)) - .SetItems([item]) - .SetPatchId(patchId) - .BuildResult(); - - await _task.ChildTask.CompleteUnsafeAsync().ConfigureAwait(false); - - // we will register this same task again to get the next item. - operationContext.DeferredScheduler.Register(this, patchId); - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - catch (Exception ex) - { - var result = OperationResultBuilder.CreateError(ErrorBuilder.FromException(ex).Build()); - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - finally - { - operationContextOwner.Dispose(); - } - } - - private sealed class StreamExecutionTask : ExecutionTask - { - private readonly DeferredStream _deferredStream; - private OperationContext _operationContext = null!; - private IImmutableDictionary _scopedContextData; - - public StreamExecutionTask(DeferredStream deferredStream) - { - _deferredStream = deferredStream; - _scopedContextData = _deferredStream.ScopedContextData; - } - - protected override IExecutionTaskContext Context => _operationContext; - - public ResolverTask? ChildTask { get; private set; } - - protected override async ValueTask ExecuteAsync(CancellationToken cancellationToken) - { - ChildTask = null; - _deferredStream.Index++; - var hasNext = await _deferredStream.Enumerator.MoveNextAsync(); - - if (hasNext) - { - ChildTask = ResolverTaskFactory.EnqueueElementTasks( - _operationContext, - _deferredStream.Selection, - _deferredStream.Parent, - _deferredStream.Path, - _deferredStream.Index, - _deferredStream.Enumerator, - _scopedContextData); - } - } - - public void Reset(OperationContext operationContext, uint taskId) - { - _operationContext = operationContext; - _scopedContextData = _scopedContextData.SetItem(DeferredResultId, taskId); - Reset(); - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs deleted file mode 100644 index 4bbccb2a888..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkScheduler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using HotChocolate.Execution.DependencyInjection; -using HotChocolate.Execution.Instrumentation; -using Microsoft.Extensions.DependencyInjection; -using static HotChocolate.Execution.OperationResultBuilder; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a backlog for deferred work. -/// -internal sealed class DeferredWorkScheduler -{ - private readonly object _stateSync = new(); - private IFactory _operationContextFactory = null!; - private IFactory _deferredWorkStateFactory = null!; - private OperationContext _parentContext = null!; - private DeferredWorkStateOwner? _stateOwner; - - private DeferredWorkStateOwner StateOwner - { - get - { - if (_stateOwner is null) - { - lock (_stateSync) - { - _stateOwner ??= _deferredWorkStateFactory.Create(); - } - } - - return _stateOwner; - } - } - - /// - /// Specifies if there was deferred work enqueued. - /// - public bool HasResults => _stateOwner?.State.HasResults is true; - - public void Initialize(OperationContext operationContext) - { - var services = operationContext.Services; - - _stateOwner = null; - _parentContext = operationContext; - _operationContextFactory = services.GetRequiredService>(); - _deferredWorkStateFactory = services.GetRequiredService>(); - } - - public void InitializeFrom(OperationContext operationContext, DeferredWorkScheduler scheduler) - { - _stateOwner = scheduler.StateOwner; - _parentContext = operationContext; - _operationContextFactory = scheduler._operationContextFactory; - _deferredWorkStateFactory = scheduler._deferredWorkStateFactory; - } - - /// - /// Registers deferred work - /// - public void Register(DeferredExecutionTask task, ResultData parentResult) - { - // first we get the result identifier which is used to refer to the result that we defer. - var resultId = StateOwner.State.CreateId(); - - // next we assign a patch identifier to the result set into which the deferred result - // shall be patched into. - var patchId = StateOwner.State.AssignPatchId(parentResult); - - // for the spawned execution we need an operation context which we will initialize - // from the current operation context. - var taskContextOwner = _operationContextFactory.Create(); - taskContextOwner.OperationContext.InitializeFrom(_parentContext); - - // Last we register our patch identifier with the parent result so that - // we can more efficiently mark discarded result sets to not send down - // patches that cannot be applied. - _parentContext.Result.AddPatchId(patchId); - - // with all in place we will start the execution of the deferred task. - task.Begin(taskContextOwner, resultId, patchId); - } - - public void Register(DeferredExecutionTask task, uint patchId) - { - var resultId = StateOwner.State.CreateId(); - var taskContextOwner = _operationContextFactory.Create(); - taskContextOwner.OperationContext.InitializeFrom(_parentContext); - task.Begin(taskContextOwner, resultId, patchId); - } - - public void Complete(DeferredExecutionTaskResult result) - => StateOwner.State.Complete(result); - - public IAsyncEnumerable CreateResultStream(IOperationResult initialResult) - => new DeferredResultStream( - initialResult, - StateOwner, - _parentContext.Operation, - _parentContext.DiagnosticEvents); - - public void Clear() - { - _stateOwner = null; - _operationContextFactory = null!; - _deferredWorkStateFactory = null!; - _parentContext = null!; - } - - private class DeferredResultStream : IAsyncEnumerable - { - private readonly IOperationResult _initialResult; - private readonly DeferredWorkStateOwner _stateOwner; - private readonly IOperation _operation; - private readonly IExecutionDiagnosticEvents _diagnosticEvents; - - public DeferredResultStream( - IOperationResult initialResult, - DeferredWorkStateOwner stateOwner, - IOperation operation, - IExecutionDiagnosticEvents diagnosticEvents) - { - _initialResult = FromResult(initialResult).SetHasNext(true).Build(); - _stateOwner = stateOwner; - _operation = operation; - _diagnosticEvents = diagnosticEvents; - } - - public async IAsyncEnumerator GetAsyncEnumerator( - CancellationToken cancellationToken = default) - { - var span = _diagnosticEvents.ExecuteStream(_operation); - var state = _stateOwner.State; - var hasNext = true; - var completed = false; - - try - { - yield return _initialResult; - - while (!cancellationToken.IsCancellationRequested) - { - var result = await state - .TryDequeueResultsAsync(cancellationToken) - .ConfigureAwait(false); - - if (result is not null) - { - hasNext = result.HasNext ?? false; - yield return result; - } - else if (state.IsCompleted) - { - if (hasNext) - { - yield return new OperationResult(null, hasNext: false); - } - - yield break; - } - } - - completed = !cancellationToken.IsCancellationRequested; - } - finally - { - span.Dispose(); - } - - // we only return the state back to its pool if the operation was not cancelled - // or otherwise faulted. - if (completed) - { - _stateOwner.Dispose(); - } - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs deleted file mode 100644 index ad678864378..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkState.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Runtime.CompilerServices; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -internal sealed class DeferredWorkState -{ - private readonly object _completeSync = new(); - private readonly object _deliverSync = new(); - private readonly object _patchSync = new(); - - private readonly List _ready = []; - private readonly Queue _deliverable = new(); - private readonly HashSet _completed = []; - private readonly HashSet _notPatchable = []; - private SemaphoreSlim _semaphore = new(0); - private uint _taskId; - private uint _work; - private uint _patchId; - - public bool HasResults => _taskId > 0; - - public bool IsCompleted => _work is 0; - - public uint CreateId() - { - lock (_deliverSync) - { - _work++; - return ++_taskId; - } - } - - public uint AssignPatchId(ResultData resultData) - { - if (resultData.PatchId == 0) - { - lock (_patchSync) - { - if (resultData.PatchId == 0) - { - var patchId = ++_patchId; - resultData.PatchId = patchId; - return patchId; - } - } - } - - return resultData.PatchId; - } - - public void Complete(DeferredExecutionTaskResult result) - { - var update = true; - - try - { - lock (_completeSync) - { - if (result.ParentTaskId is 0 || _completed.Contains(result.ParentTaskId)) - { - _completed.Add(result.TaskId); - EnqueueResult(result.Result); - - var evaluateDeferredResults = _ready.Count > 0; - - while (evaluateDeferredResults) - { - var i = 0; - evaluateDeferredResults = false; - - while (_ready.Count > 0 && i < _ready.Count) - { - var current = _ready[i]; - - if (_completed.Contains(current.ParentTaskId)) - { - _completed.Add(current.TaskId); - _ready.RemoveAt(i); - EnqueueResult(current.Result); - evaluateDeferredResults = true; - } - else - { - i++; - } - } - } - } - else - { - _ready.Add(result); - update = false; - } - } - } - finally - { - if (update) - { - _semaphore.Release(); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnqueueResult(IOperationResult? queryResult) - { - lock (_deliverSync) - { - if (queryResult is not null) - { - _deliverable.Enqueue(queryResult); - } - else - { - _work--; - } - } - } - - public async ValueTask TryDequeueResultsAsync( - CancellationToken cancellationToken) - { - await _semaphore.WaitAsync(cancellationToken); - - lock (_deliverSync) - { - if (_deliverable.Count > 0) - { - var hasNext = true; - var result = new IOperationResult[_deliverable.Count]; - var consumed = 0; - - for (var i = 0; i < result.Length; i++) - { - var deliverable = _deliverable.Dequeue(); - - if (--_work is 0) - { - _semaphore.Release(); - hasNext = false; - } - - // if the deferred result can still be patched into the result set from which - // it was being spawned of we will add it to the result batch. - if ((deliverable.ContextData?.TryGetValue(PatchId, out var value) ?? false) - && value is uint patchId - && !_notPatchable.Contains(patchId)) - { - AddRemovedResultSetsToNotPatchable(deliverable, _notPatchable); - result[consumed++] = deliverable; - } - - // if the item is not patchable we will discard it and mark all dependant - // results as not patchable. - else - { - AddAllResultSetsToNotPatchable(deliverable, _notPatchable); - } - } - - if (consumed < result.Length) - { - Array.Resize(ref result, consumed); - } - - return new OperationResult(null, incremental: result, hasNext: hasNext); - } - } - - return null; - - static void AddRemovedResultSetsToNotPatchable( - IOperationResult result, - HashSet notPatchable) - { - if ((result.ContextData?.TryGetValue(RemovedResults, out var value) ?? false) - && value is IEnumerable patchIds) - { - foreach (var patchId in patchIds) - { - notPatchable.Add(patchId); - } - } - } - - static void AddAllResultSetsToNotPatchable( - IOperationResult result, - HashSet notPatchable) - { - if ((result.ContextData?.TryGetValue(ExpectedPatches, out var value) ?? false) - && value is IEnumerable patchIds) - { - foreach (var patchId in patchIds) - { - notPatchable.Add(patchId); - } - } - } - } - - public void Reset() - { - _semaphore.Dispose(); - _semaphore = new SemaphoreSlim(0); - _ready.Clear(); - _completed.Clear(); - _deliverable.Clear(); - _notPatchable.Clear(); - _taskId = 0; - _work = 0; - _patchId = 0; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs deleted file mode 100644 index 3e3d2a1c42c..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Fragment.cs +++ /dev/null @@ -1,52 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -internal sealed class Fragment : IFragment -{ - private readonly ulong _includeCondition; - private readonly ulong _deferIfCondition; - - public Fragment( - int id, - IObjectTypeDefinition typeCondition, - ISyntaxNode syntaxNode, - IReadOnlyList directives, - ISelectionSet selectionSet, - ulong includeCondition, - ulong deferIfCondition, - bool isInternal = false) - { - Id = id; - TypeCondition = typeCondition; - SyntaxNode = syntaxNode; - Directives = directives; - SelectionSet = selectionSet; - _includeCondition = includeCondition; - _deferIfCondition = deferIfCondition; - IsInternal = isInternal; - } - - public int Id { get; } - - public IObjectTypeDefinition TypeCondition { get; } - - public ISyntaxNode SyntaxNode { get; } - - public IReadOnlyList Directives { get; } - - public ISelectionSet SelectionSet { get; } - - public bool IsInternal { get; } - - public bool IsConditional => _includeCondition is not 0 || _deferIfCondition is not 0; - - public string? GetLabel(IVariableValueCollection variables) - => Directives.GetDeferDirective(variables)?.Label; - - public bool IsIncluded(ulong includeFlags, bool allowInternals = false) - => (includeFlags & _includeCondition) == _includeCondition - && (_deferIfCondition is 0 || (includeFlags & _deferIfCondition) != _deferIfCondition) - && (!IsInternal || allowInternals); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs deleted file mode 100644 index a2947ee1382..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs +++ /dev/null @@ -1,46 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deferred fragment. -/// -public interface IFragment : IOptionalSelection -{ - /// - /// Gets the internal fragment identifier. - /// - int Id { get; } - - /// - /// Gets the type condition. - /// - IObjectTypeDefinition TypeCondition { get; } - - /// - /// Gets the syntax node from the original GraphQL request document. - /// - ISyntaxNode SyntaxNode { get; } - - /// - /// Gets the collection of directives that are annotated to this fragment. - /// - IReadOnlyList Directives { get; } - - /// - /// Gets the selection set of this fragment. - /// - ISelectionSet SelectionSet { get; } - - /// - /// Gets the fragment label. - /// - /// - /// The variable values. - /// - /// - /// Returns the fragment label. - /// - string? GetLabel(IVariableValueCollection variables); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs deleted file mode 100644 index 29635565407..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs +++ /dev/null @@ -1,194 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a compiled GraphQL operation. -/// -public interface IOperation : IHasReadOnlyContextData, IEnumerable -{ - /// - /// Gets the internal unique identifier for this operation. - /// - string Id { get; } - - /// - /// Gets the parsed query document that contains the - /// operation-. - /// - DocumentNode Document { get; } - - /// - /// Gets the syntax node representing the operation definition. - /// - OperationDefinitionNode Definition { get; } - - /// - /// Gets the root type on which the operation is executed. - /// - ObjectType RootType { get; } - - /// - /// Gets the name of the operation. - /// - string? Name { get; } - - /// - /// Gets the operation type (Query, Mutation, Subscription). - /// - OperationType Type { get; } - - /// - /// Gets the prepared root selections for this operation. - /// - /// - /// Returns the prepared root selections for this operation. - /// - ISelectionSet RootSelectionSet { get; } - - /// - /// Gets all selection variants of this operation. - /// - IReadOnlyList SelectionVariants { get; } - - /// - /// Defines if this operation has deferred fragments or streams. - /// - bool HasIncrementalParts { get; } - - /// - /// Gets the schema for which this operation is compiled. - /// - ISchemaDefinition Schema { get; } - - /// - /// Gets the selection set for the specified and - /// . - /// - /// - /// The selection set for which the selection set shall be resolved. - /// - /// - /// The result type context. - /// - /// - /// Returns the selection set for the specified and - /// . - /// - /// - /// The specified has no selection set. - /// - ISelectionSet GetSelectionSet(ISelection selection, ObjectType typeContext); - - /// - /// Gets the possible return types for the . - /// - /// - /// The selection for which the possible result types shall be returned. - /// - /// - /// Returns the possible return types for the specified . - /// - /// - /// The specified has no selection set. - /// - IEnumerable GetPossibleTypes(ISelection selection); - - /// - /// Creates the include flags for the specified variable values. - /// - /// - /// The variable values. - /// - /// - /// Returns the include flags for the specified variable values. - /// - ulong CreateIncludeFlags(IVariableValueCollection variables); - - bool TryGetState(out TState? state); - - bool TryGetState(string key, out TState? state); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - Func createState); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The type of the context. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// The context that is passed to the factory. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - Func createState, - TContext context); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The key of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - string key, - Func createState); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The type of the context. - /// - /// - /// The key of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// The context that is passed to the factory. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - string key, - Func createState, - TContext context); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs deleted file mode 100644 index fb6839101d4..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace HotChocolate.Execution.Processing; - -/// -/// Represents selections with inclusion conditions. -/// -public interface IOptionalSelection -{ - /// - /// Defines that this selection is only needed for internal processing. - /// - bool IsInternal { get; } - - /// - /// Defines that this selection is conditional and will not always be included. - /// - bool IsConditional { get; } - - /// - /// Defines if this selection will be included into the request execution. - /// - /// - /// The execution include flags. - /// - /// - /// Allow internal selections to be included. - /// - /// - /// True, if this selection shall be included into the request execution. - /// - bool IsIncluded(ulong includeFlags, bool allowInternals = false); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs deleted file mode 100644 index 5eba9ad5385..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs +++ /dev/null @@ -1,113 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Resolvers; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a field selection during execution. -/// -public interface ISelection : IOptionalSelection -{ - /// - /// Gets an operation unique identifier of this selection. - /// - int Id { get; } - - /// - /// Gets the name this field will have in the response map. - /// - string ResponseName { get; } - - /// - /// Gets the UTF-8 encoded name this field will have in the response map. - /// - byte[] Utf8ResponseName { get; } - - /// - /// Gets the field that was selected. - /// - ObjectField Field { get; } - - /// - /// Gets the type of the selection. - /// - IType Type { get; } - - /// - /// Gets the type kind of the selection. - /// - TypeKind TypeKind { get; } - - /// - /// Specifies if the return type of this selection is a list type. - /// - bool IsList { get; } - - /// - /// Gets the type that declares the field that is selected by this selection. - /// - ObjectType DeclaringType { get; } - - /// - /// Gets the selectionSet that declares this selection. - /// - ISelectionSet DeclaringSelectionSet { get; } - - /// - /// Gets the operation that declares this selection. - /// - IOperation DeclaringOperation { get; } - - /// - /// Gets the merged field selection syntax node. - /// - FieldNode SyntaxNode { get; } - - /// - /// Gets the field selection syntax node. - /// - IReadOnlyList SyntaxNodes { get; } - - /// - /// If this selection selects a field that returns a composite type - /// then this selection set represents the fields that are selected - /// on that returning composite type. - /// - /// If this selection however selects a leaf field than this - /// selection set will be null. - /// - SelectionSetNode? SelectionSet { get; } - - /// - /// Gets the execution kind. - /// - SelectionExecutionStrategy Strategy { get; } - - /// - /// The compiled resolver pipeline for this selection. - /// - FieldDelegate? ResolverPipeline { get; } - - /// - /// The compiled pure resolver. - /// - PureFieldDelegate? PureResolver { get; } - - /// - /// The arguments that have been pre-coerced for this field selection. - /// - ArgumentMap Arguments { get; } - - /// - /// Defines if this selection is annotated with the stream directive. - /// - /// - /// The execution include flags that determine if the stream directive is applied for the - /// current execution run. - /// - /// - /// Returns if this selection is annotated with the stream directive. - /// - bool HasStreamDirective(ulong includeFlags); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs deleted file mode 100644 index 6ef00358cc4..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace HotChocolate.Execution.Processing; - -/// -/// A selection set is primarily composed of field selections. -/// When needed a selection set can preserve fragments so that the execution engine -/// can branch the processing of these fragments. -/// -public interface ISelectionSet -{ - /// - /// Gets an operation unique selection-set identifier of this selection. - /// - int Id { get; } - - /// - /// Defines if this list needs post-processing for skip and include. - /// - bool IsConditional { get; } - - /// - /// Gets the selections that shall be executed. - /// - IReadOnlyList Selections { get; } - - /// - /// Gets the deferred fragments if any were preserved for execution. - /// - IReadOnlyList Fragments { get; } - - /// - /// Gets the declaring operation. - /// - IOperation DeclaringOperation { get; } - - /// - /// Tries to resolve a selection by name. - /// - /// - /// The selection response name. - /// - /// - /// The resolved selection. - /// - /// - /// Returns true if the selection was successfully resolved. - /// - bool TryGetSelection(string responseName, [NotNullWhen(true)] out ISelection? selection); - - /// - /// Tries to resolve a selection by name. - /// - /// - /// The selection response name. - /// - /// - /// The resolved selection. - /// - /// - /// Returns true if the selection was successfully resolved. - /// - bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out ISelection? selection); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs deleted file mode 100644 index 87061dba0df..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Runtime.CompilerServices; -using HotChocolate.Language; -using HotChocolate.Types; -using HotChocolate.Utilities; - -namespace HotChocolate.Execution.Processing; - -/// -/// This struct represents the include condition of a Field, InlineFragment or FragmentSpread. -/// -public readonly struct IncludeCondition : IEquatable -{ - internal IncludeCondition(IValueNode skip, IValueNode include) - { - Skip = skip; - Include = include; - } - - /// - /// Gets the skip value. - /// - public IValueNode Skip { get; } - - /// - /// Gets the include value. - /// - public IValueNode Include { get; } - - /// - /// If and are null then - /// there is no valid include condition. - /// - public bool IsDefault => Skip is null && Include is null; - - /// - /// Specifies if selections with this include condition are included with the - /// current variable values. - /// - /// - /// The variable values. - /// - /// - /// Returns true if selections with this include condition are included. - /// - public bool IsIncluded(IVariableValueCollection variables) - { - ArgumentNullException.ThrowIfNull(variables); - - if (Skip is null || Include is null) - { - return true; - } - - var skip = false; - - if (Skip.Kind is SyntaxKind.BooleanValue) - { - skip = ((BooleanValueNode)Skip).Value; - } - else if (Skip.Kind is SyntaxKind.Variable) - { - var variable = Unsafe.As(Skip); - skip = variables.GetValue(variable.Name.Value).Value; - } - - var include = true; - - if (Include.Kind is SyntaxKind.BooleanValue) - { - include = ((BooleanValueNode)Include).Value; - } - else if (Include.Kind is SyntaxKind.Variable) - { - var variable = Unsafe.As(Include); - include = variables.GetValue(variable.Name.Value).Value; - } - - return !skip && include; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// if the current object is equal to the - /// parameter; otherwise, . - /// - public bool Equals(IncludeCondition other) - => Skip.Equals(other.Skip, SyntaxComparison.Syntax) - && Include.Equals(other.Include, SyntaxComparison.Syntax); - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// - /// The object to compare with the current instance. - /// - /// - /// if and this instance is the same - /// type and represents the same value; otherwise, . - /// - public override bool Equals(object? obj) - => obj is IncludeCondition other && Equals(other); - - /// - /// Returns the hash code for this instance. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - public override int GetHashCode() - => HashCode.Combine( - SyntaxComparer.BySyntax.GetHashCode(Skip), - SyntaxComparer.BySyntax.GetHashCode(Include)); - - /// - /// Tries to extract the include condition from a field. - /// - /// - /// The selection to extract the include condition from. - /// - /// - /// Returns true if the selection has a custom visibility configuration. - /// - public static IncludeCondition FromSelection(ISelectionNode selection) - { - ArgumentNullException.ThrowIfNull(selection); - - IValueNode? skip = null; - IValueNode? include = null; - - if (selection.Directives.Count == 0) - { - return default; - } - - for (var i = 0; i < selection.Directives.Count; i++) - { - var directive = selection.Directives[i]; - - if (directive.Arguments.Count != 1) - { - // the skip and include arguments have a single argument. - continue; - } - - if (directive.Name.Value.EqualsOrdinal(DirectiveNames.Skip.Name)) - { - skip = directive.Arguments[0].Value; - } - - if (directive.Name.Value.EqualsOrdinal(DirectiveNames.Include.Name)) - { - include = directive.Arguments[0].Value; - } - - if (skip is not null && include is not null) - { - break; - } - } - - if (skip is null && include is null) - { - return default; - } - - return new IncludeCondition( - skip ?? NullValueNode.Default, - include ?? NullValueNode.Default); - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index ed5b4c63bf0..72c8ed60f88 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -1,5 +1,8 @@ -using System.Collections; -using System.Collections.Immutable; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using HotChocolate.Features; using HotChocolate.Language; using HotChocolate.Types; using static HotChocolate.Execution.Properties.Resources; @@ -7,226 +10,252 @@ namespace HotChocolate.Execution.Processing; -internal sealed class Operation : IOperation +public sealed class Operation : IOperation { #if NET9_0_OR_GREATER - private readonly Lock _writeLock = new(); + private readonly Lock _sync = new(); #else - private readonly object _writeLock = new(); + private readonly object _sync = new(); #endif - private SelectionVariants[] _selectionVariants = []; - private IncludeCondition[] _includeConditions = []; - private ImmutableDictionary _contextData = -#if NET10_0_OR_GREATER - []; -#else - ImmutableDictionary.Empty; -#endif - private bool _sealed; - public Operation( + private readonly ConcurrentDictionary<(int, string), SelectionSet> _selectionSets = []; + private readonly OperationCompiler2 _compiler; + private readonly IncludeConditionCollection _includeConditions; + private readonly OperationFeatureCollection _features; + private readonly bool _isFinal; + private object[] _elementsById; + private int _lastId; + + internal Operation( string id, - DocumentNode document, + string hash, OperationDefinitionNode definition, ObjectType rootType, - ISchemaDefinition schema) + Schema schema, + SelectionSet rootSelectionSet, + OperationCompiler2 compiler, + IncludeConditionCollection includeConditions, + int lastId, + object[] elementsById, + bool isFinal) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentException.ThrowIfNullOrWhiteSpace(hash); + ArgumentNullException.ThrowIfNull(definition); + ArgumentNullException.ThrowIfNull(rootType); + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(rootSelectionSet); + ArgumentNullException.ThrowIfNull(compiler); + ArgumentNullException.ThrowIfNull(includeConditions); + ArgumentNullException.ThrowIfNull(elementsById); + Id = id; - Document = document; + Hash = hash; Definition = definition; RootType = rootType; - Type = definition.Operation; Schema = schema; - - if (definition.Name?.Value is { } name) - { - Name = name; - } + RootSelectionSet = rootSelectionSet; + _compiler = compiler; + _includeConditions = includeConditions; + _lastId = lastId; + _elementsById = elementsById; + _isFinal = isFinal; + + _features = new OperationFeatureCollection(); + rootSelectionSet.Complete(this, seal: isFinal); } + /// + /// Gets the internal unique identifier for this operation. + /// public string Id { get; } - public DocumentNode Document { get; } + /// + /// Gets the hash of the original operation document. + /// + public string Hash { get; } + /// + /// Gets the name of the operation. + /// + public string? Name => Definition.Name?.Value; + + /// + /// Gets the syntax node representing the operation definition. + /// public OperationDefinitionNode Definition { get; } + /// + /// Gets the root type on which the operation is executed. + /// public ObjectType RootType { get; } - public string? Name { get; } - - public OperationType Type { get; } - - public ISelectionSet RootSelectionSet { get; private set; } = null!; - - public IReadOnlyList SelectionVariants - => _selectionVariants; - - public bool HasIncrementalParts { get; private set; } - - public IReadOnlyList IncludeConditions - => _includeConditions; - - public IReadOnlyDictionary ContextData => _contextData; - - public ISchemaDefinition Schema { get; } - - public ISelectionSet GetSelectionSet(ISelection selection, ObjectType typeContext) + IObjectTypeDefinition IOperation.RootType => RootType; + + public OperationType Kind => Definition.Operation; + + /// + /// Gets the schema for which this operation is compiled. + /// + public Schema Schema { get; } + + ISchemaDefinition IOperation.Schema => Schema; + + /// + /// Gets the prepared root selections for this operation. + /// + /// + /// Returns the prepared root selections for this operation. + /// + public SelectionSet RootSelectionSet { get; } + + ISelectionSet IOperation.RootSelectionSet + => RootSelectionSet; + + /// + public IFeatureCollection Features => _features; + + /// + /// Gets the selection set for the specified + /// if the selections named return type is an object type. + /// + /// + /// The selection set for which the selection set shall be resolved. + /// + /// + /// Returns the selection set for the specified and + /// the named return type of the selection. + /// + /// + /// - The specified has no selection set. + /// - The specified returns an abstract named type. + /// + public SelectionSet GetSelectionSet(Selection selection) { ArgumentNullException.ThrowIfNull(selection); - ArgumentNullException.ThrowIfNull(typeContext); - - var selectionSetId = ((Selection)selection).SelectionSetId; - - if (selectionSetId is -1) - { - throw Operation_NoSelectionSet(); - } - - return _selectionVariants[selectionSetId].GetSelectionSet(typeContext); + var typeContext = selection.Field.Type.NamedType(); + return GetSelectionSet(selection, typeContext); } - public IEnumerable GetPossibleTypes(ISelection selection) + /// + /// Gets the selection set for the specified and + /// . + /// + /// + /// The selection set for which the selection set shall be resolved. + /// + /// + /// The result type context. + /// + /// + /// Returns the selection set for the specified and + /// . + /// + /// + /// The specified has no selection set. + /// + public SelectionSet GetSelectionSet(Selection selection, IObjectTypeDefinition typeContext) { ArgumentNullException.ThrowIfNull(selection); + ArgumentNullException.ThrowIfNull(typeContext); - var selectionSetId = ((Selection)selection).SelectionSetId; - - if (selectionSetId == -1) - { - throw new ArgumentException(Operation_GetPossibleTypes_NoSelectionSet); - } - - return _selectionVariants[selectionSetId].GetPossibleTypes(); - } - - public long CreateIncludeFlags(IVariableValueCollection variables) - { - long context = 0; - - for (var i = 0; i < _includeConditions.Length; i++) - { - if (_includeConditions[i].IsIncluded(variables)) - { - long flag = 1; - flag <<= i; - context |= flag; - } - } - - return context; - } - - public bool TryGetState(out TState? state) - { - var key = typeof(TState).FullName ?? throw new InvalidOperationException(); - return TryGetState(key, out state); - } - - public bool TryGetState(string key, out TState? state) - { - if (_contextData.TryGetValue(key, out var value) - && value is TState casted) + if (typeContext is not ObjectType objectType) { - state = casted; - return true; + throw new ArgumentException( + "typeContext is not an ObjectType object.", + nameof(typeContext)); } - state = default; - return false; - } - - public TState GetOrAddState(Func createState) - => GetOrAddState(_ => createState(), null); - - public TState GetOrAddState(Func createState, TContext context) - { - var key = typeof(TState).FullName ?? throw new InvalidOperationException(); + var key = (selection.Id, typeContext.Name); - // ReSharper disable once InconsistentlySynchronizedField - if (!_contextData.TryGetValue(key, out var state)) + if (!_selectionSets.TryGetValue(key, out var selectionSet)) { - lock (_writeLock) + lock (_sync) { - if (!_contextData.TryGetValue(key, out state)) + if (!_selectionSets.TryGetValue(key, out selectionSet)) { - var newState = createState(context); - _contextData = _contextData.SetItem(key, newState); - return newState; + selectionSet = + _compiler.CompileSelectionSet( + selection, + objectType, + _includeConditions, + ref _elementsById, + ref _lastId); + selectionSet.Complete(this, seal: _isFinal); + _selectionSets.TryAdd(key, selectionSet); } } } - return (TState)state!; + return selectionSet; } - public TState GetOrAddState( - string key, - Func createState) - => GetOrAddState(key, (k, _) => createState(k), null); - - public TState GetOrAddState( - string key, - Func createState, - TContext context) + ISelectionSet IOperation.GetSelectionSet(ISelection selection, IObjectTypeDefinition typeContext) { - // ReSharper disable once InconsistentlySynchronizedField - if (!_contextData.TryGetValue(key, out var state)) - { - lock (_writeLock) - { - if (!_contextData.TryGetValue(key, out state)) - { - var newState = createState(key, context); - _contextData = _contextData.SetItem(key, newState); - return newState; - } - } - } - - return (TState)state!; - } + ArgumentNullException.ThrowIfNull(selection); + ArgumentNullException.ThrowIfNull(typeContext); - internal void Seal( - IReadOnlyDictionary contextData, - SelectionVariants[] selectionVariants, - bool hasIncrementalParts, - IncludeCondition[] includeConditions) - { - if (!_sealed) + if (selection is not Selection internalSelection) { - _contextData = contextData.ToImmutableDictionary(); - var root = selectionVariants[0]; - RootSelectionSet = root.GetSelectionSet(RootType); - _selectionVariants = selectionVariants; - HasIncrementalParts = hasIncrementalParts; - _includeConditions = includeConditions; - _sealed = true; + throw new InvalidOperationException( + $"Only selections of the type {typeof(Selection).FullName} are supported."); } - } - - public SelectionSet GetSelectionSetById(int selectionSetId) - { + return GetSelectionSet(internalSelection, typeContext); } - public Selection GetSelectionById(int selectionSetId) + /// + /// Gets the possible return types for the . + /// + /// + /// The selection for which the possible result types shall be returned. + /// + /// + /// Returns the possible return types for the specified . + /// + /// + /// The specified has no selection set. + /// + public IEnumerable GetPossibleTypes(Selection selection) { + ArgumentNullException.ThrowIfNull(selection); + return Schema.GetPossibleTypes(selection.Field.Type.NamedType()); } - public IEnumerator GetEnumerator() + IEnumerable IOperation.GetPossibleTypes(ISelection selection) + => Schema.GetPossibleTypes(selection.Field.Type.NamedType()); + + /// + /// Creates the include flags for the specified variable values. + /// + /// + /// The variable values. + /// + /// + /// Returns the include flags for the specified variable values. + /// + public ulong CreateIncludeFlags(IVariableValueCollection variables) { - foreach (var selectionVariant in _selectionVariants) + var index = 0; + var includeFlags = 0ul; + + foreach (var includeCondition in _includeConditions) { - foreach (var objectType in selectionVariant.GetPossibleTypes()) + if (includeCondition.IsIncluded(variables)) { - yield return selectionVariant.GetSelectionSet(objectType); + includeFlags |= 1ul << index++; } } + + return includeFlags; } - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); + internal Selection GetSelectionById(int id) + => Unsafe.As(Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_elementsById), id)); + + internal SelectionSet GetSelectionSetById(int id) + => Unsafe.As(Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_elementsById), id)); public override string ToString() => OperationPrinter.Print(this); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs new file mode 100644 index 00000000000..8f6068bbf80 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs @@ -0,0 +1,122 @@ +using System.Buffers; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Fusion.Rewriters; +using HotChocolate.Language; +using HotChocolate.Language.Visitors; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.ObjectPool; + +namespace HotChocolate.Execution.Processing; + +internal sealed partial class OperationCompiler2 +{ + private ArgumentMap? CoerceArgumentValues( + ObjectField field, + FieldNode selection) + { + if (field.Arguments.Count == 0) + { + return null; + } + + var arguments = new Dictionary(StringComparer.Ordinal); + + for (var i = 0; i < selection.Arguments.Count; i++) + { + var argumentValue = selection.Arguments[i]; + if (field.Arguments.TryGetField( + argumentValue.Name.Value, + out var argument)) + { + arguments[argument.Name] = CreateArgumentValue(argument, argumentValue, argumentValue.Value, false); + } + } + + for (var i = 0; i < field.Arguments.Count; i++) + { + var argument = field.Arguments[i]; + if (!arguments.ContainsKey(argument.Name)) + { + var value = argument.DefaultValue ?? NullValueNode.Default; + arguments[argument.Name] = CreateArgumentValue(argument, null, value, true); + } + } + + return new ArgumentMap(arguments); + } + + private ArgumentValue CreateArgumentValue( + Argument argument, + ArgumentNode? argumentValue, + IValueNode value, + bool isDefaultValue) + { + var validationResult = + ArgumentNonNullValidator.Validate( + argument, + value, + Path.Root.Append(argument.Name)); + + if (argumentValue is not null && validationResult.HasErrors) + { + return new ArgumentValue( + argument, + ErrorHelper.ArgumentNonNullError( + argumentValue, + validationResult)); + } + + if (argument.Type.IsLeafType() && CanBeCompiled(value)) + { + try + { + return new ArgumentValue( + argument, + value.GetValueKind(), + true, + isDefaultValue, + _inputValueParser.ParseLiteral(value, argument), + value); + } + catch (SerializationException ex) + { + return new ArgumentValue( + argument, + ErrorHelper.ArgumentValueIsInvalid(argumentValue, ex)); + } + } + + return new ArgumentValue( + argument, + value.GetValueKind(), + false, + isDefaultValue, + null, + value); + } + + private static bool CanBeCompiled(IValueNode valueLiteral) + { + switch (valueLiteral.Kind) + { + case SyntaxKind.Variable: + case SyntaxKind.ObjectValue: + return false; + + case SyntaxKind.ListValue: + var list = (ListValueNode)valueLiteral; + for (var i = 0; i < list.Items.Count; i++) + { + if (!CanBeCompiled(list.Items[i])) + { + return false; + } + } + break; + } + + return true; + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 174bc3ca348..0984e29625a 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -1,787 +1,633 @@ -using System.Collections.Immutable; -using System.Runtime.CompilerServices; +using System.Buffers; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Fusion.Rewriters; using HotChocolate.Language; -using HotChocolate.Resolvers; +using HotChocolate.Language.Visitors; using HotChocolate.Types; -using HotChocolate.Utilities; -using static System.Runtime.InteropServices.CollectionsMarshal; -using static System.Runtime.InteropServices.MemoryMarshal; -using static System.StringComparer; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; +using Microsoft.Extensions.ObjectPool; namespace HotChocolate.Execution.Processing; -/// -/// The operation compiler will analyze a specific operation of a GraphQL request document -/// and create from it an optimized executable operation tree. -/// -public sealed partial class OperationCompiler +internal sealed partial class OperationCompiler2 { - private readonly InputParser _parser; - private readonly CreateFieldPipeline _createFieldPipeline; - private readonly Queue _backlog = []; - private readonly Dictionary _selectionLookup = []; - private readonly Dictionary _selectionSetIdLookup = []; - private readonly Dictionary _selectionVariants = []; - private readonly Dictionary _fragmentDefinitions = []; - private readonly Dictionary _contextData = []; - private readonly List _selections = []; - private readonly HashSet _directiveNames = new(Ordinal); - private readonly List _pipelineComponents = []; - private readonly HashSet _enqueuedSelectionSets = []; - private IncludeCondition[] _includeConditions = []; - private CompilerContext? _deferContext; - private ImmutableArray _operationOptimizers = []; - private int _nextSelectionId; - private int _nextSelectionSetRefId; - private int _nextSelectionSetId; - private int _nextFragmentId; - private bool _hasIncrementalParts; - private OperationCompilerMetrics _metrics; - - public OperationCompiler(InputParser parser) + private readonly Schema _schema; + private readonly ObjectPool>> _fieldsPool; + private readonly DocumentRewriter _documentRewriter; + private readonly InputParser _inputValueParser; + private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; + + public OperationCompiler2( + Schema schema, + InputParser inputValueParser, + ObjectPool>> fieldsPool) { - _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - _createFieldPipeline = - (schema, field, selection) - => CreateFieldPipeline( - schema, - field, - selection, - null, - _directiveNames, - _pipelineComponents); - } + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(fieldsPool); - internal OperationCompilerMetrics Metrics => _metrics; + _schema = schema; + _inputValueParser = inputValueParser; + _fieldsPool = fieldsPool; + _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); + } - public IOperation Compile(OperationCompilerRequest request) + public Operation Compile(string id, string hash, OperationDefinitionNode operationDefinition) { - ArgumentException.ThrowIfNullOrEmpty(request.Id, nameof(request)); + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentNullException.ThrowIfNull(operationDefinition); - try - { - var backlogMaxSize = 0; - var selectionSetOptimizers = request.SelectionSetOptimizers; - _operationOptimizers = request.OperationOptimizers; - - // collect root fields - var rootPath = SelectionPath.Root; - var id = GetOrCreateSelectionSetRefId(request.Definition.SelectionSet, request.RootType.Name, rootPath); - var variants = GetOrCreateSelectionVariants(id); - SelectionSetInfo[] infos = [new(request.Definition.SelectionSet, 0)]; - - var context = new CompilerContext((Schema)request.Schema, request.Document); - context.Initialize(request.RootType, variants, infos, rootPath, selectionSetOptimizers); - CompileSelectionSet(context); - - // process consecutive selections - while (_backlog.Count > 0) - { - backlogMaxSize = Math.Max(backlogMaxSize, _backlog.Count); + var document = new DocumentNode(new IDefinitionNode[] { operationDefinition }); + document = _documentRewriter.RewriteDocument(document); + operationDefinition = (OperationDefinitionNode)document.Definitions[0]; - var current = _backlog.Dequeue(); - var type = current.Type; - variants = GetOrCreateSelectionVariants(current.SelectionSetId); + var includeConditions = new IncludeConditionCollection(); + IncludeConditionVisitor.Instance.Visit(operationDefinition, includeConditions); + var fields = _fieldsPool.Get(); - if (!variants.ContainsSelectionSet(type)) - { - infos = _selectionLookup[current.Selection]; - context.Initialize(type, variants, infos, current.Path, current.Optimizers); - CompileSelectionSet(context); - } - } + var compilationContext = new CompilationContext(s_objectArrayPool.Rent(128)); - // create operation - var operation = CreateOperation(request); - - _metrics = new OperationCompilerMetrics( - _nextSelectionId, - _selectionVariants.Count, - backlogMaxSize); + try + { + var lastId = 0; + const ulong parentIncludeFlags = 0ul; + var rootType = _schema.GetOperationType(operationDefinition.Operation); - return operation; + CollectFields( + parentIncludeFlags, + operationDefinition.SelectionSet.Selections, + rootType, + fields, + includeConditions); + + var selectionSet = BuildSelectionSet( + fields, + rootType, + compilationContext, + ref lastId); + + compilationContext.Register(selectionSet, selectionSet.Id); + + return new Operation( + id, + hash, + operationDefinition, + rootType, + _schema, + selectionSet, + this, + includeConditions, + lastId, + compilationContext.ElementsById, + isFinal: true); // todo : add the interceptors back } finally { - _nextSelectionId = 0; - _nextSelectionSetRefId = 0; - _nextSelectionId = 0; - _nextFragmentId = 0; - _hasIncrementalParts = false; - - _backlog.Clear(); - _selectionLookup.Clear(); - _selectionSetIdLookup.Clear(); - _selectionVariants.Clear(); - _fragmentDefinitions.Clear(); - _contextData.Clear(); - _selections.Clear(); - _directiveNames.Clear(); - _pipelineComponents.Clear(); - _enqueuedSelectionSets.Clear(); - - _operationOptimizers = []; - - _includeConditions = []; - _deferContext = null; + _fieldsPool.Return(fields); } } - private Operation CreateOperation(OperationCompilerRequest request) + internal SelectionSet CompileSelectionSet( + Selection selection, + ObjectType objectType, + IncludeConditionCollection includeConditions, + ref object[] elementsById, + ref int lastId) { - var operation = new Operation( - request.Id, - request.Document, - request.Definition, - request.RootType, - request.Schema); - - var schema = Unsafe.As(request.Schema); + var compilationContext = new CompilationContext(elementsById); + var fields = _fieldsPool.Get(); + fields.Clear(); - var variants = new SelectionVariants[_selectionVariants.Count]; - - if (_operationOptimizers.Length == 0) + try { - CompleteResolvers(schema); + var nodes = selection.SyntaxNodes; + var first = nodes[0]; - // if we do not have any optimizers, we will copy - // the variants and seal them in one go. - foreach (var item in _selectionVariants) + CollectFields( + first.PathIncludeFlags, + first.Node.SelectionSet!.Selections, + objectType, + fields, + includeConditions); + + if (nodes.Length > 1) { - variants[item.Key] = item.Value; - item.Value.Seal(operation); + for (var i = 1; i < nodes.Length; i++) + { + var node = nodes[i]; + + CollectFields( + node.PathIncludeFlags, + node.Node.SelectionSet!.Selections, + objectType, + fields, + includeConditions); + } } + + var selectionSet = BuildSelectionSet(fields, objectType, compilationContext, ref lastId); + compilationContext.Register(selectionSet, selectionSet.Id); + elementsById = compilationContext.ElementsById; + return selectionSet; } - else + finally { - // if we have optimizers, we will first copy the variants to its array, - // after that we will run the optimizers and give them a chance to do some - // more mutations on the compiled selection variants. - // after we have executed all optimizers, we will seal the selection variants. - var context = new OperationOptimizerContext( - request.Id, - request.Document, - request.Definition, - schema, - request.RootType, - variants, - _includeConditions, - _contextData, - _hasIncrementalParts, - _createFieldPipeline); - - foreach (var item in _selectionVariants) - { - variants[item.Key] = item.Value; - } - - // we will complete the selection variants, sets and selections - // without sealing them so that analyzers in this step can fully - // inspect them. - var variantsSpan = variants.AsSpan(); - ref var variantsStart = ref GetReference(variantsSpan); - ref var variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length); - - while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) - { - variantsStart.Complete(operation); - variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; - } + _fieldsPool.Return(fields); + } + } - var optSpan = _operationOptimizers.AsSpan(); - ref var optStart = ref GetReference(optSpan); - ref var optEnd = ref Unsafe.Add(ref optStart, optSpan.Length); + private void CollectFields( + ulong parentIncludeFlags, + IReadOnlyList selections, + IObjectTypeDefinition typeContext, + OrderedDictionary> fields, + IncludeConditionCollection includeConditions) + { + for (var i = 0; i < selections.Count; i++) + { + var selection = selections[i]; - while (Unsafe.IsAddressLessThan(ref optStart, ref optEnd)) + if (selection is FieldNode fieldNode) { - optStart.OptimizeOperation(context); - optStart = ref Unsafe.Add(ref optStart, 1)!; - } + var responseName = fieldNode.Alias?.Value ?? fieldNode.Name.Value; + var pathIncludeFlags = parentIncludeFlags; - CompleteResolvers(schema); + if (!fields.TryGetValue(responseName, out var nodes)) + { + nodes = []; + fields.Add(responseName, nodes); + } - variantsSpan = variants.AsSpan(); - variantsStart = ref GetReference(variantsSpan)!; - variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length)!; + if (IncludeCondition.TryCreate(fieldNode, out var includeCondition)) + { + var index = includeConditions.IndexOf(includeCondition); + pathIncludeFlags |= 1ul << index; + } - while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) - { - variantsStart.Seal(operation); - variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; + nodes.Add(new FieldSelectionNode(fieldNode, pathIncludeFlags)); } - } - - operation.Seal(_contextData, variants, _hasIncrementalParts, _includeConditions); - return operation; - } - private void CompleteResolvers(Schema schema) - { - ref var searchSpace = ref GetReference(AsSpan(_selections)); - - Path? path = null; - for (var i = 0; i < _selections.Count; i++) - { - var selection = Unsafe.Add(ref searchSpace, i); - path = path?.Append(selection.ResponseName); - if (selection.ResolverPipeline is null && selection.PureResolver is null) + if (selection is InlineFragmentNode inlineFragmentNode + && DoesTypeApply(inlineFragmentNode.TypeCondition, typeContext)) { - var field = selection.Field; - var syntaxNode = selection.SyntaxNode; - if (syntaxNode.Directives.Count > 0 && path == null) + var pathIncludeFlags = parentIncludeFlags; + + if (IncludeCondition.TryCreate(inlineFragmentNode, out var includeCondition)) { - // create the path only on demand - path = PathHelper.CreatePathFromSelection(_selections, i + 1); + var index = includeConditions.IndexOf(includeCondition); + pathIncludeFlags |= 1ul << index; } - var resolver = CreateFieldPipeline( - schema, - field, - syntaxNode, - path, - _directiveNames, - _pipelineComponents); - var pureResolver = TryCreatePureField(schema, field, syntaxNode); - selection.SetResolvers(resolver, pureResolver); + CollectFields( + pathIncludeFlags, + inlineFragmentNode.SelectionSet.Selections, + typeContext, + fields, + includeConditions); } } } - private void CompileSelectionSet(CompilerContext context) - { - // We first collect the fields that we find in the selection set ... - CollectFields(context); - - // next we will call the selection set optimizers to rewrite the - // selection set if necessary. - OptimizeSelectionSet(context); - - // after that we start completing the selections and build the SelectionSet from - // the completed selections. - CompleteSelectionSet(context); - } - - private void CompleteSelectionSet(CompilerContext context) + private SelectionSet BuildSelectionSet( + OrderedDictionary> fieldMap, + ObjectType typeContext, + CompilationContext compilationContext, + ref int lastId) { - var selections = new Selection[context.Fields.Values.Count]; - var fragments = context.Fragments.Count is not 0 - ? new Fragment[context.Fragments.Count] - : []; - var selectionIndex = 0; + var i = 0; + var selections = new Selection[fieldMap.Count]; var isConditional = false; + var includeFlags = new List(); + var selectionSetId = ++lastId; - foreach (var selection in context.Fields.Values) + foreach (var (responseName, nodes) in fieldMap) { - // if the field of the selection returns a composite type, we will traverse - // the child selection-sets as well. - var fieldType = selection.Type.NamedType(); - var selectionSetId = -1; + includeFlags.Clear(); - if (selection.IsConditional) + var first = nodes[0]; + var isInternal = IsInternal(first.Node); + + if (first.PathIncludeFlags > 0) { - isConditional = true; + includeFlags.Add(first.PathIncludeFlags); } - // Determines if the type is a composite type. - if (fieldType.IsCompositeType()) + if (nodes.Count > 1) { - if (selection.SelectionSet is null) + for (var j = 1; j < nodes.Count; j++) { - // composite fields always have to have a selection-set - // otherwise we need to throw. - throw QueryCompiler_CompositeTypeSelectionSet(selection.SyntaxNode); - } - - var selectionPath = context.Path.Append(selection.ResponseName); - selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, fieldType.Name, selectionPath); - var possibleTypes = context.Schema.GetPossibleTypes(fieldType); + var next = nodes[j]; - if (_enqueuedSelectionSets.Add(selectionSetId)) - { - for (var i = possibleTypes.Count - 1; i >= 0; i--) + if (!first.Node.Name.Value.Equals(next.Node.Name.Value, StringComparison.Ordinal)) { - _backlog.Enqueue( - new BacklogItem( - possibleTypes[i], - selectionSetId, - selection, - selectionPath, - ResolveOptimizers(context.Optimizers, selection.Field))); + throw new InvalidOperationException( + $"The syntax nodes for the response name {responseName} are not all the same."); } - } - // We are waiting for the latest stream and defer spec discussions to be codified - // before we change the overall stream handling. - // - // For now, we only allow streams on lists of composite types. - if (selection.SyntaxNode.IsStreamable()) - { - var streamDirective = selection.SyntaxNode.GetStreamDirective(); - var nullValue = NullValueNode.Default; - var ifValue = streamDirective?.GetArgumentValue(DirectiveNames.Stream.Arguments.If) ?? nullValue; - long ifConditionFlags = 0; - - if (ifValue.Kind is not SyntaxKind.NullValue) + if (next.PathIncludeFlags > 0) { - var ifCondition = new IncludeCondition(ifValue, nullValue); - ifConditionFlags = GetSelectionIncludeCondition(ifCondition, 0); + includeFlags.Add(next.PathIncludeFlags); } - selection.MarkAsStream(ifConditionFlags); - _hasIncrementalParts = true; + if (isInternal) + { + isInternal = IsInternal(next.Node); + } } } - selection.SetSelectionSetId(selectionSetId); - selections[selectionIndex++] = selection; - _selections.Add(selection); - } - - if (context.Fragments.Count > 0) - { - for (var i = 0; i < context.Fragments.Count; i++) + if (includeFlags.Count > 1) { - fragments[i] = context.Fragments[i]; + CollapseIncludeFlags(includeFlags); } - } - context.SelectionVariants.AddSelectionSet( - _nextSelectionSetId++, - context.Type, - selections, - fragments, - isConditional); - } + var field = typeContext.Fields[first.Node.Name.Value]; - private void CollectFields(CompilerContext context) - { - foreach (var selectionSetInfo in context.SelectionInfos) - { - CollectFields( - context, - selectionSetInfo.SelectionSet, - selectionSetInfo.IncludeCondition); - } - } + var selection = new Selection( + ++lastId, + responseName, + field, + nodes.ToArray(), + includeFlags.ToArray(), + isInternal); - private void CollectFields( - CompilerContext context, - SelectionSetNode selectionSet, - ulong includeCondition) - { - for (var j = 0; j < selectionSet.Selections.Count; j++) - { - ResolveFields(context, selectionSet.Selections[j], includeCondition); - } - } + // Register the selection in the elements array + compilationContext.Register(selection, selection.Id); + selections[i++] = selection; - private void ResolveFields( - CompilerContext context, - ISelectionNode selection, - long includeCondition) - { - switch (selection.Kind) - { - case SyntaxKind.Field: - ResolveField( - context, - (FieldNode)selection, - includeCondition); - break; - - case SyntaxKind.InlineFragment: - ResolveInlineFragment( - context, - (InlineFragmentNode)selection, - includeCondition); - break; - - case SyntaxKind.FragmentSpread: - ResolveFragmentSpread( - context, - (FragmentSpreadNode)selection, - includeCondition); - break; + if (includeFlags.Count > 1) + { + isConditional = true; + } } + + return new SelectionSet(selectionSetId, typeContext, selections, isConditional); } - private void ResolveField( - CompilerContext context, - FieldNode selection, - ulong includeCondition) + private static void CollapseIncludeFlags(List includeFlags) { - includeCondition = GetSelectionIncludeCondition(selection, includeCondition); + // we sort the include flags to improve early elimination and stability + includeFlags.Sort(); - var fieldName = selection.Name.Value; - var responseName = selection.Alias?.Value ?? fieldName; + var write = 0; - if (context.Type.Fields.TryGetField(fieldName, out var field)) + for (var read = 0; read < includeFlags.Count; read++) { - var fieldType = field.Type; + var candidate = includeFlags[read]; + var covered = false; - if (context.Fields.TryGetValue(responseName, out var preparedSelection)) + // we check if the candidate is already covered + for (var i = 0; i < write; i++) { - preparedSelection.AddSelection(selection, includeCondition); - - if (selection.SelectionSet is not null) + if ((candidate & includeFlags[i]) == includeFlags[i]) { - var selectionSetInfo = new SelectionSetInfo( - selection.SelectionSet!, - includeCondition); - var selectionInfos = _selectionLookup[preparedSelection]; - var next = selectionInfos.Length; - Array.Resize(ref selectionInfos, next + 1); - selectionInfos[next] = selectionSetInfo; - _selectionLookup[preparedSelection] = selectionInfos; + covered = true; + break; } } - else + + if (!covered) { - var id = GetNextSelectionId(); - - // if this is the first time we've found a selection to this field, we have to - // create a new prepared selection. - preparedSelection = new Selection.Sealed( - id, - context.Type, - field, - fieldType, - selection.SelectionSet is not null - ? selection.WithSelectionSet( - selection.SelectionSet.WithSelections( - selection.SelectionSet.Selections)) - : selection, - responseName: responseName, - arguments: CoerceArgumentValues(field, selection), - includeConditions: includeCondition == 0 - ? null - : [includeCondition], - isParallelExecutable: field.IsParallelExecutable); - - context.Fields.Add(responseName, preparedSelection); - - if (selection.SelectionSet is not null) + // lastly we remove more restrictive flags from the already written range + for (var i = 0; i < write;) + { + if ((includeFlags[i] & candidate) == candidate) + { + includeFlags[i] = includeFlags[--write]; + } + else + { + i++; + } + } + + if (write < read) { - var selectionSetInfo = new SelectionSetInfo( - selection.SelectionSet!, - includeCondition); - _selectionLookup.Add(preparedSelection, [selectionSetInfo]); + includeFlags[write] = candidate; } + write++; } } - else + + // we trim the list to the collapsed set + if (write < includeFlags.Count) { - throw FieldDoesNotExistOnType(selection, context.Type.Name); + includeFlags.RemoveRange(write, includeFlags.Count - write); } } - private void ResolveInlineFragment( - CompilerContext context, - InlineFragmentNode inlineFragment, - long includeCondition) + private bool DoesTypeApply(NamedTypeNode? typeCondition, IObjectTypeDefinition typeContext) { - ResolveFragment( - context, - inlineFragment, - inlineFragment.TypeCondition, - inlineFragment.SelectionSet, - inlineFragment.Directives, - includeCondition); - } + if (typeCondition is null) + { + return true; + } - private void ResolveFragmentSpread( - CompilerContext context, - FragmentSpreadNode fragmentSpread, - long includeCondition) - { - var fragmentDef = GetFragmentDefinition(context, fragmentSpread); - - ResolveFragment( - context, - fragmentSpread, - fragmentDef.TypeCondition, - fragmentDef.SelectionSet, - fragmentSpread.Directives, - includeCondition); + if (typeCondition.Name.Value.Equals(typeContext.Name, StringComparison.Ordinal)) + { + return true; + } + + if (_schema.Types.TryGetType(typeCondition.Name.Value, out var type)) + { + return type.IsAssignableFrom(typeContext); + } + + return false; } - private void ResolveFragment( - CompilerContext context, - ISelectionNode selection, - NamedTypeNode? typeCondition, - SelectionSetNode selectionSet, - IReadOnlyList directives, - ulong includeCondition) + private static bool IsInternal(FieldNode fieldNode) { - if (typeCondition is null - || (context.Schema.Types.TryGetType(typeCondition, out IType? typeCon) - && DoesTypeApply(typeCon, context.Type))) - { - includeCondition = GetSelectionIncludeCondition(selection, includeCondition); + const string isInternal = "fusion__requirement"; + var directives = fieldNode.Directives; - if (directives.IsDeferrable()) - { - var deferDirective = directives.GetDeferDirectiveNode(); - var nullValue = NullValueNode.Default; - var ifValue = deferDirective?.GetArgumentValue(DirectiveNames.Defer.Arguments.If) ?? nullValue; + if (directives.Count == 0) + { + return false; + } - ulong ifConditionFlags = 0; + if (directives.Count == 1) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } - if (ifValue.Kind is not SyntaxKind.NullValue) - { - var ifCondition = new IncludeCondition(ifValue, nullValue); - ifConditionFlags = GetSelectionIncludeCondition(ifCondition, includeCondition); - } + if (directives.Count == 2) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[1].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } - var typeName = typeCondition?.Name.Value ?? context.Type.Name; - var id = GetOrCreateSelectionSetRefId(selectionSet, typeName, context.Path); - var variants = GetOrCreateSelectionVariants(id); - var infos = new SelectionSetInfo[] { new(selectionSet, includeCondition) }; + if (directives.Count == 3) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[1].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[2].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } - if (!variants.ContainsSelectionSet(context.Type)) - { - var deferContext = RentContext(context); - deferContext.Initialize(context.Type, variants, infos, context.Path); - CompileSelectionSet(deferContext); - ReturnContext(deferContext); - } + for (var i = 0; i < directives.Count; i++) + { + var directive = directives[i]; - var fragment = new Fragment( - GetNextFragmentId(), - context.Type, - selection, - directives, - variants.GetSelectionSet(context.Type), - includeCondition, - ifConditionFlags); - - context.Fragments.Add(fragment); - _hasIncrementalParts = true; - - // if we have if-condition flags, there will be a runtime validation if something - // shall be deferred, so we need to prepare for both cases. - // - // this means that we will collect the fields with our if condition flags as - // if the fragment was not deferred. - if (ifConditionFlags is not 0) - { - CollectFields(context, selectionSet, ifConditionFlags); - } - } - else + if (directive.Name.Value.Equals(isInternal, StringComparison.Ordinal)) { - CollectFields(context, selectionSet, includeCondition); + return true; } } + + return false; } - private static bool DoesTypeApply(IType typeCondition, IObjectTypeDefinition current) - => typeCondition.Kind switch - { - TypeKind.Object => ReferenceEquals(typeCondition, current), - TypeKind.Interface => current.IsImplementing((InterfaceType)typeCondition), - TypeKind.Union => ((UnionType)typeCondition).Types.ContainsName(current.Name), - _ => false - }; - - private FragmentDefinitionNode GetFragmentDefinition( - CompilerContext context, - FragmentSpreadNode fragmentSpread) + private class IncludeConditionVisitor : SyntaxWalker { - var fragmentName = fragmentSpread.Name.Value; + public static readonly IncludeConditionVisitor Instance = new(); - if (!_fragmentDefinitions.TryGetValue(fragmentName, out var value)) + protected override ISyntaxVisitorAction Enter( + FieldNode node, + IncludeConditionCollection context) { - var document = context.Document; - - for (var i = 0; i < document.Definitions.Count; i++) + if (IncludeCondition.TryCreate(node, out var condition)) { - if (document.Definitions[i] is FragmentDefinitionNode fragmentDefinition - && fragmentDefinition.Name.Value.EqualsOrdinal(fragmentName)) - { - value = fragmentDefinition; - _fragmentDefinitions.Add(fragmentName, value); - goto EXIT; - } + context.Add(condition); } - throw new InvalidOperationException( - string.Format( - OperationCompiler_FragmentNotFound, - fragmentName)); + return base.Enter(node, context); } -EXIT: - return value; - } - - internal int GetNextSelectionId() => _nextSelectionId++; + protected override ISyntaxVisitorAction Enter( + InlineFragmentNode node, + IncludeConditionCollection context) + { + if (IncludeCondition.TryCreate(node, out var condition)) + { + context.Add(condition); + } - private int GetNextFragmentId() => _nextFragmentId++; + return base.Enter(node, context); + } + } - private int GetOrCreateSelectionSetRefId( - SelectionSetNode selectionSet, - string selectionSetTypeName, - SelectionPath path) + private class CompilationContext(object[] elementsById) { - var selectionSetRef = new SelectionSetRef(selectionSet, selectionSetTypeName, path); + private object[] _elementsById = elementsById; - if (!_selectionSetIdLookup.TryGetValue(selectionSetRef, out var selectionSetId)) + public object[] ElementsById => _elementsById; + + public void Register(object element, int id) { - selectionSetId = _nextSelectionSetRefId++; - _selectionSetIdLookup.Add(selectionSetRef, selectionSetId); - } + if (id >= _elementsById.Length) + { + var newArray = s_objectArrayPool.Rent(_elementsById.Length * 2); + _elementsById.AsSpan().CopyTo(newArray); + s_objectArrayPool.Return(_elementsById); + _elementsById = newArray; + } - return selectionSetId; + _elementsById[id] = element; + } } +} + +/// +/// Represents a field selection node with its path include flags. +/// +/// +/// The syntax node that represents the field selection. +/// +/// +/// The flags that must be all set for this selection to be included. +/// +internal sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); + +internal class IncludeConditionCollection : ICollection +{ + private readonly OrderedDictionary _dictionary = []; + + public IncludeCondition this[int index] + => _dictionary.GetAt(index).Key; + + public int Count => _dictionary.Count; + + public bool IsReadOnly => false; - private SelectionVariants GetOrCreateSelectionVariants(int selectionSetId) + public bool Add(IncludeCondition item) { - if (!_selectionVariants.TryGetValue(selectionSetId, out var variants)) + if (_dictionary.Count == 64) { - variants = new SelectionVariants(selectionSetId); - _selectionVariants.Add(selectionSetId, variants); + throw new InvalidOperationException( + "The maximum number of include conditions has been reached."); } - return variants; + return _dictionary.TryAdd(item, _dictionary.Count); } - private ulong GetSelectionIncludeCondition( - ISelectionNode selectionSyntax, - ulong parentIncludeCondition) + void ICollection.Add(IncludeCondition item) + => Add(item); + + public bool Remove(IncludeCondition item) + => throw new InvalidOperationException("This is an add only collection."); + + void ICollection.Clear() + => throw new InvalidOperationException("This is an add only collection."); + + public bool Contains(IncludeCondition item) + => _dictionary.ContainsKey(item); + + public int IndexOf(IncludeCondition item) + => _dictionary.GetValueOrDefault(item, -1); + + public void CopyTo(IncludeCondition[] array, int arrayIndex) + => _dictionary.Keys.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() + => _dictionary.Keys.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); +} + +internal readonly struct IncludeCondition : IEquatable +{ + private readonly string? _skip; + private readonly string? _include; + + public IncludeCondition(string? skip, string? include) { - var condition = IncludeCondition.FromSelection(selectionSyntax); + _skip = skip; + _include = include; + } - if (condition.IsDefault) - { - return parentIncludeCondition; - } + public string? Skip => _skip; - var pos = Array.IndexOf(_includeConditions, condition); + public string? Include => _include; - if (pos == -1) + public bool IsIncluded(IVariableValueCollection variableValues) + { + if (_skip is not null) { - pos = _includeConditions.Length; - - if (pos == 64) + if (!variableValues.TryGetValue(_skip, out var value)) { - throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); + throw new InvalidOperationException($"The variable {_skip} has an invalid value."); } - if (_includeConditions.Length == 0) + if (value.Value) { - _includeConditions = new IncludeCondition[1]; + return false; } - else + } + + if (_include is not null) + { + if (!variableValues.TryGetValue(_include, out var value)) { - Array.Resize(ref _includeConditions, pos + 1); + throw new InvalidOperationException($"The variable {_include} has an invalid value."); } - _includeConditions[pos] = condition; + if (!value.Value) + { + return false; + } } - long selectionIncludeCondition = 1; - selectionIncludeCondition <<= pos; + return true; + } - if (parentIncludeCondition == 0) - { - return selectionIncludeCondition; - } + public bool Equals(IncludeCondition other) + => string.Equals(Skip, other.Skip, StringComparison.Ordinal) + && string.Equals(Include, other.Include, StringComparison.Ordinal); - parentIncludeCondition |= selectionIncludeCondition; - return parentIncludeCondition; - } + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is IncludeCondition other && Equals(other); + + public override int GetHashCode() + => HashCode.Combine(Skip, Include); + + public static bool TryCreate(FieldNode field, out IncludeCondition includeCondition) + => TryCreate(field.Directives, out includeCondition); + + public static bool TryCreate(InlineFragmentNode inlineFragment, out IncludeCondition includeCondition) + => TryCreate(inlineFragment.Directives, out includeCondition); - private long GetSelectionIncludeCondition( - IncludeCondition condition, - ulong parentIncludeCondition) + private static bool TryCreate(IReadOnlyList directives, out IncludeCondition includeCondition) { - var pos = Array.IndexOf(_includeConditions, condition); + string? skip = null; + string? include = null; - if (pos == -1) + if (directives.Count == 0) { - pos = _includeConditions.Length; + includeCondition = default; + return false; + } - if (pos == 64) + if (directives.Count == 1) + { + TryParseDirective(directives[0], ref skip, ref include); + if (TryCreateIncludeCondition(out includeCondition)) { - throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); + return true; } + } - if (_includeConditions.Length == 0) - { - _includeConditions = new IncludeCondition[1]; - } - else + if (directives.Count == 2) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); + } + + if (directives.Count == 3) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + + if (skip is not null && include is not null) { - Array.Resize(ref _includeConditions, pos + 1); + includeCondition = new IncludeCondition(skip, include); + return true; } - _includeConditions[pos] = condition; + TryParseDirective(directives[2], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); } - long selectionIncludeCondition = 1; - selectionIncludeCondition <<= pos; - - if (parentIncludeCondition == 0) + for (var i = 0; i < directives.Count; i++) { - return selectionIncludeCondition; + TryParseDirective(directives[i], ref skip, ref include); + + if (skip is not null && include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } } - parentIncludeCondition |= selectionIncludeCondition; - return parentIncludeCondition; - } + includeCondition = default; + return false; - private CompilerContext RentContext(CompilerContext context) - { - if (_deferContext is null) + bool TryCreateIncludeCondition(out IncludeCondition includeCondition) { - return new CompilerContext(context.Schema, context.Document); - } + if (skip is not null || include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } - var temp = _deferContext; - _deferContext = null; - return temp; + includeCondition = default; + return false; + } } - private void ReturnContext(CompilerContext context) - => _deferContext ??= context; - - internal void RegisterNewSelection(Selection newSelection) + private static void TryParseDirective(DirectiveNode directive, ref string? skip, ref string? include) { - if (newSelection.SyntaxNode.SelectionSet is not null) + if (directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode skipVariable) { - var selectionSetInfo = new SelectionSetInfo(newSelection.SelectionSet!, 0); - _selectionLookup.Add(newSelection, [selectionSetInfo]); + skip = skipVariable.Name.Value; + } + else if (directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode includeVariable) + { + include = includeVariable.Name.Value; } - } - - private readonly struct SelectionSetRef( - SelectionSetNode selectionSet, - string selectionSetTypeName, - SelectionPath path) - : IEquatable - { - public readonly SelectionSetNode SelectionSet = selectionSet; - - public readonly SelectionPath Path = path; - - public readonly string SelectionSetTypeName = selectionSetTypeName; - - public bool Equals(SelectionSetRef other) - => SyntaxComparer.BySyntax.Equals(SelectionSet, other.SelectionSet) - && Path.Equals(other.Path) - && Ordinal.Equals(SelectionSetTypeName, other.SelectionSetTypeName); - - public override bool Equals(object? obj) - => obj is SelectionSetRef other && Equals(other); - - public override int GetHashCode() - => HashCode.Combine( - SyntaxComparer.BySyntax.GetHashCode(SelectionSet), - Path.GetHashCode(), - Ordinal.GetHashCode(SelectionSetTypeName)); } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs new file mode 100644 index 00000000000..025d3eb9600 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs @@ -0,0 +1,113 @@ +using System.Collections; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Features; + +namespace HotChocolate.Execution.Processing; + +[SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] +internal sealed class OperationFeatureCollection : IFeatureCollection +{ +#if NET9_0_OR_GREATER + private readonly Lock _writeLock = new(); +#else + private readonly object _writeLock = new(); +#endif +#if NET10_0_OR_GREATER + private ImmutableDictionary _features = []; +#else + private ImmutableDictionary _features = ImmutableDictionary.Empty; +#endif + private volatile int _containerRevision; + + /// + /// Initializes a new instance of . + /// + public OperationFeatureCollection() + { + } + + /// + public bool IsReadOnly => false; + + /// + public bool IsEmpty => _features.Count == 0; + + /// + public int Revision => _containerRevision; + + /// + public object? this[Type key] + { + get + { + ArgumentNullException.ThrowIfNull(key); + + return _features.GetValueOrDefault(key); + } + set + { + ArgumentNullException.ThrowIfNull(key); + + lock (_writeLock) + { + if (value == null) + { + _features = _features.Remove(key); + _containerRevision++; + return; + } + + _features = _features.SetItem(key, value); + _containerRevision++; + } + } + } + + /// + public TFeature? Get() + { + if (typeof(TFeature).IsValueType) + { + var feature = this[typeof(TFeature)]; + if (feature is null && Nullable.GetUnderlyingType(typeof(TFeature)) is null) + { + throw new InvalidOperationException( + $"{typeof(TFeature).FullName} does not exist in the feature collection " + + "and because it is a struct the method can't return null. " + + $"Use 'featureCollection[typeof({typeof(TFeature).FullName})] is not null' " + + "to check if the feature exists."); + } + + return (TFeature?)feature; + } + + return (TFeature?)this[typeof(TFeature)]; + } + + /// + public bool TryGet([NotNullWhen(true)] out TFeature? feature) + { + if (_features.TryGetValue(typeof(TFeature), out var result) + && result is TFeature f) + { + feature = f; + return true; + } + + feature = default; + return false; + } + + /// + public void Set(TFeature? instance) + { + this[typeof(TFeature)] = instance; + } + + /// + public IEnumerator> GetEnumerator() + => _features.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index 9470b054e68..26be9ca9c7f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; +using HotChocolate.Caching.Memory; using HotChocolate.Execution.Properties; using HotChocolate.Language; using HotChocolate.Resolvers; @@ -13,280 +15,158 @@ namespace HotChocolate.Execution.Processing; public class Selection : ISelection { private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; - private ulong[] _includeConditions; - private ulong _streamIfCondition; + private readonly FieldSelectionNode[] _syntaxNodes; + private readonly ulong[] _includeFlags; + private readonly byte[] _utf8ResponseName; private Flags _flags; - private FieldNode _syntaxNode; - private FieldNode[] _syntaxNodes; - public Selection( + internal Selection( int id, - ObjectType declaringType, - ObjectField field, - IType type, - FieldNode syntaxNode, string responseName, + ObjectField field, + FieldSelectionNode[] syntaxNodes, + ulong[] includeFlags, + bool isInternal, ArgumentMap? arguments = null, - ulong[]? includeConditions = null, - bool isInternal = false, bool isParallelExecutable = true, FieldDelegate? resolverPipeline = null, PureFieldDelegate? pureResolver = null) { + ArgumentNullException.ThrowIfNull(field); + + if (syntaxNodes.Length == 0) + { + throw new ArgumentException( + "The syntaxNodes collection cannot be empty.", + nameof(syntaxNodes)); + } + Id = id; - DeclaringType = declaringType; - Field = field; - Type = type; - _syntaxNode = syntaxNode; - _syntaxNodes = [syntaxNode]; ResponseName = responseName; + Field = field; Arguments = arguments ?? s_emptyArguments; ResolverPipeline = resolverPipeline; PureResolver = pureResolver; - Strategy = InferStrategy(!isParallelExecutable, pureResolver is not null); - - _includeConditions = includeConditions ?? []; - - _flags = isInternal - ? Flags.Internal - : Flags.None; + Strategy = InferStrategy( + isSerial: !isParallelExecutable, + hasPureResolver: pureResolver is not null); + _syntaxNodes = syntaxNodes; + _includeFlags = includeFlags; + _flags = isInternal ? Flags.Internal : Flags.None; + + if (field.Type.NamedType().IsLeafType()) + { + _flags |= Flags.Leaf; + } - if (Type.IsListType()) + if (field.Type.IsListType()) { _flags |= Flags.List; } - } - protected Selection(Selection selection) - { - ArgumentNullException.ThrowIfNull(selection); - - Id = selection.Id; - Strategy = selection.Strategy; - DeclaringType = selection.DeclaringType; - Field = selection.Field; - Type = selection.Type; - _syntaxNode = selection._syntaxNode; - _syntaxNodes = selection._syntaxNodes; - ResponseName = selection.ResponseName; - ResolverPipeline = selection.ResolverPipeline; - PureResolver = selection.PureResolver; - Arguments = selection.Arguments; - _flags = selection._flags; - - _includeConditions = - selection._includeConditions.Length == 0 - ? [] - : selection._includeConditions.ToArray(); + _utf8ResponseName = Utf8StringCache.GetUtf8String(responseName); } /// public int Id { get; } - public CustomOptionsFlags CustomOptions { get; private set; } - /// - public SelectionExecutionStrategy Strategy { get; private set; } - - /// - public ObjectType DeclaringType { get; } - - /// - public ISelectionSet DeclaringSelectionSet { get; private set; } = null!; - - public IOperation DeclaringOperation { get; private set; } = null!; + public string ResponseName { get; } - /// - public ObjectField Field { get; } + internal ReadOnlySpan Utf8ResponseName => _utf8ResponseName; - /// - public IType Type { get; } + public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; - /// - public TypeKind TypeKind => Type.Kind; + public bool IsConditional => _includeFlags.Length > 0; - /// public bool IsList => (_flags & Flags.List) == Flags.List; - /// - public FieldNode SyntaxNode => _syntaxNode; - - /// - public IReadOnlyList SyntaxNodes => _syntaxNodes; + public bool IsLeaf => (_flags & Flags.Leaf) == Flags.Leaf; - public int SelectionSetId { get; private set; } + public ObjectField Field { get; } /// - public SelectionSetNode? SelectionSet => _syntaxNode.SelectionSet; + IOutputFieldDefinition ISelection.Field => Field; - /// - public string ResponseName { get; } + public IType Type => Field.Type; - /// - public byte[] Utf8ResponseName => _utf8ResponseName ??= Encoding.UTF8.GetBytes(ResponseName); - private byte[]? _utf8ResponseName; + public ObjectType DeclaringType => Field.DeclaringType; - /// - public FieldDelegate? ResolverPipeline { get; private set; } + public SelectionSet DeclaringSelectionSet { get; private set; } = null!; - /// - public PureFieldDelegate? PureResolver { get; private set; } + ISelectionSet ISelection.DeclaringSelectionSet => DeclaringSelectionSet; - /// - public ArgumentMap Arguments { get; } + public Operation DeclaringOperation => DeclaringSelectionSet.DeclaringOperation; - /// - public bool HasStreamDirective(ulong includeFlags) - => (_flags & Flags.Stream) == Flags.Stream - && (_streamIfCondition is 0 || (includeFlags & _streamIfCondition) != _streamIfCondition); + public ArgumentMap Arguments { get; } - /// - /// Specifies if the current selection is immutable. - /// - public bool IsReadOnly => (_flags & Flags.Sealed) == Flags.Sealed; + public SelectionExecutionStrategy Strategy { get; private set; } - /// - public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; + public FieldDelegate? ResolverPipeline { get; private set; } - /// - public bool IsConditional - => _includeConditions.Length > 0 || (_flags & Flags.Internal) == Flags.Internal; + public PureFieldDelegate? PureResolver { get; private set; } - internal ReadOnlySpan IncludeConditions => _includeConditions; + internal ReadOnlySpan SyntaxNodes => _syntaxNodes; - public bool IsIncluded(ulong includeFlags, bool allowInternals = false) + IEnumerable ISelection.GetSyntaxNodes() { - // in most case we do not have any include condition, - // so we can take the easy way out here if we do not have any flags. - if (_includeConditions.Length is 0) - { - return !IsInternal || allowInternals; - } - - // if there are flags in most cases we just have one, so we can - // check the first and optimize for this. - var includeCondition = _includeConditions[0]; - - if ((includeFlags & includeCondition) == includeCondition) - { - return !IsInternal || allowInternals; - } - - // if we just have one flag and the flags are not fulfilled we can just exit. - if (_includeConditions.Length is 1) - { - return false; - } - - // else, we will iterate over the rest of the conditions and validate them one by one. - for (var i = 1; i < _includeConditions.Length; i++) + for (var i = 0; i < SyntaxNodes.Length; i++) { - includeCondition = _includeConditions[i]; - - if ((includeFlags & includeCondition) == includeCondition) - { - return !IsInternal || allowInternals; - } + yield return SyntaxNodes[i].Node; } - - return false; } - public override string ToString() - => _syntaxNode.ToString(); - - internal void AddSelection(FieldNode selectionSyntax, ulong includeCondition = 0) + public bool IsIncluded(ulong includeFlags) { - if ((_flags & Flags.Sealed) == Flags.Sealed) + if (_includeFlags.Length == 0) { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); + return true; } - if (includeCondition == 0) + if (_includeFlags.Length == 1) { - if (_includeConditions.Length > 0) - { - _includeConditions = []; - } + var flags1 = _includeFlags[0]; + return (flags1 & includeFlags) == flags1; } - else if (_includeConditions.Length > 0 && Array.IndexOf(_includeConditions, includeCondition) == -1) + + if (_includeFlags.Length == 2) { - var next = _includeConditions.Length; - Array.Resize(ref _includeConditions, next + 1); - _includeConditions[next] = includeCondition; + var flags1 = _includeFlags[0]; + var flags2 = _includeFlags[1]; + return (flags1 & includeFlags) == flags1 || (flags2 & includeFlags) == flags2; } - if (!_syntaxNode.Equals(selectionSyntax, SyntaxComparison.Syntax)) + if (_includeFlags.Length == 3) { - // enlarge the syntax nodes array and add the new syntax node. - var temp = new FieldNode[_syntaxNodes.Length + 1]; - Array.Copy(_syntaxNodes, temp, _syntaxNodes.Length); - temp[_syntaxNodes.Length] = selectionSyntax; - _syntaxNodes = temp; - - _syntaxNode = MergeField(_syntaxNode, selectionSyntax); + var flags1 = _includeFlags[0]; + var flags2 = _includeFlags[1]; + var flags3 = _includeFlags[2]; + return (flags1 & includeFlags) == flags1 + || (flags2 & includeFlags) == flags2 + || (flags3 & includeFlags) == flags3; } - } - private static FieldNode MergeField( - FieldNode first, - FieldNode other) - { - var directives = first.Directives; + var span = _includeFlags.AsSpan(); - if (other.Directives.Count > 0) + for (var i = 0; i < span.Length; i++) { - if (directives.Count == 0) - { - directives = other.Directives; - } - else + if ((span[i] & includeFlags) == span[i]) { - var temp = new DirectiveNode[directives.Count + other.Directives.Count]; - var next = 0; - - for (var i = 0; i < directives.Count; i++) - { - temp[next++] = directives[i]; - } - - for (var i = 0; i < other.Directives.Count; i++) - { - temp[next++] = other.Directives[i]; - } - - directives = temp; + return true; } } - var selectionSet = first.SelectionSet; + return false; + } - if (selectionSet is not null && other.SelectionSet is not null) + public override string ToString() + { + if (SyntaxNodes[0].Node.Alias is not null) { - var selections = new ISelectionNode[ - selectionSet.Selections.Count + other.SelectionSet.Selections.Count]; - var next = 0; - - for (var i = 0; i < selectionSet.Selections.Count; i++) - { - selections[next++] = selectionSet.Selections[i]; - } - - for (var i = 0; i < other.SelectionSet.Selections.Count; i++) - { - selections[next++] = other.SelectionSet.Selections[i]; - } - - selectionSet = selectionSet.WithSelections(selections); + return $"{ResponseName} : {Field.Name}"; } - return new FieldNode( - first.Location, - first.Name, - first.Alias, - directives, - first.Arguments, - selectionSet); + return Field.Name; } internal void SetResolvers( @@ -303,67 +183,24 @@ internal void SetResolvers( Strategy = InferStrategy(hasPureResolver: pureResolver is not null); } - internal void SetSelectionSetId(int selectionSetId) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - SelectionSetId = selectionSetId; - } - - internal void MarkAsStream(ulong ifCondition) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - _streamIfCondition = ifCondition; - _flags |= Flags.Stream; - } - - public void SetOption(CustomOptionsFlags customOptions) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - CustomOptions |= customOptions; - } - /// /// Completes the selection without sealing it. /// - internal void Complete(IOperation declaringOperation, ISelectionSet declaringSelectionSet) + internal void Complete(SelectionSet selectionSet, bool seal) { - Debug.Assert(declaringSelectionSet is not null); + ArgumentNullException.ThrowIfNull(selectionSet); - if ((_flags & Flags.Sealed) != Flags.Sealed) + if ((_flags & Flags.Sealed) == Flags.Sealed) { - DeclaringSelectionSet = declaringSelectionSet; - DeclaringOperation = declaringOperation; + throw new InvalidOperationException("Selection is already sealed."); } - Debug.Assert( - ReferenceEquals(declaringSelectionSet, DeclaringSelectionSet), - "Selections can only belong to a single selectionSet."); - } + DeclaringSelectionSet = selectionSet; - internal void Seal(IOperation declaringOperation, ISelectionSet declaringSelectionSet) - { - if ((_flags & Flags.Sealed) != Flags.Sealed) + if (seal) { - DeclaringSelectionSet = declaringSelectionSet; - DeclaringOperation = declaringOperation; _flags |= Flags.Sealed; } - - Debug.Assert( - ReferenceEquals(declaringSelectionSet, DeclaringSelectionSet), - "Selections can only belong to a single selectionSet."); } private SelectionExecutionStrategy InferStrategy( @@ -391,20 +228,8 @@ private enum Flags Internal = 1, Sealed = 2, List = 4, - Stream = 8 - } - - [Flags] - public enum CustomOptionsFlags : byte - { - None = 0, - Option1 = 1, - Option2 = 2, - Option3 = 4, - Option4 = 8, - Option5 = 16, - Option6 = 32, - Option7 = 64 + Stream = 8, + Leaf = 16 } internal sealed class Sealed : Selection @@ -438,3 +263,13 @@ public Sealed( } } } + +internal static class Utf8StringCache +{ + private static readonly Encoding s_utf8 = Encoding.UTF8; + private static readonly Cache s_cache = new(capacity: 4 * 1024); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] GetUtf8String(string s) + => s_cache.GetOrCreate(s, static k => s_utf8.GetBytes(k)); +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 88223788ca3..6968ad6a7a3 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -1,5 +1,4 @@ using System.Collections.Frozen; -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using HotChocolate.Types; @@ -11,97 +10,74 @@ namespace HotChocolate.Execution.Processing; /// When needed a selection set can preserve fragments so that the execution engine /// can branch the processing of these fragments. /// -internal sealed class SelectionSet : ISelectionSet +public sealed class SelectionSet : ISelectionSet { - private static readonly Fragment[] s_empty = []; private readonly Selection[] _selections; - private readonly FrozenDictionary _responseNameLookup; + private readonly FrozenDictionary _responseNameLookup; private readonly SelectionLookup _utf8ResponseNameLookup; - private readonly Fragment[] _fragments; private Flags _flags; - /// - /// Initializes a new instance of . - /// - /// - /// The selection set unique id. - /// - /// - /// A list of executable field selections. - /// - /// - /// A list of preserved fragments that can be used to branch of - /// some of the execution. - /// - /// - /// Defines if this list needs post-processing for skip and include. - /// - public SelectionSet( - int id, - Selection[] selections, - Fragment[]? fragments, - bool isConditional) + public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) { + ArgumentNullException.ThrowIfNull(selections); + + if (selections.Length == 0) + { + throw new ArgumentException("Selections cannot be empty.", nameof(selections)); + } + Id = id; + Type = type; + _flags = isConditional ? Flags.Conditional : Flags.None; _selections = selections; - _responseNameLookup = _selections.ToFrozenDictionary(t => t.ResponseName, ISelection (t) => t); + _responseNameLookup = _selections.ToFrozenDictionary(t => t.ResponseName); _utf8ResponseNameLookup = SelectionLookup.Create(this); - _fragments = fragments ?? s_empty; - _flags = isConditional ? Flags.Conditional : Flags.None; } - /// + /// + /// Gets an operation unique selection-set identifier of this selection. + /// public int Id { get; } - /// + /// + /// Defines if this list needs post-processing for skip and include. + /// public bool IsConditional => (_flags & Flags.Conditional) == Flags.Conditional; - /// - public IReadOnlyList Selections => _selections; - - /// - public IReadOnlyList Fragments => _fragments; - - /// - public IOperation DeclaringOperation { get; private set; } = null!; + /// + /// Gets the type that declares this selection set. + /// + public IObjectTypeDefinition Type { get; } - /// - public bool TryGetSelection(string responseName, [NotNullWhen(true)] out ISelection? selection) - => _responseNameLookup.TryGetValue(responseName, out selection); + /// + /// Gets the declaring operation. + /// + public Operation DeclaringOperation { get; private set; } = null!; - /// - public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out ISelection? selection) - => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); + IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; /// - /// Completes the selection set without sealing it. + /// Gets the selections that shall be executed. /// - internal void Complete(IOperation declaringOperation) - { - if ((_flags & Flags.Sealed) != Flags.Sealed) - { - DeclaringOperation = declaringOperation; - - for (var i = 0; i < _selections.Length; i++) - { - _selections[i].Complete(declaringOperation, this); - } - } - } + public ReadOnlySpan Selections => _selections; - internal void Seal(IOperation declaringOperation) + IEnumerable ISelectionSet.GetSelections() => _selections; + + internal void Complete(Operation declaringOperation, bool seal) { - if ((_flags & Flags.Sealed) != Flags.Sealed) + if ((_flags & Flags.Sealed) == Flags.Sealed) { - DeclaringOperation = declaringOperation; + throw new InvalidOperationException("Selection set is already sealed."); + } - for (var i = 0; i < _selections.Length; i++) - { - _selections[i].Seal(declaringOperation, this); - } + DeclaringOperation = declaringOperation; - _flags |= Flags.Sealed; + foreach (var selection in _selections) + { + selection.Complete(declaringOperation, this); } + + _flags |= Flags.Sealed; } /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs deleted file mode 100644 index 7cc029c1c6c..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionVariants.cs +++ /dev/null @@ -1,194 +0,0 @@ -using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; - -namespace HotChocolate.Execution.Processing; - -internal sealed class SelectionVariants(int id) : ISelectionVariants -{ - private ObjectType? _firstType; - private SelectionSet? _firstSelectionSet; - private ObjectType? _secondType; - private SelectionSet? _secondSelectionSet; - private Dictionary? _map; - private bool _readOnly; - - /// - public int Id { get; } = id; - - /// - public IOperation DeclaringOperation { get; private set; } = null!; - - public IEnumerable GetPossibleTypes() - => _map?.Keys ?? GetPossibleTypesLazy(); - - public bool IsPossibleType(ObjectType typeContext) - { - if (_map is not null) - { - return _map.ContainsKey(typeContext); - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return true; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return true; - } - - return false; - } - - private IEnumerable GetPossibleTypesLazy() - { - yield return _firstType!; - - if (_secondType is not null) - { - yield return _secondType; - } - } - - public ISelectionSet GetSelectionSet(ObjectType typeContext) - { - if (_map is not null) - { - if (_map.TryGetValue(typeContext, out var selections)) - { - return selections; - } - else - { - throw SelectionSet_TypeContextInvalid(typeContext); - } - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return _firstSelectionSet!; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return _secondSelectionSet!; - } - - throw SelectionSet_TypeContextInvalid(typeContext); - } - - internal bool ContainsSelectionSet(ObjectType typeContext) - { - if (_map is not null) - { - return _map.ContainsKey(typeContext); - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return true; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return true; - } - - return false; - } - - internal void AddSelectionSet( - int id, - ObjectType typeContext, - Selection[] selections, - Fragment[]? fragments, - bool isConditional) - { - if (_readOnly) - { - throw new NotSupportedException(SelectionVariants_ReadOnly); - } - - var selectionSet = new SelectionSet(id, selections, fragments, isConditional); - - if (_map is not null) - { - _map[typeContext] = selectionSet; - } - else - { - if (_firstType is null) - { - _firstType = typeContext; - _firstSelectionSet = selectionSet; - } - else if (_secondType is null) - { - if (typeContext == _firstType) - { - throw SelectionSet_TypeAlreadyAdded(typeContext); - } - - _secondType = typeContext; - _secondSelectionSet = selectionSet; - } - else - { - _map = new Dictionary - { - { _firstType, _firstSelectionSet! }, - { _secondType, _secondSelectionSet! }, - { typeContext, selectionSet } - }; - - _firstType = null; - _firstSelectionSet = null; - _secondType = null; - _secondSelectionSet = null; - } - } - } - - /// - /// Completes the selection variant without sealing it. - /// - internal void Complete(IOperation declaringOperation) - { - if (!_readOnly) - { - DeclaringOperation = declaringOperation; - _firstSelectionSet?.Complete(declaringOperation); - _secondSelectionSet?.Complete(declaringOperation); - - if (_map is not null) - { - foreach (var selectionSet in _map.Values) - { - selectionSet.Complete(declaringOperation); - } - } - } - } - - internal void Seal(IOperation declaringOperation) - { - if (!_readOnly) - { - DeclaringOperation = declaringOperation; - _firstSelectionSet?.Seal(declaringOperation); - _secondSelectionSet?.Seal(declaringOperation); - - if (_map is not null) - { - foreach (var selectionSet in _map.Values) - { - selectionSet.Seal(declaringOperation); - } - } - - _readOnly = true; - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.ArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs similarity index 98% rename from src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.ArgumentValues.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs index a5c2aaf134a..c867fbb89d8 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.ArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs @@ -215,8 +215,3 @@ private static void BuildDirectivePipeline( } } } - -internal delegate FieldDelegate CreateFieldPipeline( - Schema schema, - ObjectField field, - FieldNode selection); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs new file mode 100644 index 00000000000..174bc3ca348 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs @@ -0,0 +1,787 @@ +using System.Collections.Immutable; +using System.Runtime.CompilerServices; +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using HotChocolate.Utilities; +using static System.Runtime.InteropServices.CollectionsMarshal; +using static System.Runtime.InteropServices.MemoryMarshal; +using static System.StringComparer; +using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Execution.ThrowHelper; + +namespace HotChocolate.Execution.Processing; + +/// +/// The operation compiler will analyze a specific operation of a GraphQL request document +/// and create from it an optimized executable operation tree. +/// +public sealed partial class OperationCompiler +{ + private readonly InputParser _parser; + private readonly CreateFieldPipeline _createFieldPipeline; + private readonly Queue _backlog = []; + private readonly Dictionary _selectionLookup = []; + private readonly Dictionary _selectionSetIdLookup = []; + private readonly Dictionary _selectionVariants = []; + private readonly Dictionary _fragmentDefinitions = []; + private readonly Dictionary _contextData = []; + private readonly List _selections = []; + private readonly HashSet _directiveNames = new(Ordinal); + private readonly List _pipelineComponents = []; + private readonly HashSet _enqueuedSelectionSets = []; + private IncludeCondition[] _includeConditions = []; + private CompilerContext? _deferContext; + private ImmutableArray _operationOptimizers = []; + private int _nextSelectionId; + private int _nextSelectionSetRefId; + private int _nextSelectionSetId; + private int _nextFragmentId; + private bool _hasIncrementalParts; + private OperationCompilerMetrics _metrics; + + public OperationCompiler(InputParser parser) + { + _parser = parser ?? throw new ArgumentNullException(nameof(parser)); + + _createFieldPipeline = + (schema, field, selection) + => CreateFieldPipeline( + schema, + field, + selection, + null, + _directiveNames, + _pipelineComponents); + } + + internal OperationCompilerMetrics Metrics => _metrics; + + public IOperation Compile(OperationCompilerRequest request) + { + ArgumentException.ThrowIfNullOrEmpty(request.Id, nameof(request)); + + try + { + var backlogMaxSize = 0; + var selectionSetOptimizers = request.SelectionSetOptimizers; + _operationOptimizers = request.OperationOptimizers; + + // collect root fields + var rootPath = SelectionPath.Root; + var id = GetOrCreateSelectionSetRefId(request.Definition.SelectionSet, request.RootType.Name, rootPath); + var variants = GetOrCreateSelectionVariants(id); + SelectionSetInfo[] infos = [new(request.Definition.SelectionSet, 0)]; + + var context = new CompilerContext((Schema)request.Schema, request.Document); + context.Initialize(request.RootType, variants, infos, rootPath, selectionSetOptimizers); + CompileSelectionSet(context); + + // process consecutive selections + while (_backlog.Count > 0) + { + backlogMaxSize = Math.Max(backlogMaxSize, _backlog.Count); + + var current = _backlog.Dequeue(); + var type = current.Type; + variants = GetOrCreateSelectionVariants(current.SelectionSetId); + + if (!variants.ContainsSelectionSet(type)) + { + infos = _selectionLookup[current.Selection]; + context.Initialize(type, variants, infos, current.Path, current.Optimizers); + CompileSelectionSet(context); + } + } + + // create operation + var operation = CreateOperation(request); + + _metrics = new OperationCompilerMetrics( + _nextSelectionId, + _selectionVariants.Count, + backlogMaxSize); + + return operation; + } + finally + { + _nextSelectionId = 0; + _nextSelectionSetRefId = 0; + _nextSelectionId = 0; + _nextFragmentId = 0; + _hasIncrementalParts = false; + + _backlog.Clear(); + _selectionLookup.Clear(); + _selectionSetIdLookup.Clear(); + _selectionVariants.Clear(); + _fragmentDefinitions.Clear(); + _contextData.Clear(); + _selections.Clear(); + _directiveNames.Clear(); + _pipelineComponents.Clear(); + _enqueuedSelectionSets.Clear(); + + _operationOptimizers = []; + + _includeConditions = []; + _deferContext = null; + } + } + + private Operation CreateOperation(OperationCompilerRequest request) + { + var operation = new Operation( + request.Id, + request.Document, + request.Definition, + request.RootType, + request.Schema); + + var schema = Unsafe.As(request.Schema); + + var variants = new SelectionVariants[_selectionVariants.Count]; + + if (_operationOptimizers.Length == 0) + { + CompleteResolvers(schema); + + // if we do not have any optimizers, we will copy + // the variants and seal them in one go. + foreach (var item in _selectionVariants) + { + variants[item.Key] = item.Value; + item.Value.Seal(operation); + } + } + else + { + // if we have optimizers, we will first copy the variants to its array, + // after that we will run the optimizers and give them a chance to do some + // more mutations on the compiled selection variants. + // after we have executed all optimizers, we will seal the selection variants. + var context = new OperationOptimizerContext( + request.Id, + request.Document, + request.Definition, + schema, + request.RootType, + variants, + _includeConditions, + _contextData, + _hasIncrementalParts, + _createFieldPipeline); + + foreach (var item in _selectionVariants) + { + variants[item.Key] = item.Value; + } + + // we will complete the selection variants, sets and selections + // without sealing them so that analyzers in this step can fully + // inspect them. + var variantsSpan = variants.AsSpan(); + ref var variantsStart = ref GetReference(variantsSpan); + ref var variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length); + + while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) + { + variantsStart.Complete(operation); + variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; + } + + var optSpan = _operationOptimizers.AsSpan(); + ref var optStart = ref GetReference(optSpan); + ref var optEnd = ref Unsafe.Add(ref optStart, optSpan.Length); + + while (Unsafe.IsAddressLessThan(ref optStart, ref optEnd)) + { + optStart.OptimizeOperation(context); + optStart = ref Unsafe.Add(ref optStart, 1)!; + } + + CompleteResolvers(schema); + + variantsSpan = variants.AsSpan(); + variantsStart = ref GetReference(variantsSpan)!; + variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length)!; + + while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) + { + variantsStart.Seal(operation); + variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; + } + } + + operation.Seal(_contextData, variants, _hasIncrementalParts, _includeConditions); + return operation; + } + + private void CompleteResolvers(Schema schema) + { + ref var searchSpace = ref GetReference(AsSpan(_selections)); + + Path? path = null; + for (var i = 0; i < _selections.Count; i++) + { + var selection = Unsafe.Add(ref searchSpace, i); + path = path?.Append(selection.ResponseName); + if (selection.ResolverPipeline is null && selection.PureResolver is null) + { + var field = selection.Field; + var syntaxNode = selection.SyntaxNode; + if (syntaxNode.Directives.Count > 0 && path == null) + { + // create the path only on demand + path = PathHelper.CreatePathFromSelection(_selections, i + 1); + } + + var resolver = CreateFieldPipeline( + schema, + field, + syntaxNode, + path, + _directiveNames, + _pipelineComponents); + var pureResolver = TryCreatePureField(schema, field, syntaxNode); + selection.SetResolvers(resolver, pureResolver); + } + } + } + + private void CompileSelectionSet(CompilerContext context) + { + // We first collect the fields that we find in the selection set ... + CollectFields(context); + + // next we will call the selection set optimizers to rewrite the + // selection set if necessary. + OptimizeSelectionSet(context); + + // after that we start completing the selections and build the SelectionSet from + // the completed selections. + CompleteSelectionSet(context); + } + + private void CompleteSelectionSet(CompilerContext context) + { + var selections = new Selection[context.Fields.Values.Count]; + var fragments = context.Fragments.Count is not 0 + ? new Fragment[context.Fragments.Count] + : []; + var selectionIndex = 0; + var isConditional = false; + + foreach (var selection in context.Fields.Values) + { + // if the field of the selection returns a composite type, we will traverse + // the child selection-sets as well. + var fieldType = selection.Type.NamedType(); + var selectionSetId = -1; + + if (selection.IsConditional) + { + isConditional = true; + } + + // Determines if the type is a composite type. + if (fieldType.IsCompositeType()) + { + if (selection.SelectionSet is null) + { + // composite fields always have to have a selection-set + // otherwise we need to throw. + throw QueryCompiler_CompositeTypeSelectionSet(selection.SyntaxNode); + } + + var selectionPath = context.Path.Append(selection.ResponseName); + selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, fieldType.Name, selectionPath); + var possibleTypes = context.Schema.GetPossibleTypes(fieldType); + + if (_enqueuedSelectionSets.Add(selectionSetId)) + { + for (var i = possibleTypes.Count - 1; i >= 0; i--) + { + _backlog.Enqueue( + new BacklogItem( + possibleTypes[i], + selectionSetId, + selection, + selectionPath, + ResolveOptimizers(context.Optimizers, selection.Field))); + } + } + + // We are waiting for the latest stream and defer spec discussions to be codified + // before we change the overall stream handling. + // + // For now, we only allow streams on lists of composite types. + if (selection.SyntaxNode.IsStreamable()) + { + var streamDirective = selection.SyntaxNode.GetStreamDirective(); + var nullValue = NullValueNode.Default; + var ifValue = streamDirective?.GetArgumentValue(DirectiveNames.Stream.Arguments.If) ?? nullValue; + long ifConditionFlags = 0; + + if (ifValue.Kind is not SyntaxKind.NullValue) + { + var ifCondition = new IncludeCondition(ifValue, nullValue); + ifConditionFlags = GetSelectionIncludeCondition(ifCondition, 0); + } + + selection.MarkAsStream(ifConditionFlags); + _hasIncrementalParts = true; + } + } + + selection.SetSelectionSetId(selectionSetId); + selections[selectionIndex++] = selection; + _selections.Add(selection); + } + + if (context.Fragments.Count > 0) + { + for (var i = 0; i < context.Fragments.Count; i++) + { + fragments[i] = context.Fragments[i]; + } + } + + context.SelectionVariants.AddSelectionSet( + _nextSelectionSetId++, + context.Type, + selections, + fragments, + isConditional); + } + + private void CollectFields(CompilerContext context) + { + foreach (var selectionSetInfo in context.SelectionInfos) + { + CollectFields( + context, + selectionSetInfo.SelectionSet, + selectionSetInfo.IncludeCondition); + } + } + + private void CollectFields( + CompilerContext context, + SelectionSetNode selectionSet, + ulong includeCondition) + { + for (var j = 0; j < selectionSet.Selections.Count; j++) + { + ResolveFields(context, selectionSet.Selections[j], includeCondition); + } + } + + private void ResolveFields( + CompilerContext context, + ISelectionNode selection, + long includeCondition) + { + switch (selection.Kind) + { + case SyntaxKind.Field: + ResolveField( + context, + (FieldNode)selection, + includeCondition); + break; + + case SyntaxKind.InlineFragment: + ResolveInlineFragment( + context, + (InlineFragmentNode)selection, + includeCondition); + break; + + case SyntaxKind.FragmentSpread: + ResolveFragmentSpread( + context, + (FragmentSpreadNode)selection, + includeCondition); + break; + } + } + + private void ResolveField( + CompilerContext context, + FieldNode selection, + ulong includeCondition) + { + includeCondition = GetSelectionIncludeCondition(selection, includeCondition); + + var fieldName = selection.Name.Value; + var responseName = selection.Alias?.Value ?? fieldName; + + if (context.Type.Fields.TryGetField(fieldName, out var field)) + { + var fieldType = field.Type; + + if (context.Fields.TryGetValue(responseName, out var preparedSelection)) + { + preparedSelection.AddSelection(selection, includeCondition); + + if (selection.SelectionSet is not null) + { + var selectionSetInfo = new SelectionSetInfo( + selection.SelectionSet!, + includeCondition); + var selectionInfos = _selectionLookup[preparedSelection]; + var next = selectionInfos.Length; + Array.Resize(ref selectionInfos, next + 1); + selectionInfos[next] = selectionSetInfo; + _selectionLookup[preparedSelection] = selectionInfos; + } + } + else + { + var id = GetNextSelectionId(); + + // if this is the first time we've found a selection to this field, we have to + // create a new prepared selection. + preparedSelection = new Selection.Sealed( + id, + context.Type, + field, + fieldType, + selection.SelectionSet is not null + ? selection.WithSelectionSet( + selection.SelectionSet.WithSelections( + selection.SelectionSet.Selections)) + : selection, + responseName: responseName, + arguments: CoerceArgumentValues(field, selection), + includeConditions: includeCondition == 0 + ? null + : [includeCondition], + isParallelExecutable: field.IsParallelExecutable); + + context.Fields.Add(responseName, preparedSelection); + + if (selection.SelectionSet is not null) + { + var selectionSetInfo = new SelectionSetInfo( + selection.SelectionSet!, + includeCondition); + _selectionLookup.Add(preparedSelection, [selectionSetInfo]); + } + } + } + else + { + throw FieldDoesNotExistOnType(selection, context.Type.Name); + } + } + + private void ResolveInlineFragment( + CompilerContext context, + InlineFragmentNode inlineFragment, + long includeCondition) + { + ResolveFragment( + context, + inlineFragment, + inlineFragment.TypeCondition, + inlineFragment.SelectionSet, + inlineFragment.Directives, + includeCondition); + } + + private void ResolveFragmentSpread( + CompilerContext context, + FragmentSpreadNode fragmentSpread, + long includeCondition) + { + var fragmentDef = GetFragmentDefinition(context, fragmentSpread); + + ResolveFragment( + context, + fragmentSpread, + fragmentDef.TypeCondition, + fragmentDef.SelectionSet, + fragmentSpread.Directives, + includeCondition); + } + + private void ResolveFragment( + CompilerContext context, + ISelectionNode selection, + NamedTypeNode? typeCondition, + SelectionSetNode selectionSet, + IReadOnlyList directives, + ulong includeCondition) + { + if (typeCondition is null + || (context.Schema.Types.TryGetType(typeCondition, out IType? typeCon) + && DoesTypeApply(typeCon, context.Type))) + { + includeCondition = GetSelectionIncludeCondition(selection, includeCondition); + + if (directives.IsDeferrable()) + { + var deferDirective = directives.GetDeferDirectiveNode(); + var nullValue = NullValueNode.Default; + var ifValue = deferDirective?.GetArgumentValue(DirectiveNames.Defer.Arguments.If) ?? nullValue; + + ulong ifConditionFlags = 0; + + if (ifValue.Kind is not SyntaxKind.NullValue) + { + var ifCondition = new IncludeCondition(ifValue, nullValue); + ifConditionFlags = GetSelectionIncludeCondition(ifCondition, includeCondition); + } + + var typeName = typeCondition?.Name.Value ?? context.Type.Name; + var id = GetOrCreateSelectionSetRefId(selectionSet, typeName, context.Path); + var variants = GetOrCreateSelectionVariants(id); + var infos = new SelectionSetInfo[] { new(selectionSet, includeCondition) }; + + if (!variants.ContainsSelectionSet(context.Type)) + { + var deferContext = RentContext(context); + deferContext.Initialize(context.Type, variants, infos, context.Path); + CompileSelectionSet(deferContext); + ReturnContext(deferContext); + } + + var fragment = new Fragment( + GetNextFragmentId(), + context.Type, + selection, + directives, + variants.GetSelectionSet(context.Type), + includeCondition, + ifConditionFlags); + + context.Fragments.Add(fragment); + _hasIncrementalParts = true; + + // if we have if-condition flags, there will be a runtime validation if something + // shall be deferred, so we need to prepare for both cases. + // + // this means that we will collect the fields with our if condition flags as + // if the fragment was not deferred. + if (ifConditionFlags is not 0) + { + CollectFields(context, selectionSet, ifConditionFlags); + } + } + else + { + CollectFields(context, selectionSet, includeCondition); + } + } + } + + private static bool DoesTypeApply(IType typeCondition, IObjectTypeDefinition current) + => typeCondition.Kind switch + { + TypeKind.Object => ReferenceEquals(typeCondition, current), + TypeKind.Interface => current.IsImplementing((InterfaceType)typeCondition), + TypeKind.Union => ((UnionType)typeCondition).Types.ContainsName(current.Name), + _ => false + }; + + private FragmentDefinitionNode GetFragmentDefinition( + CompilerContext context, + FragmentSpreadNode fragmentSpread) + { + var fragmentName = fragmentSpread.Name.Value; + + if (!_fragmentDefinitions.TryGetValue(fragmentName, out var value)) + { + var document = context.Document; + + for (var i = 0; i < document.Definitions.Count; i++) + { + if (document.Definitions[i] is FragmentDefinitionNode fragmentDefinition + && fragmentDefinition.Name.Value.EqualsOrdinal(fragmentName)) + { + value = fragmentDefinition; + _fragmentDefinitions.Add(fragmentName, value); + goto EXIT; + } + } + + throw new InvalidOperationException( + string.Format( + OperationCompiler_FragmentNotFound, + fragmentName)); + } + +EXIT: + return value; + } + + internal int GetNextSelectionId() => _nextSelectionId++; + + private int GetNextFragmentId() => _nextFragmentId++; + + private int GetOrCreateSelectionSetRefId( + SelectionSetNode selectionSet, + string selectionSetTypeName, + SelectionPath path) + { + var selectionSetRef = new SelectionSetRef(selectionSet, selectionSetTypeName, path); + + if (!_selectionSetIdLookup.TryGetValue(selectionSetRef, out var selectionSetId)) + { + selectionSetId = _nextSelectionSetRefId++; + _selectionSetIdLookup.Add(selectionSetRef, selectionSetId); + } + + return selectionSetId; + } + + private SelectionVariants GetOrCreateSelectionVariants(int selectionSetId) + { + if (!_selectionVariants.TryGetValue(selectionSetId, out var variants)) + { + variants = new SelectionVariants(selectionSetId); + _selectionVariants.Add(selectionSetId, variants); + } + + return variants; + } + + private ulong GetSelectionIncludeCondition( + ISelectionNode selectionSyntax, + ulong parentIncludeCondition) + { + var condition = IncludeCondition.FromSelection(selectionSyntax); + + if (condition.IsDefault) + { + return parentIncludeCondition; + } + + var pos = Array.IndexOf(_includeConditions, condition); + + if (pos == -1) + { + pos = _includeConditions.Length; + + if (pos == 64) + { + throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); + } + + if (_includeConditions.Length == 0) + { + _includeConditions = new IncludeCondition[1]; + } + else + { + Array.Resize(ref _includeConditions, pos + 1); + } + + _includeConditions[pos] = condition; + } + + long selectionIncludeCondition = 1; + selectionIncludeCondition <<= pos; + + if (parentIncludeCondition == 0) + { + return selectionIncludeCondition; + } + + parentIncludeCondition |= selectionIncludeCondition; + return parentIncludeCondition; + } + + private long GetSelectionIncludeCondition( + IncludeCondition condition, + ulong parentIncludeCondition) + { + var pos = Array.IndexOf(_includeConditions, condition); + + if (pos == -1) + { + pos = _includeConditions.Length; + + if (pos == 64) + { + throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); + } + + if (_includeConditions.Length == 0) + { + _includeConditions = new IncludeCondition[1]; + } + else + { + Array.Resize(ref _includeConditions, pos + 1); + } + + _includeConditions[pos] = condition; + } + + long selectionIncludeCondition = 1; + selectionIncludeCondition <<= pos; + + if (parentIncludeCondition == 0) + { + return selectionIncludeCondition; + } + + parentIncludeCondition |= selectionIncludeCondition; + return parentIncludeCondition; + } + + private CompilerContext RentContext(CompilerContext context) + { + if (_deferContext is null) + { + return new CompilerContext(context.Schema, context.Document); + } + + var temp = _deferContext; + _deferContext = null; + return temp; + } + + private void ReturnContext(CompilerContext context) + => _deferContext ??= context; + + internal void RegisterNewSelection(Selection newSelection) + { + if (newSelection.SyntaxNode.SelectionSet is not null) + { + var selectionSetInfo = new SelectionSetInfo(newSelection.SelectionSet!, 0); + _selectionLookup.Add(newSelection, [selectionSetInfo]); + } + } + + private readonly struct SelectionSetRef( + SelectionSetNode selectionSet, + string selectionSetTypeName, + SelectionPath path) + : IEquatable + { + public readonly SelectionSetNode SelectionSet = selectionSet; + + public readonly SelectionPath Path = path; + + public readonly string SelectionSetTypeName = selectionSetTypeName; + + public bool Equals(SelectionSetRef other) + => SyntaxComparer.BySyntax.Equals(SelectionSet, other.SelectionSet) + && Path.Equals(other.Path) + && Ordinal.Equals(SelectionSetTypeName, other.SelectionSetTypeName); + + public override bool Equals(object? obj) + => obj is SelectionSetRef other && Equals(other); + + public override int GetHashCode() + => HashCode.Combine( + SyntaxComparer.BySyntax.GetHashCode(SelectionSet), + Path.GetHashCode(), + Ordinal.GetHashCode(SelectionSetTypeName)); + } +} diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index ae9e6fe3046..2729033aa3b 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -62,10 +62,12 @@ + + diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs index a4818fa44ee..c6eed75faa7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs @@ -37,14 +37,7 @@ public bool Contains(IncludeCondition item) => _dictionary.ContainsKey(item); public int IndexOf(IncludeCondition item) - { - if (_dictionary.TryGetValue(item, out var index)) - { - return index; - } - - return -1; - } + => _dictionary.GetValueOrDefault(item, -1); public void CopyTo(IncludeCondition[] array, int arrayIndex) => _dictionary.Keys.CopyTo(array, arrayIndex); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs index 01a4d0a8b48..2ab53071b58 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs @@ -5,6 +5,11 @@ namespace HotChocolate.Fusion.Execution.Nodes; +/// +/// A selection set is primarily composed of field selections. +/// When needed a selection set can preserve fragments so that the execution engine +/// can branch the processing of these fragments. +/// public sealed class SelectionSet : ISelectionSet { private readonly Selection[] _selections; @@ -44,6 +49,13 @@ public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, /// public IObjectTypeDefinition Type { get; } + /// + /// Gets the declaring operation. + /// + public Operation DeclaringOperation { get; private set; } = null!; + + IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; + /// /// Gets the selections that shall be executed. /// @@ -81,13 +93,6 @@ public bool TryGetSelection(string responseName, [NotNullWhen(true)] out Selecti public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out Selection? selection) => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); - /// - /// Gets the declaring operation. - /// - public Operation DeclaringOperation { get; private set; } = null!; - - IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; - internal void Seal(Operation operation) { if (_isSealed) From 95ae2c6224fe7496fbf58c9e02366dd5107776a7 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 28 Nov 2025 11:46:47 +0100 Subject: [PATCH 10/30] wip --- .../ISelection.cs | 2 +- .../DeferredWorkStateOwnerFactory.cs | 39 ------------------- .../InternalServiceCollectionExtensions.cs | 27 ------------- .../Processing/DeferredWorkStateOwner.cs | 25 ------------ .../Processing/MiddlewareContext.Selection.cs | 12 ++---- .../Processing/OperationContext.Operation.cs | 4 +- .../src/Types/Resolvers/IResolverContext.cs | 16 ++++---- .../Types/Resolvers/ISelectionCollection.cs | 6 +-- .../src/Types/Text/Json/ResultDocument.cs | 3 +- .../Core/src/Types/Text/Json/ResultElement.cs | 6 +-- .../src/Types/Text/Json/ResultProperty.cs | 4 +- 11 files changed, 25 insertions(+), 119 deletions(-) delete mode 100644 src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs diff --git a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs index 40d92b1dd1e..5de00d5bd0b 100644 --- a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs +++ b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs @@ -96,7 +96,7 @@ public interface ISelection /// Gets the original syntax nodes that contributed to this selection. /// /// - /// An enumerable of field nodes from the original GraphQL document that + /// An enumerable collection of field nodes from the original GraphQL document that /// were merged to create this selection. Multiple nodes may be returned /// if field merging occurred (same response name, compatible arguments). /// diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs deleted file mode 100644 index 0e75c785bae..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -using HotChocolate.Execution.DependencyInjection; -using HotChocolate.Execution.Processing; -using Microsoft.Extensions.ObjectPool; - -namespace Microsoft.Extensions.DependencyInjection; - -/// -/// The deferred is injected as a scoped services and -/// preserves the instance it creates. -/// -/// This is done so that the executions running on one service scope share the deferred execution -/// state between each other. -/// -/// is disposable and will be disposed with the request scope. -/// -internal sealed class DeferredWorkStateOwnerFactory : IFactory -{ - private readonly object _sync = new(); - private readonly ObjectPool _pool; - private DeferredWorkStateOwner? _owner; - - public DeferredWorkStateOwnerFactory(ObjectPool pool) - { - _pool = pool; - } - - public DeferredWorkStateOwner Create() - { - if (_owner is null) - { - lock (_sync) - { - _owner ??= new DeferredWorkStateOwner(_pool); - } - } - - return _owner; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs index 62551e42713..6a1afa07ec7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs @@ -97,22 +97,6 @@ internal static IServiceCollection TryAddOperationContextPool( return services; } - internal static IServiceCollection TryAddDeferredWorkStatePool( - this IServiceCollection services) - { - services.TryAddSingleton( - sp => - { - var provider = sp.GetRequiredService(); - var policy = new DeferredWorkStatePooledObjectPolicy(); - return provider.Create(policy); - }); - - services.TryAddScoped, DeferredWorkStateOwnerFactory>(); - - return services; - } - internal static IServiceCollection TryAddTypeConverter( this IServiceCollection services) { @@ -233,15 +217,4 @@ public override bool Return(OperationContext obj) return false; } } - - private sealed class DeferredWorkStatePooledObjectPolicy : PooledObjectPolicy - { - public override DeferredWorkState Create() => new(); - - public override bool Return(DeferredWorkState obj) - { - obj.Reset(); - return true; - } - } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs deleted file mode 100644 index 4e9b95ed54d..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/DeferredWorkStateOwner.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.ObjectPool; - -namespace HotChocolate.Execution.Processing; - -internal sealed class DeferredWorkStateOwner : IDisposable -{ - private readonly ObjectPool _pool; - private int _disposed; - - public DeferredWorkStateOwner(ObjectPool pool) - { - _pool = pool ?? throw new ArgumentNullException(nameof(pool)); - State = pool.Get(); - } - - public DeferredWorkState State { get; } - - public void Dispose() - { - if (_disposed == 0 && Interlocked.CompareExchange(ref _disposed, 1, 0) == 0) - { - _pool.Return(State); - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs index 96099d85405..8bf018d95db 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs @@ -8,7 +8,7 @@ namespace HotChocolate.Execution.Processing; internal partial class MiddlewareContext { private readonly PureResolverContext _childContext; - private ISelection _selection = null!; + private Selection _selection = null!; public ObjectType ObjectType => _selection.DeclaringType; @@ -43,14 +43,14 @@ public bool TryCreatePureContext( public IReadOnlyList GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) { ArgumentNullException.ThrowIfNull(typeContext); selection ??= _selection; - if (selection.SelectionSet is null) + if (selection.IsLeaf) { return []; } @@ -60,14 +60,10 @@ public IReadOnlyList GetSelections( if (selectionSet.IsConditional) { var operationIncludeFlags = _operationContext.IncludeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var selectionRef = ref ((SelectionSet)selectionSet).GetSelectionsReference(); var finalFields = new List(); - for (var i = 0; i < selectionCount; i++) + foreach (var childSelection in selectionSet.Selections) { - var childSelection = Unsafe.Add(ref selectionRef, i); - if (childSelection.IsIncluded(operationIncludeFlags, allowInternals)) { finalFields.Add(childSelection); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs index 0005bf324d2..8921485ebd5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs @@ -20,7 +20,7 @@ public Schema Schema /// /// Gets the operation that is being executed. /// - public IOperation Operation + public Operation Operation { get { @@ -70,7 +70,7 @@ public object? RootValue /// The type context. /// /// - public ISelectionSet CollectFields(ISelection selection, ObjectType typeContext) + public SelectionSet CollectFields(Selection selection, ObjectType typeContext) { AssertInitialized(); return Operation.GetSelectionSet(selection, typeContext); diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index f3cdd188068..207af448055 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -26,13 +26,13 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// /// Gets the operation from the query that is being executed. /// - IOperation Operation { get; } + Operation Operation { get; } /// /// Gets the field selection for which a field resolver is /// being executed. /// - ISelection Selection { get; } + Selection Selection { get; } /// /// Gets access to the coerced variable values of the request. @@ -48,7 +48,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Gets the previous (parent) resolver result. /// /// - /// The type to which the result shall be casted. + /// The type to which the result shall be cast. /// /// /// Returns the previous (parent) resolver result. @@ -62,7 +62,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as literal. @@ -76,7 +76,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as literal. @@ -90,7 +90,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as optional. @@ -182,7 +182,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// /// Notifies when the connection underlying this request is aborted - /// and thus request operations should be cancelled. + /// and thus request operations should be canceled. /// CancellationToken RequestAborted { get; } @@ -249,7 +249,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Returns the pre-compiled selections for the /// with the specified . /// - IReadOnlyList GetSelections( + IReadOnlyList GetSelections( ObjectType typeContext, ISelection? selection = null, bool allowInternals = false); diff --git a/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs b/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs index 569871e746b..27de07f4665 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs @@ -1,4 +1,4 @@ -using HotChocolate.Execution.Processing; +using HotChocolate.Execution; using HotChocolate.Types; namespace HotChocolate.Resolvers; @@ -37,14 +37,14 @@ public interface ISelectionCollection : IReadOnlyList ISelectionCollection Select(ReadOnlySpan fieldNames); /// - /// Selects all selections where the typeContext is assignable from the field`s declaring type. + /// Selects all selections where the typeContext is assignable from the field's declaring type. /// /// /// The type context to select. /// /// /// Returns a containing - /// the selections where the typeContext is assignable from the field`s declaring type. + /// the selections where the typeContext is assignable from the field's declaring type. /// ISelectionCollection Select(ITypeDefinition typeContext); diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs index 780e99ee305..da69ffbf89d 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Text; using HotChocolate.Buffers; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Types; @@ -447,7 +448,7 @@ private ReadOnlySpan ReadLocalData(int location, int size) + "Use WriteLocalDataTo for writing to an IBufferWriter instead."); } - internal ResultElement CreateObject(Cursor parent, ISelectionSet selectionSet) + internal ResultElement CreateObject(Cursor parent, SelectionSet selectionSet) { var startObjectCursor = WriteStartObject(parent, selectionSet.Id); diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs index 5b363b8bb8a..1cf8c384bb6 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs @@ -76,7 +76,7 @@ public ResultElement this[int index] /// /// Gets the operation this element belongs to. /// - public IOperation Operation + public Operation Operation { get { @@ -90,7 +90,7 @@ public IOperation Operation /// Gets the if this element represents the data of a selection set; /// otherwise, null. /// - public ISelectionSet? SelectionSet + public SelectionSet? SelectionSet { get { @@ -104,7 +104,7 @@ public ISelectionSet? SelectionSet /// Gets the if this element represents the data of a field selection; /// otherwise, null. /// - public ISelection? Selection + public Selection? Selection { get { diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs index 695bc20aa08..b76011d25ca 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs @@ -26,9 +26,9 @@ internal ResultProperty(ResultElement value) /// public string Name => Value.GetPropertyName(); - public ISelection? Selection => Value.Selection; + public Selection? Selection => Value.Selection; - public ISelection AssertSelection() => Value.AssertSelection(); + public Selection AssertSelection() => Value.AssertSelection(); /// /// Compares to the name of this property. From 4630aba9d17fb97b47ff55615f08fa33566c8c8b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 28 Nov 2025 14:07:12 +0100 Subject: [PATCH 11/30] wip --- ...fsetPaginationResolverContextExtensions.cs | 15 ++- .../Processing/MiddlewareContext.Arguments.cs | 26 ++--- .../Processing/MiddlewareContext.Global.cs | 24 +++-- .../Processing/MiddlewareContext.Pooling.cs | 2 +- .../Processing/MiddlewareContext.Pure.cs | 36 ++++--- .../Processing/MiddlewareContext.Selection.cs | 27 +----- .../OperationCompiler.CompileResolver.cs | 97 +++++++++++++++++++ .../Execution/Processing/OperationCompiler.cs | 15 ++- .../Processing/OperationContext.Execution.cs | 13 --- .../Types/Execution/Processing/Selection.cs | 5 +- .../_OperationCompiler.ArgumentValues.cs | 2 +- ... => _OperationCompiler.CompilerContext.cs} | 0 .../Core/src/Types/HotChocolate.Types.csproj | 28 ++++++ .../src/Types/Resolvers/IMiddlewareContext.cs | 2 +- .../src/Types/Resolvers/IResolverContext.cs | 4 +- .../Types/Resolvers/SelectionEnumerator.cs | 77 +++++++++++++++ .../Core/src/Types/Text/Json/ResultElement.cs | 4 +- .../src/Types/Text/Json/ResultProperty.cs | 2 +- .../src/Types/Types/Relay/NodeTypeFeature.cs | 6 +- 19 files changed, 295 insertions(+), 90 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs rename src/HotChocolate/Core/src/Types/Execution/Processing/{OperationCompiler.CompilerContext.cs => _OperationCompiler.CompilerContext.cs} (100%) create mode 100644 src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs index e29fbba5915..63f5829a6a9 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs @@ -6,10 +6,11 @@ namespace HotChocolate.Types.Pagination; internal static class OffsetPaginationResolverContextExtensions { /// + /// /// TotalCount is one of the heaviest operations. It is only necessary to load totalCount /// when it is enabled (IncludeTotalCount) and when it is contained in the selection set. - /// - /// This method checks if the total count is selected + /// + /// This method checks if the total count is selected /// /// /// @@ -17,10 +18,14 @@ public static bool IsTotalCountSelected(this IResolverContext context) { // TotalCount is one of the heaviest operations. It is only necessary to load totalCount // when it is enabled (IncludeTotalCount) and when it is contained in the selection set. - if (context.Selection.Type is ObjectType objectType - && context.Selection.SyntaxNode.SelectionSet is not null) + if (context.Selection is { Type: ObjectType objectType, IsLeaf: false }) { - var selections = context.GetSelections(objectType, null, true); + var selectionSet = context.Selection.DeclaringOperation.GetSelectionSet(context.Selection, objectType); + + foreach (var selection in selectionSet.Selections) + { )) + + } for (var i = 0; i < selections.Count; i++) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs index 92e99932d2b..e69ab937ba7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs @@ -17,7 +17,7 @@ public T ArgumentValue(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } try @@ -40,7 +40,7 @@ public Optional ArgumentOptional(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return argument.IsDefaultValue @@ -54,7 +54,7 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } var literal = argument.ValueLiteral!; @@ -65,7 +65,11 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV } throw ResolverContext_LiteralNotCompatible( - _selection.SyntaxNode, Path, name, typeof(TValueNode), literal.GetType()); + _selection.SyntaxNodes[0].Node, + Path, + name, + typeof(TValueNode), + literal.GetType()); } public ValueKind ArgumentKind(string name) @@ -74,10 +78,10 @@ public ValueKind ArgumentKind(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } - // There can only be no kind if there was an error which would have + // There can only be no kind of there was an error which would have // already been raised at this point. return argument.Kind ?? ValueKind.Unknown; } @@ -114,7 +118,7 @@ private T CoerceArgumentValue(ArgumentValue argument) if (typeof(IValueNode).IsAssignableFrom(typeof(T))) { throw ResolverContext_LiteralsNotSupported( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T)); @@ -122,7 +126,7 @@ private T CoerceArgumentValue(ArgumentValue argument) // we are unable to convert the argument to the request type. throw ResolverContext_CannotConvertArgument( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T), @@ -173,16 +177,14 @@ public ArgumentValue ReplaceArgument(string argumentName, ArgumentValue newArgum Arguments = mutableArguments; } - if (!mutableArguments.TryGetValue(argumentName, out var argumentValue)) + // we remove the original argument name ... + if (!mutableArguments.Remove(argumentName, out var argumentValue)) { throw new ArgumentException( string.Format(MiddlewareContext_ReplaceArgument_InvalidKey, argumentName), nameof(argumentName)); } - // we remove the original argument name ... - mutableArguments.Remove(argumentName); - // and allow the argument to be replaces with a new argument that could also have // a new name. mutableArguments.Add(newArgumentValue.Name, newArgumentValue); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs index 4b665e1c6fe..9211f4d4c0e 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs @@ -2,7 +2,6 @@ using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Execution.Processing; @@ -26,7 +25,7 @@ public IServiceProvider Services public Schema Schema => _operationContext.Schema; - public IOperation Operation => _operationContext.Operation; + public Operation Operation => _operationContext.Operation; public IOperationResultBuilder OperationResult => _operationResultBuilder; @@ -48,7 +47,7 @@ public void ReportError(string errorMessage) ErrorBuilder.New() .SetMessage(errorMessage) .SetPath(Path) - .AddLocation(_selection.SyntaxNode) + .AddLocations(_selection) .Build()); } @@ -75,7 +74,7 @@ public void ReportError(Exception exception, Action? configure = n var errorBuilder = ErrorBuilder .FromException(exception) .SetPath(Path) - .AddLocation(_selection.SyntaxNode); + .AddLocations(_selection); configure?.Invoke(errorBuilder); @@ -108,14 +107,14 @@ void ReportSingle(IError singleError) { foreach (var ie in ar.Errors) { - var errorWithPath = EnsurePathAndLocation(ie, _selection.SyntaxNode, Path); + var errorWithPath = EnsurePathAndLocation(ie, _selection.SyntaxNodes[0].Node, Path); _operationContext.Result.AddError(errorWithPath, _selection); diagnosticEvents.ResolverError(this, errorWithPath); } } else { - var errorWithPath = EnsurePathAndLocation(handled, _selection.SyntaxNode, Path); + var errorWithPath = EnsurePathAndLocation(handled, _selection.SyntaxNodes[0].Node, Path); _operationContext.Result.AddError(errorWithPath, _selection); diagnosticEvents.ResolverError(this, errorWithPath); } @@ -290,3 +289,16 @@ c is null s))); } } + +file static class Extensions +{ + public static ErrorBuilder AddLocations(this ErrorBuilder errorBuilder, Selection selection) + { + foreach (var (node, _) in selection.SyntaxNodes) + { + errorBuilder.AddLocation(node); + } + + return errorBuilder; + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs index 6f29541152f..73d88af6347 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs @@ -18,7 +18,7 @@ public MiddlewareContext() public void Initialize( OperationContext operationContext, - ISelection selection, + Selection selection, ObjectResult parentResult, int responseIndex, object? parent, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs index c8e21be1f27..20c1d467e6f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs @@ -5,7 +5,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; -using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Execution.ThrowHelper; namespace HotChocolate.Execution.Processing; @@ -16,13 +15,13 @@ private sealed class PureResolverContext(MiddlewareContext parentContext) : IRes { private ITypeConverter? _typeConverter; private IReadOnlyDictionary _argumentValues = null!; - private ISelection _selection = null!; + private Selection _selection = null!; private ObjectType _parentType = null!; private ObjectResult _parentResult = null!; private object? _parent; public bool Initialize( - ISelection selection, + Selection selection, ObjectType parentType, ObjectResult parentResult, object? parent) @@ -60,9 +59,9 @@ public void Clear() public ObjectType ObjectType => _parentType; - public IOperation Operation => parentContext.Operation; + public Operation Operation => parentContext.Operation; - public ISelection Selection => _selection; + public Selection Selection => _selection; public Path Path => PathHelper.CreatePathFromContext(_selection, _parentResult, -1); @@ -77,9 +76,9 @@ public void ReportError(IError error) public void ReportError(Exception exception, Action? configure = null) => throw new NotSupportedException(); - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) => throw new NotSupportedException(); @@ -135,7 +134,7 @@ public T ArgumentValue(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return CoerceArgumentValue(argument); @@ -148,7 +147,7 @@ public TValueNode ArgumentLiteral(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } var literal = argument.ValueLiteral!; @@ -159,7 +158,11 @@ public TValueNode ArgumentLiteral(string name) } throw ResolverContext_LiteralNotCompatible( - _selection.SyntaxNode, Path, name, typeof(TValueNode), literal.GetType()); + _selection.SyntaxNodes[0].Node, + Path, + name, + typeof(TValueNode), + literal.GetType()); } public Optional ArgumentOptional(string name) @@ -168,7 +171,7 @@ public Optional ArgumentOptional(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return argument.IsDefaultValue @@ -180,10 +183,10 @@ public ValueKind ArgumentKind(string name) { if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } - // There can only be no kind if there was an error which would have + // There can only be no kind of there was an error which would have // already been raised at this point. return argument.Kind ?? ValueKind.Unknown; } @@ -243,7 +246,10 @@ private T CoerceArgumentValue(ArgumentValue argument) if (typeof(IValueNode).IsAssignableFrom(typeof(T))) { throw ResolverContext_LiteralsNotSupported( - _selection.SyntaxNode, Path, argument.Name, typeof(T)); + _selection.SyntaxNodes[0].Node, + Path, + argument.Name, + typeof(T)); } // If the object is internally held as a dictionary structure we will try to @@ -270,7 +276,7 @@ private T CoerceArgumentValue(ArgumentValue argument) // we are unable to convert the argument to the request type. throw ResolverContext_CannotConvertArgument( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T), diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs index 8bf018d95db..660e99ded09 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs @@ -14,7 +14,7 @@ internal partial class MiddlewareContext public ObjectField Field => _selection.Field; - public ISelection Selection => _selection; + public Selection Selection => _selection; public string ResponseName => _selection.ResponseName; @@ -25,7 +25,7 @@ internal partial class MiddlewareContext public PureFieldDelegate? PureResolver => _selection.PureResolver; public bool TryCreatePureContext( - ISelection selection, + Selection selection, ObjectType parentType, ObjectResult parentResult, object? parent, @@ -41,7 +41,7 @@ public bool TryCreatePureContext( return false; } - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, Selection? selection = null, bool allowInternals = false) @@ -52,28 +52,11 @@ public IReadOnlyList GetSelections( if (selection.IsLeaf) { - return []; + return default; } var selectionSet = _operationContext.CollectFields(selection, typeContext); - - if (selectionSet.IsConditional) - { - var operationIncludeFlags = _operationContext.IncludeFlags; - var finalFields = new List(); - - foreach (var childSelection in selectionSet.Selections) - { - if (childSelection.IsIncluded(operationIncludeFlags, allowInternals)) - { - finalFields.Add(childSelection); - } - } - - return finalFields; - } - - return selectionSet.Selections; + return new SelectionEnumerator(selectionSet, _operationContext.IncludeFlags); } public ISelectionCollection Select() diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs new file mode 100644 index 00000000000..d5b73c6b40a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs @@ -0,0 +1,97 @@ +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +internal sealed partial class OperationCompiler2 +{ + internal static FieldDelegate CreateFieldPipeline(Schema schema, ObjectField field, FieldNode selection) + { + var pipeline = field.Middleware; + + if (selection.Directives.Count == 0) + { + return pipeline; + } + + var pipelineComponents = new List(); + + // if we have selection directives we will inspect them and try to build a + // pipeline from them if they have middleware components. + BuildDirectivePipeline(schema, selection, pipelineComponents); + + // if we found middleware components on the selection directives we will build a new + // pipeline. + if (pipelineComponents.Count > 0) + { + var next = pipeline; + + for (var i = pipelineComponents.Count - 1; i >= 0; i--) + { + next = pipelineComponents[i](next); + } + + pipeline = next; + } + + return pipeline; + } + + private static PureFieldDelegate? TryCreatePureField( + Schema schema, + ObjectField field, + FieldNode selection) + { + if (field.PureResolver is not null && selection.Directives.Count == 0) + { + return field.PureResolver; + } + + for (var i = 0; i < selection.Directives.Count; i++) + { + if (schema.DirectiveTypes.TryGetDirective(selection.Directives[i].Name.Value, out var type) + && type.Middleware is not null) + { + return null; + } + } + + return field.PureResolver; + } + + // TODO : this needs a rewrite, we need to discuss how we handle field merging with directives + private static void BuildDirectivePipeline( + Schema schema, + FieldNode selection, + List pipelineComponents) + { + for (var i = 0; i < selection.Directives.Count; i++) + { + var directiveNode = selection.Directives[i]; + if (schema.DirectiveTypes.TryGetDirective(directiveNode.Name.Value, out var directiveType) + && directiveType.Middleware is not null) + { + Directive directive; + try + { + directive = new Directive( + directiveType, + directiveNode, + directiveType.Parse(directiveNode)); + } + catch (SerializationException ex) + { + throw new SerializationException( + ErrorBuilder.FromError(ex.Errors[0]) + .TryAddLocation(directiveNode) + .Build(), + ex.Type); + } + + var directiveMiddleware = directiveType.Middleware; + pipelineComponents.Add(next => directiveMiddleware(next, directive)); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 0984e29625a..211d81d9e7d 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -241,6 +241,14 @@ private SelectionSet BuildSelectionSet( } var field = typeContext.Fields[first.Node.Name.Value]; + var fieldDelegate = CreateFieldPipeline(_schema, field, first.Node); + var pureFieldDelegate = TryCreatePureField(_schema, field, first.Node); + var arguments = ArgumentMap.Empty; + + if (first.Node.Arguments.Count > 0) + { + arguments = CoerceArgumentValues(field, first.Node); + } var selection = new Selection( ++lastId, @@ -248,7 +256,10 @@ private SelectionSet BuildSelectionSet( field, nodes.ToArray(), includeFlags.ToArray(), - isInternal); + isInternal, + arguments, + fieldDelegate, + pureFieldDelegate); // Register the selection in the elements array compilationContext.Register(selection, selection.Id); @@ -435,7 +446,7 @@ public void Register(object element, int id) /// /// The flags that must be all set for this selection to be included. /// -internal sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); +public sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); internal class IncludeConditionCollection : ICollection { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs index 862fb41a59a..6e74fb6e5ea 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs @@ -21,19 +21,6 @@ internal set } } - /// - /// Gets the backlog of the task that shall be processed after - /// all the main tasks have been executed. - /// - public DeferredWorkScheduler DeferredScheduler - { - get - { - AssertInitialized(); - return _deferredWorkScheduler; - } - } - /// /// The result helper which provides utilities to build up the result. /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index 26be9ca9c7f..1d44a51e445 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -28,7 +28,6 @@ internal Selection( ulong[] includeFlags, bool isInternal, ArgumentMap? arguments = null, - bool isParallelExecutable = true, FieldDelegate? resolverPipeline = null, PureFieldDelegate? pureResolver = null) { @@ -48,7 +47,7 @@ internal Selection( ResolverPipeline = resolverPipeline; PureResolver = pureResolver; Strategy = InferStrategy( - isSerial: !isParallelExecutable, + isSerial: !field.IsParallelExecutable, hasPureResolver: pureResolver is not null); _syntaxNodes = syntaxNodes; _includeFlags = includeFlags; @@ -106,7 +105,7 @@ internal Selection( public PureFieldDelegate? PureResolver { get; private set; } - internal ReadOnlySpan SyntaxNodes => _syntaxNodes; + public ReadOnlySpan SyntaxNodes => _syntaxNodes; IEnumerable ISelection.GetSyntaxNodes() { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs index c867fbb89d8..b614190999a 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs @@ -149,7 +149,7 @@ internal static FieldDelegate CreateFieldPipeline( pipeline = next; } - // at last we clear the rented lists + // at last, we clear the rented lists processed.Clear(); pipelineComponents.Clear(); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompilerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompilerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 2729033aa3b..08a474f7f21 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -143,6 +143,34 @@ True TextJsonResources.resx + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + OperationCompiler.cs + + + + OperationCompiler.cs + diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs index 0c79f8c852f..3ad1043b3b4 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs @@ -33,7 +33,7 @@ public interface IMiddlewareContext : IResolverContext /// Executes the field resolver and returns its result. /// /// - /// The type to which the result shall be casted. + /// The type to which the result shall be cast. /// /// /// Returns the resolved field value. diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index 207af448055..5eef2e5429d 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -249,9 +249,9 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Returns the pre-compiled selections for the /// with the specified . /// - IReadOnlyList GetSelections( + SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false); /// diff --git a/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs new file mode 100644 index 00000000000..aed4577252b --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs @@ -0,0 +1,77 @@ +using System.Collections; +using HotChocolate.Execution.Processing; + +namespace HotChocolate.Resolvers; + +/// +/// An SelectionSet enumerator. +/// +public struct SelectionEnumerator : IEnumerable, IEnumerator +{ + private readonly SelectionSet _selectionSet; + private readonly ulong _includeFlags; + private int _position = -1; + + internal SelectionEnumerator(SelectionSet selectionSet, ulong includeFlags) + { + _selectionSet = selectionSet; + _includeFlags = includeFlags; + Current = null!; + } + + /// + /// The currently selected selection. + /// + public Selection Current { get; private set; } + + object? IEnumerator.Current => Current; + + /// + /// Moves to the next visible selection. + /// + /// + /// true if there is another visible selection. + /// + public bool MoveNext() + { + if (_selectionSet is null) + { + return false; + } + + var length = _selectionSet.Selections.Length; + + while (_position < length) + { + _position++; + + if (_position >= length) + { + break; + } + + var selection = _selectionSet.Selections[_position]; + if (selection.IsIncluded(_includeFlags)) + { + Current = selection; + return true; + } + } + + Current = null!; + return false; + } + + public void Reset() + { + _position = -1; + } + + public SelectionEnumerator GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + + public void Dispose() { } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs index 1cf8c384bb6..de454de6cd1 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs @@ -247,7 +247,7 @@ public bool IsInternal /// /// This element does not represent the data of a selection set. /// - public ISelectionSet AssertSelectionSet() + public SelectionSet AssertSelectionSet() { var selectionSet = SelectionSet; @@ -268,7 +268,7 @@ public ISelectionSet AssertSelectionSet() /// /// This element does not represent the data of a field selection. /// - public ISelection AssertSelection() + public Selection AssertSelection() { var selection = Selection; diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs index b76011d25ca..06e15a446d4 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs @@ -115,7 +115,7 @@ public override string ToString() private string DebuggerDisplay => Value.ValueKind == JsonValueKind.Undefined ? "" : $"\"{ToString()}\""; - public void Deconstruct(out ISelection selection, out ResultElement value) + public void Deconstruct(out Selection selection, out ResultElement value) { selection = AssertSelection(); value = Value; diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs index 9106c0b2bfe..d1616dd5694 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs @@ -4,11 +4,9 @@ namespace HotChocolate.Types.Relay; internal sealed class NodeTypeFeature : ISealable { - private NodeResolverInfo? _nodeResolver; - public NodeResolverInfo? NodeResolver { - get => _nodeResolver; + get; set { if (IsReadOnly) @@ -17,7 +15,7 @@ public NodeResolverInfo? NodeResolver "The node resolver cannot be set after the feature is sealed."); } - _nodeResolver = value; + field = value; } } From fe8a8e63a4c0ed6c2b6c4347cc294a659b69b297 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 1 Dec 2025 09:16:15 +0100 Subject: [PATCH 12/30] wip --- .../Internal/ArgumentCoercionHelper.cs | 4 +- .../Internal/FieldMapPooledObjectPolicy.cs | 21 + .../Pipeline/OperationResolverMiddleware.cs | 89 +- .../Types/Execution/Processing/ArgumentMap.cs | 2 +- .../Types/Execution/Processing/Operation.cs | 4 +- .../OperationCompiler.BacklogItem.cs | 25 - .../OperationCompiler.CoerceArgumentValues.cs | 2 +- .../OperationCompiler.CompileResolver.cs | 2 +- .../OperationCompiler.Optimizers.cs | 62 -- .../OperationCompiler.SelectionSetInfo.cs | 15 - .../Execution/Processing/OperationCompiler.cs | 4 +- .../Processing/OperationCompilerOptimizers.cs | 54 +- .../Processing/OperationCompilerPool.cs | 31 - .../Processing/OperationContext.Pooling.cs | 12 +- .../_OperationCompiler.ArgumentValues.cs | 217 ----- .../_OperationCompiler.CompilerContext.cs | 46 - .../Processing/_OperationCompiler.cs | 787 ------------------ .../Types/Execution/RequestExecutorManager.cs | 16 + .../ObjectPoolProviderExtensions.cs | 15 + .../Extensions/ResolverContextExtensions.cs | 12 +- .../Core/src/Types/HotChocolate.Types.csproj | 24 + .../Pipeline/OperationPlanMiddleware.cs | 2 +- 22 files changed, 120 insertions(+), 1326 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Internal/FieldMapPooledObjectPolicy.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs create mode 100644 src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs index d1ee668a617..17ff11f9ac3 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs @@ -41,7 +41,7 @@ public static bool TryCoerceArguments( // signal that this resolver task has errors and shall end. if (arguments.HasErrors) { - foreach (var argument in arguments) + foreach (var argument in arguments.ArgumentValues) { if (argument.HasError) { @@ -73,7 +73,7 @@ internal static void CoerceArguments( { // if there are arguments that have variables and need variable replacement we will // rewrite the arguments that need variable replacement. - foreach (var argument in arguments) + foreach (var argument in arguments.ArgumentValues) { if (argument.IsFullyCoerced) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Internal/FieldMapPooledObjectPolicy.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/FieldMapPooledObjectPolicy.cs new file mode 100644 index 00000000000..aca098df582 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Internal/FieldMapPooledObjectPolicy.cs @@ -0,0 +1,21 @@ +using HotChocolate.Execution.Processing; +using Microsoft.Extensions.ObjectPool; + +namespace HotChocolate.Execution; + +internal sealed class FieldMapPooledObjectPolicy + : DefaultPooledObjectPolicy>> +{ + public override OrderedDictionary> Create() => new(StringComparer.Ordinal); + + public override bool Return(OrderedDictionary> obj) + { + if (obj.Count > 256) + { + return false; + } + + obj.Clear(); + return base.Return(obj); + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs index 730872b3e4f..9c85a312c1d 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs @@ -1,10 +1,6 @@ -using System.Runtime.CompilerServices; using HotChocolate.Execution.Instrumentation; using HotChocolate.Execution.Processing; -using HotChocolate.Language; -using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.ObjectPool; +using HotChocolate.Fusion.Rewriters; using static HotChocolate.Execution.ErrorHelper; namespace HotChocolate.Execution.Pipeline; @@ -12,24 +8,24 @@ namespace HotChocolate.Execution.Pipeline; internal sealed class OperationResolverMiddleware { private readonly RequestDelegate _next; - private readonly ObjectPool _operationCompilerPool; - private readonly OperationCompilerOptimizers _operationCompilerOptimizers; + private readonly OperationCompiler _operationPlanner; + private readonly DocumentRewriter _documentRewriter; private readonly IExecutionDiagnosticEvents _diagnosticEvents; private OperationResolverMiddleware( RequestDelegate next, - ObjectPool operationCompilerPool, - OperationCompilerOptimizers operationCompilerOptimizer, + ISchemaDefinition schema, + OperationCompiler operationPlanner, IExecutionDiagnosticEvents diagnosticEvents) { ArgumentNullException.ThrowIfNull(next); - ArgumentNullException.ThrowIfNull(operationCompilerPool); - ArgumentNullException.ThrowIfNull(operationCompilerOptimizer); + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(operationPlanner); ArgumentNullException.ThrowIfNull(diagnosticEvents); _next = next; - _operationCompilerPool = operationCompilerPool; - _operationCompilerOptimizers = operationCompilerOptimizer; + _operationPlanner = operationPlanner; + _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); _diagnosticEvents = diagnosticEvents; } @@ -46,21 +42,17 @@ public async ValueTask InvokeAsync(RequestContext context) { using (_diagnosticEvents.CompileOperation(context)) { - var operationDef = documentInfo.Document.GetOperation(context.Request.OperationName); - var operationType = ResolveOperationType(operationDef.Operation, Unsafe.As(context.Schema)); - - if (operationType is null) - { - context.Result = RootTypeNotFound(operationDef.Operation); - return; - } - - operation = CompileOperation( - documentInfo.Document, - operationDef, - operationType, + // Before we can plan an operation, we must de-fragmentize it and remove static include conditions. + var operationDocument = documentInfo.Document; + var operationName = context.Request.OperationName; + operationDocument = _documentRewriter.RewriteDocument(operationDocument, operationName); + var operationNode = operationDocument.GetOperation(operationName); + + // After optimizing the query structure we can begin the planning process. + operation = _operationPlanner.Compile( operationId ?? Guid.NewGuid().ToString("N"), - context.Schema); + documentInfo.Hash.Value, + operationNode); context.SetOperation(operation); } @@ -72,51 +64,18 @@ public async ValueTask InvokeAsync(RequestContext context) context.Result = StateInvalidForOperationResolver(); } - private IOperation CompileOperation( - DocumentNode document, - OperationDefinitionNode operationDefinition, - ObjectType operationType, - string operationId, - ISchemaDefinition schema) - { - var request = new OperationCompilerRequest( - operationId, - document, - operationDefinition, - operationType, - schema, - _operationCompilerOptimizers.OperationOptimizers, - _operationCompilerOptimizers.SelectionSetOptimizers); - - var compiler = _operationCompilerPool.Get(); - var operation = compiler.Compile(request); - _operationCompilerPool.Return(compiler); - - return operation; - } - - private static ObjectType? ResolveOperationType( - OperationType operationType, - Schema schema) - => operationType switch - { - OperationType.Query => schema.QueryType, - OperationType.Mutation => schema.MutationType, - OperationType.Subscription => schema.SubscriptionType, - _ => throw ThrowHelper.RootTypeNotSupported(operationType) - }; - public static RequestMiddlewareConfiguration Create() => new RequestMiddlewareConfiguration( (core, next) => { - var operationCompilerPool = core.Services.GetRequiredService>(); - var optimizers = core.SchemaServices.GetRequiredService(); + var schema = core.Schema; + var operationCompiler = core.Services.GetRequiredService(); var diagnosticEvents = core.SchemaServices.GetRequiredService(); + var middleware = new OperationResolverMiddleware( next, - operationCompilerPool, - optimizers, + schema, + operationCompiler, diagnosticEvents); return context => middleware.InvokeAsync(context); }, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs index 354e3bdfd73..118c9dbd800 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs @@ -99,7 +99,7 @@ IEnumerable IReadOnlyDictionary.Keys /// /// An of instances. /// - public ImmutableArray Values => _arguments.Values; + public ImmutableArray ArgumentValues => _arguments.Values; IEnumerable IReadOnlyDictionary.Values => _arguments.Values; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 72c8ed60f88..216d6fd00cd 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -19,7 +19,7 @@ public sealed class Operation : IOperation #endif private readonly ConcurrentDictionary<(int, string), SelectionSet> _selectionSets = []; - private readonly OperationCompiler2 _compiler; + private readonly OperationCompiler _compiler; private readonly IncludeConditionCollection _includeConditions; private readonly OperationFeatureCollection _features; private readonly bool _isFinal; @@ -33,7 +33,7 @@ internal Operation( ObjectType rootType, Schema schema, SelectionSet rootSelectionSet, - OperationCompiler2 compiler, + OperationCompiler compiler, IncludeConditionCollection includeConditions, int lastId, object[] elementsById, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs deleted file mode 100644 index 3345a16fd42..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -public sealed partial class OperationCompiler -{ - private readonly struct BacklogItem( - ObjectType type, - int selectionSetId, - Selection selection, - SelectionPath path, - ImmutableArray optimizers) - { - public ObjectType Type { get; } = type; - - public int SelectionSetId { get; } = selectionSetId; - - public Selection Selection { get; } = selection; - - public SelectionPath Path { get; } = path; - - public ImmutableArray Optimizers { get; } = optimizers; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs index 8f6068bbf80..f601fa279a6 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs @@ -10,7 +10,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler2 +internal sealed partial class OperationCompiler { private ArgumentMap? CoerceArgumentValues( ObjectField field, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs index d5b73c6b40a..372d52fd5b7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler2 +internal sealed partial class OperationCompiler { internal static FieldDelegate CreateFieldPipeline(Schema schema, ObjectField field, FieldNode selection) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs deleted file mode 100644 index 50b9d9e2ecf..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Types; -using static HotChocolate.Execution.Processing.OperationCompilerOptimizerHelper; - -namespace HotChocolate.Execution.Processing; - -public partial class OperationCompiler -{ - private void OptimizeSelectionSet(CompilerContext context) - { - if (context.Optimizers.Length == 0) - { - return; - } - - var optimizerContext = - new SelectionSetOptimizerContext( - this, - context, - _selectionLookup, - _contextData, - _createFieldPipeline, - context.Path); - - if (context.Optimizers.Length == 1) - { - context.Optimizers[0].OptimizeSelectionSet(optimizerContext); - } - else - { - for (var i = 0; i < context.Optimizers.Length; i++) - { - context.Optimizers[i].OptimizeSelectionSet(optimizerContext); - } - } - } - - private static ImmutableArray ResolveOptimizers( - ImmutableArray optimizers, - ObjectField field) - { - if (!TryGetOptimizers(field, out var fieldOptimizers)) - { - return optimizers; - } - - if (optimizers.IsEmpty) - { - return fieldOptimizers; - } - - foreach (var optimizer in fieldOptimizers) - { - if (!optimizers.Contains(optimizer)) - { - optimizers = optimizers.Add(optimizer); - } - } - - return optimizers; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs deleted file mode 100644 index 7c46db01ff7..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using HotChocolate.Language; - -namespace HotChocolate.Execution.Processing; - -public sealed partial class OperationCompiler -{ - internal readonly struct SelectionSetInfo( - SelectionSetNode selectionSet, - ulong includeCondition) - { - public SelectionSetNode SelectionSet { get; } = selectionSet; - - public ulong IncludeCondition { get; } = includeCondition; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 211d81d9e7d..33caee22385 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -9,7 +9,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler2 +internal sealed partial class OperationCompiler { private readonly Schema _schema; private readonly ObjectPool>> _fieldsPool; @@ -17,7 +17,7 @@ internal sealed partial class OperationCompiler2 private readonly InputParser _inputValueParser; private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; - public OperationCompiler2( + public OperationCompiler( Schema schema, InputParser inputValueParser, ObjectPool>> fieldsPool) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs index 726018644dd..e60919fe435 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs @@ -4,6 +4,7 @@ namespace HotChocolate.Execution.Processing; +// TODO : We might remove this internal sealed class OperationCompilerOptimizers { private ImmutableArray _operationOptimizers; @@ -49,56 +50,3 @@ private enum PropertyInitFlags SelectionSetOptimizers = 2 } } - -public readonly struct OperationCompilerRequest -{ - public OperationCompilerRequest( - string id, - DocumentNode document, - OperationDefinitionNode definition, - ObjectType rootType, - ISchemaDefinition schema, - ImmutableArray? operationOptimizers = null, - ImmutableArray? selectionSetOptimizers = null) - { - ArgumentException.ThrowIfNullOrEmpty(id); - - Id = id; - Document = document ?? throw new ArgumentNullException(nameof(document)); - Definition = definition ?? throw new ArgumentNullException(nameof(definition)); - RootType = rootType ?? throw new ArgumentNullException(nameof(rootType)); - Schema = schema ?? throw new ArgumentNullException(nameof(schema)); - OperationOptimizers = operationOptimizers ?? []; - SelectionSetOptimizers = selectionSetOptimizers ?? []; - } - - /// - /// Gets the internal unique identifier for this operation. - /// - public string Id { get; } - - /// - /// Gets the parsed query document that contains the - /// operation-. - /// - public DocumentNode Document { get; } - - /// - /// Gets the syntax node representing the operation definition. - /// - public OperationDefinitionNode Definition { get; } - - /// - /// Gets the root type on which the operation is executed. - /// - public ObjectType RootType { get; } - - /// - /// Gets the schema against which the operation shall be executed. - /// - public ISchemaDefinition Schema { get; } - - public ImmutableArray OperationOptimizers { get; } - - public ImmutableArray SelectionSetOptimizers { get; } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs deleted file mode 100644 index d62a317d0cb..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs +++ /dev/null @@ -1,31 +0,0 @@ -using HotChocolate.Types; -using Microsoft.Extensions.ObjectPool; - -namespace HotChocolate.Execution.Processing; - -internal sealed class OperationCompilerPool : DefaultObjectPool -{ - public OperationCompilerPool(InputParser inputParser) - : base(new Policy(inputParser)) - { - } - - public OperationCompilerPool(InputParser inputParser, int maximumRetained) - : base(new Policy(inputParser), maximumRetained) - { - } - - private sealed class Policy : IPooledObjectPolicy - { - private readonly InputParser _inputParser; - - public Policy(InputParser inputParser) - { - _inputParser = inputParser; - } - - public OperationCompiler Create() => new(_inputParser); - - public bool Return(OperationCompiler obj) => true; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs index d5e09ed7818..c396301d366 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs @@ -6,7 +6,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; -using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Execution.ThrowHelper; namespace HotChocolate.Execution.Processing; @@ -16,7 +15,6 @@ internal sealed partial class OperationContext private readonly IFactory _resolverTaskFactory; private readonly WorkScheduler _workScheduler; private WorkScheduler _currentWorkScheduler; - private readonly DeferredWorkScheduler _deferredWorkScheduler; private readonly ResultBuilder _resultBuilder; private readonly AggregateServiceScopeInitializer _serviceScopeInitializer; private RequestContext _requestContext = null!; @@ -26,7 +24,7 @@ internal sealed partial class OperationContext private IExecutionDiagnosticEvents _diagnosticEvents = null!; private IDictionary _contextData = null!; private CancellationToken _requestAborted; - private IOperation _operation = null!; + private Operation _operation = null!; private IVariableValueCollection _variables = null!; private IServiceProvider _services = null!; private Func _resolveQueryRootValue = null!; @@ -45,7 +43,6 @@ public OperationContext( _resolverTaskFactory = resolverTaskFactory; _workScheduler = new WorkScheduler(this); _currentWorkScheduler = _workScheduler; - _deferredWorkScheduler = new DeferredWorkScheduler(); _resultBuilder = resultBuilder; _serviceScopeInitializer = serviceScopeInitializer; Converter = typeConverter; @@ -59,7 +56,7 @@ public void Initialize( RequestContext requestContext, IServiceProvider scopedServices, IBatchDispatcher batchDispatcher, - IOperation operation, + Operation operation, IVariableValueCollection variables, object? rootValue, Func resolveQueryRootValue, @@ -82,9 +79,8 @@ public void Initialize( _variableIndex = variableIndex; _isInitialized = true; - IncludeFlags = _operation.CreateIncludeFlags(variables); + IncludeFlags = operation.CreateIncludeFlags(variables); _workScheduler.Initialize(batchDispatcher); - _deferredWorkScheduler.Initialize(this); _resultBuilder.Initialize(_requestContext, _diagnosticEvents); if (requestContext.RequestIndex != -1) @@ -120,7 +116,6 @@ public void InitializeFrom(OperationContext context) IncludeFlags = _operation.CreateIncludeFlags(_variables); _workScheduler.Initialize(_batchDispatcher); - _deferredWorkScheduler.InitializeFrom(this, context._deferredWorkScheduler); _resultBuilder.Initialize(_requestContext, _diagnosticEvents); if (context._requestContext.RequestIndex != -1) @@ -143,7 +138,6 @@ public void Clean() _currentWorkScheduler = _workScheduler; _workScheduler.Clear(); _resultBuilder.Clear(); - _deferredWorkScheduler.Clear(); _requestContext = null!; _schema = null!; _errorHandler = null!; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs deleted file mode 100644 index b614190999a..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Language; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using static System.StringComparer; - -namespace HotChocolate.Execution.Processing; - -public sealed partial class OperationCompiler -{ - private ArgumentMap? CoerceArgumentValues( - ObjectField field, - FieldNode selection) - { - if (field.Arguments.Count == 0) - { - return null; - } - - var arguments = new Dictionary(Ordinal); - - for (var i = 0; i < selection.Arguments.Count; i++) - { - var argumentValue = selection.Arguments[i]; - if (field.Arguments.TryGetField( - argumentValue.Name.Value, - out var argument)) - { - arguments[argument.Name] = CreateArgumentValue(argument, argumentValue, argumentValue.Value, false); - } - } - - for (var i = 0; i < field.Arguments.Count; i++) - { - var argument = field.Arguments[i]; - if (!arguments.ContainsKey(argument.Name)) - { - var value = argument.DefaultValue ?? NullValueNode.Default; - arguments[argument.Name] = CreateArgumentValue(argument, null, value, true); - } - } - - return new ArgumentMap(arguments); - } - - private ArgumentValue CreateArgumentValue( - Argument argument, - ArgumentNode? argumentValue, - IValueNode value, - bool isDefaultValue) - { - var validationResult = - ArgumentNonNullValidator.Validate( - argument, - value, - Path.Root.Append(argument.Name)); - - if (argumentValue is not null && validationResult.HasErrors) - { - return new ArgumentValue( - argument, - ErrorHelper.ArgumentNonNullError( - argumentValue, - validationResult)); - } - - if (argument.Type.IsLeafType() && CanBeCompiled(value)) - { - try - { - return new ArgumentValue( - argument, - value.GetValueKind(), - true, - isDefaultValue, - _parser.ParseLiteral(value, argument), - value); - } - catch (SerializationException ex) - { - return new ArgumentValue( - argument, - ErrorHelper.ArgumentValueIsInvalid(argumentValue, ex)); - } - } - - return new ArgumentValue( - argument, - value.GetValueKind(), - false, - isDefaultValue, - null, - value); - } - - private static bool CanBeCompiled(IValueNode valueLiteral) - { - switch (valueLiteral.Kind) - { - case SyntaxKind.Variable: - case SyntaxKind.ObjectValue: - return false; - - case SyntaxKind.ListValue: - var list = (ListValueNode)valueLiteral; - for (var i = 0; i < list.Items.Count; i++) - { - if (!CanBeCompiled(list.Items[i])) - { - return false; - } - } - break; - } - - return true; - } - - internal static FieldDelegate CreateFieldPipeline( - Schema schema, - ObjectField field, - FieldNode selection, - Path? path, - HashSet processed, - List pipelineComponents) - { - var pipeline = field.Middleware; - - if (selection.Directives.Count == 0) - { - return pipeline; - } - - // if we have selection directives we will inspect them and try to build a - // pipeline from them if they have middleware components. - BuildDirectivePipeline(schema, selection, path, processed, pipelineComponents); - - // if we found middleware components on the selection directives we will build a new - // pipeline. - if (pipelineComponents.Count > 0) - { - var next = pipeline; - - for (var i = pipelineComponents.Count - 1; i >= 0; i--) - { - next = pipelineComponents[i](next); - } - - pipeline = next; - } - - // at last, we clear the rented lists - processed.Clear(); - pipelineComponents.Clear(); - - return pipeline; - } - - private static PureFieldDelegate? TryCreatePureField( - Schema schema, - ObjectField field, - FieldNode selection) - { - if (field.PureResolver is not null && selection.Directives.Count == 0) - { - return field.PureResolver; - } - - for (var i = 0; i < selection.Directives.Count; i++) - { - if (schema.DirectiveTypes.TryGetDirective(selection.Directives[i].Name.Value, out var type) - && type.Middleware is not null) - { - return null; - } - } - - return field.PureResolver; - } - - private static void BuildDirectivePipeline( - Schema schema, - FieldNode selection, - Path? path, - HashSet processed, - List pipelineComponents) - { - for (var i = 0; i < selection.Directives.Count; i++) - { - var directiveNode = selection.Directives[i]; - if (schema.DirectiveTypes.TryGetDirective(directiveNode.Name.Value, out var directiveType) - && directiveType.Middleware is not null - && (directiveType.IsRepeatable || processed.Add(directiveType.Name))) - { - Debug.Assert(path != null, "path should not be null if a directive is present."); - Directive directive; - try - { - directive = new Directive( - directiveType, - directiveNode, - directiveType.Parse(directiveNode)); - } - catch (SerializationException ex) - { - throw new SerializationException( - ErrorBuilder.FromError(ex.Errors[0]).SetPath(path).TryAddLocation(directiveNode).Build(), - ex.Type, - path); - } - - var directiveMiddleware = directiveType.Middleware; - pipelineComponents.Add(next => directiveMiddleware(next, directive)); - } - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs deleted file mode 100644 index 977984e19d6..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Language; -using HotChocolate.Types; -using static System.StringComparer; - -namespace HotChocolate.Execution.Processing; - -public sealed partial class OperationCompiler -{ - internal sealed class CompilerContext(Schema schema, DocumentNode document) - { - public Schema Schema { get; } = schema; - - public DocumentNode Document { get; } = document; - - public ObjectType Type { get; private set; } = null!; - - public SelectionSetInfo[] SelectionInfos { get; private set; } = null!; - - public SelectionPath Path { get; private set; } = SelectionPath.Root; - - public Dictionary Fields { get; } = new(Ordinal); - - public List Fragments { get; } = []; - - public SelectionVariants SelectionVariants { get; private set; } = null!; - - public ImmutableArray Optimizers { get; private set; } - - public void Initialize( - ObjectType type, - SelectionVariants selectionVariants, - SelectionSetInfo[] selectionInfos, - SelectionPath path, - ImmutableArray? optimizers = null) - { - Type = type; - SelectionVariants = selectionVariants; - SelectionInfos = selectionInfos; - Path = path; - Optimizers = optimizers ?? []; - Fields.Clear(); - Fragments.Clear(); - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs deleted file mode 100644 index 174bc3ca348..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs +++ /dev/null @@ -1,787 +0,0 @@ -using System.Collections.Immutable; -using System.Runtime.CompilerServices; -using HotChocolate.Language; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using HotChocolate.Utilities; -using static System.Runtime.InteropServices.CollectionsMarshal; -using static System.Runtime.InteropServices.MemoryMarshal; -using static System.StringComparer; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; - -namespace HotChocolate.Execution.Processing; - -/// -/// The operation compiler will analyze a specific operation of a GraphQL request document -/// and create from it an optimized executable operation tree. -/// -public sealed partial class OperationCompiler -{ - private readonly InputParser _parser; - private readonly CreateFieldPipeline _createFieldPipeline; - private readonly Queue _backlog = []; - private readonly Dictionary _selectionLookup = []; - private readonly Dictionary _selectionSetIdLookup = []; - private readonly Dictionary _selectionVariants = []; - private readonly Dictionary _fragmentDefinitions = []; - private readonly Dictionary _contextData = []; - private readonly List _selections = []; - private readonly HashSet _directiveNames = new(Ordinal); - private readonly List _pipelineComponents = []; - private readonly HashSet _enqueuedSelectionSets = []; - private IncludeCondition[] _includeConditions = []; - private CompilerContext? _deferContext; - private ImmutableArray _operationOptimizers = []; - private int _nextSelectionId; - private int _nextSelectionSetRefId; - private int _nextSelectionSetId; - private int _nextFragmentId; - private bool _hasIncrementalParts; - private OperationCompilerMetrics _metrics; - - public OperationCompiler(InputParser parser) - { - _parser = parser ?? throw new ArgumentNullException(nameof(parser)); - - _createFieldPipeline = - (schema, field, selection) - => CreateFieldPipeline( - schema, - field, - selection, - null, - _directiveNames, - _pipelineComponents); - } - - internal OperationCompilerMetrics Metrics => _metrics; - - public IOperation Compile(OperationCompilerRequest request) - { - ArgumentException.ThrowIfNullOrEmpty(request.Id, nameof(request)); - - try - { - var backlogMaxSize = 0; - var selectionSetOptimizers = request.SelectionSetOptimizers; - _operationOptimizers = request.OperationOptimizers; - - // collect root fields - var rootPath = SelectionPath.Root; - var id = GetOrCreateSelectionSetRefId(request.Definition.SelectionSet, request.RootType.Name, rootPath); - var variants = GetOrCreateSelectionVariants(id); - SelectionSetInfo[] infos = [new(request.Definition.SelectionSet, 0)]; - - var context = new CompilerContext((Schema)request.Schema, request.Document); - context.Initialize(request.RootType, variants, infos, rootPath, selectionSetOptimizers); - CompileSelectionSet(context); - - // process consecutive selections - while (_backlog.Count > 0) - { - backlogMaxSize = Math.Max(backlogMaxSize, _backlog.Count); - - var current = _backlog.Dequeue(); - var type = current.Type; - variants = GetOrCreateSelectionVariants(current.SelectionSetId); - - if (!variants.ContainsSelectionSet(type)) - { - infos = _selectionLookup[current.Selection]; - context.Initialize(type, variants, infos, current.Path, current.Optimizers); - CompileSelectionSet(context); - } - } - - // create operation - var operation = CreateOperation(request); - - _metrics = new OperationCompilerMetrics( - _nextSelectionId, - _selectionVariants.Count, - backlogMaxSize); - - return operation; - } - finally - { - _nextSelectionId = 0; - _nextSelectionSetRefId = 0; - _nextSelectionId = 0; - _nextFragmentId = 0; - _hasIncrementalParts = false; - - _backlog.Clear(); - _selectionLookup.Clear(); - _selectionSetIdLookup.Clear(); - _selectionVariants.Clear(); - _fragmentDefinitions.Clear(); - _contextData.Clear(); - _selections.Clear(); - _directiveNames.Clear(); - _pipelineComponents.Clear(); - _enqueuedSelectionSets.Clear(); - - _operationOptimizers = []; - - _includeConditions = []; - _deferContext = null; - } - } - - private Operation CreateOperation(OperationCompilerRequest request) - { - var operation = new Operation( - request.Id, - request.Document, - request.Definition, - request.RootType, - request.Schema); - - var schema = Unsafe.As(request.Schema); - - var variants = new SelectionVariants[_selectionVariants.Count]; - - if (_operationOptimizers.Length == 0) - { - CompleteResolvers(schema); - - // if we do not have any optimizers, we will copy - // the variants and seal them in one go. - foreach (var item in _selectionVariants) - { - variants[item.Key] = item.Value; - item.Value.Seal(operation); - } - } - else - { - // if we have optimizers, we will first copy the variants to its array, - // after that we will run the optimizers and give them a chance to do some - // more mutations on the compiled selection variants. - // after we have executed all optimizers, we will seal the selection variants. - var context = new OperationOptimizerContext( - request.Id, - request.Document, - request.Definition, - schema, - request.RootType, - variants, - _includeConditions, - _contextData, - _hasIncrementalParts, - _createFieldPipeline); - - foreach (var item in _selectionVariants) - { - variants[item.Key] = item.Value; - } - - // we will complete the selection variants, sets and selections - // without sealing them so that analyzers in this step can fully - // inspect them. - var variantsSpan = variants.AsSpan(); - ref var variantsStart = ref GetReference(variantsSpan); - ref var variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length); - - while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) - { - variantsStart.Complete(operation); - variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; - } - - var optSpan = _operationOptimizers.AsSpan(); - ref var optStart = ref GetReference(optSpan); - ref var optEnd = ref Unsafe.Add(ref optStart, optSpan.Length); - - while (Unsafe.IsAddressLessThan(ref optStart, ref optEnd)) - { - optStart.OptimizeOperation(context); - optStart = ref Unsafe.Add(ref optStart, 1)!; - } - - CompleteResolvers(schema); - - variantsSpan = variants.AsSpan(); - variantsStart = ref GetReference(variantsSpan)!; - variantsEnd = ref Unsafe.Add(ref variantsStart, variantsSpan.Length)!; - - while (Unsafe.IsAddressLessThan(ref variantsStart, ref variantsEnd)) - { - variantsStart.Seal(operation); - variantsStart = ref Unsafe.Add(ref variantsStart, 1)!; - } - } - - operation.Seal(_contextData, variants, _hasIncrementalParts, _includeConditions); - return operation; - } - - private void CompleteResolvers(Schema schema) - { - ref var searchSpace = ref GetReference(AsSpan(_selections)); - - Path? path = null; - for (var i = 0; i < _selections.Count; i++) - { - var selection = Unsafe.Add(ref searchSpace, i); - path = path?.Append(selection.ResponseName); - if (selection.ResolverPipeline is null && selection.PureResolver is null) - { - var field = selection.Field; - var syntaxNode = selection.SyntaxNode; - if (syntaxNode.Directives.Count > 0 && path == null) - { - // create the path only on demand - path = PathHelper.CreatePathFromSelection(_selections, i + 1); - } - - var resolver = CreateFieldPipeline( - schema, - field, - syntaxNode, - path, - _directiveNames, - _pipelineComponents); - var pureResolver = TryCreatePureField(schema, field, syntaxNode); - selection.SetResolvers(resolver, pureResolver); - } - } - } - - private void CompileSelectionSet(CompilerContext context) - { - // We first collect the fields that we find in the selection set ... - CollectFields(context); - - // next we will call the selection set optimizers to rewrite the - // selection set if necessary. - OptimizeSelectionSet(context); - - // after that we start completing the selections and build the SelectionSet from - // the completed selections. - CompleteSelectionSet(context); - } - - private void CompleteSelectionSet(CompilerContext context) - { - var selections = new Selection[context.Fields.Values.Count]; - var fragments = context.Fragments.Count is not 0 - ? new Fragment[context.Fragments.Count] - : []; - var selectionIndex = 0; - var isConditional = false; - - foreach (var selection in context.Fields.Values) - { - // if the field of the selection returns a composite type, we will traverse - // the child selection-sets as well. - var fieldType = selection.Type.NamedType(); - var selectionSetId = -1; - - if (selection.IsConditional) - { - isConditional = true; - } - - // Determines if the type is a composite type. - if (fieldType.IsCompositeType()) - { - if (selection.SelectionSet is null) - { - // composite fields always have to have a selection-set - // otherwise we need to throw. - throw QueryCompiler_CompositeTypeSelectionSet(selection.SyntaxNode); - } - - var selectionPath = context.Path.Append(selection.ResponseName); - selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, fieldType.Name, selectionPath); - var possibleTypes = context.Schema.GetPossibleTypes(fieldType); - - if (_enqueuedSelectionSets.Add(selectionSetId)) - { - for (var i = possibleTypes.Count - 1; i >= 0; i--) - { - _backlog.Enqueue( - new BacklogItem( - possibleTypes[i], - selectionSetId, - selection, - selectionPath, - ResolveOptimizers(context.Optimizers, selection.Field))); - } - } - - // We are waiting for the latest stream and defer spec discussions to be codified - // before we change the overall stream handling. - // - // For now, we only allow streams on lists of composite types. - if (selection.SyntaxNode.IsStreamable()) - { - var streamDirective = selection.SyntaxNode.GetStreamDirective(); - var nullValue = NullValueNode.Default; - var ifValue = streamDirective?.GetArgumentValue(DirectiveNames.Stream.Arguments.If) ?? nullValue; - long ifConditionFlags = 0; - - if (ifValue.Kind is not SyntaxKind.NullValue) - { - var ifCondition = new IncludeCondition(ifValue, nullValue); - ifConditionFlags = GetSelectionIncludeCondition(ifCondition, 0); - } - - selection.MarkAsStream(ifConditionFlags); - _hasIncrementalParts = true; - } - } - - selection.SetSelectionSetId(selectionSetId); - selections[selectionIndex++] = selection; - _selections.Add(selection); - } - - if (context.Fragments.Count > 0) - { - for (var i = 0; i < context.Fragments.Count; i++) - { - fragments[i] = context.Fragments[i]; - } - } - - context.SelectionVariants.AddSelectionSet( - _nextSelectionSetId++, - context.Type, - selections, - fragments, - isConditional); - } - - private void CollectFields(CompilerContext context) - { - foreach (var selectionSetInfo in context.SelectionInfos) - { - CollectFields( - context, - selectionSetInfo.SelectionSet, - selectionSetInfo.IncludeCondition); - } - } - - private void CollectFields( - CompilerContext context, - SelectionSetNode selectionSet, - ulong includeCondition) - { - for (var j = 0; j < selectionSet.Selections.Count; j++) - { - ResolveFields(context, selectionSet.Selections[j], includeCondition); - } - } - - private void ResolveFields( - CompilerContext context, - ISelectionNode selection, - long includeCondition) - { - switch (selection.Kind) - { - case SyntaxKind.Field: - ResolveField( - context, - (FieldNode)selection, - includeCondition); - break; - - case SyntaxKind.InlineFragment: - ResolveInlineFragment( - context, - (InlineFragmentNode)selection, - includeCondition); - break; - - case SyntaxKind.FragmentSpread: - ResolveFragmentSpread( - context, - (FragmentSpreadNode)selection, - includeCondition); - break; - } - } - - private void ResolveField( - CompilerContext context, - FieldNode selection, - ulong includeCondition) - { - includeCondition = GetSelectionIncludeCondition(selection, includeCondition); - - var fieldName = selection.Name.Value; - var responseName = selection.Alias?.Value ?? fieldName; - - if (context.Type.Fields.TryGetField(fieldName, out var field)) - { - var fieldType = field.Type; - - if (context.Fields.TryGetValue(responseName, out var preparedSelection)) - { - preparedSelection.AddSelection(selection, includeCondition); - - if (selection.SelectionSet is not null) - { - var selectionSetInfo = new SelectionSetInfo( - selection.SelectionSet!, - includeCondition); - var selectionInfos = _selectionLookup[preparedSelection]; - var next = selectionInfos.Length; - Array.Resize(ref selectionInfos, next + 1); - selectionInfos[next] = selectionSetInfo; - _selectionLookup[preparedSelection] = selectionInfos; - } - } - else - { - var id = GetNextSelectionId(); - - // if this is the first time we've found a selection to this field, we have to - // create a new prepared selection. - preparedSelection = new Selection.Sealed( - id, - context.Type, - field, - fieldType, - selection.SelectionSet is not null - ? selection.WithSelectionSet( - selection.SelectionSet.WithSelections( - selection.SelectionSet.Selections)) - : selection, - responseName: responseName, - arguments: CoerceArgumentValues(field, selection), - includeConditions: includeCondition == 0 - ? null - : [includeCondition], - isParallelExecutable: field.IsParallelExecutable); - - context.Fields.Add(responseName, preparedSelection); - - if (selection.SelectionSet is not null) - { - var selectionSetInfo = new SelectionSetInfo( - selection.SelectionSet!, - includeCondition); - _selectionLookup.Add(preparedSelection, [selectionSetInfo]); - } - } - } - else - { - throw FieldDoesNotExistOnType(selection, context.Type.Name); - } - } - - private void ResolveInlineFragment( - CompilerContext context, - InlineFragmentNode inlineFragment, - long includeCondition) - { - ResolveFragment( - context, - inlineFragment, - inlineFragment.TypeCondition, - inlineFragment.SelectionSet, - inlineFragment.Directives, - includeCondition); - } - - private void ResolveFragmentSpread( - CompilerContext context, - FragmentSpreadNode fragmentSpread, - long includeCondition) - { - var fragmentDef = GetFragmentDefinition(context, fragmentSpread); - - ResolveFragment( - context, - fragmentSpread, - fragmentDef.TypeCondition, - fragmentDef.SelectionSet, - fragmentSpread.Directives, - includeCondition); - } - - private void ResolveFragment( - CompilerContext context, - ISelectionNode selection, - NamedTypeNode? typeCondition, - SelectionSetNode selectionSet, - IReadOnlyList directives, - ulong includeCondition) - { - if (typeCondition is null - || (context.Schema.Types.TryGetType(typeCondition, out IType? typeCon) - && DoesTypeApply(typeCon, context.Type))) - { - includeCondition = GetSelectionIncludeCondition(selection, includeCondition); - - if (directives.IsDeferrable()) - { - var deferDirective = directives.GetDeferDirectiveNode(); - var nullValue = NullValueNode.Default; - var ifValue = deferDirective?.GetArgumentValue(DirectiveNames.Defer.Arguments.If) ?? nullValue; - - ulong ifConditionFlags = 0; - - if (ifValue.Kind is not SyntaxKind.NullValue) - { - var ifCondition = new IncludeCondition(ifValue, nullValue); - ifConditionFlags = GetSelectionIncludeCondition(ifCondition, includeCondition); - } - - var typeName = typeCondition?.Name.Value ?? context.Type.Name; - var id = GetOrCreateSelectionSetRefId(selectionSet, typeName, context.Path); - var variants = GetOrCreateSelectionVariants(id); - var infos = new SelectionSetInfo[] { new(selectionSet, includeCondition) }; - - if (!variants.ContainsSelectionSet(context.Type)) - { - var deferContext = RentContext(context); - deferContext.Initialize(context.Type, variants, infos, context.Path); - CompileSelectionSet(deferContext); - ReturnContext(deferContext); - } - - var fragment = new Fragment( - GetNextFragmentId(), - context.Type, - selection, - directives, - variants.GetSelectionSet(context.Type), - includeCondition, - ifConditionFlags); - - context.Fragments.Add(fragment); - _hasIncrementalParts = true; - - // if we have if-condition flags, there will be a runtime validation if something - // shall be deferred, so we need to prepare for both cases. - // - // this means that we will collect the fields with our if condition flags as - // if the fragment was not deferred. - if (ifConditionFlags is not 0) - { - CollectFields(context, selectionSet, ifConditionFlags); - } - } - else - { - CollectFields(context, selectionSet, includeCondition); - } - } - } - - private static bool DoesTypeApply(IType typeCondition, IObjectTypeDefinition current) - => typeCondition.Kind switch - { - TypeKind.Object => ReferenceEquals(typeCondition, current), - TypeKind.Interface => current.IsImplementing((InterfaceType)typeCondition), - TypeKind.Union => ((UnionType)typeCondition).Types.ContainsName(current.Name), - _ => false - }; - - private FragmentDefinitionNode GetFragmentDefinition( - CompilerContext context, - FragmentSpreadNode fragmentSpread) - { - var fragmentName = fragmentSpread.Name.Value; - - if (!_fragmentDefinitions.TryGetValue(fragmentName, out var value)) - { - var document = context.Document; - - for (var i = 0; i < document.Definitions.Count; i++) - { - if (document.Definitions[i] is FragmentDefinitionNode fragmentDefinition - && fragmentDefinition.Name.Value.EqualsOrdinal(fragmentName)) - { - value = fragmentDefinition; - _fragmentDefinitions.Add(fragmentName, value); - goto EXIT; - } - } - - throw new InvalidOperationException( - string.Format( - OperationCompiler_FragmentNotFound, - fragmentName)); - } - -EXIT: - return value; - } - - internal int GetNextSelectionId() => _nextSelectionId++; - - private int GetNextFragmentId() => _nextFragmentId++; - - private int GetOrCreateSelectionSetRefId( - SelectionSetNode selectionSet, - string selectionSetTypeName, - SelectionPath path) - { - var selectionSetRef = new SelectionSetRef(selectionSet, selectionSetTypeName, path); - - if (!_selectionSetIdLookup.TryGetValue(selectionSetRef, out var selectionSetId)) - { - selectionSetId = _nextSelectionSetRefId++; - _selectionSetIdLookup.Add(selectionSetRef, selectionSetId); - } - - return selectionSetId; - } - - private SelectionVariants GetOrCreateSelectionVariants(int selectionSetId) - { - if (!_selectionVariants.TryGetValue(selectionSetId, out var variants)) - { - variants = new SelectionVariants(selectionSetId); - _selectionVariants.Add(selectionSetId, variants); - } - - return variants; - } - - private ulong GetSelectionIncludeCondition( - ISelectionNode selectionSyntax, - ulong parentIncludeCondition) - { - var condition = IncludeCondition.FromSelection(selectionSyntax); - - if (condition.IsDefault) - { - return parentIncludeCondition; - } - - var pos = Array.IndexOf(_includeConditions, condition); - - if (pos == -1) - { - pos = _includeConditions.Length; - - if (pos == 64) - { - throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); - } - - if (_includeConditions.Length == 0) - { - _includeConditions = new IncludeCondition[1]; - } - else - { - Array.Resize(ref _includeConditions, pos + 1); - } - - _includeConditions[pos] = condition; - } - - long selectionIncludeCondition = 1; - selectionIncludeCondition <<= pos; - - if (parentIncludeCondition == 0) - { - return selectionIncludeCondition; - } - - parentIncludeCondition |= selectionIncludeCondition; - return parentIncludeCondition; - } - - private long GetSelectionIncludeCondition( - IncludeCondition condition, - ulong parentIncludeCondition) - { - var pos = Array.IndexOf(_includeConditions, condition); - - if (pos == -1) - { - pos = _includeConditions.Length; - - if (pos == 64) - { - throw new InvalidOperationException(OperationCompiler_ToManyIncludeConditions); - } - - if (_includeConditions.Length == 0) - { - _includeConditions = new IncludeCondition[1]; - } - else - { - Array.Resize(ref _includeConditions, pos + 1); - } - - _includeConditions[pos] = condition; - } - - long selectionIncludeCondition = 1; - selectionIncludeCondition <<= pos; - - if (parentIncludeCondition == 0) - { - return selectionIncludeCondition; - } - - parentIncludeCondition |= selectionIncludeCondition; - return parentIncludeCondition; - } - - private CompilerContext RentContext(CompilerContext context) - { - if (_deferContext is null) - { - return new CompilerContext(context.Schema, context.Document); - } - - var temp = _deferContext; - _deferContext = null; - return temp; - } - - private void ReturnContext(CompilerContext context) - => _deferContext ??= context; - - internal void RegisterNewSelection(Selection newSelection) - { - if (newSelection.SyntaxNode.SelectionSet is not null) - { - var selectionSetInfo = new SelectionSetInfo(newSelection.SelectionSet!, 0); - _selectionLookup.Add(newSelection, [selectionSetInfo]); - } - } - - private readonly struct SelectionSetRef( - SelectionSetNode selectionSet, - string selectionSetTypeName, - SelectionPath path) - : IEquatable - { - public readonly SelectionSetNode SelectionSet = selectionSet; - - public readonly SelectionPath Path = path; - - public readonly string SelectionSetTypeName = selectionSetTypeName; - - public bool Equals(SelectionSetRef other) - => SyntaxComparer.BySyntax.Equals(SelectionSet, other.SelectionSet) - && Path.Equals(other.Path) - && Ordinal.Equals(SelectionSetTypeName, other.SelectionSetTypeName); - - public override bool Equals(object? obj) - => obj is SelectionSetRef other && Equals(other); - - public override int GetHashCode() - => HashCode.Combine( - SyntaxComparer.BySyntax.GetHashCode(SelectionSet), - Path.GetHashCode(), - Ordinal.GetHashCode(SelectionSetTypeName)); - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs index 2dac198ab62..c26e30ec3db 100644 --- a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs +++ b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs @@ -266,6 +266,22 @@ await typeModuleChangeMonitor.ConfigureAsync(context, cancellationToken) serviceCollection.AddSingleton(new SchemaVersionInfo(version)); + serviceCollection.AddSingleton(context.DescriptorContext.TypeConverter); + + serviceCollection.AddSingleton( + static sp => new InputParser(sp.GetRequiredService())); + + serviceCollection.AddSingleton( + static sp => new OperationCompiler( + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService>>>())); + + serviceCollection.AddSingleton( + static _ => new DefaultObjectPoolProvider()); + serviceCollection.AddSingleton( + static sp => sp.GetRequiredService().CreateStringBuilderPool()); + serviceCollection.AddSingleton(executorOptions); serviceCollection.AddSingleton( static sp => sp.GetRequiredService()); diff --git a/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs new file mode 100644 index 00000000000..5a4feb791b5 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs @@ -0,0 +1,15 @@ +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using Microsoft.Extensions.ObjectPool; + +namespace HotChocolate; + +internal static class ObjectPoolProviderExtensions +{ + public static ObjectPool>> CreateFieldMapPool( + this ObjectPoolProvider provider) + { + ArgumentNullException.ThrowIfNull(provider); + return provider.Create(new FieldMapPooledObjectPolicy()); + } +} diff --git a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs index 3d237262f67..8936dc920b4 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs @@ -18,7 +18,7 @@ public static class ResolverContextExtensions /// /// Returns the global state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T? GetGlobalStateOrDefault( this IResolverContext context, @@ -51,7 +51,7 @@ public static class ResolverContextExtensions /// /// Returns the global state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T GetGlobalStateOrDefault( this IResolverContext context, @@ -114,7 +114,7 @@ public static T GetGlobalState( /// /// Returns the scoped state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T? GetScopedStateOrDefault( this IResolverContext context, @@ -147,7 +147,7 @@ public static T GetGlobalState( /// /// Returns the scoped state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T GetScopedStateOrDefault( this IResolverContext context, @@ -211,7 +211,7 @@ public static T GetScopedState( /// /// Returns the local state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T? GetLocalStateOrDefault( this IResolverContext context, @@ -244,7 +244,7 @@ public static T GetScopedState( /// /// Returns the local state for the specified /// or the default value of , if the state - /// could not be found or casted to . + /// could not be found or cast to . /// public static T GetLocalStateOrDefault( this IResolverContext context, diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 08a474f7f21..2d0e7d9066a 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -171,6 +171,30 @@ OperationCompiler.cs + + + OperationContext.cs + + + + OperationContext.cs + + + + OperationContext.cs + + + + OperationContext.cs + + + + OperationContext.cs + + + + OperationContext.cs + diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Pipeline/OperationPlanMiddleware.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Pipeline/OperationPlanMiddleware.cs index 1c47df2b5db..d6df81b3a5d 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Pipeline/OperationPlanMiddleware.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Pipeline/OperationPlanMiddleware.cs @@ -21,7 +21,7 @@ private OperationPlanMiddleware( IEnumerable? interceptors, IFusionExecutionDiagnosticEvents diagnosticsEvents) { - _documentRewriter = new(schema, removeStaticallyExcludedSelections: true); + _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); _planner = planner; _interceptors = interceptors?.ToArray() ?? []; _diagnosticsEvents = diagnosticsEvents; From 152c43ccd7e70681789456ac91f904e195a58288 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 1 Dec 2025 22:55:14 +0100 Subject: [PATCH 13/30] wip --- .../CacheControlConstraintsOptimizer.cs | 33 ++- .../ErrorBuilderExtensions.cs | 161 +++++++------- .../Execution.Projections/ExpressionCache.cs | 86 ++++++++ ...tChocolateExecutionDataLoaderExtensions.cs | 13 ++ ...otChocolateExecutionSelectionExtensions.cs | 154 ++++++------- .../SelectionExpressionBuilder.cs | 15 +- .../Caching/DefaultPreparedOperationCache.cs | 6 +- .../Caching/IPreparedOperationCache.cs | 4 +- .../InternalServiceCollectionExtensions.cs | 8 - ...uestExecutorServiceCollectionExtensions.cs | 2 - ...colateExecutionRequestContextExtensions.cs | 151 ++++++------- .../Extensions/OperationContextExtensions.cs | 165 +++++++------- .../Pipeline/OperationCacheMiddleware.cs | 1 - .../Pipeline/OperationExecutionMiddleware.cs | 51 +++-- .../Types/Execution/Pipeline/OperationInfo.cs | 2 +- .../Processing/MiddlewareContext.Selection.cs | 7 +- .../Types/Execution/Processing/Operation.cs | 4 +- .../Processing/OperationFeatureCollection.cs | 26 ++- .../Processing/OperationOptimizerContext.cs | 89 ++------ .../Result/ResultBuilder.NonNullHandling.cs | 3 +- .../Types/Execution/Processing/Selection.cs | 5 + .../Processing/SelectionCollection.cs | 206 ++++++++---------- .../Execution/Processing/SelectionLookup.cs | 8 +- .../Execution/Processing/SelectionSet.cs | 9 +- .../Processing/SubscriptionExecutor.cs | 2 +- .../Processing/Tasks/ResolverTask.Execute.cs | 18 +- .../Processing/Tasks/ResolverTask.Pooling.cs | 2 +- .../Processing/Tasks/ResolverTask.cs | 8 +- .../Processing/Tasks/ResolverTaskFactory.cs | 77 +------ .../Core/src/Types/HotChocolate.Types.csproj | 12 + .../OperationParameterExpressionBuilder.cs | 11 +- 31 files changed, 643 insertions(+), 696 deletions(-) create mode 100644 src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs diff --git a/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs b/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs index c23eefea748..2fa588c8518 100644 --- a/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs +++ b/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Runtime.CompilerServices; using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.Types; @@ -15,15 +14,16 @@ internal sealed class CacheControlConstraintsOptimizer : IOperationOptimizer { public void OptimizeOperation(OperationOptimizerContext context) { - if (context.Definition.Operation is not OperationType.Query - || context.HasIncrementalParts + // TODO : we need to include this again when defer is back. + if (context.Operation.Kind is not OperationType.Query + // || context.HasIncrementalParts || ContainsIntrospectionFields(context)) { // if this is an introspection query, we will not cache it. return; } - var constraints = ComputeCacheControlConstraints(context.CreateOperation()); + var constraints = ComputeCacheControlConstraints(context.Operation); if (constraints.MaxAge is not null || constraints.SharedMaxAge is not null) { @@ -56,7 +56,7 @@ public void OptimizeOperation(OperationOptimizerContext context) } private static ImmutableCacheConstraints ComputeCacheControlConstraints( - IOperation operation) + Operation operation) { var constraints = new CacheControlConstraints(); var rootSelections = operation.RootSelectionSet.Selections; @@ -91,9 +91,9 @@ private static ImmutableCacheConstraints ComputeCacheControlConstraints( } private static void ProcessSelection( - ISelection selection, + Selection selection, CacheControlConstraints constraints, - IOperation operation) + Operation operation) { var field = selection.Field; var maxAgeSet = false; @@ -117,19 +117,18 @@ private static void ProcessSelection( } } - if (selection.SelectionSet is not null) + if (selection.HasSelections) { var possibleTypes = operation.GetPossibleTypes(selection); foreach (var type in possibleTypes) { - var selectionSet = Unsafe.As(operation.GetSelectionSet(selection, type)); - var length = selectionSet.Selections.Count; - ref var start = ref selectionSet.GetSelectionsReference(); + var selectionSet = operation.GetSelectionSet(selection, type); + var selections = selectionSet.Selections; - for (var i = 0; i < length; i++) + foreach (var childSelection in selections) { - ProcessSelection(Unsafe.Add(ref start, i), constraints, operation); + ProcessSelection(childSelection, constraints, operation); } } } @@ -223,13 +222,11 @@ void ExtractCacheControlDetailsFromDirectives( private static bool ContainsIntrospectionFields(OperationOptimizerContext context) { - var length = context.RootSelectionSet.Selections.Count; - ref var start = ref ((SelectionSet)context.RootSelectionSet).GetSelectionsReference(); + var selections = context.Operation.RootSelectionSet.Selections; - for (var i = 0; i < length; i++) + foreach (var selection in selections) { - var field = Unsafe.Add(ref start, i).Field; - + var field = selection.Field; if (field.IsIntrospectionField && !field.Name.EqualsOrdinal(IntrospectionFieldNames.TypeName)) { diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs index 628a75e4eb4..9c4d6c88050 100644 --- a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs @@ -8,109 +8,102 @@ namespace HotChocolate; /// public static class ErrorBuilderExtensions { - /// - /// Sets the field coordinate of the error. - /// - /// The error builder. - /// The field coordinate. - /// The error builder. - public static ErrorBuilder SetCoordinate( - this ErrorBuilder builder, - SchemaCoordinate coordinate) + extension(ErrorBuilder builder) { - ArgumentNullException.ThrowIfNull(builder); - - return builder.SetExtension(nameof(coordinate), coordinate.ToString()); - } + /// + /// Sets the field coordinate of the error. + /// + /// The field coordinate. + /// The error builder. + public ErrorBuilder SetCoordinate(SchemaCoordinate coordinate) + { + ArgumentNullException.ThrowIfNull(builder); - /// - /// Sets the input path of the error. - /// - /// The error builder. - /// The input path. - /// The error builder. - public static ErrorBuilder SetInputPath( - this ErrorBuilder builder, - Path inputPath) - { - ArgumentNullException.ThrowIfNull(builder); + return builder.SetExtension(nameof(coordinate), coordinate.ToString()); + } - return builder.SetExtension(nameof(inputPath), inputPath); - } + /// + /// Sets the input path of the error. + /// + /// The input path. + /// The error builder. + public ErrorBuilder SetInputPath(Path inputPath) + { + ArgumentNullException.ThrowIfNull(builder); - /// - /// Sets the message of the error. - /// - /// The error builder. - /// The format of the message. - /// The arguments for the message. - /// The error builder. - public static ErrorBuilder SetMessage(this ErrorBuilder builder, string format, params object[] args) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentException.ThrowIfNullOrEmpty(format); - ArgumentNullException.ThrowIfNull(args); + return builder.SetExtension(nameof(inputPath), inputPath); + } - return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, args)); - } + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The arguments for the message. + /// The error builder. + public ErrorBuilder SetMessage(string format, params object[] args) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + ArgumentNullException.ThrowIfNull(args); - /// - /// Adds a location to the error. - /// - /// The error builder. - /// The syntax node. - /// The error builder. - public static ErrorBuilder AddLocation(this ErrorBuilder builder, ISyntaxNode node) - { - ArgumentNullException.ThrowIfNull(node); + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, args)); + } - if (node.Location is null) + /// + /// Adds a location to the error. + /// + /// The syntax node. + /// The error builder. + public ErrorBuilder AddLocation(ISyntaxNode node) { - return builder; - } + ArgumentNullException.ThrowIfNull(node); - builder.AddLocation(new Location(node.Location.Line, node.Location.Column)); - return builder; - } + if (node.Location is null) + { + return builder; + } - /// - /// Adds a location to the error if the error does not already have a location. - /// - /// The error builder. - /// The syntax node. - /// The error builder. - public static ErrorBuilder TryAddLocation(this ErrorBuilder builder, ISyntaxNode? node) - { - if (node?.Location is null) - { + builder.AddLocation(new Location(node.Location.Line, node.Location.Column)); return builder; } - builder.TryAddLocation(new Location(node.Location.Line, node.Location.Column)); - return builder; - } + /// + /// Adds a location to the error if the error does not already have a location. + /// + /// The syntax node. + /// The error builder. + public ErrorBuilder TryAddLocation(ISyntaxNode? node) + { + if (node?.Location is null) + { + return builder; + } - /// - /// Adds multiple locations to the error. - /// - /// The error builder. - /// The syntax nodes. - /// The error builder. - public static ErrorBuilder AddLocations(this ErrorBuilder builder, IEnumerable nodes) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(nodes); + builder.TryAddLocation(new Location(node.Location.Line, node.Location.Column)); + return builder; + } - foreach (var node in nodes) + /// + /// Adds multiple locations to the error. + /// + /// The syntax nodes. + /// The error builder. + public ErrorBuilder AddLocations(IEnumerable nodes) { - if (node.Location is null) + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(nodes); + + foreach (var node in nodes) { - continue; + if (node.Location is null) + { + continue; + } + + builder.AddLocation(new Location(node.Location.Line, node.Location.Column)); } - builder.AddLocation(new Location(node.Location.Line, node.Location.Column)); + return builder; } - - return builder; } } diff --git a/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs b/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs new file mode 100644 index 00000000000..17d66d918ec --- /dev/null +++ b/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs @@ -0,0 +1,86 @@ +// ReSharper disable InconsistentlySynchronizedField +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using GreenDonut.Data; +using HotChocolate.Execution.Processing; + +namespace HotChocolate.Execution.Projections; + +internal sealed class ExpressionCache +{ + private readonly object _writeLock = new(); + private readonly ConcurrentDictionary _cache = new(); + + public bool TryGetExpression( + Selection selection, + [NotNullWhen(true)] out Expression>? expression) + { + if (_cache.TryGetValue(selection.Id, out var cachedExpression) + && cachedExpression is Expression> casted) + { + expression = casted; + return true; + } + + expression = null; + return false; + } + + public Expression> GetOrCreateExpression( + Selection selection, + SelectionExpressionBuilder expressionBuilder) + { + if (!TryGetExpression(selection, out var expression)) + { + lock (_writeLock) + { + if (!TryGetExpression(selection, out expression)) + { + expression = expressionBuilder.BuildExpression(selection); + _cache.TryAdd(selection.Id, expression); + } + } + } + + return expression; + } + + public Expression> GetOrCreateExpression( + Selection selection, + ISelectorBuilder expressionBuilder) + { + if (!TryGetExpression(selection, out var expression)) + { + lock (_writeLock) + { + if (!TryGetExpression(selection, out expression)) + { + expression = expressionBuilder.TryCompile()!; + _cache.TryAdd(selection.Id, expression); + } + } + } + + return expression; + } + + public Expression> GetOrCreateNodeExpression( + Selection selection, + SelectionExpressionBuilder expressionBuilder) + { + if (!TryGetExpression(selection, out var expression)) + { + lock (_writeLock) + { + if (!TryGetExpression(selection, out expression)) + { + expression = expressionBuilder.BuildNodeExpression(selection); + _cache.TryAdd(selection.Id, expression); + } + } + } + + return expression; + } +} diff --git a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionDataLoaderExtensions.cs index e94f6f41dc8..3773922eee2 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionDataLoaderExtensions.cs @@ -1,3 +1,4 @@ +using HotChocolate.Execution; using HotChocolate.Execution.Processing; // ReSharper disable once CheckNamespace @@ -38,6 +39,18 @@ public static IDataLoader Select( return dataLoader.Select(expression); } + public static IDataLoader Select( + this IDataLoader dataLoader, + Selection selection) + where TKey : notnull + { + ArgumentNullException.ThrowIfNull(dataLoader); + ArgumentNullException.ThrowIfNull(selection); + + var expression = selection.AsSelector(); + return dataLoader.Select(expression); + } + /// /// Selects the fields that where selected in the GraphQL selection tree. /// diff --git a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs index 7dd18bcb0dd..6c7740745af 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -1,9 +1,6 @@ using System.Buffers; -using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -using System.Text; -using System.Runtime.CompilerServices; using GreenDonut.Data; using HotChocolate.Execution.Projections; using HotChocolate.Types; @@ -18,8 +15,6 @@ namespace HotChocolate.Execution.Processing; /// public static class HotChocolateExecutionSelectionExtensions { - private static readonly SelectionExpressionBuilder s_builder = new(); - /// /// Creates a selector expression from a GraphQL selection. /// @@ -34,6 +29,31 @@ public static class HotChocolateExecutionSelectionExtensions /// public static Expression> AsSelector( this ISelection selection) + { + if (selection is not Selection casted) + { + throw new ArgumentException( + $"Expected {typeof(Selection).FullName!}.", + nameof(selection)); + } + + return AsSelector(casted); + } + + /// + /// Creates a selector expression from a GraphQL selection. + /// + /// + /// The selection that shall be converted into a selector expression. + /// + /// + /// The type of the value that is returned by the . + /// + /// + /// Returns a selector expression that can be used for data projections. + /// + public static Expression> AsSelector( + this Selection selection) { // we first check if we already have an expression for this selection, // this would be the cheapest way to get the expression. @@ -50,66 +70,48 @@ public static Expression> AsSelector( if ((flags & CoreFieldFlags.Connection) == CoreFieldFlags.Connection) { var builder = new DefaultSelectorBuilder(); - var buffer = ArrayPool.Shared.Rent(16); + var buffer = ArrayPool.Shared.Rent(16); var count = GetConnectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - builder.Add(GetOrCreateExpression(buffer[i])); + builder.Add(buffer[i].GetOrCreateExpression()); } - ArrayPool.Shared.Return(buffer); - return GetOrCreateExpression(selection, builder); + ArrayPool.Shared.Return(buffer); + return selection.GetOrCreateExpression(builder); } if ((flags & CoreFieldFlags.CollectionSegment) == CoreFieldFlags.CollectionSegment) { var builder = new DefaultSelectorBuilder(); - var buffer = ArrayPool.Shared.Rent(16); + var buffer = ArrayPool.Shared.Rent(16); var count = GetCollectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - builder.Add(GetOrCreateExpression(buffer[i])); + builder.Add(buffer[i].GetOrCreateExpression()); } - ArrayPool.Shared.Return(buffer); - return GetOrCreateExpression(selection, builder); + ArrayPool.Shared.Return(buffer); + return selection.GetOrCreateExpression(builder); } if ((flags & CoreFieldFlags.GlobalIdNodeField) == CoreFieldFlags.GlobalIdNodeField || (flags & CoreFieldFlags.GlobalIdNodesField) == CoreFieldFlags.GlobalIdNodesField) { - return GetOrCreateNodeExpression(selection); + return selection.GetOrCreateNodeExpression(); } - return GetOrCreateExpression(selection); + return selection.GetOrCreateExpression(); } - private static Expression> GetOrCreateExpression( - ISelection selection) - => selection.DeclaringOperation.GetOrAddState( - CreateExpressionKey(selection.Id), - static (_, ctx) => ctx._builder.BuildExpression(ctx.selection), - (_builder: s_builder, selection)); - - private static Expression> GetOrCreateExpression( - ISelection selection, - ISelectorBuilder builder) - => selection.DeclaringOperation.GetOrAddState( - CreateExpressionKey(selection.Id), - static (_, ctx) => ctx.builder.TryCompile()!, - (builder, selection)); - - private static Expression> GetOrCreateNodeExpression( - ISelection selection) - => selection.DeclaringOperation.GetOrAddState( - CreateNodeExpressionKey(selection.Id), - static (_, ctx) => ctx._builder.BuildNodeExpression(ctx.selection), - (_builder: s_builder, selection)); - private static bool TryGetExpression( - ISelection selection, + Selection selection, [NotNullWhen(true)] out Expression>? expression) - => selection.DeclaringOperation.TryGetState(CreateExpressionKey(selection.Id), out expression); + { + var features = selection.DeclaringOperation.Features; + var cache = features.GetOrSetSafe(); + return cache.TryGetExpression(selection, out expression); + } - private static int GetConnectionSelections(ISelection selection, Span buffer) + private static int GetConnectionSelections(Selection selection, Span buffer) { var pageType = (ObjectType)selection.Field.Type.NamedType(); var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); @@ -149,7 +151,7 @@ private static int GetConnectionSelections(ISelection selection, Span buffer) + private static int GetCollectionSelections(Selection selection, Span buffer) { var pageType = (ObjectType)selection.Field.Type.NamedType(); var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); @@ -170,59 +172,27 @@ private static int GetCollectionSelections(ISelection selection, Span span = stackalloc byte[requiredBufferSize]; - keyPrefix.CopyTo(span); - Utf8Formatter.TryFormat(key, span[keyPrefix.Length..], out var written, 'D'); - return Encoding.UTF8.GetString(span[..(written + keyPrefix.Length)]); - } - - private static string CreateNodeExpressionKey(int key) - { - var typeName = typeof(TValue).FullName!; - var typeNameLength = Encoding.UTF8.GetMaxByteCount(typeName.Length); - var keyPrefix = GetKeyPrefix(); - var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length + typeNameLength; - byte[]? rented = null; - var span = requiredBufferSize <= 256 - ? stackalloc byte[requiredBufferSize] - : (rented = ArrayPool.Shared.Rent(requiredBufferSize)); - - keyPrefix.CopyTo(span); - Utf8Formatter.TryFormat(key, span[keyPrefix.Length..], out var written, 'D'); - var typeNameWritten = Encoding.UTF8.GetBytes(typeName, span[(written + keyPrefix.Length)..]); - var keyString = Encoding.UTF8.GetString(span[..(written + keyPrefix.Length + typeNameWritten)]); - - if (rented is not null) - { - ArrayPool.Shared.Return(rented); - } - - return keyString; - } - - private static ReadOnlySpan GetKeyPrefix() - => "hc-dataloader-expr-"u8; +file static class Extensions +{ + private static readonly SelectionExpressionBuilder s_builder = new(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int EstimateIntLength(int value) + extension(Selection selection) { - if (value == 0) - { - // to print 0 we need still 1 digit - return 1; - } - - // if the number is negative we need one more digit for the sign - var length = value < 0 ? 1 : 0; - - // we add the number of digits the number has to the length of the number. - length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1); - - return length; + public Expression> GetOrCreateExpression() + => selection.DeclaringOperation.Features + .GetOrSetSafe() + .GetOrCreateExpression(selection, s_builder); + + public Expression> GetOrCreateExpression(ISelectorBuilder expressionBuilder) + => selection.DeclaringOperation.Features + .GetOrSetSafe() + .GetOrCreateExpression(selection, expressionBuilder); + + public Expression> GetOrCreateNodeExpression() + => selection.DeclaringOperation.Features + .GetOrSetSafe() + .GetOrCreateNodeExpression(selection, s_builder); } } diff --git a/src/HotChocolate/Core/src/Execution.Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution.Projections/SelectionExpressionBuilder.cs index 4d812130668..2e511cff998 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution.Projections/SelectionExpressionBuilder.cs @@ -36,7 +36,7 @@ internal sealed class SelectionExpressionBuilder typeof(char?) ]; - public Expression> BuildExpression(ISelection selection) + public Expression> BuildExpression(Selection selection) { var rootType = typeof(TRoot); var parameter = Expression.Parameter(rootType, "root"); @@ -56,7 +56,7 @@ public Expression> BuildExpression(ISelection selectio return Expression.Lambda>(selectionSetExpression, parameter); } - public Expression> BuildNodeExpression(ISelection selection) + public Expression> BuildNodeExpression(Selection selection) { var rootType = typeof(TRoot); var parameter = Expression.Parameter(rootType, "root"); @@ -66,6 +66,7 @@ public Expression> BuildNodeExpression(ISelection sele var entityType = selection.DeclaringOperation .GetPossibleTypes(selection) + .Cast() .FirstOrDefault(t => t.RuntimeType == typeof(TRoot)); if (entityType is null) @@ -94,7 +95,7 @@ public Expression> BuildNodeExpression(ISelection sele return Expression.Lambda>(selectionSetExpression, parameter); } - private void CollectTypes(Context context, ISelection selection, TypeContainer parent) + private void CollectTypes(Context context, Selection selection, TypeContainer parent) { var namedType = selection.Type.NamedType(); @@ -105,7 +106,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p if (namedType.IsAbstractType()) { - foreach (var possibleType in selection.DeclaringOperation.GetPossibleTypes(selection)) + foreach (var possibleType in selection.DeclaringOperation.GetPossibleTypes(selection).Cast()) { var possibleTypeNode = new TypeNode(possibleType.RuntimeType); var possibleSelectionSet = selection.DeclaringOperation.GetSelectionSet(selection, possibleType); @@ -191,7 +192,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p private void CollectSelection( Context context, - ISelection selection, + Selection selection, TypeNode parent) { var namedType = selection.Field.Type.NamedType(); @@ -266,7 +267,7 @@ private static void TryAddAnyLeafField( private void CollectSelections( Context context, - ISelectionSet selectionSet, + SelectionSet selectionSet, TypeNode parent) { foreach (var selection in selectionSet.Selections) @@ -343,7 +344,7 @@ private readonly record struct Context( FieldRequirementsMetadata Requirements, NullabilityInfoContext NullabilityInfoContext) { - public TypeNode? GetRequirements(ISelection selection) + public TypeNode? GetRequirements(Selection selection) { var flags = selection.Field.Flags; return (flags & CoreFieldFlags.WithRequirements) == CoreFieldFlags.WithRequirements diff --git a/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs index a4f1107f8ce..c6f852a78ab 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs @@ -6,15 +6,15 @@ namespace HotChocolate.Execution.Caching; internal sealed class DefaultPreparedOperationCache(int capacity = 256) : IPreparedOperationCache { - private readonly Cache _cache = new(capacity); + private readonly Cache _cache = new(capacity); public int Capacity => _cache.Capacity; public int Count => _cache.Count; - public void TryAddOperation(string operationId, IOperation operation) + public void TryAddOperation(string operationId, Operation operation) => _cache.GetOrCreate(operationId, static (_, op) => op, operation); - public bool TryGetOperation(string operationId, [NotNullWhen(true)] out IOperation? operation) + public bool TryGetOperation(string operationId, [NotNullWhen(true)] out Operation? operation) => _cache.TryGet(operationId, out operation); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs index 8235bd73f45..65bca19c522 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs @@ -33,7 +33,7 @@ public interface IPreparedOperationCache /// true if an operation was found that matches the specified /// , otherwise false. /// - bool TryGetOperation(string operationId, [NotNullWhen(true)] out IOperation? operation); + bool TryGetOperation(string operationId, [NotNullWhen(true)] out Operation? operation); /// /// Tries to add a new compiled operation to the cache. @@ -44,5 +44,5 @@ public interface IPreparedOperationCache /// /// The operation that shall be cached. /// - void TryAddOperation(string operationId, IOperation operation); + void TryAddOperation(string operationId, Operation operation); } diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs index 6a1afa07ec7..be71c7446e7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs @@ -71,14 +71,6 @@ internal static IServiceCollection TryAddResolverTaskPool( return services; } - internal static IServiceCollection TryAddOperationCompilerPool( - this IServiceCollection services) - { - services.TryAddSingleton>( - sp => new OperationCompilerPool(sp.GetRequiredService())); - return services; - } - internal static IServiceCollection TryAddOperationContextPool( this IServiceCollection services) { diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs index 63b88e77e99..d78ec74851f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs @@ -57,8 +57,6 @@ public static IServiceCollection AddGraphQLCore(this IServiceCollection services .TryAddResultPool() .TryAddResolverTaskPool() .TryAddOperationContextPool() - .TryAddDeferredWorkStatePool() - .TryAddOperationCompilerPool() .TryAddSingleton>(new DocumentValidatorContextPool()); // global executor services diff --git a/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs index 7032887c51f..2573e075aa9 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs @@ -8,106 +8,99 @@ namespace HotChocolate.Execution; public static class HotChocolateExecutionRequestContextExtensions { - public static bool TryGetOperationDefinition( - this RequestContext context, - [NotNullWhen(true)] out OperationDefinitionNode? operationDefinition) + extension(RequestContext context) { - ArgumentNullException.ThrowIfNull(context); + public bool TryGetOperationDefinition([NotNullWhen(true)] out OperationDefinitionNode? operationDefinition) + { + ArgumentNullException.ThrowIfNull(context); - var operationInfo = context.Features.GetOrSet(); + var operationInfo = context.Features.GetOrSet(); - if (operationInfo.Operation is not null) - { - operationDefinition = operationInfo.Operation.Definition; - return true; - } + if (operationInfo.Operation is not null) + { + operationDefinition = operationInfo.Operation.Definition; + return true; + } - if (operationInfo.Definition is not null) - { - operationDefinition = operationInfo.Definition; - return true; + if (operationInfo.Definition is not null) + { + operationDefinition = operationInfo.Definition; + return true; + } + + operationDefinition = null; + return false; } - operationDefinition = null; - return false; - } + public void SetOperationDefinition(OperationDefinitionNode operationDefinition) + { + ArgumentNullException.ThrowIfNull(context); - public static void SetOperationDefinition( - this RequestContext context, - OperationDefinitionNode operationDefinition) - { - ArgumentNullException.ThrowIfNull(context); + var operationInfo = context.Features.GetOrSet(); - var operationInfo = context.Features.GetOrSet(); + if (operationInfo.Operation is not null) + { + throw new InvalidOperationException( + "The operation definition cannot be set after " + + "the operation was already compiled."); + } - if (operationInfo.Operation is not null) - { - throw new InvalidOperationException( - "The operation definition cannot be set after " - + "the operation was already compiled."); + operationInfo.Definition = operationDefinition; } - operationInfo.Definition = operationDefinition; - } - - public static bool TryGetOperation( - this RequestContext context, - [NotNullWhen(true)] out IOperation? operation) - { - ArgumentNullException.ThrowIfNull(context); + public bool TryGetOperation([NotNullWhen(true)] out Operation? operation) + { + ArgumentNullException.ThrowIfNull(context); - var operationInfo = context.Features.GetOrSet(); - operation = operationInfo.Operation; - return operation is not null; - } + var operationInfo = context.Features.GetOrSet(); + operation = operationInfo.Operation; + return operation is not null; + } - public static bool TryGetOperation( - this RequestContext context, - [NotNullWhen(true)] out IOperation? operation, - [NotNullWhen(true)] out string? operationId) - { - ArgumentNullException.ThrowIfNull(context); + public bool TryGetOperation([NotNullWhen(true)] out Operation? operation, + [NotNullWhen(true)] out string? operationId) + { + ArgumentNullException.ThrowIfNull(context); - var operationInfo = context.Features.GetOrSet(); - operation = operationInfo.Operation; - operationId = operationInfo.Id; - return operation is not null; - } + var operationInfo = context.Features.GetOrSet(); + operation = operationInfo.Operation; + operationId = operationInfo.Id; + return operation is not null; + } - public static IOperation GetOperation(this RequestContext context) - { - ArgumentNullException.ThrowIfNull(context); + public Operation GetOperation() + { + ArgumentNullException.ThrowIfNull(context); - return context.Features.GetRequired().Operation - ?? throw new InvalidOperationException("The operation is not initialized."); - } + return context.Features.GetRequired().Operation + ?? throw new InvalidOperationException("The operation is not initialized."); + } - public static void SetOperation( - this RequestContext context, - IOperation operation) - { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(operation); + public void SetOperation(Operation operation) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(operation); - var operationInfo = context.Features.GetOrSet(); - operationInfo.Operation = operation; - operationInfo.Id = operation.Id; - operationInfo.Definition = operation.Definition; - } + var operationInfo = context.Features.GetOrSet(); + operationInfo.Operation = operation; + operationInfo.Id = operation.Id; + operationInfo.Definition = operation.Definition; + } - public static bool TryGetOperationId(this RequestContext context, [NotNullWhen(true)] out string? operationId) - { - ArgumentNullException.ThrowIfNull(context); + public bool TryGetOperationId([NotNullWhen(true)] out string? operationId) + { + ArgumentNullException.ThrowIfNull(context); - operationId = context.Features.GetOrSet().Id; - return operationId is not null; - } + operationId = context.Features.GetOrSet().Id; + return operationId is not null; + } - public static void SetOperationId(this RequestContext context, string operationId) - { - ArgumentNullException.ThrowIfNull(context); + public void SetOperationId(string operationId) + { + ArgumentNullException.ThrowIfNull(context); - var operationInfo = context.Features.GetOrSet(); - operationInfo.Id = operationId; + var operationInfo = context.Features.GetOrSet(); + operationInfo.Id = operationId; + } } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs index 32a7fcb170f..d721a1e8f24 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs @@ -5,61 +5,99 @@ namespace HotChocolate.Execution; internal static class OperationContextExtensions { - public static OperationContext ReportError( - this OperationContext operationContext, - Exception exception, - MiddlewareContext resolverContext, - ISelection? selection = null, - Path? path = null) + extension(OperationContext context) { - selection ??= resolverContext.Selection; - path ??= resolverContext.Path; + public OperationContext ReportError(Exception exception, + MiddlewareContext resolverContext, + ISelection? selection = null, + Path? path = null) + { + selection ??= resolverContext.Selection; + path ??= resolverContext.Path; - ArgumentNullException.ThrowIfNull(exception); + ArgumentNullException.ThrowIfNull(exception); - if (exception is GraphQLException ex) - { - foreach (var error in ex.Errors) + if (exception is GraphQLException ex) + { + foreach (var error in ex.Errors) + { + ReportError(context, error, resolverContext, selection); + } + } + else { - ReportError(operationContext, error, resolverContext, selection); + var error = ErrorBuilder + .FromException(exception) + .SetPath(path) + .AddLocations(selection.GetSyntaxNodes()) + .Build(); + + ReportError(context, error, resolverContext, selection); } + + return context; } - else + + public OperationContext ReportError(IError error, + MiddlewareContext resolverContext, + ISelection? selection = null) { - var error = ErrorBuilder - .FromException(exception) - .SetPath(path) - .AddLocation(selection.SyntaxNode) - .Build(); + var errors = new List(); + + ReportSingleError( + context.ErrorHandler, + error, + errors); - ReportError(operationContext, error, resolverContext, selection); + selection ??= resolverContext.Selection; + + foreach (var handled in errors) + { + context.Result.AddError(handled, selection); + context.DiagnosticEvents.ResolverError(resolverContext, handled); + } + + return context; } - return operationContext; - } + public OperationContext SetLabel(string? label) + { + context.Result.SetLabel(label); + return context; + } - public static OperationContext ReportError( - this OperationContext operationContext, - IError error, - MiddlewareContext resolverContext, - ISelection? selection = null) - { - var errors = new List(); + public OperationContext SetPath(Path? path) + { + context.Result.SetPath(path); + return context; + } + + public OperationContext SetData(ObjectResult objectResult) + { + context.Result.SetData(objectResult); + return context; + } - ReportSingleError( - operationContext.ErrorHandler, - error, - errors); + public OperationContext SetItems(IReadOnlyList items) + { + context.Result.SetItems(items); + return context; + } - selection ??= resolverContext.Selection; + public OperationContext SetPatchId(uint patchId) + { + context.Result.SetContextData(WellKnownContextData.PatchId, patchId); + return context; + } - foreach (var handled in errors) + public OperationContext ClearResult() { - operationContext.Result.AddError(handled, selection); - operationContext.DiagnosticEvents.ResolverError(resolverContext, handled); + context.Result.Clear(); + return context; } - return operationContext; + public IOperationResult BuildResult() + => context.Result.BuildResult(); } private static void ReportSingleError( @@ -93,55 +131,4 @@ private static void ReportSingleError( errors.Add(error); } } - - public static OperationContext SetLabel( - this OperationContext context, - string? label) - { - context.Result.SetLabel(label); - return context; - } - - public static OperationContext SetPath( - this OperationContext context, - Path? path) - { - context.Result.SetPath(path); - return context; - } - - public static OperationContext SetData( - this OperationContext context, - ObjectResult objectResult) - { - context.Result.SetData(objectResult); - return context; - } - - public static OperationContext SetItems( - this OperationContext context, - IReadOnlyList items) - { - context.Result.SetItems(items); - return context; - } - - public static OperationContext SetPatchId( - this OperationContext context, - uint patchId) - { - context.Result.SetContextData(WellKnownContextData.PatchId, patchId); - return context; - } - - public static OperationContext ClearResult( - this OperationContext context) - { - context.Result.Clear(); - return context; - } - - public static IOperationResult BuildResult( - this OperationContext context) => - context.Result.BuildResult(); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs index b21a9ccaff5..5e0bad45ab0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs @@ -1,6 +1,5 @@ using HotChocolate.Execution.Caching; using HotChocolate.Execution.Instrumentation; -using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Execution.Pipeline; diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs index a891e31e320..6355bf99f58 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs @@ -6,7 +6,6 @@ using HotChocolate.Fetching; using HotChocolate.Language; using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Execution.RequestFlags; using static HotChocolate.Execution.ThrowHelper; @@ -91,7 +90,7 @@ public async ValueTask InvokeAsync( private async Task ExecuteOperationRequestAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation) + Operation operation) { if (operation.Definition.Operation is OperationType.Subscription) { @@ -114,7 +113,7 @@ await ExecuteQueryOrMutationAsync( private async Task ExecuteVariableBatchRequestAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation) + Operation operation) { if (operation.Definition.Operation is OperationType.Query) { @@ -137,7 +136,7 @@ private async Task ExecuteVariableBatchRequestAsync( private async Task ExecuteVariableBatchRequestOptimizedAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation) + Operation operation) { var variableSets = context.VariableValues; var query = GetQueryRootValue(context); @@ -182,7 +181,7 @@ await _queryExecutor.ExecuteBatchAsync( static void Initialize( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation, + Operation operation, object? query, Span operationContexts, IVariableValueCollection variables, @@ -249,7 +248,7 @@ static void ReleaseResources( private async Task ExecuteQueryOrMutationAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation, + Operation operation, IVariableValueCollection variables) { var operationContextOwner = _contextFactory.Create(); @@ -266,15 +265,16 @@ await ExecuteQueryOrMutationAsync( variables) .ConfigureAwait(false); - if (operationContext.DeferredScheduler.HasResults) - { - var results = operationContext.DeferredScheduler.CreateResultStream(result); - var responseStream = new ResponseStream(() => results, ExecutionResultKind.DeferredResult); - responseStream.RegisterForCleanup(result); - responseStream.RegisterForCleanup(operationContextOwner); - operationContextOwner = null; - return responseStream; - } + // TODO : DEFER + // if (operationContext.DeferredScheduler.HasResults) + // { + // var results = operationContext.DeferredScheduler.CreateResultStream(result); + // var responseStream = new ResponseStream(() => results, ExecutionResultKind.DeferredResult); + // responseStream.RegisterForCleanup(result); + // responseStream.RegisterForCleanup(operationContextOwner); + // operationContextOwner = null; + // return responseStream; + // } return result; } @@ -296,7 +296,7 @@ await ExecuteQueryOrMutationAsync( private async Task ExecuteQueryOrMutationNoStreamAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation, + Operation operation, IVariableValueCollection variables, int variableIndex) { @@ -332,7 +332,7 @@ private async Task ExecuteQueryOrMutationNoStreamAsync( private async Task ExecuteQueryOrMutationAsync( RequestContext context, IBatchDispatcher batchDispatcher, - IOperation operation, + Operation operation, OperationContext operationContext, IVariableValueCollection variables, int variableIndex = -1) @@ -397,7 +397,7 @@ private async Task ExecuteQueryOrMutationAsync( Unsafe.As(context.Schema.MutationType)!, ref _cachedMutation); - private static bool IsOperationAllowed(IOperation operation, IOperationRequest request) + private static bool IsOperationAllowed(Operation operation, IOperationRequest request) { if (request.Flags is AllowAll) { @@ -412,21 +412,24 @@ private static bool IsOperationAllowed(IOperation operation, IOperationRequest r _ => true }; - if (allowed && operation.HasIncrementalParts) - { - return allowed && (request.Flags & AllowStreams) == AllowStreams; - } + // TODO : DEFER + // if (allowed && operation.HasIncrementalParts) + // { + // return allowed && (request.Flags & AllowStreams) == AllowStreams; + // } return allowed; } private static bool IsRequestTypeAllowed( - IOperation operation, + Operation operation, IReadOnlyList? variables) { if (variables is { Count: > 1 }) { - return operation.Definition.Operation is not OperationType.Subscription && !operation.HasIncrementalParts; + // TODO : DEFER + return operation.Definition.Operation is not OperationType.Subscription; + // && !operation.HasIncrementalParts; } return true; diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs index 831ae5e3aed..15a709dea36 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs @@ -7,7 +7,7 @@ internal sealed class OperationInfo : RequestFeature { public string? Id { get; set; } - public IOperation? Operation { get; set; } + public Operation? Operation { get; set; } public OperationDefinitionNode? Definition { get; set; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs index 660e99ded09..6efa2b63cac 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs @@ -61,8 +61,11 @@ public SelectionEnumerator GetSelections( public ISelectionCollection Select() { - var schema = Unsafe.As(_operationContext.Schema); - return new SelectionCollection(schema, Operation, [Selection], _operationContext.IncludeFlags); + return new SelectionCollection( + _operationContext.Schema, + Operation, + [Selection], + _operationContext.IncludeFlags); } public ISelectionCollection Select(string fieldName) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 216d6fd00cd..4c440b5b1c0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -113,7 +113,9 @@ ISelectionSet IOperation.RootSelectionSet => RootSelectionSet; /// - public IFeatureCollection Features => _features; + public OperationFeatureCollection Features => _features; + + IFeatureCollection IFeatureProvider.Features => Features; /// /// Gets the selection set for the specified diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs index 025d3eb9600..553e910d594 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Execution.Processing; [SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] -internal sealed class OperationFeatureCollection : IFeatureCollection +public sealed class OperationFeatureCollection : IFeatureCollection { #if NET9_0_OR_GREATER private readonly Lock _writeLock = new(); @@ -23,7 +23,7 @@ internal sealed class OperationFeatureCollection : IFeatureCollection /// /// Initializes a new instance of . /// - public OperationFeatureCollection() + internal OperationFeatureCollection() { } @@ -85,6 +85,28 @@ public object? this[Type key] return (TFeature?)this[typeof(TFeature)]; } + public TFeature GetOrSetSafe() where TFeature : new() + => GetOrSetSafe(static () => new TFeature()); + + public TFeature GetOrSetSafe(Func factory) + { + ArgumentNullException.ThrowIfNull(factory); + + if (!TryGet(out var feature)) + { + lock (_writeLock) + { + if (!TryGet(out feature)) + { + feature = factory(); + this[typeof(TFeature)] = feature; + } + } + } + + return feature; + } + /// public bool TryGet([NotNullWhen(true)] out TFeature? feature) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs index 16f6fa38373..4aaabec22ae 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs @@ -10,86 +10,31 @@ namespace HotChocolate.Execution.Processing; /// public readonly ref struct OperationOptimizerContext { - private readonly SelectionVariants[] _variants; - private readonly IncludeCondition[] _includeConditions; - private readonly ObjectType _rootType; - private readonly Dictionary _contextData; - private readonly bool _hasIncrementalParts; private readonly CreateFieldPipeline _createFieldPipeline; /// /// Initializes a new instance of /// internal OperationOptimizerContext( - string id, - DocumentNode document, - OperationDefinitionNode definition, - Schema schema, - ObjectType rootType, - SelectionVariants[] variants, - IncludeCondition[] includeConditions, + Operation operation, Dictionary contextData, - bool hasIncrementalParts, CreateFieldPipeline createFieldPipeline) { - Id = id; - Document = document; - Definition = definition; - Schema = schema; - _rootType = rootType; - _variants = variants; - _includeConditions = includeConditions; - _contextData = contextData; - _hasIncrementalParts = hasIncrementalParts; + Operation = operation; + ContextData = contextData; _createFieldPipeline = createFieldPipeline; } /// - /// Gets the internal unique identifier for this operation. + /// Gets the operation. /// - public string Id { get; } - - /// - /// Gets the parsed query document that contains the - /// operation-. - /// - public DocumentNode Document { get; } - - /// - /// Gets the syntax node representing the operation definition. - /// - public OperationDefinitionNode Definition { get; } - - /// - /// Gets the schema for which the query is compiled. - /// - public Schema Schema { get; } - - /// - /// Gets the root type on which the operation is executed. - /// - public ObjectType RootType => _rootType; - - /// - /// Gets the prepared root selections for this operation. - /// - public ISelectionSet RootSelectionSet => _variants[0].GetSelectionSet(RootType); - - /// - /// Gets all selection variants of this operation. - /// - public IReadOnlyList SelectionVariants => _variants; + public Operation Operation { get; } /// /// The context data dictionary can be used by middleware components and /// resolvers to store and retrieve data during execution. /// - public IDictionary ContextData => _contextData; - - /// - /// Defines if the operation has incremental parts. - /// - public bool HasIncrementalParts => _hasIncrementalParts; + public IDictionary ContextData { get; } /// /// Sets the resolvers on the specified . @@ -104,29 +49,21 @@ internal OperationOptimizerContext( /// The pure resolver. /// public void SetResolver( - ISelection selection, + Selection selection, FieldDelegate? resolverPipeline = null, PureFieldDelegate? pureResolver = null) - => ((Selection)selection).SetResolvers(resolverPipeline, pureResolver); + => selection.SetResolvers(resolverPipeline, pureResolver); /// /// Allows to compile the field resolver pipeline for a field. /// /// The field. - /// The selection of the field. + /// The selection of the field. /// /// Returns a representing the field resolver pipeline. /// - public FieldDelegate CompileResolverPipeline(ObjectField field, FieldNode selection) - => _createFieldPipeline(Schema, field, selection); - - /// - /// Creates a temporary operation object for the optimizer. - /// - public IOperation CreateOperation() - { - var operation = new Operation(Id, Document, Definition, _rootType, Schema); - operation.Seal(_contextData, _variants, _hasIncrementalParts, _includeConditions); - return operation; - } + public FieldDelegate CompileResolverPipeline( + ObjectField field, + FieldNode fieldSelection) + => _createFieldPipeline(Operation.Schema, field, fieldSelection); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs index 8b127c59995..2eda9295e82 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs @@ -1,5 +1,4 @@ using HotChocolate.Language; -using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Execution.ErrorHelper; namespace HotChocolate.Execution.Processing; @@ -30,7 +29,7 @@ private void ApplyNonNullViolations( continue; } - var error = NonNullOutputFieldViolation(violation.Path, violation.Selection.SyntaxNode); + var error = NonNullOutputFieldViolation(violation.Path, violation.Selection.GetSyntaxNodes().First()); error = errorHandler.Handle(error); _diagnosticEvents.ResolverError(_context, violation.Selection, error); errors.Add(error); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index 1d44a51e445..f4538b26dfe 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -82,6 +82,11 @@ internal Selection( public bool IsLeaf => (_flags & Flags.Leaf) == Flags.Leaf; + /// + /// Defines if this selection has child selections. + /// + public bool HasSelections => !IsLeaf; + public ObjectField Field { get; } /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs index 6a7370dfe04..4729fb96bde 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs @@ -10,20 +10,34 @@ namespace HotChocolate.Execution.Processing; -internal sealed class SelectionCollection( - Schema schema, - IOperation operation, - ISelection[] selections, - ulong includeFlags) - : ISelectionCollection +internal sealed class SelectionCollection : ISelectionCollection { - private readonly Schema _schema = schema ?? throw new ArgumentNullException(nameof(schema)); - private readonly IOperation _operation = operation ?? throw new ArgumentNullException(nameof(operation)); - private readonly ISelection[] _selections = selections ?? throw new ArgumentNullException(nameof(selections)); + private readonly Schema _schema; + private readonly Operation _operation; + private readonly Selection[] _selections; + private readonly ulong _includeFlags; + + public SelectionCollection( + Schema schema, + Operation operation, + Selection[] selections, + ulong includeFlags) + { + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(operation); + ArgumentNullException.ThrowIfNull(selections); + + _includeFlags = includeFlags; + _schema = schema; + _operation = operation; + _selections = selections; + } public int Count => _selections.Length; - public ISelection this[int index] => _selections[index]; + public Selection this[int index] => _selections[index]; + + ISelection IReadOnlyList.this[int index] => _selections[index]; public ISelectionCollection Select(string fieldName) { @@ -31,26 +45,26 @@ public ISelectionCollection Select(string fieldName) if (!CollectSelections(fieldName, out var buffer, out var size)) { - return new SelectionCollection(_schema, _operation, [], includeFlags); + return new SelectionCollection(_schema, _operation, [], _includeFlags); } - var selections = new ISelection[size]; + var selections = new Selection[size]; buffer.AsSpan()[..size].CopyTo(selections); - ArrayPool.Shared.Return(buffer); - return new SelectionCollection(_schema, _operation, selections, includeFlags); + ArrayPool.Shared.Return(buffer); + return new SelectionCollection(_schema, _operation, selections, _includeFlags); } public ISelectionCollection Select(ReadOnlySpan fieldNames) { if (!CollectSelections(fieldNames, out var buffer, out var size)) { - return new SelectionCollection(_schema, _operation, [], includeFlags); + return new SelectionCollection(_schema, _operation, [], _includeFlags); } - var selections = new ISelection[size]; + var selections = new Selection[size]; buffer.AsSpan()[..size].CopyTo(selections); - ArrayPool.Shared.Return(buffer); - return new SelectionCollection(_schema, _operation, selections, includeFlags); + ArrayPool.Shared.Return(buffer); + return new SelectionCollection(_schema, _operation, selections, _includeFlags); } public ISelectionCollection Select(ITypeDefinition typeContext) @@ -59,13 +73,13 @@ public ISelectionCollection Select(ITypeDefinition typeContext) if (!CollectSelections(typeContext, out var buffer, out var size)) { - return new SelectionCollection(_schema, _operation, [], includeFlags); + return new SelectionCollection(_schema, _operation, [], _includeFlags); } - var selections = new ISelection[size]; + var selections = new Selection[size]; buffer.AsSpan()[..size].CopyTo(selections); - ArrayPool.Shared.Return(buffer); - return new SelectionCollection(_schema, _operation, selections, includeFlags); + ArrayPool.Shared.Return(buffer); + return new SelectionCollection(_schema, _operation, selections, _includeFlags); } public bool IsSelected(string fieldName) @@ -90,7 +104,7 @@ public bool IsSelected(string fieldName) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, possibleType, start, fieldName)) @@ -103,7 +117,7 @@ public bool IsSelected(string fieldName) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, Unsafe.As(ref namedType), start, fieldName)) @@ -118,27 +132,22 @@ public bool IsSelected(string fieldName) return false; static bool IsChildSelected( - IOperation operation, + Operation operation, ulong includeFlags, ObjectType objectType, - ISelection parent, + Selection parent, string fieldName) { var selectionSet = operation.GetSelectionSet(parent, objectType); var operationIncludeFlags = includeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var start = ref Unsafe.As(ref selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref start, selectionCount); - while (Unsafe.IsAddressLessThan(ref start, ref end)) + foreach (var child in selectionSet.Selections) { - if (start.IsIncluded(operationIncludeFlags) - && fieldName.EqualsOrdinal(start.Field.Name)) + if (child.IsIncluded(operationIncludeFlags) + && fieldName.EqualsOrdinal(child.Field.Name)) { return true; } - - start = ref Unsafe.Add(ref start, 1)!; } return false; @@ -168,7 +177,7 @@ public bool IsSelected(string fieldName1, string fieldName2) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, possibleType, start, fieldName1, @@ -182,7 +191,7 @@ public bool IsSelected(string fieldName1, string fieldName2) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, Unsafe.As(ref namedType), start, fieldName1, @@ -198,29 +207,25 @@ public bool IsSelected(string fieldName1, string fieldName2) return false; static bool IsChildSelected( - IOperation operation, + Operation operation, ulong includeFlags, ObjectType objectType, - ISelection parent, + Selection parent, string fieldName1, string fieldName2) { var selectionSet = operation.GetSelectionSet(parent, objectType); var operationIncludeFlags = includeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var start = ref Unsafe.As(ref selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref start, selectionCount); + var selections = selectionSet.Selections; - while (Unsafe.IsAddressLessThan(ref start, ref end)) + foreach (var selection in selections) { - if (start.IsIncluded(operationIncludeFlags) - && (fieldName1.EqualsOrdinal(start.Field.Name) - || fieldName2.EqualsOrdinal(start.Field.Name))) + if (selection.IsIncluded(operationIncludeFlags) + && (fieldName1.EqualsOrdinal(selection.Field.Name) + || fieldName2.EqualsOrdinal(selection.Field.Name))) { return true; } - - start = ref Unsafe.Add(ref start, 1)!; } return false; @@ -251,7 +256,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, possibleType, start, fieldName1, @@ -266,7 +271,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, Unsafe.As(ref namedType), start, fieldName1, @@ -283,31 +288,27 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) return false; static bool IsChildSelected( - IOperation operation, + Operation operation, ulong includeFlags, ObjectType objectType, - ISelection parent, + Selection parent, string fieldName1, string fieldName2, string fieldName3) { var selectionSet = operation.GetSelectionSet(parent, objectType); var operationIncludeFlags = includeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var start = ref Unsafe.As(ref selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref start, selectionCount); + var selections = selectionSet.Selections; - while (Unsafe.IsAddressLessThan(ref start, ref end)) + foreach (var selection in selections) { - if (start.IsIncluded(operationIncludeFlags) - && (fieldName1.EqualsOrdinal(start.Field.Name) - || fieldName2.EqualsOrdinal(start.Field.Name) - || fieldName3.EqualsOrdinal(start.Field.Name))) + if (selection.IsIncluded(operationIncludeFlags) + && (fieldName1.EqualsOrdinal(selection.Field.Name) + || fieldName2.EqualsOrdinal(selection.Field.Name) + || fieldName3.EqualsOrdinal(selection.Field.Name))) { return true; } - - start = ref Unsafe.Add(ref start, 1)!; } return false; @@ -334,7 +335,7 @@ public bool IsSelected(ISet fieldNames) { foreach (var possibleType in _schema.GetPossibleTypes(namedType)) { - if (IsChildSelected(_operation, includeFlags, possibleType, start, fieldNames)) + if (IsChildSelected(_operation, _includeFlags, possibleType, start, fieldNames)) { return true; } @@ -344,7 +345,7 @@ public bool IsSelected(ISet fieldNames) { if (IsChildSelected( _operation, - includeFlags, + _includeFlags, Unsafe.As(ref namedType), start, fieldNames)) @@ -359,27 +360,23 @@ public bool IsSelected(ISet fieldNames) return false; static bool IsChildSelected( - IOperation operation, + Operation operation, ulong includeFlags, ObjectType objectType, - ISelection parent, + Selection parent, ISet fieldNames) { var selectionSet = operation.GetSelectionSet(parent, objectType); var operationIncludeFlags = includeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var start = ref Unsafe.As(ref selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref start, selectionCount); + var selections = selectionSet.Selections; - while (Unsafe.IsAddressLessThan(ref start, ref end)) + foreach (var selection in selections) { - if (start.IsIncluded(operationIncludeFlags) - && fieldNames.Contains(start.Field.Name)) + if (selection.IsIncluded(operationIncludeFlags) + && fieldNames.Contains(selection.Field.Name)) { return true; } - - start = ref Unsafe.Add(ref start, 1)!; } return false; @@ -388,7 +385,7 @@ static bool IsChildSelected( private bool CollectSelections( string fieldName, - out ISelection[] buffer, + out Selection[] buffer, out int size) { var fieldNames = ArrayPool.Shared.Rent(1); @@ -402,47 +399,42 @@ private bool CollectSelections( private bool CollectSelections( ReadOnlySpan fieldNames, - out ISelection[] buffer, + out Selection[] buffer, out int size) { - buffer = ArrayPool.Shared.Rent(4); + buffer = ArrayPool.Shared.Rent(4); size = 0; - ref var start = ref MemoryMarshal.GetReference(_selections.AsSpan()); - ref var end = ref Unsafe.Add(ref start, _selections.Length); - - while (Unsafe.IsAddressLessThan(ref start, ref end)) + foreach (var selection in _selections) { - var namedType = start.Type.NamedType(); + var namedType = selection.Type.NamedType(); if (!namedType.IsCompositeType()) { - goto NEXT; + continue; } if (namedType.IsAbstractType()) { foreach (var possibleType in _schema.GetPossibleTypes(namedType)) { - var selectionSet = _operation.GetSelectionSet(start, possibleType); - CollectFields(fieldNames, includeFlags, ref buffer, selectionSet, size, out var written); + var selectionSet = _operation.GetSelectionSet(selection, possibleType); + CollectFields(fieldNames, _includeFlags, ref buffer, selectionSet, size, out var written); size += written; } } else { - var selectionSet = _operation.GetSelectionSet(start, Unsafe.As(ref namedType)); - CollectFields(fieldNames, includeFlags, ref buffer, selectionSet, size, out var written); + var objectType = Unsafe.As(ref namedType); + var selectionSet = _operation.GetSelectionSet(selection, objectType); + CollectFields(fieldNames, _includeFlags, ref buffer, selectionSet, size, out var written); size += written; } - -NEXT: - start = ref Unsafe.Add(ref start, 1)!; } if (size == 0) { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(buffer); buffer = []; } @@ -451,10 +443,10 @@ private bool CollectSelections( private bool CollectSelections( ITypeDefinition typeContext, - out ISelection[] buffer, + out Selection[] buffer, out int size) { - buffer = ArrayPool.Shared.Rent(_selections.Length); + buffer = ArrayPool.Shared.Rent(_selections.Length); size = 0; ref var start = ref MemoryMarshal.GetReference(_selections.AsSpan()); @@ -472,7 +464,7 @@ private bool CollectSelections( if (size == 0) { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(buffer); buffer = []; } @@ -482,38 +474,32 @@ private bool CollectSelections( private static void CollectFields( ReadOnlySpan fieldNames, ulong includeFlags, - ref ISelection[] buffer, - ISelectionSet selectionSet, + ref Selection[] buffer, + SelectionSet selectionSet, int index, out int written) { written = 0; - var operationIncludeFlags = includeFlags; - var selectionCount = selectionSet.Selections.Count; + var selections = selectionSet.Selections; - ref var selectionRef = ref ((SelectionSet)selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref selectionRef, selectionCount); + EnsureCapacity(ref buffer, index, selections.Length); - EnsureCapacity(ref buffer, index, selectionCount); - - while (Unsafe.IsAddressLessThan(ref selectionRef, ref end)) + foreach (var selection in selections) { foreach (var fieldName in fieldNames) { - if (selectionRef.IsIncluded(operationIncludeFlags) - && selectionRef.Field.Name.EqualsOrdinal(fieldName)) + if (selection.IsIncluded(includeFlags) + && selection.Field.Name.EqualsOrdinal(fieldName)) { - buffer[index++] = selectionRef; + buffer[index++] = selection; written++; } } - - selectionRef = ref Unsafe.Add(ref selectionRef, 1)!; } } - private static void EnsureCapacity(ref ISelection[] buffer, int index, int requiredSpace) + private static void EnsureCapacity(ref Selection[] buffer, int index, int requiredSpace) { var capacity = buffer.Length - index; @@ -527,9 +513,9 @@ private static void EnsureCapacity(ref ISelection[] buffer, int index, int requi capacity *= 2; } - var newBuffer = ArrayPool.Shared.Rent(capacity); + var newBuffer = ArrayPool.Shared.Rent(capacity); buffer.AsSpan()[..index].CopyTo(newBuffer); - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(buffer); buffer = newBuffer; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs index 3986a6aab5a..e6614d3827f 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs @@ -19,7 +19,7 @@ private SelectionLookup(Entry[] table, int seed, int mask) public static SelectionLookup Create(SelectionSet selectionSet) { var selections = selectionSet.Selections; - var tableSize = NextPowerOfTwo(Math.Max(selections.Count * 2, 4)); + var tableSize = NextPowerOfTwo(Math.Max(selections.Length * 2, 4)); var mask = tableSize - 1; var table = new Entry[tableSize]; @@ -82,7 +82,7 @@ public static SelectionLookup Create(SelectionSet selectionSet) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetSelection(ReadOnlySpan name, [NotNullWhen(true)] out ISelection? selection) + public bool TryGetSelection(ReadOnlySpan name, [NotNullWhen(true)] out Selection? selection) { var table = _table.AsSpan(); @@ -148,9 +148,9 @@ private static int NextPowerOfTwo(int n) return n; } - private readonly struct Entry(int hashCode, ISelection selection) + private readonly struct Entry(int hashCode, Selection selection) { public readonly int HashCode = hashCode; - public readonly ISelection? Selection = selection; + public readonly Selection? Selection = selection; } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 6968ad6a7a3..39af1b52e5a 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -62,7 +62,7 @@ public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, public ReadOnlySpan Selections => _selections; IEnumerable ISelectionSet.GetSelections() => _selections; - + internal void Complete(Operation declaringOperation, bool seal) { if ((_flags & Flags.Sealed) == Flags.Sealed) @@ -74,10 +74,13 @@ internal void Complete(Operation declaringOperation, bool seal) foreach (var selection in _selections) { - selection.Complete(declaringOperation, this); + selection.Complete(this, seal); } - _flags |= Flags.Sealed; + if (seal) + { + _flags |= Flags.Sealed; + } } /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs index ce8e84a3630..e10abc1e2a5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs @@ -36,7 +36,7 @@ public async Task ExecuteAsync( var operation = requestContext.GetOperation(); var selectionSet = operation.RootSelectionSet; - if (selectionSet.Selections.Count != 1) + if (selectionSet.Selections.Length != 1) { throw SubscriptionExecutor_SubscriptionsMustHaveOneField(); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs index 441f034c9e7..112df731c60 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs @@ -91,7 +91,7 @@ private async ValueTask TryExecuteAsync(CancellationToken cancellationToke // signal that this resolver task has errors and shall end. if (Selection.Arguments.HasErrors) { - foreach (var argument in Selection.Arguments) + foreach (var argument in Selection.Arguments.ArgumentValues) { if (argument.HasError) { @@ -157,12 +157,13 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat return; } - if (_selection.IsList && _selection.HasStreamDirective(_operationContext.IncludeFlags)) - { - var stream = postProcessor.ToStreamResultAsync(result, cancellationToken); - _context.Result = await CreateStreamResultAsync(stream).ConfigureAwait(false); - return; - } + // TODO: DEFER + // if (_selection.IsList && _selection.HasStreamDirective(_operationContext.IncludeFlags)) + // { + // var stream = postProcessor.ToStreamResultAsync(result, cancellationToken); + // _context.Result = await CreateStreamResultAsync(stream).ConfigureAwait(false); + // return; + // } _context.Result = await postProcessor.ToCompletionResultAsync(result, cancellationToken).ConfigureAwait(false); } @@ -199,8 +200,10 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat if (next) { + // TODO : DEFER // if the stream has more items than the initial requested items then we will // defer the rest of the stream. + /* _operationContext.DeferredScheduler.Register( new DeferredStream( Selection, @@ -211,6 +214,7 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat enumerator, _context.ScopedContextData), _context.ParentResult); + */ } return list; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs index b6203dc5395..a5d30fe42a4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs @@ -9,7 +9,7 @@ internal sealed partial class ResolverTask /// public void Initialize( OperationContext operationContext, - ISelection selection, + Selection selection, ObjectResult parentResult, int responseIndex, object? parent, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs index 21a20cc2830..4b19453a6e7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs @@ -10,7 +10,7 @@ internal sealed partial class ResolverTask(ObjectPool objectPool) private readonly List _taskBuffer = []; private readonly Dictionary _args = new(StringComparer.Ordinal); private OperationContext _operationContext = null!; - private ISelection _selection = null!; + private Selection _selection = null!; private ExecutionTaskStatus _completionStatus = ExecutionTaskStatus.Completed; private Task? _task; @@ -32,7 +32,7 @@ internal sealed partial class ResolverTask(ObjectPool objectPool) /// /// Gets the selection for which a resolver is executed. /// - internal ISelection Selection => _selection; + internal Selection Selection => _selection; /// public ExecutionTaskKind Kind @@ -73,8 +73,4 @@ public void BeginExecute(CancellationToken cancellationToken) Status = ExecutionTaskStatus.Running; _task = ExecuteAsync(cancellationToken); } - - /// - public Task WaitForCompletionAsync(CancellationToken cancellationToken) - => _task ?? Task.CompletedTask; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs index 46066fe111b..973ef1e29e5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs @@ -1,6 +1,5 @@ using System.Collections.Immutable; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using HotChocolate.Types; using static HotChocolate.Execution.Processing.PathHelper; @@ -16,13 +15,14 @@ static ResolverTaskFactory() { } public static ObjectResult EnqueueResolverTasks( OperationContext operationContext, - ISelectionSet selectionSet, + SelectionSet selectionSet, object? parent, Path path, IImmutableDictionary scopedContext, ObjectResult? parentResult = null) { - var selectionsCount = selectionSet.Selections.Count; + var selections = selectionSet.Selections; + var selectionsCount = selections.Length; var responseIndex = selectionsCount; parentResult ??= operationContext.Result.RentObject(selectionsCount); var scheduler = operationContext.Scheduler; @@ -34,8 +34,6 @@ public static ObjectResult EnqueueResolverTasks( try { - ref var selectionSpace = ref ((SelectionSet)selectionSet).GetSelectionsReference(); - // we are iterating reverse so that in the case of a mutation the first // synchronous root selection is executed first, since the work scheduler // is using two stacks one for parallel work and one for synchronous work. @@ -44,7 +42,7 @@ public static ObjectResult EnqueueResolverTasks( // guarantees while executing efficient. for (var i = selectionsCount - 1; i >= 0; i--) { - ref var selection = ref Unsafe.Add(ref selectionSpace, i); + var selection = selections[i]; if (final || selection.IsIncluded(includeFlags)) { @@ -69,17 +67,6 @@ public static ObjectResult EnqueueResolverTasks( scheduler.Register(CollectionsMarshal.AsSpan(bufferedTasks)); } - if (selectionSet.Fragments.Count > 0) - { - TryHandleDeferredFragments( - operationContext, - selectionSet, - scopedContext, - path, - parent, - parentResult); - } - return parentResult; } finally @@ -144,10 +131,11 @@ public static ResolverTask EnqueueElementTasks( ResultData parentResult, int parentIndex, object parent, - ISelectionSet selectionSet) + SelectionSet selectionSet) { var responseIndex = 0; - var selectionsCount = selectionSet.Selections.Count; + var selections = selectionSet.Selections; + var selectionsCount = selections.Length; var operationContext = context.OperationContext; var result = operationContext.Result.RentObject(selectionsCount); var includeFlags = operationContext.IncludeFlags; @@ -155,10 +143,7 @@ public static ResolverTask EnqueueElementTasks( result.SetParent(parentResult, parentIndex); - ref var selection = ref ((SelectionSet)selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref selection, selectionsCount); - - while (Unsafe.IsAddressLessThan(ref selection, ref end)) + foreach (var selection in selections) { if (result.IsInvalidated) { @@ -167,7 +152,7 @@ public static ResolverTask EnqueueElementTasks( if (!final && !selection.IsIncluded(includeFlags)) { - goto NEXT; + continue; } if (selection.Strategy is SelectionExecutionStrategy.Pure) @@ -190,20 +175,6 @@ public static ResolverTask EnqueueElementTasks( responseIndex++, context.ResolverContext.ScopedContextData)); } - -NEXT: - selection = ref Unsafe.Add(ref selection, 1)!; - } - - if (selectionSet.Fragments.Count > 0) - { - TryHandleDeferredFragments( - operationContext, - selectionSet, - context.ResolverContext.ScopedContextData, - CreatePathFromContext(result), - parent, - result); } return result.IsInvalidated ? null : result; @@ -211,7 +182,7 @@ public static ResolverTask EnqueueElementTasks( private static void ResolveAndCompleteInline( ValueCompletionContext context, - ISelection selection, + Selection selection, int responseIndex, ObjectType parentType, object parent, @@ -332,34 +303,6 @@ private static void CommitValue( } } - private static void TryHandleDeferredFragments( - OperationContext operationContext, - ISelectionSet selectionSet, - IImmutableDictionary scopedContext, - Path path, - object? parent, - ObjectResult parentResult) - { - var fragments = selectionSet.Fragments; - var includeFlags = operationContext.IncludeFlags; - - for (var i = 0; i < fragments.Count; i++) - { - var fragment = fragments[i]; - if (!fragment.IsConditional || fragment.IsIncluded(includeFlags)) - { - operationContext.DeferredScheduler.Register( - new DeferredFragment( - fragment, - fragment.GetLabel(operationContext.Variables), - path, - parent, - scopedContext), - parentResult); - } - } - } - private sealed class NoOpExecutionTask(OperationContext context) : ExecutionTask { protected override IExecutionTaskContext Context { get; } = context; diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 2d0e7d9066a..a16f3d639cd 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -195,6 +195,18 @@ OperationContext.cs + + + ResolverTask.cs + + + + ResolverTask.cs + + + + ResolverTask.cs + diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/OperationParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/OperationParameterExpressionBuilder.cs index 06cc6ce1d09..0598c766c67 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/OperationParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/OperationParameterExpressionBuilder.cs @@ -1,13 +1,14 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Internal; namespace HotChocolate.Resolvers.Expressions.Parameters; internal sealed class OperationParameterExpressionBuilder() - : LambdaParameterExpressionBuilder(ctx => ctx.Operation, isPure: true) + : LambdaParameterExpressionBuilder(ctx => ctx.Operation, isPure: true) , IParameterBindingFactory , IParameterBinding { @@ -15,10 +16,12 @@ public override ArgumentKind Kind => ArgumentKind.Operation; public override bool CanHandle(ParameterInfo parameter) - => typeof(IOperation) == parameter.ParameterType; + => typeof(IOperation) == parameter.ParameterType + || typeof(Operation) == parameter.ParameterType; public bool CanHandle(ParameterDescriptor parameter) - => typeof(IOperation) == parameter.Type; + => typeof(IOperation) == parameter.Type + || typeof(Operation) == parameter.Type; public IParameterBinding Create(ParameterDescriptor parameter) => this; @@ -27,6 +30,6 @@ public T Execute(IResolverContext context) { Debug.Assert(typeof(IOperation).IsAssignableFrom(typeof(T))); var operation = context.Operation; - return Unsafe.As(ref operation); + return Unsafe.As(ref operation); } } From a96af9a67125c4246358b3cb8c03d7459a75aead Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 1 Dec 2025 23:34:54 +0100 Subject: [PATCH 14/30] wip --- .../src/Authorization/AuthorizeMiddleware.cs | 2 +- .../ErrorBuilderExtensions.cs | 3 +- ...fsetPaginationResolverContextExtensions.cs | 7 +- .../Processing/FieldSelectionNode.cs | 14 + .../Execution/Processing/IncludeCondition.cs | 153 ++ .../Processing/IncludeConditionCollection.cs | 50 + .../Execution/Processing/OperationCompiler.cs | 208 --- .../Processing/OperationContext.Execution.cs | 2 +- .../Processing/OperationContextOwner.cs | 7 +- .../Types/Execution/Processing/Selection.cs | 13 - .../Processing/Tasks/ResolverTaskFactory.cs | 2 +- .../Execution/Processing/Utf8StringCache.cs | 15 + .../Core/src/Types/HotChocolate.Types.csproj | 10 +- .../Core/src/Validation/ErrorHelper.cs | 1295 ++++++++--------- 14 files changed, 849 insertions(+), 932 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/FieldSelectionNode.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/IncludeConditionCollection.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/Utf8StringCache.cs diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs index 0a5aa008ea1..e7de30d536b 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs @@ -69,7 +69,7 @@ private void SetError( .SetMessage(AuthorizeMiddleware_NoDefaultPolicy) .SetCode(ErrorCodes.Authentication.NoDefaultPolicy) .SetPath(context.Path) - .AddLocation(context.Selection.SyntaxNode) + .AddLocations(context.Selection.SyntaxNodes) .Build(), AuthorizeResult.PolicyNotFound => ErrorBuilder.New() diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs index 9c4d6c88050..eb9d4a2843b 100644 --- a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Globalization; using HotChocolate.Language; @@ -40,7 +41,7 @@ public ErrorBuilder SetInputPath(Path inputPath) /// The format of the message. /// The arguments for the message. /// The error builder. - public ErrorBuilder SetMessage(string format, params object[] args) + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, params object?[] args) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(format); diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs index 63f5829a6a9..089dccdd442 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs @@ -23,13 +23,8 @@ public static bool IsTotalCountSelected(this IResolverContext context) var selectionSet = context.Selection.DeclaringOperation.GetSelectionSet(context.Selection, objectType); foreach (var selection in selectionSet.Selections) - { )) - - } - - for (var i = 0; i < selections.Count; i++) { - if (selections[i].Field.Name is OffsetPagingFieldNames.TotalCount) + if (selection.Field.Name is OffsetPagingFieldNames.TotalCount) { return true; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/FieldSelectionNode.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/FieldSelectionNode.cs new file mode 100644 index 00000000000..f5964f13f64 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/FieldSelectionNode.cs @@ -0,0 +1,14 @@ +using HotChocolate.Language; + +namespace HotChocolate.Execution.Processing; + +/// +/// Represents a field selection node with its path include flags. +/// +/// +/// The syntax node that represents the field selection. +/// +/// +/// The flags that must be all set for this selection to be included. +/// +public sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs new file mode 100644 index 00000000000..f59672de25a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeCondition.cs @@ -0,0 +1,153 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Language; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +internal readonly struct IncludeCondition : IEquatable +{ + private readonly string? _skip; + private readonly string? _include; + + public IncludeCondition(string? skip, string? include) + { + _skip = skip; + _include = include; + } + + public string? Skip => _skip; + + public string? Include => _include; + + public bool IsIncluded(IVariableValueCollection variableValues) + { + if (_skip is not null) + { + if (!variableValues.TryGetValue(_skip, out var value)) + { + throw new InvalidOperationException($"The variable {_skip} has an invalid value."); + } + + if (value.Value) + { + return false; + } + } + + if (_include is not null) + { + if (!variableValues.TryGetValue(_include, out var value)) + { + throw new InvalidOperationException($"The variable {_include} has an invalid value."); + } + + if (!value.Value) + { + return false; + } + } + + return true; + } + + public bool Equals(IncludeCondition other) + => string.Equals(Skip, other.Skip, StringComparison.Ordinal) + && string.Equals(Include, other.Include, StringComparison.Ordinal); + + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is IncludeCondition other && Equals(other); + + public override int GetHashCode() + => HashCode.Combine(Skip, Include); + + public static bool TryCreate(FieldNode field, out IncludeCondition includeCondition) + => TryCreate(field.Directives, out includeCondition); + + public static bool TryCreate(InlineFragmentNode inlineFragment, out IncludeCondition includeCondition) + => TryCreate(inlineFragment.Directives, out includeCondition); + + private static bool TryCreate(IReadOnlyList directives, out IncludeCondition includeCondition) + { + string? skip = null; + string? include = null; + + if (directives.Count == 0) + { + includeCondition = default; + return false; + } + + if (directives.Count == 1) + { + TryParseDirective(directives[0], ref skip, ref include); + if (TryCreateIncludeCondition(out includeCondition)) + { + return true; + } + } + + if (directives.Count == 2) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); + } + + if (directives.Count == 3) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + + if (skip is not null && include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + + TryParseDirective(directives[2], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); + } + + for (var i = 0; i < directives.Count; i++) + { + TryParseDirective(directives[i], ref skip, ref include); + + if (skip is not null && include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + } + + includeCondition = default; + return false; + + bool TryCreateIncludeCondition(out IncludeCondition includeCondition) + { + if (skip is not null || include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + + includeCondition = default; + return false; + } + } + + private static void TryParseDirective(DirectiveNode directive, ref string? skip, ref string? include) + { + if (directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode skipVariable) + { + skip = skipVariable.Name.Value; + } + else if (directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode includeVariable) + { + include = includeVariable.Name.Value; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeConditionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeConditionCollection.cs new file mode 100644 index 00000000000..f9cbbdd292f --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IncludeConditionCollection.cs @@ -0,0 +1,50 @@ +using System.Collections; + +namespace HotChocolate.Execution.Processing; + +internal class IncludeConditionCollection : ICollection +{ + private readonly OrderedDictionary _dictionary = []; + + public IncludeCondition this[int index] + => _dictionary.GetAt(index).Key; + + public int Count => _dictionary.Count; + + public bool IsReadOnly => false; + + public bool Add(IncludeCondition item) + { + if (_dictionary.Count == 64) + { + throw new InvalidOperationException( + "The maximum number of include conditions has been reached."); + } + + return _dictionary.TryAdd(item, _dictionary.Count); + } + + void ICollection.Add(IncludeCondition item) + => Add(item); + + public bool Remove(IncludeCondition item) + => throw new InvalidOperationException("This is an add only collection."); + + void ICollection.Clear() + => throw new InvalidOperationException("This is an add only collection."); + + public bool Contains(IncludeCondition item) + => _dictionary.ContainsKey(item); + + public int IndexOf(IncludeCondition item) + => _dictionary.GetValueOrDefault(item, -1); + + public void CopyTo(IncludeCondition[] array, int arrayIndex) + => _dictionary.Keys.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() + => _dictionary.Keys.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 33caee22385..e83d352bbb0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -1,6 +1,4 @@ using System.Buffers; -using System.Collections; -using System.Diagnostics.CodeAnalysis; using HotChocolate.Fusion.Rewriters; using HotChocolate.Language; using HotChocolate.Language.Visitors; @@ -436,209 +434,3 @@ public void Register(object element, int id) } } } - -/// -/// Represents a field selection node with its path include flags. -/// -/// -/// The syntax node that represents the field selection. -/// -/// -/// The flags that must be all set for this selection to be included. -/// -public sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); - -internal class IncludeConditionCollection : ICollection -{ - private readonly OrderedDictionary _dictionary = []; - - public IncludeCondition this[int index] - => _dictionary.GetAt(index).Key; - - public int Count => _dictionary.Count; - - public bool IsReadOnly => false; - - public bool Add(IncludeCondition item) - { - if (_dictionary.Count == 64) - { - throw new InvalidOperationException( - "The maximum number of include conditions has been reached."); - } - - return _dictionary.TryAdd(item, _dictionary.Count); - } - - void ICollection.Add(IncludeCondition item) - => Add(item); - - public bool Remove(IncludeCondition item) - => throw new InvalidOperationException("This is an add only collection."); - - void ICollection.Clear() - => throw new InvalidOperationException("This is an add only collection."); - - public bool Contains(IncludeCondition item) - => _dictionary.ContainsKey(item); - - public int IndexOf(IncludeCondition item) - => _dictionary.GetValueOrDefault(item, -1); - - public void CopyTo(IncludeCondition[] array, int arrayIndex) - => _dictionary.Keys.CopyTo(array, arrayIndex); - - public IEnumerator GetEnumerator() - => _dictionary.Keys.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); -} - -internal readonly struct IncludeCondition : IEquatable -{ - private readonly string? _skip; - private readonly string? _include; - - public IncludeCondition(string? skip, string? include) - { - _skip = skip; - _include = include; - } - - public string? Skip => _skip; - - public string? Include => _include; - - public bool IsIncluded(IVariableValueCollection variableValues) - { - if (_skip is not null) - { - if (!variableValues.TryGetValue(_skip, out var value)) - { - throw new InvalidOperationException($"The variable {_skip} has an invalid value."); - } - - if (value.Value) - { - return false; - } - } - - if (_include is not null) - { - if (!variableValues.TryGetValue(_include, out var value)) - { - throw new InvalidOperationException($"The variable {_include} has an invalid value."); - } - - if (!value.Value) - { - return false; - } - } - - return true; - } - - public bool Equals(IncludeCondition other) - => string.Equals(Skip, other.Skip, StringComparison.Ordinal) - && string.Equals(Include, other.Include, StringComparison.Ordinal); - - public override bool Equals([NotNullWhen(true)] object? obj) - => obj is IncludeCondition other && Equals(other); - - public override int GetHashCode() - => HashCode.Combine(Skip, Include); - - public static bool TryCreate(FieldNode field, out IncludeCondition includeCondition) - => TryCreate(field.Directives, out includeCondition); - - public static bool TryCreate(InlineFragmentNode inlineFragment, out IncludeCondition includeCondition) - => TryCreate(inlineFragment.Directives, out includeCondition); - - private static bool TryCreate(IReadOnlyList directives, out IncludeCondition includeCondition) - { - string? skip = null; - string? include = null; - - if (directives.Count == 0) - { - includeCondition = default; - return false; - } - - if (directives.Count == 1) - { - TryParseDirective(directives[0], ref skip, ref include); - if (TryCreateIncludeCondition(out includeCondition)) - { - return true; - } - } - - if (directives.Count == 2) - { - TryParseDirective(directives[0], ref skip, ref include); - TryParseDirective(directives[1], ref skip, ref include); - return TryCreateIncludeCondition(out includeCondition); - } - - if (directives.Count == 3) - { - TryParseDirective(directives[0], ref skip, ref include); - TryParseDirective(directives[1], ref skip, ref include); - - if (skip is not null && include is not null) - { - includeCondition = new IncludeCondition(skip, include); - return true; - } - - TryParseDirective(directives[2], ref skip, ref include); - return TryCreateIncludeCondition(out includeCondition); - } - - for (var i = 0; i < directives.Count; i++) - { - TryParseDirective(directives[i], ref skip, ref include); - - if (skip is not null && include is not null) - { - includeCondition = new IncludeCondition(skip, include); - return true; - } - } - - includeCondition = default; - return false; - - bool TryCreateIncludeCondition(out IncludeCondition includeCondition) - { - if (skip is not null || include is not null) - { - includeCondition = new IncludeCondition(skip, include); - return true; - } - - includeCondition = default; - return false; - } - } - - private static void TryParseDirective(DirectiveNode directive, ref string? skip, ref string? include) - { - if (directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal) - && directive.Arguments.Count == 1 - && directive.Arguments[0].Value is VariableNode skipVariable) - { - skip = skipVariable.Name.Value; - } - else if (directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal) - && directive.Arguments.Count == 1 - && directive.Arguments[0].Value is VariableNode includeVariable) - { - include = includeVariable.Name.Value; - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs index 6e74fb6e5ea..3fca2464889 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs @@ -43,7 +43,7 @@ public RequestContext RequestContext } public ResolverTask CreateResolverTask( - ISelection selection, + Selection selection, object? parent, ObjectResult parentResult, int responseIndex, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs index ae26c204bad..c0aa82e3049 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs @@ -4,12 +4,15 @@ namespace HotChocolate.Execution.Processing; /// +/// /// The operation context owner abstracts the interaction of resolving of -/// an instance from its pool and returning to to +/// an instance from its pool and returning to /// the pool through the implementation of . -/// +/// +/// /// In some cases its desirable to not call dispose and abandon a pooled /// . +/// /// internal sealed class OperationContextOwner : IDisposable { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index f4538b26dfe..cd95444549b 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -1,7 +1,4 @@ using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text; -using HotChocolate.Caching.Memory; using HotChocolate.Execution.Properties; using HotChocolate.Language; using HotChocolate.Resolvers; @@ -267,13 +264,3 @@ public Sealed( } } } - -internal static class Utf8StringCache -{ - private static readonly Encoding s_utf8 = Encoding.UTF8; - private static readonly Cache s_cache = new(capacity: 4 * 1024); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] GetUtf8String(string s) - => s_cache.GetOrCreate(s, static k => s_utf8.GetBytes(k)); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs index 973ef1e29e5..420df73c557 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs @@ -78,7 +78,7 @@ public static ObjectResult EnqueueResolverTasks( public static ResolverTask EnqueueElementTasks( OperationContext operationContext, - ISelection selection, + Selection selection, object? parent, Path path, int index, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Utf8StringCache.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Utf8StringCache.cs new file mode 100644 index 00000000000..330f0c33daf --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Utf8StringCache.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using System.Text; +using HotChocolate.Caching.Memory; + +namespace HotChocolate.Execution.Processing; + +internal static class Utf8StringCache +{ + private static readonly Encoding s_utf8 = Encoding.UTF8; + private static readonly Cache s_cache = new(capacity: 4 * 1024); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] GetUtf8String(string s) + => s_cache.GetOrCreate(s, static k => s_utf8.GetBytes(k)); +} diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index a16f3d639cd..7b3d5a23453 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -46,7 +46,6 @@ - @@ -65,19 +64,18 @@ - - - - - + + + + diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index 415369aa554..daaadbe780f 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -6,763 +6,672 @@ namespace HotChocolate.Validation; internal static class ErrorHelper { - public static IError VariableNotUsed( - this DocumentValidatorContext context, - OperationDefinitionNode node, - IEnumerable unusedVariables) - { - return ErrorBuilder.New() - .SetMessage( - "The following variables were not used: " - + $"{string.Join(", ", unusedVariables)}.") - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-All-Variables-Used") - .Build(); - } - - public static IError VariableNotDeclared( - this DocumentValidatorContext context, - OperationDefinitionNode node, - IEnumerable usedVariables) - { - return ErrorBuilder.New() - .SetMessage( - "The following variables were not declared: " - + $"{string.Join(", ", usedVariables)}.") - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-All-Variable-Uses-Defined") - .Build(); - } - - public static IError OneOfVariableIsNotCompatible( - this DocumentValidatorContext context, - VariableNode variable, - VariableDefinitionNode variableDefinition) - { - var variableName = variableDefinition.Variable.Name.Value; - - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_OneOfVariableIsNotCompatible, - variableName) - .AddLocation(variable) - .SetPath(context.CreateErrorPath()) - .SetExtension("variable", variableName) - .SpecifiedBy("sec-All-Variable-Usages-are-Allowed") - .Build(); - } - - public static IError VariableIsNotCompatible( - this DocumentValidatorContext context, - VariableNode variable, - VariableDefinitionNode variableDefinition) - { - var variableName = variableDefinition.Variable.Name.Value; - - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_VariableIsNotCompatible, - variableName) - .AddLocation(variable) - .SetPath(context.CreateErrorPath()) - .SetExtension("variable", variableName) - .SetExtension("variableType", variableDefinition.Type.ToString()) - .SetExtension("locationType", context.Types.Peek().FullTypeName()) - .SpecifiedBy("sec-All-Variable-Usages-are-Allowed") - .Build(); - } - - public static IError DirectiveNotValidInLocation( - this DocumentValidatorContext context, - DirectiveNode node) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_DirectiveNotValidInLocation) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-Directives-Are-In-Valid-Locations") - .Build(); - } - - public static IError DirectiveNotSupported( - this DocumentValidatorContext context, - DirectiveNode node) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_DirectiveNotSupported, - node.Name.Value) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-Directives-Are-Defined") - .Build(); - } - - public static IError DirectiveMustBeUniqueInLocation( - this DocumentValidatorContext context, - DirectiveNode node) => - ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_DirectiveMustBeUniqueInLocation) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-Directives-Are-Unique-Per-Location") - .Build(); - - public static IError TypeSystemDefinitionNotAllowed( - this DocumentValidatorContext context, - IDefinitionNode node) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_TypeSystemDefinitionNotAllowed) - .AddLocation(node) - .SpecifiedBy("sec-Executable-Definitions") - .Build(); - } - - public static IError UnionFieldError( - this DocumentValidatorContext context, - SelectionSetNode node, - IUnionTypeDefinition type) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_UnionFieldError) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("type", type.Name) - .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") - .Build(); - } - - public static IError FieldDoesNotExist( - this DocumentValidatorContext context, - FieldNode node, - IComplexTypeDefinition outputType) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_FieldDoesNotExist, - node.Name.Value, outputType.Name) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("type", outputType.Name) - .SetExtension("field", node.Name.Value) - .SetExtension("responseName", (node.Alias ?? node.Name).Value) - .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") - .Build(); - } - - public static IError LeafFieldsCannotHaveSelections( - this DocumentValidatorContext context, - FieldNode node, - IComplexTypeDefinition declaringType, - IType fieldType) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_LeafFieldsCannotHaveSelections, - node.Name.Value, - fieldType.FullTypeName()) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("declaringType", declaringType.Name) - .SetExtension("field", node.Name.Value) - .SetExtension("type", fieldType.FullTypeName()) - .SetExtension("responseName", (node.Alias ?? node.Name).Value) - .SpecifiedBy("sec-Leaf-Field-Selections") - .Build(); - } - - public static IError ArgumentValueIsNotCompatible( - this DocumentValidatorContext context, - ArgumentNode node, - IInputType locationType, - IValueNode value) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_ArgumentValueIsNotCompatible) - .AddLocation(value) - .SetPath(context.CreateErrorPath()) - .SetExtension("argument", node.Name.Value) - .SetExtension("argumentValue", value.ToString()) - .SetExtension("locationType", locationType.FullTypeName()) - .SpecifiedBy("sec-Values-of-Correct-Type") - .Build(); - } - - public static IError FieldValueIsNotCompatible( - this DocumentValidatorContext context, - IInputValueDefinition field, - IInputType locationType, - IValueNode valueNode) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FieldValueIsNotCompatible, field.Name) - .AddLocation(valueNode) - .SetExtension("fieldName", field.Name) - .SetExtension("fieldType", field.Type.FullTypeName()) - .SetExtension("locationType", locationType.FullTypeName()) - .SetPath(context.CreateErrorPath()) - .SpecifiedBy("sec-Values-of-Correct-Type") - .Build(); - } - - public static IError VariableDefaultValueIsNotCompatible( - this DocumentValidatorContext context, - VariableDefinitionNode node, - IInputType locationType, - IValueNode valueNode) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_VariableDefaultValueIsNotCompatible, - node.Variable.Name.Value) - .AddLocation(valueNode) - .SetPath(context.CreateErrorPath()) - .SetExtension("variable", node.Variable.Name.Value) - .SetExtension("variableType", node.Type.ToString()) - .SetExtension("locationType", locationType.FullTypeName()) - .SpecifiedBy("sec-Values-of-Correct-Type") + public static IError SkipAndIncludeNotAllowedOnSubscriptionRootField( + ISelectionNode selection) + => ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_SkipAndIncludeNotAllowedOnSubscriptionRootField) + .AddLocation(selection) + .SpecifiedBy("sec-Single-Root-Field", rfc: 860) .Build(); - } - public static IError NoSelectionOnCompositeField( - this DocumentValidatorContext context, - FieldNode node, - IComplexTypeDefinition declaringType, - IType fieldType) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_NoSelectionOnCompositeField, - node.Name.Value, - fieldType.ToTypeNode().ToString()) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("declaringType", declaringType.Name) - .SetExtension("field", node.Name.Value) - .SetExtension("type", fieldType.FullTypeName()) - .SetExtension("responseName", (node.Alias ?? node.Name).Value) - .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") + public static IError DeferAndStreamNotAllowedOnMutationOrSubscriptionRoot( + ISelectionNode selection) + => ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_DeferAndStreamNotAllowedOnMutationOrSubscriptionRoot) + .AddLocation(selection) + .SpecifiedBy("sec-Defer-And-Stream-Directives-Are-Used-On-Valid-Root-Field") .Build(); - } - public static IError NoSelectionOnRootType( - this DocumentValidatorContext context, - OperationDefinitionNode node, - IType fieldType) + extension(DocumentValidatorContext context) { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_NoSelectionOnRootType, - node.Name?.Value ?? "Unnamed") - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("operation", node.Name?.Value ?? "Unnamed") - .SetExtension("type", fieldType.FullTypeName()) - .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") - .Build(); - } + public IError VariableNotUsed(OperationDefinitionNode node, + IEnumerable unusedVariables) + { + return ErrorBuilder.New() + .SetMessage( + "The following variables were not used: " + + $"{string.Join(", ", unusedVariables)}.") + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-All-Variables-Used") + .Build(); + } - public static IError FieldIsRequiredButNull( - this DocumentValidatorContext context, - ISyntaxNode node, - string fieldName) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FieldIsRequiredButNull, fieldName) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("field", fieldName) - .SpecifiedBy("sec-Input-Object-Required-Fields") - .Build(); - } + public IError VariableNotDeclared(OperationDefinitionNode node, + IEnumerable usedVariables) + { + return ErrorBuilder.New() + .SetMessage( + "The following variables were not declared: " + + $"{string.Join(", ", usedVariables)}.") + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-All-Variable-Uses-Defined") + .Build(); + } - public static IError FieldsAreNotMergeable( - this DocumentValidatorContext context, - FieldInfo fieldA, - FieldInfo fieldB) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FieldsAreNotMergeable) - .AddLocation(fieldA.SyntaxNode) - .AddLocation(fieldB.SyntaxNode) - .SetExtension("declaringTypeA", fieldA.DeclaringType.NamedType().Name) - .SetExtension("declaringTypeB", fieldB.DeclaringType.NamedType().Name) - .SetExtension("fieldA", fieldA.SyntaxNode.Name.Value) - .SetExtension("fieldB", fieldB.SyntaxNode.Name.Value) - .SetExtension("typeA", fieldA.Type.FullTypeName()) - .SetExtension("typeB", fieldB.Type.FullTypeName()) - .SetExtension("responseNameA", fieldA.ResponseName) - .SetExtension("responseNameB", fieldB.ResponseName) - .SpecifiedBy("sec-Field-Selection-Merging") - .Build(); - } + public IError OneOfVariableIsNotCompatible(VariableNode variable, + VariableDefinitionNode variableDefinition) + { + var variableName = variableDefinition.Variable.Name.Value; - public static IError OperationNotSupported( - this DocumentValidatorContext context, - OperationType operationType) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_OperationNotSupported, - operationType) - .Build(); - } + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_OneOfVariableIsNotCompatible, + variableName) + .AddLocation(variable) + .SetPath(context.CreateErrorPath()) + .SetExtension("variable", variableName) + .SpecifiedBy("sec-All-Variable-Usages-are-Allowed") + .Build(); + } - public static IError FragmentNameNotUnique( - this DocumentValidatorContext context, - FragmentDefinitionNode fragmentDefinition) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_FragmentNameNotUnique, - fragmentDefinition.Name.Value) - .AddLocation(fragmentDefinition) - .SetExtension("fragment", fragmentDefinition.Name.Value) - .SpecifiedBy("sec-Fragment-Name-Uniqueness") - .Build(); - } + public IError VariableIsNotCompatible(VariableNode variable, + VariableDefinitionNode variableDefinition) + { + var variableName = variableDefinition.Variable.Name.Value; - public static IError FragmentNotUsed( - this DocumentValidatorContext context, - FragmentDefinitionNode fragmentDefinition) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_FragmentNotUsed, - fragmentDefinition.Name.Value) - .AddLocation(fragmentDefinition) - .SetPath(context.CreateErrorPath()) - .SetExtension("fragment", fragmentDefinition.Name.Value) - .SpecifiedBy("sec-Fragments-Must-Be-Used") - .Build(); - } + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_VariableIsNotCompatible, + variableName) + .AddLocation(variable) + .SetPath(context.CreateErrorPath()) + .SetExtension("variable", variableName) + .SetExtension("variableType", variableDefinition.Type.ToString()) + .SetExtension("locationType", context.Types.Peek().FullTypeName()) + .SpecifiedBy("sec-All-Variable-Usages-are-Allowed") + .Build(); + } - public static IError FragmentCycleDetected( - this DocumentValidatorContext context, - FragmentSpreadNode fragmentSpread) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FragmentCycleDetected) - .AddLocation(fragmentSpread) - .SetPath(context.CreateErrorPath()) - .SetExtension("fragment", fragmentSpread.Name.Value) - .SpecifiedBy("sec-Fragment-spreads-must-not-form-cycles") - .Build(); - } + public IError DirectiveNotValidInLocation(DirectiveNode node) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_DirectiveNotValidInLocation) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-Directives-Are-In-Valid-Locations") + .Build(); + } - public static IError FragmentDoesNotExist( - this DocumentValidatorContext context, - FragmentSpreadNode fragmentSpread) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_FragmentDoesNotExist, - fragmentSpread.Name.Value) - .AddLocation(fragmentSpread) - .SetPath(context.CreateErrorPath()) - .SetExtension("fragment", fragmentSpread.Name.Value) - .SpecifiedBy("sec-Fragment-spread-target-defined") - .Build(); - } + public IError DirectiveNotSupported(DirectiveNode node) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_DirectiveNotSupported, + node.Name.Value) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-Directives-Are-Defined") + .Build(); + } - public static IError FragmentNotPossible( - this DocumentValidatorContext context, - ISyntaxNode node, - ITypeDefinition typeCondition, - ITypeDefinition parentType) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FragmentNotPossible) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("typeCondition", typeCondition.Name) - .SetExtension("selectionSetType", parentType.Name) - .SetFragmentName(node) - .SpecifiedBy("sec-Fragment-spread-is-possible") - .Build(); - } + public IError DirectiveMustBeUniqueInLocation(DirectiveNode node) => + ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_DirectiveMustBeUniqueInLocation) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-Directives-Are-Unique-Per-Location") + .Build(); - public static IError FragmentTypeConditionUnknown( - this DocumentValidatorContext context, - ISyntaxNode node, - NamedTypeNode typeCondition) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_FragmentTypeConditionUnknown, - typeCondition.Name.Value) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("typeCondition", typeCondition.Name.Value) - .SetFragmentName(node) - .SpecifiedBy("sec-Fragment-Spread-Type-Existence") - .Build(); - } + public IError TypeSystemDefinitionNotAllowed(IDefinitionNode node) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_TypeSystemDefinitionNotAllowed) + .AddLocation(node) + .SpecifiedBy("sec-Executable-Definitions") + .Build(); + } - public static IError FragmentOnlyCompositeType( - this DocumentValidatorContext context, - ISyntaxNode node, - ITypeDefinition type) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_FragmentOnlyCompositeType) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("typeCondition", type.FullTypeName()) - .SetFragmentName(node) - .SpecifiedBy("sec-Fragments-On-Composite-Types") - .Build(); - } + public IError UnionFieldError(SelectionSetNode node, + IUnionTypeDefinition type) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_UnionFieldError) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("type", type.Name) + .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") + .Build(); + } - public static IError InputFieldAmbiguous( - this DocumentValidatorContext context, - ObjectFieldNode field) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_InputFieldAmbiguous, field.Name.Value) - .AddLocation(field) - .SetPath(context.CreateErrorPath()) - .SetExtension("field", field.Name.Value) - .SpecifiedBy("sec-Input-Object-Field-Uniqueness") - .Build(); - } + public IError FieldDoesNotExist(FieldNode node, + IComplexTypeDefinition outputType) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_FieldDoesNotExist, + node.Name.Value, outputType.Name) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("type", outputType.Name) + .SetExtension("field", node.Name.Value) + .SetExtension("responseName", (node.Alias ?? node.Name).Value) + .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") + .Build(); + } - public static IError InputFieldDoesNotExist( - this DocumentValidatorContext context, - ObjectFieldNode field) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_InputFieldDoesNotExist, - field.Name.Value) - .AddLocation(field) - .SetPath(context.CreateErrorPath()) - .SetExtension("field", field.Name.Value) - .SpecifiedBy("sec-Input-Object-Field-Names") - .Build(); - } + public IError LeafFieldsCannotHaveSelections(FieldNode node, + IComplexTypeDefinition declaringType, + IType fieldType) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_LeafFieldsCannotHaveSelections, + node.Name.Value, + fieldType.FullTypeName()) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("declaringType", declaringType.Name) + .SetExtension("field", node.Name.Value) + .SetExtension("type", fieldType.FullTypeName()) + .SetExtension("responseName", (node.Alias ?? node.Name).Value) + .SpecifiedBy("sec-Leaf-Field-Selections") + .Build(); + } - public static IError InputFieldRequired( - this DocumentValidatorContext context, - ISyntaxNode node, - string fieldName) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_InputFieldRequired, fieldName) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("field", fieldName) - .SpecifiedBy("sec-Input-Object-Required-Fields") - .Build(); - } + public IError ArgumentValueIsNotCompatible(ArgumentNode node, + IInputType locationType, + IValueNode value) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_ArgumentValueIsNotCompatible) + .AddLocation(value) + .SetPath(context.CreateErrorPath()) + .SetExtension("argument", node.Name.Value) + .SetExtension("argumentValue", value.ToString()) + .SetExtension("locationType", locationType.FullTypeName()) + .SpecifiedBy("sec-Values-of-Correct-Type") + .Build(); + } - public static IError OperationNameNotUnique( - this DocumentValidatorContext context, - OperationDefinitionNode operation, - string operationName) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_OperationNameNotUnique, - operationName) - .AddLocation(operation) - .SetExtension("operation", operationName) - .SpecifiedBy("sec-Operation-Name-Uniqueness") - .Build(); - } + public IError FieldValueIsNotCompatible(IInputValueDefinition field, + IInputType locationType, + IValueNode valueNode) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FieldValueIsNotCompatible, field.Name) + .AddLocation(valueNode) + .SetExtension("fieldName", field.Name) + .SetExtension("fieldType", field.Type.FullTypeName()) + .SetExtension("locationType", locationType.FullTypeName()) + .SetPath(context.CreateErrorPath()) + .SpecifiedBy("sec-Values-of-Correct-Type") + .Build(); + } - public static IError OperationAnonymousMoreThanOne( - this DocumentValidatorContext context, - OperationDefinitionNode operation, - int operations) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_OperationAnonymousMoreThanOne) - .AddLocation(operation) - .SetExtension("operations", operations) - .SpecifiedBy("sec-Lone-Anonymous-Operation") - .Build(); - } + public IError VariableDefaultValueIsNotCompatible(VariableDefinitionNode node, + IInputType locationType, + IValueNode valueNode) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_VariableDefaultValueIsNotCompatible, + node.Variable.Name.Value) + .AddLocation(valueNode) + .SetPath(context.CreateErrorPath()) + .SetExtension("variable", node.Variable.Name.Value) + .SetExtension("variableType", node.Type.ToString()) + .SetExtension("locationType", locationType.FullTypeName()) + .SpecifiedBy("sec-Values-of-Correct-Type") + .Build(); + } - public static IError VariableNotInputType( - this DocumentValidatorContext context, - VariableDefinitionNode node, - string variableName) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_VariableNotInputType, variableName) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("variable", variableName) - .SetExtension("variableType", node.Type.ToString()) - .SpecifiedBy("sec-Variables-Are-Input-Types") - .Build(); - } + public IError NoSelectionOnCompositeField(FieldNode node, + IComplexTypeDefinition declaringType, + IType fieldType) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_NoSelectionOnCompositeField, + node.Name.Value, + fieldType.ToTypeNode().ToString()) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("declaringType", declaringType.Name) + .SetExtension("field", node.Name.Value) + .SetExtension("type", fieldType.FullTypeName()) + .SetExtension("responseName", (node.Alias ?? node.Name).Value) + .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") + .Build(); + } - public static IError VariableNameNotUnique( - this DocumentValidatorContext context, - VariableDefinitionNode node, - string variableName) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_VariableNameNotUnique) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension("variable", variableName) - .SetExtension("variableType", node.Type.ToString()) - .SpecifiedBy("sec-Variable-Uniqueness") - .Build(); - } + public IError NoSelectionOnRootType(OperationDefinitionNode node, + IType fieldType) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_NoSelectionOnRootType, + node.Name?.Value ?? "Unnamed") + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("operation", node.Name?.Value ?? "Unnamed") + .SetExtension("type", fieldType.FullTypeName()) + .SpecifiedBy("sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types") + .Build(); + } - public static IError ArgumentNotUnique( - this DocumentValidatorContext context, - ArgumentNode node, - SchemaCoordinate? field = null, - IDirectiveDefinition? directive = null) - { - var builder = ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_ArgumentNotUnique) - .AddLocation(node) - .SetPath(context.CreateErrorPath()); + public IError FieldIsRequiredButNull(ISyntaxNode node, + string fieldName) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FieldIsRequiredButNull, fieldName) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("field", fieldName) + .SpecifiedBy("sec-Input-Object-Required-Fields") + .Build(); + } - if (field.HasValue) + public IError FieldsAreNotMergeable(FieldInfo fieldA, + FieldInfo fieldB) { - builder - .SetExtension("type", field.Value.Name) - .SetExtension("field", field.Value.MemberName); + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FieldsAreNotMergeable) + .AddLocation(fieldA.SyntaxNode) + .AddLocation(fieldB.SyntaxNode) + .SetExtension("declaringTypeA", fieldA.DeclaringType.NamedType().Name) + .SetExtension("declaringTypeB", fieldB.DeclaringType.NamedType().Name) + .SetExtension("fieldA", fieldA.SyntaxNode.Name.Value) + .SetExtension("fieldB", fieldB.SyntaxNode.Name.Value) + .SetExtension("typeA", fieldA.Type.FullTypeName()) + .SetExtension("typeB", fieldB.Type.FullTypeName()) + .SetExtension("responseNameA", fieldA.ResponseName) + .SetExtension("responseNameB", fieldB.ResponseName) + .SpecifiedBy("sec-Field-Selection-Merging") + .Build(); } - if (directive is not null) + public IError OperationNotSupported(OperationType operationType) { - builder.SetExtension("directive", directive.Name); + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_OperationNotSupported, + operationType) + .Build(); } - return builder - .SetExtension("argument", node.Name.Value) - .SpecifiedBy("sec-Argument-Uniqueness") - .Build(); - } + public IError FragmentNameNotUnique(FragmentDefinitionNode fragmentDefinition) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_FragmentNameNotUnique, + fragmentDefinition.Name.Value) + .AddLocation(fragmentDefinition) + .SetExtension("fragment", fragmentDefinition.Name.Value) + .SpecifiedBy("sec-Fragment-Name-Uniqueness") + .Build(); + } - public static IError ArgumentRequired( - this DocumentValidatorContext context, - ISyntaxNode node, - string argumentName, - SchemaCoordinate? field = null, - IDirectiveDefinition? directive = null) - { - var builder = ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_ArgumentRequired, argumentName) - .AddLocation(node) - .SetPath(context.CreateErrorPath()); + public IError FragmentNotUsed(FragmentDefinitionNode fragmentDefinition) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_FragmentNotUsed, + fragmentDefinition.Name.Value) + .AddLocation(fragmentDefinition) + .SetPath(context.CreateErrorPath()) + .SetExtension("fragment", fragmentDefinition.Name.Value) + .SpecifiedBy("sec-Fragments-Must-Be-Used") + .Build(); + } - if (field.HasValue) + public IError FragmentCycleDetected(FragmentSpreadNode fragmentSpread) { - builder - .SetExtension("type", field.Value.Name) - .SetExtension("field", field.Value.MemberName); + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FragmentCycleDetected) + .AddLocation(fragmentSpread) + .SetPath(context.CreateErrorPath()) + .SetExtension("fragment", fragmentSpread.Name.Value) + .SpecifiedBy("sec-Fragment-spreads-must-not-form-cycles") + .Build(); } - if (directive is not null) + public IError FragmentDoesNotExist(FragmentSpreadNode fragmentSpread) { - builder.SetExtension("directive", directive.Name); + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_FragmentDoesNotExist, + fragmentSpread.Name.Value) + .AddLocation(fragmentSpread) + .SetPath(context.CreateErrorPath()) + .SetExtension("fragment", fragmentSpread.Name.Value) + .SpecifiedBy("sec-Fragment-spread-target-defined") + .Build(); } - return builder - .SetExtension("argument", argumentName) - .SpecifiedBy("sec-Required-Arguments") - .Build(); - } + public IError FragmentNotPossible(ISyntaxNode node, + ITypeDefinition typeCondition, + ITypeDefinition parentType) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FragmentNotPossible) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("typeCondition", typeCondition.Name) + .SetExtension("selectionSetType", parentType.Name) + .SetFragmentName(node) + .SpecifiedBy("sec-Fragment-spread-is-possible") + .Build(); + } - public static IError ArgumentDoesNotExist( - this DocumentValidatorContext context, - ArgumentNode node, - SchemaCoordinate? field = null, - IDirectiveDefinition? directive = null) - { - var builder = ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_ArgumentDoesNotExist, node.Name.Value) - .AddLocation(node) - .SetPath(context.CreateErrorPath()); + public IError FragmentTypeConditionUnknown(ISyntaxNode node, + NamedTypeNode typeCondition) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_FragmentTypeConditionUnknown, + typeCondition.Name.Value) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("typeCondition", typeCondition.Name.Value) + .SetFragmentName(node) + .SpecifiedBy("sec-Fragment-Spread-Type-Existence") + .Build(); + } - if (field.HasValue) + public IError FragmentOnlyCompositeType(ISyntaxNode node, + ITypeDefinition type) { - builder - .SetExtension("type", field.Value.Name) - .SetExtension("field", field.Value.MemberName); + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_FragmentOnlyCompositeType) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("typeCondition", type.FullTypeName()) + .SetFragmentName(node) + .SpecifiedBy("sec-Fragments-On-Composite-Types") + .Build(); } - if (directive is not null) + public IError InputFieldAmbiguous(ObjectFieldNode field) { - builder.SetExtension("directive", directive.Name); + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_InputFieldAmbiguous, field.Name.Value) + .AddLocation(field) + .SetPath(context.CreateErrorPath()) + .SetExtension("field", field.Name.Value) + .SpecifiedBy("sec-Input-Object-Field-Uniqueness") + .Build(); } - return builder - .SetExtension("argument", node.Name.Value) - .SpecifiedBy("sec-Required-Arguments") - .Build(); - } + public IError InputFieldDoesNotExist(ObjectFieldNode field) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_InputFieldDoesNotExist, + field.Name.Value) + .AddLocation(field) + .SetPath(context.CreateErrorPath()) + .SetExtension("field", field.Name.Value) + .SpecifiedBy("sec-Input-Object-Field-Names") + .Build(); + } - public static IError SubscriptionSingleRootField( - this DocumentValidatorContext context, - OperationDefinitionNode operation) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_SubscriptionSingleRootField) - .AddLocation(operation) - .SpecifiedBy("sec-Single-root-field") - .Build(); - } + public IError InputFieldRequired(ISyntaxNode node, + string fieldName) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_InputFieldRequired, fieldName) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("field", fieldName) + .SpecifiedBy("sec-Input-Object-Required-Fields") + .Build(); + } - public static IError SubscriptionNoTopLevelIntrospectionField( - this DocumentValidatorContext context, - OperationDefinitionNode operation) - { - return ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_SubscriptionNoTopLevelIntrospectionField) - .AddLocation(operation) - .SpecifiedBy("sec-Single-root-field") - .Build(); - } + public IError OperationNameNotUnique(OperationDefinitionNode operation, + string operationName) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_OperationNameNotUnique, + operationName) + .AddLocation(operation) + .SetExtension("operation", operationName) + .SpecifiedBy("sec-Operation-Name-Uniqueness") + .Build(); + } - public static IError MaxExecutionDepth( - this DocumentValidatorContext context, - OperationDefinitionNode operation, - int allowedExecutionDepth, - int detectedExecutionDepth) - { - return ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_MaxExecutionDepth, - detectedExecutionDepth, allowedExecutionDepth) - .AddLocation(operation) - .SetExtension("allowedExecutionDepth", allowedExecutionDepth) - .SetExtension("detectedExecutionDepth", detectedExecutionDepth) - .Build(); - } + public IError OperationAnonymousMoreThanOne(OperationDefinitionNode operation, + int operations) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_OperationAnonymousMoreThanOne) + .AddLocation(operation) + .SetExtension("operations", operations) + .SpecifiedBy("sec-Lone-Anonymous-Operation") + .Build(); + } - public static IError IntrospectionNotAllowed( - this DocumentValidatorContext context, - FieldNode field, - string? customErrorMessage) - { - var message = customErrorMessage ?? Resources.ErrorHelper_IntrospectionNotAllowed; + public IError VariableNotInputType(VariableDefinitionNode node, + string variableName) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_VariableNotInputType, variableName) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("variable", variableName) + .SetExtension("variableType", node.Type.ToString()) + .SpecifiedBy("sec-Variables-Are-Input-Types") + .Build(); + } - return ErrorBuilder.New() - .SetMessage(message) - .AddLocation(field) - .SetExtension(nameof(field), field.Name) - .SetCode(ErrorCodes.Validation.IntrospectionNotAllowed) - .Build(); - } + public IError VariableNameNotUnique(VariableDefinitionNode node, + string variableName) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_VariableNameNotUnique) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension("variable", variableName) + .SetExtension("variableType", node.Type.ToString()) + .SpecifiedBy("sec-Variable-Uniqueness") + .Build(); + } - public static IError OneOfMustHaveExactlyOneField( - this DocumentValidatorContext context, - ISyntaxNode node, - IInputObjectTypeDefinition type) - => ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_OneOfMustHaveExactlyOneField, type.Name) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetExtension(nameof(type), type.Name) - .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) - .Build(); + public IError ArgumentNotUnique(ArgumentNode node, + SchemaCoordinate? field = null, + IDirectiveDefinition? directive = null) + { + var builder = ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_ArgumentNotUnique) + .AddLocation(node) + .SetPath(context.CreateErrorPath()); + + if (field.HasValue) + { + builder + .SetExtension("type", field.Value.Name) + .SetExtension("field", field.Value.MemberName); + } + + if (directive is not null) + { + builder.SetExtension("directive", directive.Name); + } + + return builder + .SetExtension("argument", node.Name.Value) + .SpecifiedBy("sec-Argument-Uniqueness") + .Build(); + } - public static IError OneOfVariablesMustBeNonNull( - this DocumentValidatorContext context, - ISyntaxNode node, - SchemaCoordinate fieldCoordinate, - string variableName) - => ErrorBuilder.New() - .SetMessage( - Resources.ErrorHelper_OneOfVariablesMustBeNonNull, - variableName, - fieldCoordinate.MemberName!, - fieldCoordinate.Name) - .AddLocation(node) - .SetPath(context.CreateErrorPath()) - .SetCoordinate(fieldCoordinate) - .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) - .Build(); + public IError ArgumentRequired(ISyntaxNode node, + string argumentName, + SchemaCoordinate? field = null, + IDirectiveDefinition? directive = null) + { + var builder = ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_ArgumentRequired, argumentName) + .AddLocation(node) + .SetPath(context.CreateErrorPath()); + + if (field.HasValue) + { + builder + .SetExtension("type", field.Value.Name) + .SetExtension("field", field.Value.MemberName); + } + + if (directive is not null) + { + builder.SetExtension("directive", directive.Name); + } + + return builder + .SetExtension("argument", argumentName) + .SpecifiedBy("sec-Required-Arguments") + .Build(); + } - public static IError SkipAndIncludeNotAllowedOnSubscriptionRootField( - ISelectionNode selection) - => ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_SkipAndIncludeNotAllowedOnSubscriptionRootField) - .AddLocation(selection) - .SpecifiedBy("sec-Single-Root-Field", rfc: 860) - .Build(); + public IError ArgumentDoesNotExist(ArgumentNode node, + SchemaCoordinate? field = null, + IDirectiveDefinition? directive = null) + { + var builder = ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_ArgumentDoesNotExist, node.Name.Value) + .AddLocation(node) + .SetPath(context.CreateErrorPath()); + + if (field.HasValue) + { + builder + .SetExtension("type", field.Value.Name) + .SetExtension("field", field.Value.MemberName); + } + + if (directive is not null) + { + builder.SetExtension("directive", directive.Name); + } + + return builder + .SetExtension("argument", node.Name.Value) + .SpecifiedBy("sec-Required-Arguments") + .Build(); + } - public static IError DeferAndStreamNotAllowedOnMutationOrSubscriptionRoot( - ISelectionNode selection) - => ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_DeferAndStreamNotAllowedOnMutationOrSubscriptionRoot) - .AddLocation(selection) - .SpecifiedBy("sec-Defer-And-Stream-Directives-Are-Used-On-Valid-Root-Field") - .Build(); + public IError SubscriptionSingleRootField(OperationDefinitionNode operation) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_SubscriptionSingleRootField) + .AddLocation(operation) + .SpecifiedBy("sec-Single-root-field") + .Build(); + } - public static IError DeferAndStreamDuplicateLabel( - this DocumentValidatorContext context, - ISyntaxNode selection, - string label) - => ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_DeferAndStreamDuplicateLabel) - .AddLocation(selection) - .SpecifiedBy("sec-Defer-And-Stream-Directive-Labels-Are-Unique") - .SetExtension(nameof(label), label) - .SetPath(context.CreateErrorPath()) - .Build(); + public IError SubscriptionNoTopLevelIntrospectionField(OperationDefinitionNode operation) + { + return ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_SubscriptionNoTopLevelIntrospectionField) + .AddLocation(operation) + .SpecifiedBy("sec-Single-root-field") + .Build(); + } - public static IError DeferAndStreamLabelIsVariable( - this DocumentValidatorContext context, - ISyntaxNode selection, - string variable) - => ErrorBuilder.New() - .SetMessage(Resources.ErrorHelper_DeferAndStreamLabelIsVariable) - .AddLocation(selection) - .SpecifiedBy("sec-Defer-And-Stream-Directive-Labels-Are-Unique") - .SetExtension(nameof(variable), $"${variable}") - .SetPath(context.CreateErrorPath()) - .Build(); + public IError MaxExecutionDepth(OperationDefinitionNode operation, + int allowedExecutionDepth, + int detectedExecutionDepth) + { + return ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_MaxExecutionDepth, + detectedExecutionDepth, allowedExecutionDepth) + .AddLocation(operation) + .SetExtension("allowedExecutionDepth", allowedExecutionDepth) + .SetExtension("detectedExecutionDepth", detectedExecutionDepth) + .Build(); + } - public static IError StreamOnNonListField( - this DocumentValidatorContext context, - ISyntaxNode selection) - => ErrorBuilder.New() - .SetMessage("@stream directive is only valid on list fields.") - .AddLocation(selection) - .SpecifiedBy("sec-Stream-Directives-Are-Used-On-List-Fields") - .SetPath(context.CreateErrorPath()) - .Build(); + public IError IntrospectionNotAllowed(FieldNode field, + string? customErrorMessage) + { + var message = customErrorMessage ?? Resources.ErrorHelper_IntrospectionNotAllowed; + + return ErrorBuilder.New() + .SetMessage(message) + .AddLocation(field) + .SetExtension(nameof(field), field.Name) + .SetCode(ErrorCodes.Validation.IntrospectionNotAllowed) + .Build(); + } - public static void ReportMaxIntrospectionDepthOverflow( - this DocumentValidatorContext context, - ISyntaxNode selection) - { - context.FatalErrorDetected = true; - context.ReportError( - ErrorBuilder.New() - .SetMessage("Maximum allowed introspection depth exceeded.") - .SetCode(ErrorCodes.Validation.MaxIntrospectionDepthOverflow) + public IError OneOfMustHaveExactlyOneField(ISyntaxNode node, + IInputObjectTypeDefinition type) + => ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_OneOfMustHaveExactlyOneField, type.Name) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetExtension(nameof(type), type.Name) + .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) + .Build(); + + public IError OneOfVariablesMustBeNonNull(ISyntaxNode node, + SchemaCoordinate fieldCoordinate, + string variableName) + => ErrorBuilder.New() + .SetMessage( + Resources.ErrorHelper_OneOfVariablesMustBeNonNull, + variableName, + fieldCoordinate.MemberName!, + fieldCoordinate.Name) + .AddLocation(node) + .SetPath(context.CreateErrorPath()) + .SetCoordinate(fieldCoordinate) + .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) + .Build(); + + public IError DeferAndStreamDuplicateLabel(ISyntaxNode selection, + string label) + => ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_DeferAndStreamDuplicateLabel) .AddLocation(selection) + .SpecifiedBy("sec-Defer-And-Stream-Directive-Labels-Are-Unique") + .SetExtension(nameof(label), label) .SetPath(context.CreateErrorPath()) - .Build()); - } + .Build(); - public static void ReportMaxCoordinateCycleDepthOverflow( - this DocumentValidatorContext context, - ISyntaxNode selection) - { - context.FatalErrorDetected = true; + public IError DeferAndStreamLabelIsVariable(ISyntaxNode selection, + string variable) + => ErrorBuilder.New() + .SetMessage(Resources.ErrorHelper_DeferAndStreamLabelIsVariable) + .AddLocation(selection) + .SpecifiedBy("sec-Defer-And-Stream-Directive-Labels-Are-Unique") + .SetExtension(nameof(variable), $"${variable}") + .SetPath(context.CreateErrorPath()) + .Build(); - context.ReportError( - ErrorBuilder.New() - .SetMessage("Maximum allowed coordinate cycle depth was exceeded.") - .SetCode(ErrorCodes.Validation.MaxCoordinateCycleDepthOverflow) + public IError StreamOnNonListField(ISyntaxNode selection) + => ErrorBuilder.New() + .SetMessage("@stream directive is only valid on list fields.") .AddLocation(selection) + .SpecifiedBy("sec-Stream-Directives-Are-Used-On-List-Fields") .SetPath(context.CreateErrorPath()) - .Build()); + .Build(); + + public void ReportMaxIntrospectionDepthOverflow(ISyntaxNode selection) + { + context.FatalErrorDetected = true; + context.ReportError( + ErrorBuilder.New() + .SetMessage("Maximum allowed introspection depth exceeded.") + .SetCode(ErrorCodes.Validation.MaxIntrospectionDepthOverflow) + .AddLocation(selection) + .SetPath(context.CreateErrorPath()) + .Build()); + } + + public void ReportMaxCoordinateCycleDepthOverflow(ISyntaxNode selection) + { + context.FatalErrorDetected = true; + + context.ReportError( + ErrorBuilder.New() + .SetMessage("Maximum allowed coordinate cycle depth was exceeded.") + .SetCode(ErrorCodes.Validation.MaxCoordinateCycleDepthOverflow) + .AddLocation(selection) + .SetPath(context.CreateErrorPath()) + .Build()); + } } } From 1d42995b1c4665dc24719eb75e26f17a1c106691 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 2 Dec 2025 12:12:49 +0100 Subject: [PATCH 15/30] wip --- .../src/Caching/QueryCacheMiddleware.cs | 3 +- .../src/Authorization/AuthorizeMiddleware.cs | 6 +- .../ErrorBuilderExtensions.cs | 23 +- .../Execution.Projections/ExpressionCache.cs | 86 -- ...otChocolateExecutionSelectionExtensions.cs | 18 +- .../Core/src/Features/IFeatureCollection.cs | 23 +- .../Core/src/Types/Execution/ErrorHelper.cs | 22 +- .../Processing/ISelectionVariants.cs | 49 - .../Processing/MiddlewareContext.Global.cs | 2 + .../Processing/MiddlewareContext.Pure.cs | 2 + .../Processing/MiddlewareContext.Selection.cs | 1 - .../Types/Execution/Processing/Operation.cs | 10 +- .../Execution/Processing/OperationCompiler.cs | 8 +- .../OperationFeatureCollection.Selections.cs | 84 + .../Processing/OperationFeatureCollection.cs | 4 +- .../Execution/Processing/OperationPrinter.cs | 238 --- .../Types/Execution/Processing/Selection.cs | 96 +- .../Processing/SelectionFeatureCollection.cs | 44 + .../Execution/Processing/SelectionSet.cs | 43 +- .../SubscriptionExecutor.Subscription.cs | 7 +- .../Processing/Tasks/ResolverTask.Execute.cs | 6 +- .../Processing/Tasks/ResolverTask.Pooling.cs | 1 + .../Processing/Tasks/ResolverTaskFactory.cs | 2 +- .../Processing/ValueCompletion.Leaf.cs | 6 +- .../Processing/ValueCompletion.List.cs | 6 +- .../Execution/Processing/ValueCompletion.cs | 6 +- .../Extensions/ErrorBuilderExtensions.cs | 20 + .../Extensions/ResolverContextExtensions.cs | 1348 ++++++++--------- .../Core/src/Types/HotChocolate.Types.csproj | 16 + .../Resolvers/DefaultResolverCompiler.cs | 1 - .../FieldSyntaxParameterExpressionBuilder.cs | 32 - .../SelectionParameterExpressionBuilder.cs | 9 +- .../src/Types/Resolvers/IResolverContext.cs | 5 + .../Resolvers/ParameterBindingResolver.cs | 1 - .../Types/Text/Json/ResultDocument.WriteTo.cs | 22 +- .../src/Types/Text/Json/ResultDocument.cs | 5 +- .../DirectiveCollectionExtensions.cs | 257 ---- .../Types/Pagination/PagingMiddleware.cs | 2 +- .../Types/Types/Relay/NodeFieldResolvers.cs | 8 +- .../Core/src/Types/Utilities/ErrorHelper.cs | 9 +- .../IsProjectedProjectionOptimizer.cs | 9 +- .../Visitor/IProjectionOptimizer.cs | 2 +- .../Execution/Nodes/Selection.cs | 18 + 43 files changed, 1064 insertions(+), 1496 deletions(-) delete mode 100644 src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionVariants.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs delete mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs create mode 100644 src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs create mode 100644 src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs delete mode 100644 src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/FieldSyntaxParameterExpressionBuilder.cs delete mode 100644 src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs diff --git a/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs b/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs index 266b4011184..190acf51ae9 100644 --- a/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs +++ b/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs @@ -30,8 +30,7 @@ public async ValueTask InvokeAsync(RequestContext context) } if (!context.TryGetOperation(out var operation) - || !operation.ContextData.TryGetValue(ExecutionContextData.CacheControlHeaderValue, out var value) - || value is not CacheControlHeaderValue cacheControlHeaderValue) + || !operation.Features.TryGet(out var cacheControlHeaderValue)) { return; } diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs index e7de30d536b..38f27393a8e 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs @@ -69,7 +69,7 @@ private void SetError( .SetMessage(AuthorizeMiddleware_NoDefaultPolicy) .SetCode(ErrorCodes.Authentication.NoDefaultPolicy) .SetPath(context.Path) - .AddLocations(context.Selection.SyntaxNodes) + .AddLocations(context.Selection) .Build(), AuthorizeResult.PolicyNotFound => ErrorBuilder.New() @@ -78,7 +78,7 @@ private void SetError( _directive.Policy!) .SetCode(ErrorCodes.Authentication.PolicyNotFound) .SetPath(context.Path) - .AddLocation(context.Selection.SyntaxNode) + .AddLocations(context.Selection) .Build(), _ => ErrorBuilder.New() @@ -88,7 +88,7 @@ private void SetError( ? ErrorCodes.Authentication.NotAuthorized : ErrorCodes.Authentication.NotAuthenticated) .SetPath(context.Path) - .AddLocation(context.Selection.SyntaxNode) + .AddLocations(context.Selection) .Build() }; } diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs index eb9d4a2843b..7b13fee3b11 100644 --- a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs @@ -35,17 +35,36 @@ public ErrorBuilder SetInputPath(Path inputPath) return builder.SetExtension(nameof(inputPath), inputPath); } + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, object? arg) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg)); + } + /// /// Sets the message of the error. /// /// The format of the message. /// The arguments for the message. /// The error builder. - public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, params object?[] args) + public ErrorBuilder SetMessage( + [StringSyntax("CompositeFormat")] string format, +#if NET10_0_OR_GREATER + params ReadOnlySpan args) +#else + params object[] args) +#endif { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(format); - ArgumentNullException.ThrowIfNull(args); return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, args)); } diff --git a/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs b/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs deleted file mode 100644 index 17d66d918ec..00000000000 --- a/src/HotChocolate/Core/src/Execution.Projections/ExpressionCache.cs +++ /dev/null @@ -1,86 +0,0 @@ -// ReSharper disable InconsistentlySynchronizedField -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using GreenDonut.Data; -using HotChocolate.Execution.Processing; - -namespace HotChocolate.Execution.Projections; - -internal sealed class ExpressionCache -{ - private readonly object _writeLock = new(); - private readonly ConcurrentDictionary _cache = new(); - - public bool TryGetExpression( - Selection selection, - [NotNullWhen(true)] out Expression>? expression) - { - if (_cache.TryGetValue(selection.Id, out var cachedExpression) - && cachedExpression is Expression> casted) - { - expression = casted; - return true; - } - - expression = null; - return false; - } - - public Expression> GetOrCreateExpression( - Selection selection, - SelectionExpressionBuilder expressionBuilder) - { - if (!TryGetExpression(selection, out var expression)) - { - lock (_writeLock) - { - if (!TryGetExpression(selection, out expression)) - { - expression = expressionBuilder.BuildExpression(selection); - _cache.TryAdd(selection.Id, expression); - } - } - } - - return expression; - } - - public Expression> GetOrCreateExpression( - Selection selection, - ISelectorBuilder expressionBuilder) - { - if (!TryGetExpression(selection, out var expression)) - { - lock (_writeLock) - { - if (!TryGetExpression(selection, out expression)) - { - expression = expressionBuilder.TryCompile()!; - _cache.TryAdd(selection.Id, expression); - } - } - } - - return expression; - } - - public Expression> GetOrCreateNodeExpression( - Selection selection, - SelectionExpressionBuilder expressionBuilder) - { - if (!TryGetExpression(selection, out var expression)) - { - lock (_writeLock) - { - if (!TryGetExpression(selection, out expression)) - { - expression = expressionBuilder.BuildNodeExpression(selection); - _cache.TryAdd(selection.Id, expression); - } - } - } - - return expression; - } -} diff --git a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs index 6c7740745af..652dedfb075 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Projections/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -105,11 +105,7 @@ public static Expression> AsSelector( private static bool TryGetExpression( Selection selection, [NotNullWhen(true)] out Expression>? expression) - { - var features = selection.DeclaringOperation.Features; - var cache = features.GetOrSetSafe(); - return cache.TryGetExpression(selection, out expression); - } + => selection.Features.TryGet(out expression); private static int GetConnectionSelections(Selection selection, Span buffer) { @@ -181,18 +177,12 @@ file static class Extensions extension(Selection selection) { public Expression> GetOrCreateExpression() - => selection.DeclaringOperation.Features - .GetOrSetSafe() - .GetOrCreateExpression(selection, s_builder); + => selection.Features.GetOrSetSafe(() => s_builder.BuildExpression(selection)); public Expression> GetOrCreateExpression(ISelectorBuilder expressionBuilder) - => selection.DeclaringOperation.Features - .GetOrSetSafe() - .GetOrCreateExpression(selection, expressionBuilder); + => selection.Features.GetOrSetSafe(() => expressionBuilder.TryCompile()!); public Expression> GetOrCreateNodeExpression() - => selection.DeclaringOperation.Features - .GetOrSetSafe() - .GetOrCreateNodeExpression(selection, s_builder); + => selection.Features.GetOrSetSafe(() => s_builder.BuildNodeExpression(selection)); } } diff --git a/src/HotChocolate/Core/src/Features/IFeatureCollection.cs b/src/HotChocolate/Core/src/Features/IFeatureCollection.cs index 1e6f6654470..ee319b7b841 100644 --- a/src/HotChocolate/Core/src/Features/IFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Features/IFeatureCollection.cs @@ -36,7 +36,25 @@ public interface IFeatureCollection : IEnumerable> /// /// The feature key. /// The requested feature, or null if it is not present. - TFeature? Get(); + TFeature? Get() + { + if (typeof(TFeature).IsValueType) + { + var feature = this[typeof(TFeature)]; + if (feature is null && Nullable.GetUnderlyingType(typeof(TFeature)) is null) + { + throw new InvalidOperationException( + $"{typeof(TFeature).FullName} does not exist in the feature collection " + + "and because it is a struct the method can't return null. " + + $"Use 'featureCollection[typeof({typeof(TFeature).FullName})] is not null' " + + "to check if the feature exists."); + } + + return (TFeature?)feature; + } + + return (TFeature?)this[typeof(TFeature)]; + } /// /// Tries to retrieve the requested feature from the collection. @@ -55,5 +73,6 @@ public interface IFeatureCollection : IEnumerable> /// /// The feature key. /// The feature value. - void Set(TFeature? instance); + void Set(TFeature? instance) + => this[typeof(TFeature)] = instance; } diff --git a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs index 1de37cf2a30..5ed2a9d615d 100644 --- a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs @@ -32,11 +32,11 @@ public static IError ArgumentValueIsInvalid( public static IError InvalidLeafValue( GraphQLException exception, - FieldNode field, + Selection selection, Path path) { return ErrorBuilder.FromError(exception.Errors[0]) - .TryAddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.CannotSerializeLeafValue) .Build(); @@ -44,12 +44,12 @@ public static IError InvalidLeafValue( public static IError UnexpectedLeafValueSerializationError( Exception exception, - FieldNode field, + Selection selection, Path path) { return ErrorBuilder .FromException(exception) - .AddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.CannotSerializeLeafValue) .Build(); @@ -85,24 +85,24 @@ public static IError UnexpectedErrorWhileResolvingAbstractType( public static IError ListValueIsNotSupported( Type listType, - FieldNode field, + Selection selection, Path path) { return ErrorBuilder.New() .SetMessage(ErrorHelper_ListValueIsNotSupported_Message, listType.FullName!) - .AddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.ListTypeNotSupported) .Build(); } public static IError UnexpectedValueCompletionError( - FieldNode field, + Selection selection, Path path) { return ErrorBuilder.New() .SetMessage(ErrorHelper_UnexpectedValueCompletionError_Message) - .AddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.ListTypeNotSupported) .Build(); @@ -137,16 +137,16 @@ public static IOperationResult StateInvalidForOperationExecution() => .Build()); public static IError ValueCompletion_CouldNotResolveAbstractType( - FieldNode field, + Selection selection, Path path, object result) => ErrorBuilder.New() .SetMessage( ErrorHelper_ValueCompletion_CouldNotResolveAbstractType_Message, result.GetType().FullName ?? result.GetType().Name, - field.Name) + selection.ResponseName) .SetPath(path) - .AddLocation(field) + .AddLocations(selection) .Build(); public static IOperationResult OperationKindNotAllowed() => diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionVariants.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionVariants.cs deleted file mode 100644 index 19d60e8bb67..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionVariants.cs +++ /dev/null @@ -1,49 +0,0 @@ -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents all the selection set variants of a field. -/// -public interface ISelectionVariants -{ - /// - /// Gets the operation unique id for this variant. - /// - int Id { get; } - - /// - /// Gets the operation that declares this variant. - /// - IOperation DeclaringOperation { get; } - - /// - /// Gets all the possible return types of the field to which this variant belongs to. - /// - IEnumerable GetPossibleTypes(); - - /// - /// Evaluates if the specified type context is a possible type for this variant. - /// - /// - /// The type context to evaluate. - /// - /// - /// Returns true if the specified type context is a possible type for this variant; - /// - bool IsPossibleType(ObjectType typeContext); - - /// - /// Gets the selection set for the specified field return type. - /// - /// - /// The field return type. - /// - /// - /// Returns the selection set for the specified field return type. - /// - /// - /// Invalid field return type. - /// - ISelectionSet GetSelectionSet(ObjectType typeContext); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs index 9211f4d4c0e..d1ba41b2545 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs @@ -33,6 +33,8 @@ public IServiceProvider Services public IVariableValueCollection Variables => _operationContext.Variables; + public ulong IncludeFlags => _operationContext.IncludeFlags; + public CancellationToken RequestAborted { get; private set; } public bool HasCleanupTasks => _cleanupTasks.Count > 0; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs index 20c1d467e6f..896dd713716 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs @@ -65,6 +65,8 @@ public void Clear() public Path Path => PathHelper.CreatePathFromContext(_selection, _parentResult, -1); + public ulong IncludeFlags => parentContext.IncludeFlags; + public CancellationToken RequestAborted => parentContext.RequestAborted; public void ReportError(string errorMessage) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs index 6efa2b63cac..88ac8f0e8d5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using HotChocolate.Resolvers; using HotChocolate.Types; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 4c440b5b1c0..10151268f48 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -29,6 +29,7 @@ public sealed class Operation : IOperation internal Operation( string id, string hash, + DocumentNode document, OperationDefinitionNode definition, ObjectType rootType, Schema schema, @@ -41,6 +42,7 @@ internal Operation( { ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentException.ThrowIfNullOrWhiteSpace(hash); + ArgumentNullException.ThrowIfNull(document); ArgumentNullException.ThrowIfNull(definition); ArgumentNullException.ThrowIfNull(rootType); ArgumentNullException.ThrowIfNull(schema); @@ -51,6 +53,7 @@ internal Operation( Id = id; Hash = hash; + Document = document; Definition = definition; RootType = rootType; Schema = schema; @@ -80,6 +83,11 @@ internal Operation( /// public string? Name => Definition.Name?.Value; + /// + /// Gets the normalized operation document. + /// + public DocumentNode Document { get; } + /// /// Gets the syntax node representing the operation definition. /// @@ -259,5 +267,5 @@ internal Selection GetSelectionById(int id) internal SelectionSet GetSelectionSetById(int id) => Unsafe.As(Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_elementsById), id)); - public override string ToString() => OperationPrinter.Print(this); + public override string ToString() => Definition.ToString(indented: true); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index e83d352bbb0..ee24611434a 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -29,12 +29,15 @@ public OperationCompiler( _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); } - public Operation Compile(string id, string hash, OperationDefinitionNode operationDefinition) + public Operation Compile( + string id, + string hash, + OperationDefinitionNode operationDefinition) { ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(operationDefinition); - var document = new DocumentNode(new IDefinitionNode[] { operationDefinition }); + var document = new DocumentNode([operationDefinition]); document = _documentRewriter.RewriteDocument(document); operationDefinition = (OperationDefinitionNode)document.Definitions[0]; @@ -68,6 +71,7 @@ public Operation Compile(string id, string hash, OperationDefinitionNode operati return new Operation( id, hash, + document, operationDefinition, rootType, _schema, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs new file mode 100644 index 00000000000..22cda22d024 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs @@ -0,0 +1,84 @@ +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +namespace HotChocolate.Execution.Processing; + +[SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] +public sealed partial class OperationFeatureCollection +{ + internal bool TryGet(int selectionId, [NotNullWhen(true)] out TFeature? feature) + { + if (_selectionFeatures.TryGetValue((selectionId, typeof(TFeature)), out var result) + && result is TFeature f) + { + feature = f; + return true; + } + + feature = default; + return false; + } + + internal object? this[int selectionId, Type featureType] + { + get + { + ArgumentOutOfRangeException.ThrowIfLessThan(selectionId, 0); + ArgumentNullException.ThrowIfNull(featureType); + + return _selectionFeatures.GetValueOrDefault((selectionId, featureType)); + } + set + { + ArgumentOutOfRangeException.ThrowIfLessThan(selectionId, 0); + ArgumentNullException.ThrowIfNull(featureType); + + lock (_writeLock) + { + if (value == null) + { + _selectionFeatures = _selectionFeatures.Remove((selectionId, featureType)); + _containerRevision++; + return; + } + + _selectionFeatures = _selectionFeatures.SetItem((selectionId, featureType), value); + _containerRevision++; + } + } + } + + internal TFeature GetOrSetSafe(int selectionId, Func factory) + { + ArgumentNullException.ThrowIfNull(factory); + + if (!TryGet(selectionId, out var feature)) + { + lock (_writeLock) + { + if (!TryGet(selectionId, out feature)) + { + feature = factory(); + this[selectionId, typeof(TFeature)] = feature; + } + } + } + + return feature; + } + + internal IEnumerable> GetFeatures(int selectionId) + { + foreach (var ((id, type), value) in _selectionFeatures) + { + if (selectionId == id) + { + yield return new KeyValuePair(type, value); + } + } + } + + internal bool HasFeatures(int selectionId) + => !_selectionFeatures.IsEmpty + && _selectionFeatures.Keys.Any(t => t.Item1 == selectionId); +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs index 553e910d594..69972333267 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Execution.Processing; [SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] -public sealed class OperationFeatureCollection : IFeatureCollection +public sealed partial class OperationFeatureCollection : IFeatureCollection { #if NET9_0_OR_GREATER private readonly Lock _writeLock = new(); @@ -15,8 +15,10 @@ public sealed class OperationFeatureCollection : IFeatureCollection #endif #if NET10_0_OR_GREATER private ImmutableDictionary _features = []; + private ImmutableDictionary<(int, Type), object> _selectionFeatures = []; #else private ImmutableDictionary _features = ImmutableDictionary.Empty; + private ImmutableDictionary<(int, Type), object> _selectionFeatures = ImmutableDictionary<(int, Type), object>.Empty; #endif private volatile int _containerRevision; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs deleted file mode 100644 index 22c45421c45..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs +++ /dev/null @@ -1,238 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// This operation printer is made for testing purposes. -/// -internal static class OperationPrinter -{ - public static string Print(Operation operation) - { - var directives = operation.Definition.Directives; - - if (operation.IncludeConditions.Count > 0) - { - var temp = operation.Definition.Directives.ToList(); - directives = temp; - - for (var i = 0; i < operation.IncludeConditions.Count; i++) - { - var includeCondition = operation.IncludeConditions[i]; - long flag = 2 ^ i; - - var arguments = new List { new("flag", new IntValueNode(flag)) }; - - if (includeCondition.Skip is BooleanValueNode) - { - arguments.Add(new ArgumentNode("skip", includeCondition.Skip)); - } - - if (includeCondition.Include is BooleanValueNode) - { - arguments.Add(new ArgumentNode("include", includeCondition.Include)); - } - - temp.Add(new DirectiveNode("includeCondition", arguments)); - } - } - - var definitions = new List(); - var context = new PrintContext(operation, operation.SelectionVariants[0], definitions); - - var selectionSet = Visit(context); - - var operationDefinition = new OperationDefinitionNode( - operation.Definition.Location, - operation.Definition.Name, - operation.Definition.Description, - operation.Definition.Operation, - operation.Definition.VariableDefinitions, - directives, - selectionSet); - definitions.Insert(0, operationDefinition); - - return new DocumentNode(definitions).ToString(); - } - - private static SelectionSetNode Visit(PrintContext context) - { - var fragments = new List(); - - foreach (var objectType in context.SelectionVariants.GetPossibleTypes()) - { - var typeContext = objectType; - var selectionSet = context.SelectionVariants.GetSelectionSet(typeContext); - var selections = new List(); - - fragments.Add(new InlineFragmentNode( - null, - new NamedTypeNode(typeContext.Name), - Array.Empty(), - CreateSelectionSet(context, selectionSet, selections))); - - foreach (var fragment in selectionSet.Fragments) - { - if (context.GetOrCreateFragmentName(fragment.SelectionSet.Id, out var fragmentName)) - { - var index = context.Definitions.Count; - context.Definitions.Add(null!); - - context.Definitions[index] = - new FragmentDefinitionNode( - null, - new(fragmentName), - description: null, - Array.Empty(), - new NamedTypeNode(typeContext.Name), - Array.Empty(), - CreateSelectionSet(context, fragment.SelectionSet, [])); - } - - selections.Add( - new FragmentSpreadNode( - null, - new(fragmentName), - new[] { new DirectiveNode("defer") })); - } - } - - return new SelectionSetNode(fragments); - } - - private static SelectionSetNode CreateSelectionSet( - PrintContext context, - ISelectionSet selectionSet, - List selections) - { - foreach (var selection in selectionSet.Selections) - { - var selectionSetId = ((Selection)selection).SelectionSetId; - SelectionSetNode? selectionSetNode = null; - - if (selection.SelectionSet is not null) - { - var childSelectionSet = context.Operation.SelectionVariants[selectionSetId]; - selectionSetNode = Visit(context.Branch(childSelectionSet)); - } - - selections.Add(CreateSelection(selection, selectionSetNode)); - } - - return new SelectionSetNode(selections); - } - - private static FieldNode CreateSelection( - ISelection selection, - SelectionSetNode? selectionSet) - { - var directives = new List(); - - if (selection.IsConditional) - { - directives.Add(new DirectiveNode("conditional")); - } - - directives.Add(CreateExecutionInfo(selection)); - - return new FieldNode( - null, - selection.SyntaxNode.Name, - selection.SyntaxNode.Alias, - directives, - selection.SyntaxNode.Arguments, - selectionSet); - } - - private static DirectiveNode CreateExecutionInfo(ISelection selection) - { - var arguments = new ArgumentNode[selection.IsInternal ? 4 : 3]; - arguments[0] = new ArgumentNode("id", new IntValueNode(selection.Id)); - arguments[1] = new ArgumentNode("kind", new EnumValueNode(selection.Strategy)); - - if (selection.Type.IsListType()) - { - if (selection.Type.NamedType().IsLeafType()) - { - arguments[2] = new ArgumentNode("type", new EnumValueNode("LEAF_LIST")); - } - else - { - arguments[2] = new ArgumentNode("type", new EnumValueNode("COMPOSITE_LIST")); - } - } - else if (selection.Type.IsCompositeType()) - { - arguments[2] = new ArgumentNode("type", new EnumValueNode("COMPOSITE")); - } - else if (selection.Type.IsLeafType()) - { - arguments[2] = new ArgumentNode("type", new EnumValueNode("LEAF")); - } - - if (selection.IsInternal) - { - arguments[3] = new ArgumentNode("internal", BooleanValueNode.True); - } - - return new DirectiveNode("__execute", arguments); - } - - private sealed class PrintContext - { - private readonly GlobalState _state; - - public PrintContext( - Operation operation, - ISelectionVariants selectionVariants, - List definitions) - { - Operation = operation; - SelectionVariants = selectionVariants; - Definitions = definitions; - _state = new(); - } - - private PrintContext( - Operation operation, - ISelectionVariants selectionVariants, - List definitions, - GlobalState state) - { - Operation = operation; - SelectionVariants = selectionVariants; - Definitions = definitions; - _state = state; - } - - public Operation Operation { get; } - - public ISelectionVariants SelectionVariants { get; } - - public List Definitions { get; } - - public bool GetOrCreateFragmentName(int selectionSetId, out string fragmentName) - { - if (!_state.FragmentNames.TryGetValue(selectionSetId, out var name)) - { - name = $"Fragment_{_state.FragmentId++}"; - _state.FragmentNames.Add(selectionSetId, name); - fragmentName = name; - return true; - } - - fragmentName = name; - return false; - } - - public PrintContext Branch(ISelectionVariants selectionVariants) - => new(Operation, selectionVariants, Definitions, _state); - - private sealed class GlobalState - { - public int FragmentId; - public readonly Dictionary FragmentNames = []; - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index cd95444549b..b782c6cf467 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -1,5 +1,5 @@ -using System.Diagnostics; using HotChocolate.Execution.Properties; +using HotChocolate.Features; using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -9,7 +9,7 @@ namespace HotChocolate.Execution.Processing; /// /// Represents a field selection during execution. /// -public class Selection : ISelection +public sealed class Selection : ISelection, IFeatureProvider { private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; private readonly FieldSelectionNode[] _syntaxNodes; @@ -24,9 +24,9 @@ internal Selection( FieldSelectionNode[] syntaxNodes, ulong[] includeFlags, bool isInternal, - ArgumentMap? arguments = null, - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) + ArgumentMap? arguments, + FieldDelegate? resolverPipeline, + PureFieldDelegate? pureResolver) { ArgumentNullException.ThrowIfNull(field); @@ -71,42 +71,84 @@ internal Selection( internal ReadOnlySpan Utf8ResponseName => _utf8ResponseName; + /// public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; + /// public bool IsConditional => _includeFlags.Length > 0; + /// + /// Gets a value indicating whether this selection returns a list type. + /// public bool IsList => (_flags & Flags.List) == Flags.List; + /// public bool IsLeaf => (_flags & Flags.Leaf) == Flags.Leaf; /// - /// Defines if this selection has child selections. + /// Gets a value indicating whether this selection has child selections. /// public bool HasSelections => !IsLeaf; + /// + /// Gets the field definition from the schema that this selection targets. + /// public ObjectField Field { get; } /// IOutputFieldDefinition ISelection.Field => Field; + /// public IType Type => Field.Type; + /// + /// Gets the object type that declares the field being selected. + /// public ObjectType DeclaringType => Field.DeclaringType; + /// + /// Gets the selection set that contains this selection. + /// public SelectionSet DeclaringSelectionSet { get; private set; } = null!; + /// ISelectionSet ISelection.DeclaringSelectionSet => DeclaringSelectionSet; + /// + /// Gets the operation that contains this selection. + /// public Operation DeclaringOperation => DeclaringSelectionSet.DeclaringOperation; + /// + /// Gets the selection features. + /// + public SelectionFeatureCollection Features => new(DeclaringOperation.Features, Id); + + IFeatureCollection IFeatureProvider.Features => Features; + + /// + /// Gets the arguments that were provided to this field selection. + /// public ArgumentMap Arguments { get; } + /// + /// Gets the execution strategy for this selection. + /// public SelectionExecutionStrategy Strategy { get; private set; } + /// + /// Gets the resolver pipeline delegate for this selection. + /// public FieldDelegate? ResolverPipeline { get; private set; } + /// + /// Gets the pure resolver delegate for this selection. + /// public PureFieldDelegate? PureResolver { get; private set; } + /// + /// Gets the syntax nodes that contributed to this selection. + /// public ReadOnlySpan SyntaxNodes => _syntaxNodes; IEnumerable ISelection.GetSyntaxNodes() @@ -117,6 +159,17 @@ IEnumerable ISelection.GetSyntaxNodes() } } + /// + /// Determines whether this selection should be skipped based on conditional flags. + /// + /// The conditional inclusion flags. + /// + /// true if this selection should be included; otherwise, false. + /// + public bool IsSkipped(ulong includeFlags) + => IsIncluded(includeFlags); + + /// public bool IsIncluded(ulong includeFlags) { if (_includeFlags.Length == 0) @@ -232,35 +285,4 @@ private enum Flags Stream = 8, Leaf = 16 } - - internal sealed class Sealed : Selection - { - public Sealed( - int id, - ObjectType declaringType, - ObjectField field, - IType type, - FieldNode syntaxNode, - string responseName, - ArgumentMap? arguments = null, - ulong[]? includeConditions = null, - bool isInternal = false, - bool isParallelExecutable = true, - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) : base( - id, - declaringType, - field, - type, - syntaxNode, - responseName, - arguments, - includeConditions, - isInternal, - isParallelExecutable, - resolverPipeline, - pureResolver) - { - } - } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs new file mode 100644 index 00000000000..c122ee84ae3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Features; + +namespace HotChocolate.Execution.Processing; + +public readonly struct SelectionFeatureCollection : IFeatureCollection +{ + private readonly OperationFeatureCollection _parent; + private readonly int _selectionId; + + internal SelectionFeatureCollection(OperationFeatureCollection parent, int selectionId) + { + _parent = parent; + _selectionId = selectionId; + } + + public bool IsReadOnly => _parent.IsReadOnly; + + public bool IsEmpty => !_parent.HasFeatures(_selectionId); + + public int Revision => _parent.Revision; + + public object? this[Type key] + { + get => _parent[_selectionId, key]; + set => _parent[_selectionId, key] = value; + } + + public bool TryGet([NotNullWhen(true)] out TFeature? feature) + => _parent.TryGet(_selectionId, out feature); + + public TFeature GetOrSetSafe() where TFeature : new() + => GetOrSetSafe(static () => new TFeature()); + + internal TFeature GetOrSetSafe(Func factory) + => _parent.GetOrSetSafe(_selectionId, factory); + + public IEnumerator> GetEnumerator() + => _parent.GetFeatures(_selectionId).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 39af1b52e5a..46cce24dd61 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -1,4 +1,5 @@ using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; using HotChocolate.Types; @@ -17,7 +18,7 @@ public sealed class SelectionSet : ISelectionSet private readonly SelectionLookup _utf8ResponseNameLookup; private Flags _flags; - public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) + internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) { ArgumentNullException.ThrowIfNull(selections); @@ -63,6 +64,36 @@ public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, IEnumerable ISelectionSet.GetSelections() => _selections; + /// + /// Tries to resolve a selection by name. + /// + /// + /// The selection response name. + /// + /// + /// The resolved selection. + /// + /// + /// Returns true if the selection was successfully resolved. + /// + public bool TryGetSelection(string responseName, [NotNullWhen(true)] out Selection? selection) + => _responseNameLookup.TryGetValue(responseName, out selection); + + /// + /// Tries to resolve a selection by name. + /// + /// + /// The selection response name. + /// + /// + /// The resolved selection. + /// + /// + /// Returns true if the selection was successfully resolved. + /// + public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out Selection? selection) + => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); + internal void Complete(Operation declaringOperation, bool seal) { if ((_flags & Flags.Sealed) == Flags.Sealed) @@ -83,16 +114,6 @@ internal void Complete(Operation declaringOperation, bool seal) } } - /// - /// Returns a reference to the 0th element of the underlying selections array. - /// If the selections array is empty, returns a reference to the location where the 0th element - /// would have been stored. Such a reference may or may not be null. - /// It can be used for pinning but must never be de-referenced. - /// This is only meant for use by the execution engine. - /// - internal ref Selection GetSelectionsReference() - => ref MemoryMarshal.GetReference(_selections.AsSpan()); - [Flags] private enum Flags { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs index 5d0609fafb7..ef15af5a215 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs @@ -3,7 +3,6 @@ using HotChocolate.Execution.Internal; using HotChocolate.Fetching; using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.ObjectPool; namespace HotChocolate.Execution.Processing; @@ -20,7 +19,7 @@ private sealed class Subscription : ISubscription, IAsyncDisposable private IDisposable? _subscriptionScope; private readonly RequestContext _requestContext; private readonly ObjectType _subscriptionType; - private readonly ISelectionSet _rootSelections; + private readonly SelectionSet _rootSelections; private readonly Func _resolveQueryRootValue; private ISourceStream _sourceStream = null!; private object? _cachedRootValue; @@ -33,7 +32,7 @@ private Subscription( QueryExecutor queryExecutor, RequestContext requestContext, ObjectType subscriptionType, - ISelectionSet rootSelections, + SelectionSet rootSelections, Func resolveQueryRootValue, IExecutionDiagnosticEvents diagnosticEvents) { @@ -85,7 +84,7 @@ public static async ValueTask SubscribeAsync( QueryExecutor queryExecutor, RequestContext requestContext, ObjectType subscriptionType, - ISelectionSet rootSelections, + SelectionSet rootSelections, Func resolveQueryRootValue, IExecutionDiagnosticEvents diagnosticsEvents) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs index 112df731c60..0ffe890c8a2 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs @@ -1,6 +1,5 @@ using HotChocolate.Execution.Internal; using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; using System.Runtime.InteropServices; namespace HotChocolate.Execution.Processing.Tasks; @@ -168,6 +167,8 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat _context.Result = await postProcessor.ToCompletionResultAsync(result, cancellationToken).ConfigureAwait(false); } + // TODO : DEFER + /* private async ValueTask> CreateStreamResultAsync(IAsyncEnumerable stream) { var streamDirective = _selection.GetStreamDirective(_context.Variables)!; @@ -203,7 +204,6 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat // TODO : DEFER // if the stream has more items than the initial requested items then we will // defer the rest of the stream. - /* _operationContext.DeferredScheduler.Register( new DeferredStream( Selection, @@ -214,7 +214,6 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat enumerator, _context.ScopedContextData), _context.ParentResult); - */ } return list; @@ -230,6 +229,7 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat } } } + */ /// /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs index a5d30fe42a4..5bd5f9edf88 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs @@ -40,6 +40,7 @@ internal bool Reset() Next = null; Previous = null; State = null; + _task = null; _taskBuffer.Clear(); _args.Clear(); return true; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs index 420df73c557..c54b36a56f5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs @@ -251,7 +251,7 @@ private static void ResolveAndCompleteInline( private static void CompleteInline( OperationContext operationContext, MiddlewareContext resolverContext, - ISelection selection, + Selection selection, IType type, int responseIndex, ObjectResult parentResult, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs index b5adb3b5674..a2866256f59 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs @@ -9,7 +9,7 @@ internal static partial class ValueCompletion { private static object? CompleteLeafValue( ValueCompletionContext context, - ISelection selection, + Selection selection, IType type, ResultData parent, int index, @@ -34,13 +34,13 @@ internal static partial class ValueCompletion catch (SerializationException ex) { var errorPath = CreatePathFromContext(selection, parent, index); - var error = InvalidLeafValue(ex, selection.SyntaxNode, errorPath); + var error = InvalidLeafValue(ex, selection, errorPath); operationContext.ReportError(error, resolverContext, selection); } catch (Exception ex) { var errorPath = CreatePathFromContext(selection, parent, index); - var error = UnexpectedLeafValueSerializationError(ex, selection.SyntaxNode, errorPath); + var error = UnexpectedLeafValueSerializationError(ex, selection, errorPath); operationContext.ReportError(error, resolverContext, selection); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs index 4608d697f24..cfa0ba15099 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs @@ -10,7 +10,7 @@ internal static partial class ValueCompletion { private static object? CompleteListValue( ValueCompletionContext context, - ISelection selection, + Selection selection, IType type, ResultData parent, int index, @@ -114,7 +114,7 @@ internal static partial class ValueCompletion } var errorPath = CreatePathFromContext(selection, parent, index); - var error = ListValueIsNotSupported(result.GetType(), selection.SyntaxNode, errorPath); + var error = ListValueIsNotSupported(result.GetType(), selection, errorPath); operationContext.ReportError(error, resolverContext, selection); return null; @@ -122,7 +122,7 @@ internal static partial class ValueCompletion private static bool TryCompleteElement( ValueCompletionContext context, - ISelection selection, + Selection selection, IType elementType, bool isLeafType, ListResult list, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs index a18286305d3..3eb8a859f72 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs @@ -8,7 +8,7 @@ internal static partial class ValueCompletion { public static object? Complete( ValueCompletionContext context, - ISelection selection, + Selection selection, ResultData parent, int index, object? result) @@ -16,7 +16,7 @@ internal static partial class ValueCompletion public static object? Complete( ValueCompletionContext context, - ISelection selection, + Selection selection, IType type, ResultData parent, int index, @@ -51,7 +51,7 @@ internal static partial class ValueCompletion } var errorPath = CreatePathFromContext(selection, parent, index); - var error = UnexpectedValueCompletionError(selection.SyntaxNode, errorPath); + var error = UnexpectedValueCompletionError(selection, errorPath); context.OperationContext.ReportError(error, context.ResolverContext, selection); return null; } diff --git a/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs new file mode 100644 index 00000000000..7a6362d85c2 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs @@ -0,0 +1,20 @@ +using System.Diagnostics; +using HotChocolate.Execution.Processing; + +namespace HotChocolate; + +internal static class ErrorBuilderExtensions +{ + public static ErrorBuilder AddLocations(this ErrorBuilder errorBuilder, Selection selection) + { + Debug.Assert(errorBuilder is not null); + Debug.Assert(selection is not null); + + foreach (var syntaxNode in selection.SyntaxNodes) + { + errorBuilder.AddLocation(syntaxNode.Node); + } + + return errorBuilder; + } +} diff --git a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs index 8936dc920b4..29c5d692654 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs @@ -8,779 +8,750 @@ namespace HotChocolate; public static class ResolverContextExtensions { - /// - /// Gets the global state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the global state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T? GetGlobalStateOrDefault( - this IResolverContext context, - string name) + extension(IResolverContext context) { - ArgumentNullException.ThrowIfNull(context); + /// + /// Gets the global state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the global state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T? GetGlobalStateOrDefault(string name) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + if (context.ContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } + + return default; } - if (context.ContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the global state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The default value. + /// The type of the state. + /// + /// Returns the global state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T GetGlobalStateOrDefault(string name, + T defaultValue) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return default; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the global state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The default value. - /// The type of the state. - /// - /// Returns the global state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T GetGlobalStateOrDefault( - this IResolverContext context, - string name, - T defaultValue) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + return defaultValue; } - if (context.ContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the global state for the specified , + /// or throws if the state does not exist. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the global state for the specified . + /// + public T GetGlobalState(string name) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return defaultValue; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the global state for the specified , - /// or throws if the state does not exist. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the global state for the specified . - /// - public static T GetGlobalState( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ContextData.TryGetValue(name, out var value) && value is T typedValue) + { + return typedValue; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + throw new ArgumentException( + string.Format(ResolverContextExtensions_ContextData_KeyNotFound, name)); } - if (context.ContextData.TryGetValue(name, out var value) && value is T typedValue) + /// + /// Gets the scoped state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the scoped state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T? GetScopedStateOrDefault(string name) { - return typedValue; - } + ArgumentNullException.ThrowIfNull(context); - throw new ArgumentException( - string.Format(ResolverContextExtensions_ContextData_KeyNotFound, name)); - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the scoped state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the scoped state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T? GetScopedStateOrDefault( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ScopedContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + return default; } - if (context.ScopedContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the scoped state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The default value. + /// The type of the state. + /// + /// Returns the scoped state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T GetScopedStateOrDefault(string name, + T defaultValue) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return default; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the scoped state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The default value. - /// The type of the state. - /// - /// Returns the scoped state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T GetScopedStateOrDefault( - this IResolverContext context, - string name, - T defaultValue) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ScopedContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + return defaultValue; } - if (context.ScopedContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the scoped state for the specified , + /// or throws if the state does not exist. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the scoped state for the specified . + /// + public T GetScopedState(string name) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return defaultValue; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the scoped state for the specified , - /// or throws if the state does not exist. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the scoped state for the specified . - /// - public static T GetScopedState( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ScopedContextData.TryGetValue(name, out var value) + && value is T typedValue) + { + return typedValue; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + throw new ArgumentException( + string.Format(ResolverContextExtensions_ScopedContextData_KeyNotFound, name)); } - if (context.ScopedContextData.TryGetValue(name, out var value) - && value is T typedValue) + /// + /// Gets the local state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the local state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T? GetLocalStateOrDefault(string name) { - return typedValue; - } + ArgumentNullException.ThrowIfNull(context); - throw new ArgumentException( - string.Format(ResolverContextExtensions_ScopedContextData_KeyNotFound, name)); - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the local state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the local state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T? GetLocalStateOrDefault( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.LocalContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + return default; } - if (context.LocalContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the local state for the specified , + /// or a default value if the state could not be resolved. + /// + /// The name of the state. + /// The default value. + /// The type of the state. + /// + /// Returns the local state for the specified + /// or the default value of , if the state + /// could not be found or cast to . + /// + public T GetLocalStateOrDefault(string name, + T defaultValue) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return default; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the local state for the specified , - /// or a default value if the state could not be resolved. - /// - /// The resolver context. - /// The name of the state. - /// The default value. - /// The type of the state. - /// - /// Returns the local state for the specified - /// or the default value of , if the state - /// could not be found or cast to . - /// - public static T GetLocalStateOrDefault( - this IResolverContext context, - string name, - T defaultValue) - { - ArgumentNullException.ThrowIfNull(context); + if (context.LocalContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + return defaultValue; } - if (context.LocalContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Gets the local state for the specified , + /// or throws if the state does not exist. + /// + /// The name of the state. + /// The type of the state. + /// + /// Returns the local state for the specified . + /// + public T GetLocalState(string name) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - return defaultValue; - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the local state for the specified , - /// or throws if the state does not exist. - /// - /// The resolver context. - /// The name of the state. - /// The type of the state. - /// - /// Returns the local state for the specified . - /// - public static T GetLocalState( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.LocalContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + throw new ArgumentException( + string.Format(ResolverContextExtensions_LocalContextData_KeyNotFound, name)); } - if (context.LocalContextData.TryGetValue(name, out var value) - && value is T casted) + /// + /// Sets the global state for + /// to the specified . + /// State set previously using the same + /// will be overwritten. + /// + /// The name of the state. + /// The new state value. + /// The type of the state. + public void SetGlobalState(string name, + T value) { - return casted; - } + ArgumentNullException.ThrowIfNull(context); - throw new ArgumentException( - string.Format(ResolverContextExtensions_LocalContextData_KeyNotFound, name)); - } - - /// - /// Sets the global state for - /// to the specified . - /// State set previously using the same - /// will be overwritten. - /// - /// The resolver context. - /// The name of the state. - /// The new state value. - /// The type of the state. - public static void SetGlobalState( - this IResolverContext context, - string name, - T value) - { - ArgumentNullException.ThrowIfNull(context); + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + context.ContextData[name] = value; } - context.ContextData[name] = value; - } - - /// - /// Sets the scoped state for - /// to the specified . - /// State set previously using the same - /// will be overwritten. - /// - /// The resolver context. - /// The name of the state. - /// The new state value. - /// The type of the state. - public static void SetScopedState( - this IResolverContext context, - string name, - T value) - { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) + /// + /// Sets the scoped state for + /// to the specified . + /// State set previously using the same + /// will be overwritten. + /// + /// The name of the state. + /// The new state value. + /// The type of the state. + public void SetScopedState(string name, + T value) { - throw String_NullOrEmpty(nameof(name)); - } - - context.ScopedContextData = context.ScopedContextData.SetItem(name, value); - } + ArgumentNullException.ThrowIfNull(context); - /// - /// Sets the local state for - /// to the specified . - /// State set previously using the same - /// will be overwritten. - /// - /// The resolver context. - /// The name of the state. - /// The new state value. - /// The type of the state. - public static void SetLocalState( - this IResolverContext context, - string name, - T value) - { - ArgumentNullException.ThrowIfNull(context); + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + context.ScopedContextData = context.ScopedContextData.SetItem(name, value); } - context.LocalContextData = context.LocalContextData.SetItem(name, value); - } - - /// - /// Gets or sets the global state for the specified . - /// - /// The resolver context. - /// The name of the state. - /// - /// A function that receives the name of the state as an argument - /// and returns the new state value that should be set. - /// - /// The type of the state. - /// - /// The existing state for the specified , - /// or the newly created state using the function. - /// - public static T GetOrSetGlobalState( - this IResolverContext context, - string name, - Func createValue) - { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) + /// + /// Sets the local state for + /// to the specified . + /// State set previously using the same + /// will be overwritten. + /// + /// The name of the state. + /// The new state value. + /// The type of the state. + public void SetLocalState(string name, + T value) { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(createValue); + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (context.ContextData.TryGetValue(name, out var value) - && value is T casted) - { - return casted; + context.LocalContextData = context.LocalContextData.SetItem(name, value); } - var newValue = createValue(name); - SetGlobalState(context, name, newValue); - return newValue; - } - - /// - /// Gets or sets the scoped state for the specified . - /// - /// The resolver context. - /// The name of the state. - /// - /// A function that receives the name of the state as an argument - /// and returns the new state value that should be set. - /// - /// The type of the state. - /// - /// The existing state for the specified , - /// or the newly created state using the function. - /// - public static T GetOrSetScopedState( - this IResolverContext context, - string name, - Func createValue) - { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) + /// + /// Gets or sets the global state for the specified . + /// + /// The name of the state. + /// + /// A function that receives the name of the state as an argument + /// and returns the new state value that should be set. + /// + /// The type of the state. + /// + /// The existing state for the specified , + /// or the newly created state using the function. + /// + public T GetOrSetGlobalState(string name, + Func createValue) { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(createValue); + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (context.ScopedContextData.TryGetValue(name, out var value) - && value is T casted) - { - return casted; - } + ArgumentNullException.ThrowIfNull(createValue); - var newValue = createValue(name); - SetScopedState(context, name, newValue); - return newValue; - } + if (context.ContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - /// - /// Gets or sets the local state for the specified . - /// - /// The resolver context. - /// The name of the state. - /// - /// A function that receives the name of the state as an argument - /// and returns the new state value that should be set. - /// - /// The type of the state. - /// - /// The existing state for the specified , - /// or the newly created state using the function. - /// - public static T GetOrSetLocalState( - this IResolverContext context, - string name, - Func createValue) - { - ArgumentNullException.ThrowIfNull(context); + var newValue = createValue(name); + SetGlobalState(context, name, newValue); + return newValue; + } + + /// + /// Gets or sets the scoped state for the specified . + /// + /// The name of the state. + /// + /// A function that receives the name of the state as an argument + /// and returns the new state value that should be set. + /// + /// The type of the state. + /// + /// The existing state for the specified , + /// or the newly created state using the function. + /// + public T GetOrSetScopedState(string name, + Func createValue) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentNullException.ThrowIfNull(createValue); - ArgumentNullException.ThrowIfNull(createValue); + if (context.ScopedContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (context.LocalContextData.TryGetValue(name, out var value) - && value is T casted) - { - return casted; - } + var newValue = createValue(name); + SetScopedState(context, name, newValue); + return newValue; + } + + /// + /// Gets or sets the local state for the specified . + /// + /// The name of the state. + /// + /// A function that receives the name of the state as an argument + /// and returns the new state value that should be set. + /// + /// The type of the state. + /// + /// The existing state for the specified , + /// or the newly created state using the function. + /// + public T GetOrSetLocalState(string name, + Func createValue) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - var newValue = createValue(name); - SetLocalState(context, name, newValue); - return newValue; - } + ArgumentNullException.ThrowIfNull(createValue); - /// - /// Removes the scoped state set for the specified . - /// - /// The resolver context. - /// The name of the state. - public static void RemoveScopedState( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); + if (context.LocalContextData.TryGetValue(name, out var value) + && value is T casted) + { + return casted; + } - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); + var newValue = createValue(name); + SetLocalState(context, name, newValue); + return newValue; } - context.ScopedContextData = context.ScopedContextData.Remove(name); - } - - /// - /// Removes the local state set for the specified . - /// - /// The resolver context. - /// The name of the state. - public static void RemoveLocalState( - this IResolverContext context, - string name) - { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) + /// + /// Removes the scoped state set for the specified . + /// + /// The name of the state. + public void RemoveScopedState(string name) { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentNullException.ThrowIfNull(context); - context.LocalContextData = context.LocalContextData.Remove(name); - } + if (string.IsNullOrEmpty(name)) + { + throw String_NullOrEmpty(nameof(name)); + } - /// - /// Gets the event message. - /// - /// The resolver context. - /// The type of the event message. - /// The event message. - public static T GetEventMessage(this IResolverContext context) - { - ArgumentNullException.ThrowIfNull(context); + context.ScopedContextData = context.ScopedContextData.Remove(name); + } - if (context.ScopedContextData.TryGetValue( - WellKnownContextData.EventMessage, - out var value) && value is { }) + /// + /// Removes the local state set for the specified . + /// + /// The name of the state. + public void RemoveLocalState(string name) { - if (value is T casted) + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrEmpty(name)) { - return casted; + throw String_NullOrEmpty(nameof(name)); } - throw EventMessage_InvalidCast(typeof(T), value.GetType()); + context.LocalContextData = context.LocalContextData.Remove(name); } - throw EventMessage_NotFound(); - } + /// + /// Gets the event message. + /// + /// The type of the event message. + /// The event message. + public T GetEventMessage() + { + ArgumentNullException.ThrowIfNull(context); - /// - /// Gets the user for this request. - /// - /// - /// The resolver context. - /// - /// - /// Returns the user for this request. - /// - public static ClaimsPrincipal? GetUser(this IResolverContext context) - => context.GetGlobalStateOrDefault(nameof(ClaimsPrincipal)); - - /// - /// Checks if a field is selected in the current selection set. - /// - /// - /// The resolver context. - /// - /// - /// The name of the field that shall be checked. - /// - /// - /// true if the field is selected; otherwise, false. - /// - /// - /// is null. - /// - /// - /// is null or whitespace. - /// - public static bool IsSelected(this IResolverContext context, string fieldName) - { - ArgumentNullException.ThrowIfNull(context); + if (context.ScopedContextData.TryGetValue( + WellKnownContextData.EventMessage, + out var value) && value is { }) + { + if (value is T casted) + { + return casted; + } - if (string.IsNullOrWhiteSpace(fieldName)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName)); - } + throw EventMessage_InvalidCast(typeof(T), value.GetType()); + } - var namedType = context.Selection.Type.NamedType(); + throw EventMessage_NotFound(); + } + + /// + /// Gets the user for this request. + /// + /// + /// Returns the user for this request. + /// + public ClaimsPrincipal? GetUser() + => context.GetGlobalStateOrDefault(nameof(ClaimsPrincipal)); + + /// + /// Checks if a field is selected in the current selection set. + /// + /// + /// The name of the field that shall be checked. + /// + /// + /// true if the field is selected; otherwise, false. + /// + /// + /// is null. + /// + /// + /// is null or whitespace. + /// + public bool IsSelected(string fieldName) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrWhiteSpace(fieldName)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName)); + } - if (!namedType.IsCompositeType()) - { - return false; - } + var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) - { - foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) + if (!namedType.IsCompositeType()) { - var selections = context.GetSelections(possibleType, context.Selection); + return false; + } - for (var i = 0; i < selections.Count; i++) + if (namedType.IsAbstractType()) + { + foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) { - if (selections[i].Field.Name.Equals(fieldName)) + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); + + foreach(var selection in selectionSet.Selections) { - return true; + if (selection.IsIncluded(includeFlags) + && selection.Field.Name.Equals(fieldName)) + { + return true; + } } } } - } - else - { - var selections = context.GetSelections((ObjectType)namedType, context.Selection); - - for (var i = 0; i < selections.Count; i++) + else { - if (selections[i].Field.Name.Equals(fieldName)) + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); + + foreach(var selection in selectionSet.Selections) { - return true; + if (selection.IsIncluded(includeFlags) + && selection.Field.Name.Equals(fieldName)) + { + return true; + } } } - } - - return false; - } - /// - /// Checks if a field is selected in the current selection set. - /// - /// - /// The resolver context. - /// - /// - /// The name of the first field that shall be checked. - /// - /// - /// The name of the second field that shall be checked. - /// - /// - /// - /// is null. - /// - /// - /// is null or whitespace or - /// is null or whitespace. - /// - public static bool IsSelected(this IResolverContext context, string fieldName1, string fieldName2) - { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrWhiteSpace(fieldName1)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName1)); + return false; } - if (string.IsNullOrWhiteSpace(fieldName2)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName2)); - } + /// + /// Checks if a field is selected in the current selection set. + /// + /// + /// The name of the first field that shall be checked. + /// + /// + /// The name of the second field that shall be checked. + /// + /// + /// + /// is null. + /// + /// + /// is null or whitespace or + /// is null or whitespace. + /// + public bool IsSelected(string fieldName1, string fieldName2) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrWhiteSpace(fieldName1)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName1)); + } - var namedType = context.Selection.Type.NamedType(); + if (string.IsNullOrWhiteSpace(fieldName2)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName2)); + } - if (!namedType.IsCompositeType()) - { - return false; - } + var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) - { - foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) + if (!namedType.IsCompositeType()) { - var selections = context.GetSelections(possibleType, context.Selection); + return false; + } - for (var i = 0; i < selections.Count; i++) + if (namedType.IsAbstractType()) + { + foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) { - var selection = selections[i]; + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - if (selection.Field.Name.Equals(fieldName1) || selection.Field.Name.Equals(fieldName2)) + foreach(var selection in selectionSet.Selections) { - return true; + if (selection.IsSkipped(includeFlags)) + { + continue; + } + + if (selection.Field.Name.Equals(fieldName1) || selection.Field.Name.Equals(fieldName2)) + { + return true; + } } } } - } - else - { - var selections = context.GetSelections((ObjectType)namedType, context.Selection); - - for (var i = 0; i < selections.Count; i++) + else { - var selection = selections[i]; + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - if (selection.Field.Name.Equals(fieldName1) || selection.Field.Name.Equals(fieldName2)) + foreach(var selection in selectionSet.Selections) { - return true; + if (selection.IsSkipped(includeFlags)) + { + continue; + } + + if (selection.Field.Name.Equals(fieldName1) || selection.Field.Name.Equals(fieldName2)) + { + return true; + } } } - } - return false; - } + return false; + } - /// - /// Checks if a field is selected in the current selection set. - /// - /// - /// The resolver context. - /// - /// - /// The name of the first field that shall be checked. - /// - /// - /// The name of the second field that shall be checked. - /// - /// - /// The name of the third field that shall be checked. - /// - /// - /// - /// is null. - /// - /// - /// is null or whitespace or - /// is null or whitespace or - /// is null or whitespace. - /// - public static bool IsSelected( - this IResolverContext context, - string fieldName1, - string fieldName2, - string fieldName3) - { - ArgumentNullException.ThrowIfNull(context); + /// + /// Checks if a field is selected in the current selection set. + /// + /// + /// The name of the first field that shall be checked. + /// + /// + /// The name of the second field that shall be checked. + /// + /// + /// The name of the third field that shall be checked. + /// + /// + /// + /// is null. + /// + /// + /// is null or whitespace or + /// is null or whitespace or + /// is null or whitespace. + /// + public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) + { + ArgumentNullException.ThrowIfNull(context); + + if (string.IsNullOrWhiteSpace(fieldName1)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName1)); + } - if (string.IsNullOrWhiteSpace(fieldName1)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName1)); - } + if (string.IsNullOrWhiteSpace(fieldName2)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName2)); + } - if (string.IsNullOrWhiteSpace(fieldName2)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName2)); - } + if (string.IsNullOrWhiteSpace(fieldName3)) + { + throw new ArgumentException( + ResolverContextExtensions_IsSelected_FieldNameEmpty, + nameof(fieldName3)); + } - if (string.IsNullOrWhiteSpace(fieldName3)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName3)); - } + var namedType = context.Selection.Type.NamedType(); - var namedType = context.Selection.Type.NamedType(); + if (!namedType.IsCompositeType()) + { + return false; + } - if (!namedType.IsCompositeType()) - { - return false; - } + if (namedType.IsAbstractType()) + { + foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) + { + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - if (namedType.IsAbstractType()) - { - foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) + foreach(var selection in selectionSet.Selections) + { + if (selection.IsSkipped(includeFlags)) + { + continue; + } + + if (selection.Field.Name.Equals(fieldName1) + || selection.Field.Name.Equals(fieldName2) + || selection.Field.Name.Equals(fieldName3)) + { + return true; + } + } + } + } + else { - var selections = context.GetSelections(possibleType, context.Selection); + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - for (var i = 0; i < selections.Count; i++) + foreach(var selection in selectionSet.Selections) { - var selection = selections[i]; + if (selection.IsSkipped(includeFlags)) + { + continue; + } if (selection.Field.Name.Equals(fieldName1) || selection.Field.Name.Equals(fieldName2) @@ -790,83 +761,66 @@ public static bool IsSelected( } } } + + return false; } - else + + /// + /// Checks if a field is selected in the current selection set. + /// + /// + /// The names of the fields that shall be checked. + /// + /// + /// + /// is null or + /// is null. + /// + public bool IsSelected(ISet fieldNames) { - var selections = context.GetSelections((ObjectType)namedType, context.Selection); + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(fieldNames); + + var namedType = context.Selection.Type.NamedType(); - for (var i = 0; i < selections.Count; i++) + if (!namedType.IsCompositeType()) { - var selection = selections[i]; + return false; + } - if (selection.Field.Name.Equals(fieldName1) - || selection.Field.Name.Equals(fieldName2) - || selection.Field.Name.Equals(fieldName3)) + if (namedType.IsAbstractType()) + { + foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) { - return true; + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); + + foreach(var selection in selectionSet.Selections) + { + if (selection.IsIncluded(includeFlags) + && fieldNames.Contains(selection.Field.Name)) + { + return true; + } + } } } - } - - return false; - } - - /// - /// Checks if a field is selected in the current selection set. - /// - /// - /// The resolver context. - /// - /// - /// The names of the fields that shall be checked. - /// - /// - /// - /// is null or - /// is null. - /// - public static bool IsSelected( - this IResolverContext context, - ISet fieldNames) - { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(fieldNames); - - var namedType = context.Selection.Type.NamedType(); - - if (!namedType.IsCompositeType()) - { - return false; - } - - if (namedType.IsAbstractType()) - { - foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) + else { - var selections = context.GetSelections(possibleType, context.Selection); + var includeFlags = context.IncludeFlags; + var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - for (var i = 0; i < selections.Count; i++) + foreach(var selection in selectionSet.Selections) { - if (fieldNames.Contains(selections[i].Field.Name)) + if (selection.IsIncluded(includeFlags) + && fieldNames.Contains(selection.Field.Name)) { return true; } } } - } - else - { - var selections = context.GetSelections((ObjectType)namedType, context.Selection); - for (var i = 0; i < selections.Count; i++) - { - if (fieldNames.Contains(selections[i].Field.Name)) - { - return true; - } - } + return false; } - - return false; } } diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 7b3d5a23453..a36c9b2e24c 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -205,6 +205,22 @@ ResolverTask.cs + + + ValueCompletion.cs + + + + ValueCompletion.cs + + + + ValueCompletion.cs + + + + OperationFeatureCollection.cs + diff --git a/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs b/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs index c661a27188c..e4f509b19cd 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs @@ -86,7 +86,6 @@ public DefaultResolverCompiler( expressionBuilders.Add(new ResolverContextParameterExpressionBuilder()); expressionBuilders.Add(new SchemaParameterExpressionBuilder()); expressionBuilders.Add(new SelectionParameterExpressionBuilder()); - expressionBuilders.Add(new FieldSyntaxParameterExpressionBuilder()); expressionBuilders.Add(new ObjectTypeParameterExpressionBuilder()); expressionBuilders.Add(new OperationDefinitionParameterExpressionBuilder()); expressionBuilders.Add(new OperationParameterExpressionBuilder()); diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/FieldSyntaxParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/FieldSyntaxParameterExpressionBuilder.cs deleted file mode 100644 index cc1d1fe29ec..00000000000 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/FieldSyntaxParameterExpressionBuilder.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Diagnostics; -using System.Reflection; -using System.Runtime.CompilerServices; -using HotChocolate.Internal; -using HotChocolate.Language; - -namespace HotChocolate.Resolvers.Expressions.Parameters; - -internal sealed class FieldSyntaxParameterExpressionBuilder() - : LambdaParameterExpressionBuilder(ctx => ctx.Selection.SyntaxNode, isPure: true) - , IParameterBindingFactory - , IParameterBinding -{ - public override ArgumentKind Kind - => ArgumentKind.FieldSyntax; - - public override bool CanHandle(ParameterInfo parameter) - => typeof(FieldNode) == parameter.ParameterType; - - public bool CanHandle(ParameterDescriptor parameter) - => typeof(FieldNode) == parameter.Type; - - public IParameterBinding Create(ParameterDescriptor parameter) - => this; - - public T Execute(IResolverContext context) - { - Debug.Assert(typeof(T) == typeof(FieldNode)); - var syntaxNode = context.Selection.SyntaxNode; - return Unsafe.As(ref syntaxNode); - } -} diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/SelectionParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/SelectionParameterExpressionBuilder.cs index b6a3903611f..2ce5729ad9b 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/SelectionParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/SelectionParameterExpressionBuilder.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Internal; @@ -16,10 +17,12 @@ public override ArgumentKind Kind => ArgumentKind.Selection; public override bool CanHandle(ParameterInfo parameter) - => typeof(ISelection).IsAssignableFrom(parameter.ParameterType); + => typeof(ISelection).IsAssignableFrom(parameter.ParameterType) + || typeof(Selection).IsAssignableFrom(parameter.ParameterType); public bool CanHandle(ParameterDescriptor parameter) - => typeof(ISelection).IsAssignableFrom(parameter.Type); + => typeof(ISelection).IsAssignableFrom(parameter.Type) + || typeof(Selection).IsAssignableFrom(parameter.Type); public override Expression Build(ParameterExpressionBuilderContext context) => Expression.Convert(base.Build(context), context.Parameter.ParameterType); @@ -31,6 +34,6 @@ public T Execute(IResolverContext context) { Debug.Assert(typeof(T) == typeof(ISelection)); var selection = context.Selection; - return Unsafe.As(ref selection); + return Unsafe.As(ref selection); } } diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index 5eef2e5429d..7a055e474f2 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -44,6 +44,11 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Path Path { get; } + /// + /// Gets the selection include flags. + /// + ulong IncludeFlags { get; } + /// /// Gets the previous (parent) resolver result. /// diff --git a/src/HotChocolate/Core/src/Types/Resolvers/ParameterBindingResolver.cs b/src/HotChocolate/Core/src/Types/Resolvers/ParameterBindingResolver.cs index 4c0e43d9cd7..e4b3eafd3fa 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/ParameterBindingResolver.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/ParameterBindingResolver.cs @@ -45,7 +45,6 @@ public ParameterBindingResolver( bindingFactories.Add(new ResolverContextParameterExpressionBuilder()); bindingFactories.Add(new SchemaParameterExpressionBuilder()); bindingFactories.Add(new SelectionParameterExpressionBuilder()); - bindingFactories.Add(new FieldSyntaxParameterExpressionBuilder()); bindingFactories.Add(new ObjectTypeParameterExpressionBuilder()); bindingFactories.Add(new OperationDefinitionParameterExpressionBuilder()); bindingFactories.Add(new OperationParameterExpressionBuilder()); diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs index 869b8c26d26..9246b7aca06 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs @@ -16,7 +16,7 @@ public void WriteTo(IBufferWriter writer, bool indented = false) internal ref struct RawJsonFormatter(ResultDocument document, IBufferWriter writer, bool indented) { - private int _indentLevel = 0; + private int _indentation = 0; public void Write() { @@ -25,7 +25,7 @@ public void Write() if (indented) { WriteNewLine(); - _indentLevel++; + _indentation++; } if (document._errors?.Count > 0) @@ -117,7 +117,7 @@ public void Write() if (indented) { - _indentLevel--; + _indentation--; WriteNewLine(); WriteIndent(); } @@ -144,12 +144,12 @@ public void WriteValue(Cursor cursor, DbRow row) switch (tokenType) { case ElementTokenType.StartObject - when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + when (ElementFlags.IsObject & row.Flags) != ElementFlags.IsObject: WriteObject(cursor, row); break; case ElementTokenType.StartArray - when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + when (ElementFlags.IsList & row.Flags) != ElementFlags.IsList: WriteArray(cursor, row); break; @@ -167,7 +167,7 @@ public void WriteValue(Cursor cursor, DbRow row) break; default: - document.WriteRawValueTo(writer, row, _indentLevel, indented); + document.WriteRawValueTo(writer, row, _indentation, indented); break; } } @@ -183,7 +183,7 @@ private void WriteObject(Cursor start, DbRow startRow) if (indented && current < end) { - _indentLevel++; + _indentation++; } var first = true; @@ -234,7 +234,7 @@ private void WriteObject(Cursor start, DbRow startRow) if (indented && !first) { - _indentLevel--; + _indentation--; WriteNewLine(); WriteIndent(); } @@ -253,7 +253,7 @@ private void WriteArray(Cursor start, DbRow startRow) if (indented && current < end) { - _indentLevel++; + _indentation++; } var first = true; @@ -279,7 +279,7 @@ private void WriteArray(Cursor start, DbRow startRow) if (indented && end > start + 1) { - _indentLevel--; + _indentation--; WriteNewLine(); WriteIndent(); } @@ -293,7 +293,7 @@ private void WriteArray(Cursor start, DbRow startRow) [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly void WriteIndent() { - var indentSize = _indentLevel * 2; + var indentSize = _indentation * 2; if (indentSize > 0) { var span = writer.GetSpan(indentSize); diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs index da69ffbf89d..d4fae9588fc 100644 --- a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -335,7 +335,7 @@ internal void Invalidate(Cursor current) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteRawValueTo(IBufferWriter writer, DbRow row) + private void WriteRawValueTo(IBufferWriter writer, DbRow row, int indentation, bool indented) { switch (row.TokenType) { @@ -360,6 +360,7 @@ private void WriteRawValueTo(IBufferWriter writer, DbRow row) WriteLocalDataTo(writer, row.Location, row.SizeOrLength); return; + // TODO : We need to handle any types. default: throw new NotSupportedException(); } @@ -738,7 +739,7 @@ private void WriteEmptyProperty(Cursor parent, ISelection selection) flags |= ElementFlags.IsList; } - if (selection.Type.IsCompositeType()) + if (selection.Type.NamedType().IsCompositeType()) { flags |= ElementFlags.IsObject; } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs deleted file mode 100644 index b9646781854..00000000000 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs +++ /dev/null @@ -1,257 +0,0 @@ -using HotChocolate.Execution; -using HotChocolate.Execution.Processing; -using HotChocolate.Language; -using HotChocolate.Utilities; -using ThrowHelper = HotChocolate.Utilities.ThrowHelper; - -// ReSharper disable once CheckNamespace -namespace HotChocolate.Types; - -public static class DirectiveCollectionExtensions -{ - public static T? SingleOrDefault(this DirectiveCollection directives) where T : notnull - { - foreach (var directive in directives) - { - if (typeof(T).IsAssignableFrom(directive.Type.RuntimeType)) - { - return directive.ToValue(); - } - } - - return default; - } - - internal static IValueNode? SkipValue(this IReadOnlyList directives) - { - var directive = directives.GetSkipDirectiveNode(); - - if (directive is null) - { - return null; - } - - return directive.GetArgumentValue(DirectiveNames.Skip.Arguments.If, BooleanValueNode.True) - ?? throw ThrowHelper.MissingIfArgument(directive); - } - - internal static IValueNode? IncludeValue(this IReadOnlyList directives) - { - var directive = directives.GetIncludeDirectiveNode(); - - if (directive is null) - { - return null; - } - - return directive.GetArgumentValue(DirectiveNames.Include.Arguments.If, BooleanValueNode.True) - ?? throw ThrowHelper.MissingIfArgument(directive); - } - - internal static bool IsDeferrable(this InlineFragmentNode fragmentNode) - => fragmentNode.Directives.IsDeferrable(); - - internal static bool IsDeferrable(this FragmentSpreadNode fragmentSpreadNode) - => fragmentSpreadNode.Directives.IsDeferrable(); - - internal static bool IsDeferrable(this IReadOnlyList directives) - { - var directive = directives.GetDeferDirectiveNode(); - var ifValue = directive?.GetArgumentValue(DirectiveNames.Defer.Arguments.If, BooleanValueNode.True); - - // a fragment is not deferrable if we do not find a defer directive or - // if the `if`-argument of the defer directive is a bool literal with a false value. - return directive is not null && ifValue is not { Value: false }; - } - - internal static DirectiveNode? GetSkipDirectiveNode( - this IReadOnlyList directives) - => GetDirectiveNode(directives, DirectiveNames.Skip.Name); - - internal static DirectiveNode? GetIncludeDirectiveNode( - this IReadOnlyList directives) - => GetDirectiveNode(directives, DirectiveNames.Include.Name); - - internal static DeferDirective? GetDeferDirective( - this IReadOnlyList directives, - IVariableValueCollection variables) - { - var directiveNode = GetDirectiveNode(directives, DirectiveNames.Defer.Name); - - if (directiveNode is not null) - { - var @if = true; - string? label = null; - - foreach (var argument in directiveNode.Arguments) - { - switch (argument.Name.Value) - { - case DirectiveNames.Defer.Arguments.If: - @if = argument.Value switch - { - VariableNode variable => !variables.GetBooleanValue(variable.Name.Value) ?? true, - BooleanValueNode b => b.Value, - _ => true - }; - break; - - case DirectiveNames.Defer.Arguments.Label: - label = argument.Value switch - { - VariableNode variable => variables.GetStringValue(variable.Name.Value), - StringValueNode b => b.Value, - _ => null - }; - break; - } - } - - return new DeferDirective(@if, label); - } - - return null; - } - - private static bool? GetBooleanValue(this IVariableValueCollection variables, string variableName) - => variables.TryGetValue(variableName, out BooleanValueNode? value) ? value.Value : null; - - private static string? GetStringValue(this IVariableValueCollection variables, string variableName) - => variables.TryGetValue(variableName, out StringValueNode? value) ? value.Value : null; - - private static int? GetIntValue(this IVariableValueCollection variables, string variableName) - => variables.TryGetValue(variableName, out IntValueNode? value) ? value.ToInt32() : null; - - internal static StreamDirective? GetStreamDirective( - this ISelection selection, - IVariableValueCollection variables) => - selection.SyntaxNode.GetStreamDirective(variables); - - internal static StreamDirective? GetStreamDirective( - this FieldNode fieldNode, - IVariableValueCollection variables) - { - var directiveNode = GetDirectiveNode(fieldNode.Directives, DirectiveNames.Stream.Name); - - if (directiveNode is not null) - { - var @if = true; - string? label = null; - var initialCount = 0; - - foreach (var argument in directiveNode.Arguments) - { - switch (argument.Name.Value) - { - case DirectiveNames.Stream.Arguments.If: - @if = argument.Value switch - { - VariableNode variable => !variables.GetBooleanValue(variable.Name.Value) ?? true, - BooleanValueNode b => b.Value, - _ => true - }; - break; - - case DirectiveNames.Stream.Arguments.Label: - label = argument.Value switch - { - VariableNode variable => variables.GetStringValue(variable.Name.Value), - StringValueNode b => b.Value, - _ => null - }; - break; - - case DirectiveNames.Stream.Arguments.InitialCount: - initialCount = argument.Value switch - { - VariableNode variable => variables.GetIntValue(variable.Name.Value) ?? 0, - IntValueNode b => b.ToInt32(), - _ => 0 - }; - break; - } - } - - return new StreamDirective(@if, initialCount, label); - } - - return null; - } - - internal static bool StreamDirectiveEquals( - this DirectiveNode streamA, - DirectiveNode streamB) - { - var argsA = CreateStreamArgs(streamA); - var argsB = CreateStreamArgs(streamB); - - return SyntaxComparer.BySyntax.Equals(argsA.If, argsB.If) - && SyntaxComparer.BySyntax.Equals(argsA.InitialCount, argsB.InitialCount) - && SyntaxComparer.BySyntax.Equals(argsA.Label, argsB.Label); - } - - private static StreamArgs CreateStreamArgs(DirectiveNode directiveNode) - { - var args = new StreamArgs(); - - for (var i = 0; i < directiveNode.Arguments.Count; i++) - { - var argument = directiveNode.Arguments[i]; - - switch (argument.Name.Value) - { - case DirectiveNames.Stream.Arguments.If: - args.If = argument.Value; - break; - - case DirectiveNames.Stream.Arguments.Label: - args.Label = argument.Value; - break; - - case DirectiveNames.Stream.Arguments.InitialCount: - args.InitialCount = argument.Value; - break; - } - } - - return args; - } - - internal static DirectiveNode? GetDeferDirectiveNode( - this IHasDirectives container) => - GetDirectiveNode(container.Directives, DirectiveNames.Defer.Name); - - internal static DirectiveNode? GetDeferDirectiveNode( - this IReadOnlyList directives) => - GetDirectiveNode(directives, DirectiveNames.Defer.Name); - - private static DirectiveNode? GetDirectiveNode( - this IReadOnlyList directives, - string name) - { - if (directives.Count == 0) - { - return null; - } - - for (var i = 0; i < directives.Count; i++) - { - var directive = directives[i]; - - if (directive.Name.Value.EqualsOrdinal(name)) - { - return directive; - } - } - return null; - } - - private ref struct StreamArgs - { - public IValueNode? If { get; set; } - - public IValueNode? Label { get; set; } - - public IValueNode? InitialCount { get; set; } - } -} diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingMiddleware.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingMiddleware.cs index f84b093f38f..94bee128925 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingMiddleware.cs @@ -46,7 +46,7 @@ public async ValueTask InvokeAsync(IMiddlewareContext context) { errors[i] = ErrorBuilder .FromError(ex.Errors[i]) - .AddLocations(context.Selection.SyntaxNodes) + .AddLocations(context.Selection) .SetPath(context.Path) .Build(); } diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs index ca32579e62b..c188213f885 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs @@ -40,7 +40,7 @@ public static async ValueTask ResolveSingleNodeAsync( ErrorHelper.Relay_NoNodeResolver( typeName, context.Path, - context.Selection.SyntaxNodes)); + context.Selection)); context.Result = null; } @@ -64,7 +64,7 @@ public static async ValueTask ResolveManyNodeAsync( { context.ReportError( ErrorHelper.FetchedToManyNodesAtOnce( - context.Selection.SyntaxNodes, + context.Selection, context.Path, maxAllowedNodes, list.Items.Count)); @@ -100,7 +100,7 @@ public static async ValueTask ResolveManyNodeAsync( ErrorHelper.Relay_NoNodeResolver( typeName, context.Path, - context.Selection.SyntaxNodes)); + context.Selection)); } } @@ -183,7 +183,7 @@ public static async ValueTask ResolveManyNodeAsync( ErrorHelper.Relay_NoNodeResolver( typeName, context.Path, - context.Selection.SyntaxNodes)); + context.Selection)); } context.Result = results; diff --git a/src/HotChocolate/Core/src/Types/Utilities/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Utilities/ErrorHelper.cs index df07ef5efbe..7c90512ef9d 100644 --- a/src/HotChocolate/Core/src/Types/Utilities/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Types/Utilities/ErrorHelper.cs @@ -1,4 +1,5 @@ using System.Globalization; +using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.Properties; using HotChocolate.Types; @@ -452,11 +453,11 @@ public static ISchemaError NoSchemaTypesAllowedAsRuntimeType( .SetTypeSystemObject(type) .Build(); - public static IError Relay_NoNodeResolver(string typeName, Path path, IReadOnlyList fieldNodes) + public static IError Relay_NoNodeResolver(string typeName, Path path, Selection selection) => ErrorBuilder.New() .SetMessage(ErrorHelper_Relay_NoNodeResolver, typeName) .SetPath(path) - .AddLocations(fieldNodes) + .AddLocations(selection) .Build(); public static ISchemaError NodeResolver_MustHaveExactlyOneIdArg( @@ -494,7 +495,7 @@ public static ISchemaError NodeResolverMissing( .Build(); public static IError FetchedToManyNodesAtOnce( - IReadOnlyList fieldNodes, + Selection selection, Path path, int maxAllowedNodes, int requestNodes) @@ -503,7 +504,7 @@ public static IError FetchedToManyNodesAtOnce( ErrorHelper_FetchedToManyNodesAtOnce, maxAllowedNodes, requestNodes) - .AddLocations(fieldNodes) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.FetchedToManyNodesAtOnce) .Build(); diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs index 4739c744624..feab6327d5f 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs @@ -1,20 +1,19 @@ using HotChocolate.Execution.Processing; using HotChocolate.Language; -using HotChocolate.Types; namespace HotChocolate.Data.Projections.Handlers; public class IsProjectedProjectionOptimizer : IProjectionOptimizer { - public bool CanHandle(ISelection field) => - field.DeclaringType is ObjectType objectType - && objectType.Features.Get()?.AlwaysProjectedFields.Length > 0; + public bool CanHandle(Selection field) + => field.DeclaringType is { } objectType + && objectType.Features.Get()?.AlwaysProjectedFields.Length > 0; public Selection RewriteSelection( SelectionSetOptimizerContext context, Selection selection) { - if (context.Type is not ObjectType type + if (context.Type is not { } type || !type.Features.TryGet(out ProjectionTypeFeature? feature)) { return selection; diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs index 4222ab7f8e2..53d5cfc8c2d 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs @@ -11,7 +11,7 @@ public interface IProjectionOptimizer /// /// The selection to test for /// Returns true if the selection can be handled - bool CanHandle(ISelection selection); + bool CanHandle(Selection selection); /// /// Rewrites a selection. In case nothing is rewritten, the diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/Selection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/Selection.cs index c951df63810..dcdca53e219 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/Selection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/Selection.cs @@ -5,6 +5,9 @@ namespace HotChocolate.Fusion.Execution.Nodes; +/// +/// Represents a field selection during execution in the Fusion execution engine. +/// public sealed class Selection : ISelection { private readonly FieldSelectionNode[] _syntaxNodes; @@ -44,26 +47,40 @@ public Selection( _utf8ResponseName = Utf8StringCache.GetUtf8String(responseName); } + /// public int Id { get; } + /// public string ResponseName { get; } internal ReadOnlySpan Utf8ResponseName => _utf8ResponseName; + /// public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; + /// public bool IsConditional => _includeFlags.Length > 0; + /// public bool IsLeaf => (_flags & Flags.Leaf) == Flags.Leaf; + /// public IOutputFieldDefinition Field { get; } + /// public IType Type => Field.Type; + /// + /// Gets the selection set that contains this selection. + /// public SelectionSet DeclaringSelectionSet { get; private set; } = null!; + /// ISelectionSet ISelection.DeclaringSelectionSet => DeclaringSelectionSet; + /// + /// Gets the syntax nodes that contributed to this selection. + /// public ReadOnlySpan SyntaxNodes => _syntaxNodes; internal ResolveFieldValue? Resolver => Field.Features.Get(); @@ -76,6 +93,7 @@ IEnumerable ISelection.GetSyntaxNodes() } } + /// public bool IsIncluded(ulong includeFlags) { if (_includeFlags.Length == 0) From fbb3ff7a4cac76270da57ed44d042e92ade0ac72 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 2 Dec 2025 12:16:32 +0100 Subject: [PATCH 16/30] wip --- .../Core/src/Validation/ErrorHelper.cs | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index daaadbe780f..45f730d958c 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -24,7 +24,8 @@ public static IError DeferAndStreamNotAllowedOnMutationOrSubscriptionRoot( extension(DocumentValidatorContext context) { - public IError VariableNotUsed(OperationDefinitionNode node, + public IError VariableNotUsed( + OperationDefinitionNode node, IEnumerable unusedVariables) { return ErrorBuilder.New() @@ -37,7 +38,8 @@ public IError VariableNotUsed(OperationDefinitionNode node, .Build(); } - public IError VariableNotDeclared(OperationDefinitionNode node, + public IError VariableNotDeclared( + OperationDefinitionNode node, IEnumerable usedVariables) { return ErrorBuilder.New() @@ -50,7 +52,8 @@ public IError VariableNotDeclared(OperationDefinitionNode node, .Build(); } - public IError OneOfVariableIsNotCompatible(VariableNode variable, + public IError OneOfVariableIsNotCompatible( + VariableNode variable, VariableDefinitionNode variableDefinition) { var variableName = variableDefinition.Variable.Name.Value; @@ -66,7 +69,8 @@ public IError OneOfVariableIsNotCompatible(VariableNode variable, .Build(); } - public IError VariableIsNotCompatible(VariableNode variable, + public IError VariableIsNotCompatible( + VariableNode variable, VariableDefinitionNode variableDefinition) { var variableName = variableDefinition.Variable.Name.Value; @@ -123,8 +127,7 @@ public IError TypeSystemDefinitionNotAllowed(IDefinitionNode node) .Build(); } - public IError UnionFieldError(SelectionSetNode node, - IUnionTypeDefinition type) + public IError UnionFieldError(SelectionSetNode node, IUnionTypeDefinition type) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_UnionFieldError) @@ -135,8 +138,7 @@ public IError UnionFieldError(SelectionSetNode node, .Build(); } - public IError FieldDoesNotExist(FieldNode node, - IComplexTypeDefinition outputType) + public IError FieldDoesNotExist(FieldNode node, IComplexTypeDefinition outputType) { return ErrorBuilder.New() .SetMessage( @@ -151,7 +153,8 @@ public IError FieldDoesNotExist(FieldNode node, .Build(); } - public IError LeafFieldsCannotHaveSelections(FieldNode node, + public IError LeafFieldsCannotHaveSelections( + FieldNode node, IComplexTypeDefinition declaringType, IType fieldType) { @@ -170,7 +173,8 @@ public IError LeafFieldsCannotHaveSelections(FieldNode node, .Build(); } - public IError ArgumentValueIsNotCompatible(ArgumentNode node, + public IError ArgumentValueIsNotCompatible( + ArgumentNode node, IInputType locationType, IValueNode value) { @@ -185,7 +189,8 @@ public IError ArgumentValueIsNotCompatible(ArgumentNode node, .Build(); } - public IError FieldValueIsNotCompatible(IInputValueDefinition field, + public IError FieldValueIsNotCompatible( + IInputValueDefinition field, IInputType locationType, IValueNode valueNode) { @@ -200,7 +205,8 @@ public IError FieldValueIsNotCompatible(IInputValueDefinition field, .Build(); } - public IError VariableDefaultValueIsNotCompatible(VariableDefinitionNode node, + public IError VariableDefaultValueIsNotCompatible( + VariableDefinitionNode node, IInputType locationType, IValueNode valueNode) { @@ -217,7 +223,8 @@ public IError VariableDefaultValueIsNotCompatible(VariableDefinitionNode node, .Build(); } - public IError NoSelectionOnCompositeField(FieldNode node, + public IError NoSelectionOnCompositeField( + FieldNode node, IComplexTypeDefinition declaringType, IType fieldType) { @@ -236,8 +243,7 @@ public IError NoSelectionOnCompositeField(FieldNode node, .Build(); } - public IError NoSelectionOnRootType(OperationDefinitionNode node, - IType fieldType) + public IError NoSelectionOnRootType(OperationDefinitionNode node, IType fieldType) { return ErrorBuilder.New() .SetMessage( @@ -251,8 +257,7 @@ public IError NoSelectionOnRootType(OperationDefinitionNode node, .Build(); } - public IError FieldIsRequiredButNull(ISyntaxNode node, - string fieldName) + public IError FieldIsRequiredButNull(ISyntaxNode node, string fieldName) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_FieldIsRequiredButNull, fieldName) @@ -263,8 +268,7 @@ public IError FieldIsRequiredButNull(ISyntaxNode node, .Build(); } - public IError FieldsAreNotMergeable(FieldInfo fieldA, - FieldInfo fieldB) + public IError FieldsAreNotMergeable(FieldInfo fieldA, FieldInfo fieldB) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_FieldsAreNotMergeable) @@ -340,7 +344,8 @@ public IError FragmentDoesNotExist(FragmentSpreadNode fragmentSpread) .Build(); } - public IError FragmentNotPossible(ISyntaxNode node, + public IError FragmentNotPossible( + ISyntaxNode node, ITypeDefinition typeCondition, ITypeDefinition parentType) { @@ -355,8 +360,7 @@ public IError FragmentNotPossible(ISyntaxNode node, .Build(); } - public IError FragmentTypeConditionUnknown(ISyntaxNode node, - NamedTypeNode typeCondition) + public IError FragmentTypeConditionUnknown(ISyntaxNode node, NamedTypeNode typeCondition) { return ErrorBuilder.New() .SetMessage( @@ -370,8 +374,7 @@ public IError FragmentTypeConditionUnknown(ISyntaxNode node, .Build(); } - public IError FragmentOnlyCompositeType(ISyntaxNode node, - ITypeDefinition type) + public IError FragmentOnlyCompositeType(ISyntaxNode node, ITypeDefinition type) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_FragmentOnlyCompositeType) @@ -407,8 +410,7 @@ public IError InputFieldDoesNotExist(ObjectFieldNode field) .Build(); } - public IError InputFieldRequired(ISyntaxNode node, - string fieldName) + public IError InputFieldRequired(ISyntaxNode node, string fieldName) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_InputFieldRequired, fieldName) @@ -419,8 +421,7 @@ public IError InputFieldRequired(ISyntaxNode node, .Build(); } - public IError OperationNameNotUnique(OperationDefinitionNode operation, - string operationName) + public IError OperationNameNotUnique(OperationDefinitionNode operation, string operationName) { return ErrorBuilder.New() .SetMessage( @@ -432,8 +433,7 @@ public IError OperationNameNotUnique(OperationDefinitionNode operation, .Build(); } - public IError OperationAnonymousMoreThanOne(OperationDefinitionNode operation, - int operations) + public IError OperationAnonymousMoreThanOne(OperationDefinitionNode operation, int operations) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_OperationAnonymousMoreThanOne) @@ -443,8 +443,7 @@ public IError OperationAnonymousMoreThanOne(OperationDefinitionNode operation, .Build(); } - public IError VariableNotInputType(VariableDefinitionNode node, - string variableName) + public IError VariableNotInputType(VariableDefinitionNode node, string variableName) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_VariableNotInputType, variableName) @@ -456,8 +455,7 @@ public IError VariableNotInputType(VariableDefinitionNode node, .Build(); } - public IError VariableNameNotUnique(VariableDefinitionNode node, - string variableName) + public IError VariableNameNotUnique(VariableDefinitionNode node, string variableName) { return ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_VariableNameNotUnique) @@ -469,7 +467,8 @@ public IError VariableNameNotUnique(VariableDefinitionNode node, .Build(); } - public IError ArgumentNotUnique(ArgumentNode node, + public IError ArgumentNotUnique( + ArgumentNode node, SchemaCoordinate? field = null, IDirectiveDefinition? directive = null) { @@ -496,7 +495,8 @@ public IError ArgumentNotUnique(ArgumentNode node, .Build(); } - public IError ArgumentRequired(ISyntaxNode node, + public IError ArgumentRequired( + ISyntaxNode node, string argumentName, SchemaCoordinate? field = null, IDirectiveDefinition? directive = null) @@ -524,7 +524,8 @@ public IError ArgumentRequired(ISyntaxNode node, .Build(); } - public IError ArgumentDoesNotExist(ArgumentNode node, + public IError ArgumentDoesNotExist( + ArgumentNode node, SchemaCoordinate? field = null, IDirectiveDefinition? directive = null) { @@ -569,7 +570,8 @@ public IError SubscriptionNoTopLevelIntrospectionField(OperationDefinitionNode o .Build(); } - public IError MaxExecutionDepth(OperationDefinitionNode operation, + public IError MaxExecutionDepth( + OperationDefinitionNode operation, int allowedExecutionDepth, int detectedExecutionDepth) { @@ -583,8 +585,7 @@ public IError MaxExecutionDepth(OperationDefinitionNode operation, .Build(); } - public IError IntrospectionNotAllowed(FieldNode field, - string? customErrorMessage) + public IError IntrospectionNotAllowed(FieldNode field, string? customErrorMessage) { var message = customErrorMessage ?? Resources.ErrorHelper_IntrospectionNotAllowed; @@ -596,8 +597,7 @@ public IError IntrospectionNotAllowed(FieldNode field, .Build(); } - public IError OneOfMustHaveExactlyOneField(ISyntaxNode node, - IInputObjectTypeDefinition type) + public IError OneOfMustHaveExactlyOneField(ISyntaxNode node, IInputObjectTypeDefinition type) => ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_OneOfMustHaveExactlyOneField, type.Name) .AddLocation(node) @@ -606,7 +606,8 @@ public IError OneOfMustHaveExactlyOneField(ISyntaxNode node, .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) .Build(); - public IError OneOfVariablesMustBeNonNull(ISyntaxNode node, + public IError OneOfVariablesMustBeNonNull( + ISyntaxNode node, SchemaCoordinate fieldCoordinate, string variableName) => ErrorBuilder.New() @@ -621,8 +622,7 @@ public IError OneOfVariablesMustBeNonNull(ISyntaxNode node, .SpecifiedBy("sec-All-Variable-Usages-Are-Allowed", rfc: 825) .Build(); - public IError DeferAndStreamDuplicateLabel(ISyntaxNode selection, - string label) + public IError DeferAndStreamDuplicateLabel(ISyntaxNode selection, string label) => ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_DeferAndStreamDuplicateLabel) .AddLocation(selection) @@ -631,8 +631,7 @@ public IError DeferAndStreamDuplicateLabel(ISyntaxNode selection, .SetPath(context.CreateErrorPath()) .Build(); - public IError DeferAndStreamLabelIsVariable(ISyntaxNode selection, - string variable) + public IError DeferAndStreamLabelIsVariable(ISyntaxNode selection, string variable) => ErrorBuilder.New() .SetMessage(Resources.ErrorHelper_DeferAndStreamLabelIsVariable) .AddLocation(selection) From cd27228b90310d8d3ec57473bb7a260e3fa85dfb Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 2 Dec 2025 21:16:12 +0100 Subject: [PATCH 17/30] wip --- .../Core/src/Types/Execution/Processing/Operation.cs | 7 ++----- .../Core/src/Types/Execution/Processing/SelectionSet.cs | 1 - .../Core/src/Validation/HotChocolate.Validation.csproj | 1 - .../Optimizers/QueryablePagingProjectionOptimizer.cs | 7 +++---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 10151268f48..78631edb320 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -1,12 +1,9 @@ using System.Collections.Concurrent; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using HotChocolate.Features; using HotChocolate.Language; using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; namespace HotChocolate.Execution.Processing; @@ -68,7 +65,7 @@ internal Operation( rootSelectionSet.Complete(this, seal: isFinal); } - /// + /// /// Gets the internal unique identifier for this operation. /// public string Id { get; } @@ -191,7 +188,7 @@ public SelectionSet GetSelectionSet(Selection selection, IObjectTypeDefinition t _includeConditions, ref _elementsById, ref _lastId); - selectionSet.Complete(this, seal: _isFinal); + selectionSet.Complete(this, seal: _isFinal); _selectionSets.TryAdd(key, selectionSet); } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 46cce24dd61..7c0cc5c51db 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -1,6 +1,5 @@ using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; using System.Text; using HotChocolate.Types; diff --git a/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj b/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj index 5b35cede164..1195b619af7 100644 --- a/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj +++ b/src/HotChocolate/Core/src/Validation/HotChocolate.Validation.csproj @@ -4,7 +4,6 @@ HotChocolate.Validation HotChocolate.Validation HotChocolate.Validation - diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs index 9182d19349d..c6ad38ed3ff 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs @@ -26,10 +26,9 @@ public Selection RewriteSelection( if (context.Type.NamedType() is not IPageType pageType) { - throw ThrowHelper - .PagingProjectionOptimizer_NotAPagingField( - selection.DeclaringType, - selection.Field); + throw ThrowHelper.PagingProjectionOptimizer_NotAPagingField( + selection.DeclaringType, + selection.Field); } var selections = CollectSelection(context); From 01e428470fc46a145964fc64099f8154fefccece Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 3 Dec 2025 09:45:29 +0100 Subject: [PATCH 18/30] wip --- .../src/Authorization/AuthorizeMiddleware.cs | 2 +- .../ErrorBuilderExtensions.cs | 82 ++++++++++++++++++- .../Core/src/Validation/ErrorHelper.cs | 2 +- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs index 38f27393a8e..62f998fee96 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizeMiddleware.cs @@ -75,7 +75,7 @@ private void SetError( => ErrorBuilder.New() .SetMessage( AuthorizeMiddleware_PolicyNotFound, - _directive.Policy!) + _directive.Policy) .SetCode(ErrorCodes.Authentication.PolicyNotFound) .SetPath(context.Path) .AddLocations(context.Selection) diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs index 7b13fee3b11..c269b159a40 100644 --- a/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution.Abstractions/ErrorBuilderExtensions.cs @@ -39,14 +39,90 @@ public ErrorBuilder SetInputPath(Path inputPath) /// Sets the message of the error. /// /// The format of the message. - /// The argument for the message. + /// The argument for the message. /// The error builder. - public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, object? arg) + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, object? arg1) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(format); - return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg)); + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1)); + } + + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The first argument for the message. + /// The second argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, object? arg1, object? arg2) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1, arg2)); + } + + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The first argument for the message. + /// The second argument for the message. + /// The third argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, object? arg1, object? arg2, object? arg3) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1, arg2, arg3)); + } + + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, string? arg1) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1)); + } + + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The first argument for the message. + /// The second argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, string? arg1, string? arg2) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1, arg2)); + } + + /// + /// Sets the message of the error. + /// + /// The format of the message. + /// The first argument for the message. + /// The second argument for the message. + /// The third argument for the message. + /// The error builder. + public ErrorBuilder SetMessage([StringSyntax("CompositeFormat")] string format, string? arg1, string? arg2, string? arg3) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(format); + + return builder.SetMessage(string.Format(CultureInfo.InvariantCulture, format, arg1, arg2, arg3)); } /// diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index 45f730d958c..eefd5dfed09 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -614,7 +614,7 @@ public IError OneOfVariablesMustBeNonNull( .SetMessage( Resources.ErrorHelper_OneOfVariablesMustBeNonNull, variableName, - fieldCoordinate.MemberName!, + fieldCoordinate.MemberName, fieldCoordinate.Name) .AddLocation(node) .SetPath(context.CreateErrorPath()) From 50a3a78cec48303648b810b691958e11fb775ef9 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 10:34:28 +0100 Subject: [PATCH 19/30] wip --- .../Core/src/Types/Execution/ErrorHelper.cs | 10 +- .../Types/Execution/Processing/Operation.cs | 12 +- .../Execution/Processing/OperationCompiler.cs | 102 +++++++- .../OperationCompilerOptimizerHelper.cs | 22 +- .../OperationFeatureCollection.Selections.cs | 48 +++- .../Processing/OperationOptimizerContext.cs | 45 +--- .../Types/Execution/Processing/Selection.cs | 14 +- .../Processing/SelectionFeatureCollection.cs | 46 ++++ .../Execution/Processing/SelectionSet.cs | 14 +- .../SelectionSetOptimizerContext.cs | 111 ++++----- .../Processing/ValueCompletion.Object.cs | 10 +- .../Types/Execution/RequestExecutorManager.cs | 3 +- .../Core/src/Types/Execution/ThrowHelper.cs | 6 + .../Extensions/ResolverContextExtensions.cs | 230 ++++-------------- .../Types/Pagination/ConnectionFlagsHelper.cs | 89 ++++--- .../Core/src/Types/Utilities/ThrowHelper.cs | 9 +- .../Processing/OperationCompilerTests.cs | 2 +- .../QueryablePagingProjectionOptimizer.cs | 2 +- .../IsProjectedProjectionOptimizer.cs | 2 +- 19 files changed, 370 insertions(+), 407 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs index 5ed2a9d615d..abcb9c0ca53 100644 --- a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs @@ -57,12 +57,12 @@ public static IError UnexpectedLeafValueSerializationError( public static IError UnableToResolveTheAbstractType( string typeName, - FieldNode field, + Selection selection, Path path) { return ErrorBuilder.New() .SetMessage(ErrorHelper_UnableToResolveTheAbstractType_Message, typeName) - .AddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.CannotResolveAbstractType) .Build(); @@ -71,12 +71,12 @@ public static IError UnableToResolveTheAbstractType( public static IError UnexpectedErrorWhileResolvingAbstractType( Exception exception, string typeName, - FieldNode field, + Selection selection, Path path) { return ErrorBuilder.New() .SetMessage(ErrorHelper_UnableToResolveTheAbstractType_Message, typeName) - .AddLocation(field) + .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.CannotResolveAbstractType) .SetException(exception) @@ -89,7 +89,7 @@ public static IError ListValueIsNotSupported( Path path) { return ErrorBuilder.New() - .SetMessage(ErrorHelper_ListValueIsNotSupported_Message, listType.FullName!) + .SetMessage(ErrorHelper_ListValueIsNotSupported_Message, listType.FullName) .AddLocations(selection) .SetPath(path) .SetCode(ErrorCodes.Execution.ListTypeNotSupported) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index 78631edb320..c88f27dda86 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -19,7 +19,6 @@ public sealed class Operation : IOperation private readonly OperationCompiler _compiler; private readonly IncludeConditionCollection _includeConditions; private readonly OperationFeatureCollection _features; - private readonly bool _isFinal; private object[] _elementsById; private int _lastId; @@ -33,9 +32,9 @@ internal Operation( SelectionSet rootSelectionSet, OperationCompiler compiler, IncludeConditionCollection includeConditions, + OperationFeatureCollection features, int lastId, - object[] elementsById, - bool isFinal) + object[] elementsById) { ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentException.ThrowIfNullOrWhiteSpace(hash); @@ -59,10 +58,7 @@ internal Operation( _includeConditions = includeConditions; _lastId = lastId; _elementsById = elementsById; - _isFinal = isFinal; - - _features = new OperationFeatureCollection(); - rootSelectionSet.Complete(this, seal: isFinal); + _features = features; } /// @@ -183,12 +179,12 @@ public SelectionSet GetSelectionSet(Selection selection, IObjectTypeDefinition t { selectionSet = _compiler.CompileSelectionSet( + this, selection, objectType, _includeConditions, ref _elementsById, ref _lastId); - selectionSet.Complete(this, seal: _isFinal); _selectionSets.TryAdd(key, selectionSet); } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index ee24611434a..2565aa7b5fd 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -1,4 +1,6 @@ using System.Buffers; +using System.Collections.Immutable; +using System.Runtime.InteropServices; using HotChocolate.Fusion.Rewriters; using HotChocolate.Language; using HotChocolate.Language.Visitors; @@ -11,6 +13,7 @@ internal sealed partial class OperationCompiler { private readonly Schema _schema; private readonly ObjectPool>> _fieldsPool; + private readonly OperationCompilerOptimizers _optimizers; private readonly DocumentRewriter _documentRewriter; private readonly InputParser _inputValueParser; private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; @@ -18,7 +21,8 @@ internal sealed partial class OperationCompiler public OperationCompiler( Schema schema, InputParser inputValueParser, - ObjectPool>> fieldsPool) + ObjectPool>> fieldsPool, + OperationCompilerOptimizers optimizers) { ArgumentNullException.ThrowIfNull(schema); ArgumentNullException.ThrowIfNull(fieldsPool); @@ -27,6 +31,7 @@ public OperationCompiler( _inputValueParser = inputValueParser; _fieldsPool = fieldsPool; _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); + _optimizers = optimizers; } public Operation Compile( @@ -64,11 +69,12 @@ public Operation Compile( fields, rootType, compilationContext, + _optimizers.SelectionSetOptimizers, ref lastId); compilationContext.Register(selectionSet, selectionSet.Id); - return new Operation( + var operation = new Operation( id, hash, document, @@ -76,11 +82,24 @@ public Operation Compile( rootType, _schema, selectionSet, - this, + compiler: this, includeConditions, + compilationContext.Features, lastId, - compilationContext.ElementsById, - isFinal: true); // todo : add the interceptors back + compilationContext.ElementsById); + + selectionSet.Complete(operation); + + if (_optimizers.OperationOptimizers.Length > 0) + { + var context = new OperationOptimizerContext(operation); + foreach (var optimizer in _optimizers.OperationOptimizers) + { + optimizer.OptimizeOperation(context); + } + } + + return operation; } finally { @@ -89,13 +108,16 @@ public Operation Compile( } internal SelectionSet CompileSelectionSet( + Operation operation, Selection selection, ObjectType objectType, IncludeConditionCollection includeConditions, ref object[] elementsById, ref int lastId) { - var compilationContext = new CompilationContext(elementsById); + var compilationContext = new CompilationContext(elementsById, operation.Features); + var optimizers = OperationCompilerOptimizerHelper.GetOptimizers(selection); + var fields = _fieldsPool.Get(); fields.Clear(); @@ -126,9 +148,10 @@ internal SelectionSet CompileSelectionSet( } } - var selectionSet = BuildSelectionSet(fields, objectType, compilationContext, ref lastId); + var selectionSet = BuildSelectionSet(fields, objectType, compilationContext, optimizers, ref lastId); compilationContext.Register(selectionSet, selectionSet.Id); elementsById = compilationContext.ElementsById; + selectionSet.Complete(operation); return selectionSet; } finally @@ -193,6 +216,7 @@ private SelectionSet BuildSelectionSet( OrderedDictionary> fieldMap, ObjectType typeContext, CompilationContext compilationContext, + ImmutableArray optimizers, ref int lastId) { var i = 0; @@ -263,6 +287,11 @@ private SelectionSet BuildSelectionSet( fieldDelegate, pureFieldDelegate); + if (optimizers.Length > 0) + { + selection.Features.SetSafe(optimizers); + } + // Register the selection in the elements array compilationContext.Register(selection, selection.Id); selections[i++] = selection; @@ -273,6 +302,53 @@ private SelectionSet BuildSelectionSet( } } + // if there are no optimizers registered for this selection we exit early. + if (optimizers.Length == 0) + { + return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + } + + var current = ImmutableCollectionsMarshal.AsImmutableArray(selections); + var rewritten = current; + + var optimizerContext = new SelectionSetOptimizerContext( + selectionSetId, + typeContext, + ref rewritten, + compilationContext.Features, + ref lastId, + _schema, + CreateFieldPipeline); + + foreach (var optimizer in optimizers) + { + optimizer.OptimizeSelectionSet(optimizerContext); + } + + // If `rewritten` is still the same instance as `current`, + // the optimizers did not change the selections array. + // This mean we can simply construct the SelectionSet. + if (current == rewritten) + { + return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + } + + if (current.Length < rewritten.Length) + { + for (var j = current.Length; j < rewritten.Length; j++) + { + var selection = rewritten[j]; + + if (optimizers.Length > 0) + { + selection.Features.SetSafe(optimizers); + } + + compilationContext.Register(selection, selection.Id); + } + } + + selections = ImmutableCollectionsMarshal.AsArray(rewritten)!; return new SelectionSet(selectionSetId, typeContext, selections, isConditional); } @@ -418,12 +494,20 @@ protected override ISyntaxVisitorAction Enter( } } - private class CompilationContext(object[] elementsById) + private class CompilationContext { - private object[] _elementsById = elementsById; + private object[] _elementsById; + + public CompilationContext(object[] elementsById, OperationFeatureCollection? features = null) + { + _elementsById = elementsById; + Features = features ?? new OperationFeatureCollection(); + } public object[] ElementsById => _elementsById; + public OperationFeatureCollection Features { get; } + public void Register(object element, int id) { if (id >= _elementsById.Length) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs index 4a9ed3b2d8e..43b1bd835e7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Execution.Processing; /// -/// This helper class allows adding optimizers to context data or retrieve optimizers from context data. +/// This helper class allows to register optimizers with a field configuration. /// internal static class OperationCompilerOptimizerHelper { @@ -33,8 +33,20 @@ private static void RegisterOptimizerInternal( } } - public static bool TryGetOptimizers( - IFeatureProvider featureProvider, - out ImmutableArray optimizers) - => featureProvider.Features.TryGet(out optimizers); + public static ImmutableArray GetOptimizers(Selection selection) + { + var optimizers = ImmutableArray.Empty; + + if (selection.Features.TryGet>(out var selectionOptimizers)) + { + optimizers = selectionOptimizers; + } + + if (selection.Field.Features.TryGet>(out var fieldOptimizers)) + { + optimizers = optimizers.AddRange(fieldOptimizers); + } + + return optimizers; + } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs index 22cda22d024..bb718cc5eb7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.Selections.cs @@ -6,19 +6,6 @@ namespace HotChocolate.Execution.Processing; [SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] public sealed partial class OperationFeatureCollection { - internal bool TryGet(int selectionId, [NotNullWhen(true)] out TFeature? feature) - { - if (_selectionFeatures.TryGetValue((selectionId, typeof(TFeature)), out var result) - && result is TFeature f) - { - feature = f; - return true; - } - - feature = default; - return false; - } - internal object? this[int selectionId, Type featureType] { get @@ -48,6 +35,19 @@ internal bool TryGet(int selectionId, [NotNullWhen(true)] out TFeature } } + internal bool TryGet(int selectionId, [NotNullWhen(true)] out TFeature? feature) + { + if (_selectionFeatures.TryGetValue((selectionId, typeof(TFeature)), out var result) + && result is TFeature f) + { + feature = f; + return true; + } + + feature = default; + return false; + } + internal TFeature GetOrSetSafe(int selectionId, Func factory) { ArgumentNullException.ThrowIfNull(factory); @@ -67,6 +67,28 @@ internal TFeature GetOrSetSafe(int selectionId, Func factory return feature; } + internal TFeature GetOrSetSafe( + int selectionId, + Func factory, + TContext context) + { + ArgumentNullException.ThrowIfNull(factory); + + if (!TryGet(selectionId, out var feature)) + { + lock (_writeLock) + { + if (!TryGet(selectionId, out feature)) + { + feature = factory(context); + this[selectionId, typeof(TFeature)] = feature; + } + } + } + + return feature; + } + internal IEnumerable> GetFeatures(int selectionId) { foreach (var ((id, type), value) in _selectionFeatures) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs index 4aaabec22ae..c2f721a968a 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs @@ -10,60 +10,17 @@ namespace HotChocolate.Execution.Processing; /// public readonly ref struct OperationOptimizerContext { - private readonly CreateFieldPipeline _createFieldPipeline; - /// /// Initializes a new instance of /// internal OperationOptimizerContext( - Operation operation, - Dictionary contextData, - CreateFieldPipeline createFieldPipeline) + Operation operation) { Operation = operation; - ContextData = contextData; - _createFieldPipeline = createFieldPipeline; } /// /// Gets the operation. /// public Operation Operation { get; } - - /// - /// The context data dictionary can be used by middleware components and - /// resolvers to store and retrieve data during execution. - /// - public IDictionary ContextData { get; } - - /// - /// Sets the resolvers on the specified . - /// - /// - /// The selection to set the resolvers on. - /// - /// - /// The async resolver pipeline. - /// - /// - /// The pure resolver. - /// - public void SetResolver( - Selection selection, - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) - => selection.SetResolvers(resolverPipeline, pureResolver); - - /// - /// Allows to compile the field resolver pipeline for a field. - /// - /// The field. - /// The selection of the field. - /// - /// Returns a representing the field resolver pipeline. - /// - public FieldDelegate CompileResolverPipeline( - ObjectField field, - FieldNode fieldSelection) - => _createFieldPipeline(Operation.Schema, field, fieldSelection); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index b782c6cf467..cef3f42b1cd 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -16,6 +16,7 @@ public sealed class Selection : ISelection, IFeatureProvider private readonly ulong[] _includeFlags; private readonly byte[] _utf8ResponseName; private Flags _flags; + private SelectionSet? _declaringSelectionSet; internal Selection( int id, @@ -109,7 +110,8 @@ internal Selection( /// /// Gets the selection set that contains this selection. /// - public SelectionSet DeclaringSelectionSet { get; private set; } = null!; + public SelectionSet DeclaringSelectionSet + => _declaringSelectionSet ?? throw ThrowHelper.Selection_NotFullyInitialized(); /// ISelectionSet ISelection.DeclaringSelectionSet => DeclaringSelectionSet; @@ -240,7 +242,7 @@ internal void SetResolvers( /// /// Completes the selection without sealing it. /// - internal void Complete(SelectionSet selectionSet, bool seal) + internal void Complete(SelectionSet selectionSet) { ArgumentNullException.ThrowIfNull(selectionSet); @@ -249,12 +251,8 @@ internal void Complete(SelectionSet selectionSet, bool seal) throw new InvalidOperationException("Selection is already sealed."); } - DeclaringSelectionSet = selectionSet; - - if (seal) - { - _flags |= Flags.Sealed; - } + _declaringSelectionSet = selectionSet; + _flags |= Flags.Sealed; } private SelectionExecutionStrategy InferStrategy( diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs index c122ee84ae3..b1fe6773b6e 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs @@ -4,6 +4,11 @@ namespace HotChocolate.Execution.Processing; +/// +/// Represents a collection of features associated with a specific selection +/// within a GraphQL operation. This struct provides a view into the parent +/// scoped to a single selection. +/// public readonly struct SelectionFeatureCollection : IFeatureCollection { private readonly OperationFeatureCollection _parent; @@ -15,27 +20,68 @@ internal SelectionFeatureCollection(OperationFeatureCollection parent, int selec _selectionId = selectionId; } + /// + /// Gets a value indicating whether this feature collection is read-only. + /// public bool IsReadOnly => _parent.IsReadOnly; + /// + /// Gets a value indicating whether this selection has no features. + /// public bool IsEmpty => !_parent.HasFeatures(_selectionId); + /// + /// Gets the revision number of the underlying feature collection. + /// public int Revision => _parent.Revision; + /// + /// Gets or sets a feature by its type. + /// + /// The type of the feature. + /// The feature instance, or null if not found. public object? this[Type key] { get => _parent[_selectionId, key]; set => _parent[_selectionId, key] = value; } + /// + /// Sets a feature instance for this selection. + /// + /// The type of the feature. + /// The feature instance to set, or null to remove. + /// This method is thread-safe. + public void SetSafe(TFeature? instance) + => this[typeof(TFeature)] = instance; + + /// + /// Tries to get a feature of the specified type. + /// + /// The type of the feature. + /// + /// When this method returns, contains the feature if found; otherwise, null. + /// + /// true if the feature was found; otherwise, false. public bool TryGet([NotNullWhen(true)] out TFeature? feature) => _parent.TryGet(_selectionId, out feature); + /// + /// Gets an existing feature or creates and sets a new instance using the default constructor. + /// + /// The type of the feature. + /// The existing or newly created feature instance. + /// This method is thread-safe. public TFeature GetOrSetSafe() where TFeature : new() => GetOrSetSafe(static () => new TFeature()); internal TFeature GetOrSetSafe(Func factory) => _parent.GetOrSetSafe(_selectionId, factory); + internal TFeature GetOrSetSafe(Func factory, TContext context) + => _parent.GetOrSetSafe(_selectionId, factory, context); + + /// public IEnumerator> GetEnumerator() => _parent.GetFeatures(_selectionId).GetEnumerator(); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 7c0cc5c51db..228f15f6261 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -16,6 +16,7 @@ public sealed class SelectionSet : ISelectionSet private readonly FrozenDictionary _responseNameLookup; private readonly SelectionLookup _utf8ResponseNameLookup; private Flags _flags; + private Operation? _declaringOperation; internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) { @@ -52,7 +53,7 @@ internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections /// /// Gets the declaring operation. /// - public Operation DeclaringOperation { get; private set; } = null!; + public Operation DeclaringOperation => _declaringOperation ?? throw ThrowHelper.SelectionSet_NotFullyInitialized(); IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; @@ -93,24 +94,21 @@ public bool TryGetSelection(string responseName, [NotNullWhen(true)] out Selecti public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out Selection? selection) => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); - internal void Complete(Operation declaringOperation, bool seal) + internal void Complete(Operation declaringOperation) { if ((_flags & Flags.Sealed) == Flags.Sealed) { throw new InvalidOperationException("Selection set is already sealed."); } - DeclaringOperation = declaringOperation; + _declaringOperation = declaringOperation; foreach (var selection in _selections) { - selection.Complete(this, seal); + selection.Complete(this); } - if (seal) - { - _flags |= Flags.Sealed; - } + _flags |= Flags.Sealed; } [Flags] diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs index a7beac3cfaa..afb94e02f92 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs @@ -1,6 +1,9 @@ +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; +using HotChocolate.Utilities; namespace HotChocolate.Execution.Processing; @@ -8,66 +11,67 @@ namespace HotChocolate.Execution.Processing; /// The optimizer provides helper methods /// to optimize a . /// -public readonly ref struct SelectionSetOptimizerContext +public ref struct SelectionSetOptimizerContext { - private readonly OperationCompiler _compiler; - private readonly OperationCompiler.CompilerContext _compilerContext; - private readonly Dictionary _selectionLookup; - private readonly CreateFieldPipeline _createFieldPipeline; - + private readonly int _selectionSetId; + private readonly ref ImmutableArray _selections; + private readonly OperationFeatureCollection _features; + private readonly ref int _lastSelectionId; + private readonly Func _createFieldPipeline; + private Dictionary? _selectionMap; /// /// Initializes a new instance of /// internal SelectionSetOptimizerContext( - OperationCompiler compiler, - OperationCompiler.CompilerContext compilerContext, - Dictionary selectionLookup, - Dictionary contextData, - CreateFieldPipeline createFieldPipeline, - SelectionPath path) + int selectionSetId, + ObjectType typeContext, + ref ImmutableArray selections, + OperationFeatureCollection features, + ref int lastSelectionId, + Schema schema, + Func createFieldPipeline) { - _compiler = compiler; - _compilerContext = compilerContext; - _selectionLookup = selectionLookup; + _selectionSetId = selectionSetId; + _selections = ref selections; + _features = features; + _lastSelectionId = ref lastSelectionId; _createFieldPipeline = createFieldPipeline; - ContextData = contextData; - Path = path; + TypeContext = typeContext; + Schema = schema; } /// /// Gets the schema for which the query is compiled. /// - public Schema Schema - => _compilerContext.Schema; + public Schema Schema { get; } /// /// Gets the type context of the current selection-set. /// - public ObjectType Type - => _compilerContext.Type; + public ObjectType TypeContext { get; } - /// - /// Gets the selections of this selection set. - /// - public IReadOnlyDictionary Selections - => _compilerContext.Fields; + public bool ContainsField(string fieldName) + => _selections.Any(t => t.Field.Name.EqualsOrdinal(fieldName)); - /// - /// The context data dictionary can be used by middleware components and - /// resolvers to store and retrieve data during execution. - /// - public IDictionary ContextData { get; } + public bool ContainsResponseName(string responseName) + { + _selectionMap ??= _selections.ToDictionary(t => t.ResponseName); + return _selectionMap.ContainsKey(responseName); + } - /// - /// Gets the current selection path. - /// - public SelectionPath Path { get; } + public bool TryGetSelection(string responseName, [MaybeNullWhen(false)] out Selection value) + { + _selectionMap ??= _selections.ToDictionary(t => t.ResponseName); + return _selectionMap.TryGetValue(responseName, out value); + } + + public SelectionFeatureCollection Features => new(_features, _selectionSetId); /// /// Gets the next operation unique selection id. /// - public int GetNextSelectionId() - => _compiler.GetNextSelectionId(); + public int NewSelectionId() + => ++_lastSelectionId; /// /// Sets the resolvers on the specified . @@ -99,20 +103,21 @@ public FieldDelegate CompileResolverPipeline(ObjectField field, FieldNode select => _createFieldPipeline(Schema, field, selection); /// - /// Adds an additional selection for internal purposes. + /// Adds a selection for internal purposes. /// - /// - /// The new optimized selection. + /// + /// The internal selection. /// /// - /// is null. + /// is null. /// - public void AddSelection(Selection newSelection) + public void AddSelection(Selection internalSelection) { - ArgumentNullException.ThrowIfNull(newSelection); + ArgumentNullException.ThrowIfNull(internalSelection); - _compilerContext.Fields.Add(newSelection.ResponseName, newSelection); - _compiler.RegisterNewSelection(newSelection); + _selectionMap ??= _selections.ToDictionary(t => t.ResponseName); + _selectionMap.Add(internalSelection.ResponseName, internalSelection); + _selections = _selections.Add(internalSelection); } /// @@ -132,19 +137,15 @@ public void ReplaceSelection(Selection newSelection) { ArgumentNullException.ThrowIfNull(newSelection); - if (!_compilerContext.Fields.TryGetValue( - newSelection.ResponseName, - out var currentSelection)) + _selectionMap ??= _selections.ToDictionary(t => t.ResponseName); + + if (!_selectionMap.TryGetValue(newSelection.ResponseName, out var currentSelection)) { throw new ArgumentException($"The `{newSelection.ResponseName}` does not exist."); } - _compilerContext.Fields[newSelection.ResponseName] = newSelection; - - if (_selectionLookup.TryGetValue(currentSelection, out var selectionSetInfos)) - { - _selectionLookup.Remove(currentSelection); - _selectionLookup.Add(newSelection, selectionSetInfos); - } + _selectionMap[newSelection.ResponseName] = newSelection; + var index = _selections.IndexOf(currentSelection); + _selections = _selections.SetItem(index, newSelection); } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs index 4c940e97cd1..522a3698743 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs @@ -11,7 +11,7 @@ internal static partial class ValueCompletion { private static ObjectResult? CompleteCompositeValue( ValueCompletionContext context, - ISelection selection, + Selection selection, IType type, ResultData parent, int index, @@ -34,14 +34,14 @@ internal static partial class ValueCompletion } var errorPath = CreatePathFromContext(selection, parent, index); - var error = ValueCompletion_CouldNotResolveAbstractType(selection.SyntaxNode, errorPath, result); + var error = ValueCompletion_CouldNotResolveAbstractType(selection, errorPath, result); operationContext.ReportError(error, context.ResolverContext, selection); return null; } private static bool TryResolveObjectType( ValueCompletionContext context, - ISelection selection, + Selection selection, IType fieldType, ResultData parent, int index, @@ -76,7 +76,7 @@ private static bool TryResolveObjectType( var error = UnableToResolveTheAbstractType( fieldType.Print(), - selection.SyntaxNode, + selection, CreatePathFromContext(selection, parent, index)); context.OperationContext.ReportError(error, context.ResolverContext, selection); } @@ -85,7 +85,7 @@ private static bool TryResolveObjectType( var error = UnexpectedErrorWhileResolvingAbstractType( ex, fieldType.Print(), - selection.SyntaxNode, + selection, CreatePathFromContext(selection, parent, index)); context.OperationContext.ReportError(error, context.ResolverContext, selection); } diff --git a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs index c26e30ec3db..a3ebe5b7ee4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs +++ b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs @@ -275,7 +275,8 @@ await typeModuleChangeMonitor.ConfigureAsync(context, cancellationToken) static sp => new OperationCompiler( sp.GetRequiredService(), sp.GetRequiredService(), - sp.GetRequiredService>>>())); + sp.GetRequiredService>>>(), + sp.GetRequiredService())); serviceCollection.AddSingleton( static _ => new DefaultObjectPoolProvider()); diff --git a/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs index a9b8ebe2e4e..d2ac4820927 100644 --- a/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs @@ -6,6 +6,12 @@ namespace HotChocolate.Execution; internal static class ThrowHelper { + public static InvalidOperationException SelectionSet_NotFullyInitialized() + => new InvalidOperationException("The selection set is not fully initialized."); + + public static InvalidOperationException Selection_NotFullyInitialized() + => new InvalidOperationException("The selection is not fully initialized."); + public static GraphQLException VariableIsNotAnInputType( VariableDefinitionNode variableDefinition) { diff --git a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs index 29c5d692654..9a886885497 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs @@ -23,12 +23,7 @@ public static class ResolverContextExtensions /// public T? GetGlobalStateOrDefault(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ContextData.TryGetValue(name, out var value) && value is T casted) @@ -51,15 +46,9 @@ public static class ResolverContextExtensions /// or the default value of , if the state /// could not be found or cast to . /// - public T GetGlobalStateOrDefault(string name, - T defaultValue) + public T GetGlobalStateOrDefault(string name, T defaultValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ContextData.TryGetValue(name, out var value) && value is T casted) @@ -81,12 +70,7 @@ public T GetGlobalStateOrDefault(string name, /// public T GetGlobalState(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ContextData.TryGetValue(name, out var value) && value is T typedValue) { @@ -110,12 +94,7 @@ public T GetGlobalState(string name) /// public T? GetScopedStateOrDefault(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ScopedContextData.TryGetValue(name, out var value) && value is T casted) @@ -138,15 +117,9 @@ public T GetGlobalState(string name) /// or the default value of , if the state /// could not be found or cast to . /// - public T GetScopedStateOrDefault(string name, - T defaultValue) + public T GetScopedStateOrDefault(string name, T defaultValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ScopedContextData.TryGetValue(name, out var value) && value is T casted) @@ -168,12 +141,7 @@ public T GetScopedStateOrDefault(string name, /// public T GetScopedState(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.ScopedContextData.TryGetValue(name, out var value) && value is T typedValue) @@ -198,12 +166,7 @@ public T GetScopedState(string name) /// public T? GetLocalStateOrDefault(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.LocalContextData.TryGetValue(name, out var value) && value is T casted) @@ -226,15 +189,9 @@ public T GetScopedState(string name) /// or the default value of , if the state /// could not be found or cast to . /// - public T GetLocalStateOrDefault(string name, - T defaultValue) + public T GetLocalStateOrDefault(string name, T defaultValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.LocalContextData.TryGetValue(name, out var value) && value is T casted) @@ -256,12 +213,7 @@ public T GetLocalStateOrDefault(string name, /// public T GetLocalState(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } + ArgumentException.ThrowIfNullOrEmpty(name); if (context.LocalContextData.TryGetValue(name, out var value) && value is T casted) @@ -282,16 +234,9 @@ public T GetLocalState(string name) /// The name of the state. /// The new state value. /// The type of the state. - public void SetGlobalState(string name, - T value) + public void SetGlobalState(string name, T value) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); context.ContextData[name] = value; } @@ -304,16 +249,9 @@ public void SetGlobalState(string name, /// The name of the state. /// The new state value. /// The type of the state. - public void SetScopedState(string name, - T value) + public void SetScopedState(string name, T value) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); context.ScopedContextData = context.ScopedContextData.SetItem(name, value); } @@ -326,16 +264,9 @@ public void SetScopedState(string name, /// The name of the state. /// The new state value. /// The type of the state. - public void SetLocalState(string name, - T value) + public void SetLocalState(string name, T value) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); context.LocalContextData = context.LocalContextData.SetItem(name, value); } @@ -352,16 +283,9 @@ public void SetLocalState(string name, /// The existing state for the specified , /// or the newly created state using the function. /// - public T GetOrSetGlobalState(string name, - Func createValue) + public T GetOrSetGlobalState(string name, Func createValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); ArgumentNullException.ThrowIfNull(createValue); if (context.ContextData.TryGetValue(name, out var value) @@ -388,16 +312,9 @@ public T GetOrSetGlobalState(string name, /// The existing state for the specified , /// or the newly created state using the function. /// - public T GetOrSetScopedState(string name, - Func createValue) + public T GetOrSetScopedState(string name, Func createValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); ArgumentNullException.ThrowIfNull(createValue); if (context.ScopedContextData.TryGetValue(name, out var value) @@ -424,16 +341,9 @@ public T GetOrSetScopedState(string name, /// The existing state for the specified , /// or the newly created state using the function. /// - public T GetOrSetLocalState(string name, - Func createValue) + public T GetOrSetLocalState(string name, Func createValue) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); ArgumentNullException.ThrowIfNull(createValue); if (context.LocalContextData.TryGetValue(name, out var value) @@ -453,13 +363,7 @@ public T GetOrSetLocalState(string name, /// The name of the state. public void RemoveScopedState(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); context.ScopedContextData = context.ScopedContextData.Remove(name); } @@ -469,13 +373,7 @@ public void RemoveScopedState(string name) /// The name of the state. public void RemoveLocalState(string name) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrEmpty(name)) - { - throw String_NullOrEmpty(nameof(name)); - } - + ArgumentException.ThrowIfNullOrEmpty(name); context.LocalContextData = context.LocalContextData.Remove(name); } @@ -486,8 +384,6 @@ public void RemoveLocalState(string name) /// The event message. public T GetEventMessage() { - ArgumentNullException.ThrowIfNull(context); - if (context.ScopedContextData.TryGetValue( WellKnownContextData.EventMessage, out var value) && value is { }) @@ -521,22 +417,12 @@ public T GetEventMessage() /// /// true if the field is selected; otherwise, false. /// - /// - /// is null. - /// /// /// is null or whitespace. /// public bool IsSelected(string fieldName) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrWhiteSpace(fieldName)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName)); - } + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName); var namedType = context.Selection.Type.NamedType(); @@ -589,31 +475,17 @@ public bool IsSelected(string fieldName) /// /// The name of the second field that shall be checked. /// - /// - /// - /// is null. - /// + /// + /// true if one of the fields is selected; otherwise, false. + /// /// /// is null or whitespace or /// is null or whitespace. /// public bool IsSelected(string fieldName1, string fieldName2) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrWhiteSpace(fieldName1)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName1)); - } - - if (string.IsNullOrWhiteSpace(fieldName2)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName2)); - } + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName1); + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName2); var namedType = context.Selection.Type.NamedType(); @@ -677,10 +549,9 @@ public bool IsSelected(string fieldName1, string fieldName2) /// /// The name of the third field that shall be checked. /// - /// - /// - /// is null. - /// + /// + /// true if one of the fields is selected; otherwise, false. + /// /// /// is null or whitespace or /// is null or whitespace or @@ -688,28 +559,9 @@ public bool IsSelected(string fieldName1, string fieldName2) /// public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) { - ArgumentNullException.ThrowIfNull(context); - - if (string.IsNullOrWhiteSpace(fieldName1)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName1)); - } - - if (string.IsNullOrWhiteSpace(fieldName2)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName2)); - } - - if (string.IsNullOrWhiteSpace(fieldName3)) - { - throw new ArgumentException( - ResolverContextExtensions_IsSelected_FieldNameEmpty, - nameof(fieldName3)); - } + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName1); + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName2); + ArgumentException.ThrowIfNullOrWhiteSpace(fieldName3); var namedType = context.Selection.Type.NamedType(); @@ -771,14 +623,14 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) /// /// The names of the fields that shall be checked. /// - /// + /// + /// true if one of the fields is selected; otherwise, false. + /// /// - /// is null or /// is null. /// public bool IsSelected(ISet fieldNames) { - ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(fieldNames); var namedType = context.Selection.Type.NamedType(); diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/ConnectionFlagsHelper.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/ConnectionFlagsHelper.cs index ce5b2eecdd7..9bce97065b6 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/ConnectionFlagsHelper.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/ConnectionFlagsHelper.cs @@ -12,71 +12,66 @@ namespace HotChocolate.Types.Pagination; /// public static class ConnectionFlagsHelper { - private const string KeyFormat = "HotChocolate.Types.Pagination.ConnectionFlags_{0}"; private static readonly ConcurrentDictionary s_parsedSelectionSets = new(); /// /// Gets the connection flags from the current resolver context. /// public static ConnectionFlags GetConnectionFlags(IResolverContext context) + => context.Selection.Features.GetOrSetSafe(CreateConnectionFlags, context); + + private static ConnectionFlags CreateConnectionFlags(IResolverContext context) { - return context.Operation.GetOrAddState( - string.Format(KeyFormat, context.Selection.Id), - static (_, ctx) => - { - if (ctx.Selection.Field is ObjectField field - && !field.Flags.HasFlag(CoreFieldFlags.Connection)) - { - return ConnectionFlags.None; - } + if (context.Selection.Field.Flags.HasFlag(CoreFieldFlags.Connection)) + { + return ConnectionFlags.None; + } - var options = PagingHelper.GetPagingOptions(ctx.Schema, ctx.Selection.Field); + var connectionFlags = ConnectionFlags.None; - var connectionFlags = ConnectionFlags.None; + if (context.IsSelected("edges")) + { + connectionFlags |= ConnectionFlags.Edges; + } - if (ctx.IsSelected("edges")) - { - connectionFlags |= ConnectionFlags.Edges; - } + if (context.IsSelected("nodes")) + { + connectionFlags |= ConnectionFlags.Nodes; + } - if (ctx.IsSelected("nodes")) - { - connectionFlags |= ConnectionFlags.Nodes; - } + if (context.IsSelected("totalCount")) + { + connectionFlags |= ConnectionFlags.TotalCount; + } + + var options = PagingHelper.GetPagingOptions(context.Schema, context.Selection.Field); + + if (options.PageInfoFields.Count > 0 + || ((options.EnableRelativeCursors ?? PagingDefaults.EnableRelativeCursors) + && options.RelativeCursorFields.Count > 0)) + { + var startSelections = context.Select(); + var selectionContext = new IsSelectedContext(context.Schema, startSelections); - if (ctx.IsSelected("totalCount")) + if (options.PageInfoFields.Count > 0) + { + if (ArePatternsMatched(startSelections, selectionContext, options.PageInfoFields)) { - connectionFlags |= ConnectionFlags.TotalCount; + connectionFlags |= ConnectionFlags.PageInfo; } + } - if (options.PageInfoFields.Count > 0 - || ((options.EnableRelativeCursors ?? PagingDefaults.EnableRelativeCursors) - && options.RelativeCursorFields.Count > 0)) + if ((options.EnableRelativeCursors ?? PagingDefaults.EnableRelativeCursors) + && options.RelativeCursorFields.Count > 0) + { + if (ArePatternsMatched(startSelections, selectionContext, options.RelativeCursorFields)) { - var startSelections = ctx.Select(); - var selectionContext = new IsSelectedContext(ctx.Schema, startSelections); - - if (options.PageInfoFields.Count > 0) - { - if (ArePatternsMatched(startSelections, selectionContext, options.PageInfoFields)) - { - connectionFlags |= ConnectionFlags.PageInfo; - } - } - - if ((options.EnableRelativeCursors ?? PagingDefaults.EnableRelativeCursors) - && options.RelativeCursorFields.Count > 0) - { - if (ArePatternsMatched(startSelections, selectionContext, options.RelativeCursorFields)) - { - connectionFlags |= ConnectionFlags.RelativeCursor; - } - } + connectionFlags |= ConnectionFlags.RelativeCursor; } + } + } - return connectionFlags; - }, - context); + return connectionFlags; } private static bool ArePatternsMatched( diff --git a/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs index 97b079fc8fe..60b2676dcda 100644 --- a/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs +++ b/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs @@ -12,11 +12,6 @@ namespace HotChocolate.Utilities; internal static class ThrowHelper { - public static ArgumentException String_NullOrEmpty(string parameterName) => - new ArgumentException( - $"'{parameterName}' cannot be null or empty", - parameterName); - public static GraphQLException EventMessage_InvalidCast( Type expectedType, Type messageType) => @@ -24,8 +19,8 @@ public static GraphQLException EventMessage_InvalidCast( ErrorBuilder.New() .SetMessage( ThrowHelper_EventMessage_InvalidCast, - messageType.FullName!, - expectedType.FullName!) + messageType.FullName, + expectedType.FullName) .Build()); public static GraphQLException EventMessage_NotFound() => diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs index ca9bd53c437..1274cf5d887 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs @@ -1441,7 +1441,7 @@ public void OptimizeSelectionSet(SelectionSetOptimizerContext context) var bazPipeline = context.CompileResolverPipeline(baz, bazSelection); var compiledSelection = new Selection( - context.GetNextSelectionId(), + context.NewSelectionId(), context.Type, baz, baz.Type, diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs index c6ad38ed3ff..d75c48d9c51 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs @@ -68,7 +68,7 @@ private Selection CreateCombinedSelection( context.CompileResolverPipeline(nodesField, combinedField); return new Selection.Sealed( - context.GetNextSelectionId(), + context.NewSelectionId(), declaringType, nodesField, nodesField.Type, diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs index feab6327d5f..c89ef8cbfe2 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs @@ -50,7 +50,7 @@ public Selection RewriteSelection( var nodesPipeline = context.CompileResolverPipeline(nodesField, nodesFieldNode); var compiledSelection = new Selection.Sealed( - context.GetNextSelectionId(), + context.NewSelectionId(), context.Type, nodesField, nodesField.Type, From 45ed184b524cf8b5a5e5821b69da9378860ce74b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 11:14:34 +0100 Subject: [PATCH 20/30] align extension argument validation --- ...uestExecutorServiceCollectionExtensions.cs | 6 --- .../ObjectPoolProviderExtensions.cs | 5 +-- .../SchemaBuilderExtensions.Resolvers.cs | 44 ++----------------- .../src/Types/Extensions/SchemaExtensions.cs | 16 ------- .../DataLoaderResolverContextExtensions.cs | 2 - .../src/Types/Internal/TypeInfoExtensions.cs | 1 - .../Directives/InternalDirectiveExtensions.cs | 10 +---- .../Directives/LookupDirectiveExtensions.cs | 5 +-- .../Directives/RequireDescriptorExtensions.cs | 5 --- .../ShareableDirectiveExtensions.cs | 13 +----- .../Configurations/BindableListExtensions.cs | 6 +-- .../ArgumentDescriptorExtensions.cs | 3 -- .../DirectiveTypeDescriptorExtensions.cs | 5 --- .../InputObjectTypeDescriptorExtensions.cs | 29 ++---------- .../InterfaceTypeDescriptorExtensions.cs | 5 --- .../ObjectFieldDescriptorExtensions.cs | 20 --------- .../ObjectTypeDescriptorExtensions.cs | 2 - .../Types/Types/Extensions/TypeExtensions.cs | 13 +----- .../Extensions/NodeTypeFeatureExtensions.cs | 3 -- .../Extensions/RelayIdFieldExtensions.cs | 21 --------- .../Core/src/Types/Types/TypeNamePrinter.cs | 2 - .../src/Types/Utilities/ReflectionUtils.cs | 24 ++-------- 22 files changed, 17 insertions(+), 223 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs index d78ec74851f..1c77312e894 100644 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs @@ -25,8 +25,6 @@ public static class RequestExecutorServiceCollectionExtensions /// The . public static IServiceCollection AddGraphQLCore(this IServiceCollection services) { - ArgumentNullException.ThrowIfNull(services); - services.AddOptions(); services.TryAddSingleton(); @@ -109,8 +107,6 @@ public static IRequestExecutorBuilder AddGraphQL( this IServiceCollection services, string? schemaName = null) { - ArgumentNullException.ThrowIfNull(services); - services.AddGraphQLCore(); schemaName ??= ISchemaDefinition.DefaultName; return CreateBuilder(services, schemaName); @@ -133,8 +129,6 @@ public static IRequestExecutorBuilder AddGraphQL( this IRequestExecutorBuilder builder, string? schemaName = null) { - ArgumentNullException.ThrowIfNull(builder); - schemaName ??= ISchemaDefinition.DefaultName; return CreateBuilder(builder.Services, schemaName); } diff --git a/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs index 5a4feb791b5..771e393a38c 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ObjectPoolProviderExtensions.cs @@ -8,8 +8,5 @@ internal static class ObjectPoolProviderExtensions { public static ObjectPool>> CreateFieldMapPool( this ObjectPoolProvider provider) - { - ArgumentNullException.ThrowIfNull(provider); - return provider.Create(new FieldMapPooledObjectPolicy()); - } + => provider.Create(new FieldMapPooledObjectPolicy()); } diff --git a/src/HotChocolate/Core/src/Types/Extensions/SchemaBuilderExtensions.Resolvers.cs b/src/HotChocolate/Core/src/Types/Extensions/SchemaBuilderExtensions.Resolvers.cs index c1de2f430d9..15961ae8f91 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/SchemaBuilderExtensions.Resolvers.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/SchemaBuilderExtensions.Resolvers.cs @@ -33,9 +33,7 @@ public static ISchemaBuilder AddResolver( FieldResolverDelegate resolver, Type? resultType = null) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverConfigInternal(builder, fieldCoordinate, resolver, resultType); } @@ -63,9 +61,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal( builder, typeName, @@ -97,11 +93,8 @@ public static ISchemaBuilder AddResolver( string fieldName, Func> resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - - return AddResolverInternal(builder, typeName, fieldName, - ctx => resolver(ctx)); + return AddResolverInternal(builder, typeName, fieldName, ctx => resolver(ctx)); } /// @@ -128,9 +121,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, ctx => new ValueTask(resolver(ctx))); } @@ -159,9 +150,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func> resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, async ctx => await resolver(ctx).ConfigureAwait(false)); } @@ -190,9 +179,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, _ => new ValueTask(resolver())); } @@ -221,9 +208,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func> resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, _ => resolver()); } @@ -251,9 +236,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, _ => new ValueTask(resolver())); } @@ -282,9 +265,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func> resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal( builder, typeName, @@ -318,9 +299,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, ctx => new ValueTask(resolver(ctx, ctx.RequestAborted))); } @@ -349,9 +328,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal(builder, typeName, fieldName, ctx => new ValueTask(resolver(ctx, ctx.RequestAborted))); } @@ -380,9 +357,7 @@ public static ISchemaBuilder AddResolver( string fieldName, Func> resolver) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolver); - return AddResolverInternal( builder, typeName, @@ -415,12 +390,8 @@ public static ISchemaBuilder AddResolver( string typeName, string fieldName, object? constantResult) - { - ArgumentNullException.ThrowIfNull(builder); - - return AddResolverInternal(builder, typeName, fieldName, + => AddResolverInternal(builder, typeName, fieldName, _ => new ValueTask(constantResult)); - } /// /// Adds a resolver delegate that returns a constant result. @@ -445,12 +416,8 @@ public static ISchemaBuilder AddResolver( string typeName, string fieldName, TResult constantResult) - { - ArgumentNullException.ThrowIfNull(builder); - - return AddResolverInternal(builder, typeName, fieldName, + => AddResolverInternal(builder, typeName, fieldName, _ => new ValueTask(constantResult)); - } /// /// Adds a resolver delegate for a specific field. @@ -473,7 +440,6 @@ public static ISchemaBuilder AddResolver( Type resolverType, string? typeName = null) { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(resolverType); if (resolverType is { IsClass: true, IsAbstract: false, IsPublic: true } or @@ -519,8 +485,6 @@ public static ISchemaBuilder AddResolver( public static ISchemaBuilder AddRootResolver(this ISchemaBuilder builder, Type resolverType) { - ArgumentNullException.ThrowIfNull(builder); - if (resolverType is { IsClass: true } or { IsInterface: true }) { foreach (var property in resolverType.GetProperties()) @@ -542,9 +506,7 @@ public static ISchemaBuilder AddRootResolver(this ISchemaBuilder builder) public static ISchemaBuilder AddRootResolver(this ISchemaBuilder builder, T root) where T : class { - ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(root); - InitializeResolverTypeInterceptor(builder); var feature = builder.Features.GetRequired(); feature.RootInstance = root; diff --git a/src/HotChocolate/Core/src/Types/Extensions/SchemaExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/SchemaExtensions.cs index fa8a39f604a..e66dbf29e1c 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/SchemaExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/SchemaExtensions.cs @@ -44,9 +44,6 @@ public static class SchemaExtensions /// true if a type system member was found with the given /// ; otherwise, false. /// - /// - /// is null. - /// public static bool TryGetMember( this Schema schema, string coordinateString, @@ -77,16 +74,11 @@ public static bool TryGetMember( /// true if a type system member was found with the given /// ; otherwise, false. /// - /// - /// is null. - /// public static bool TryGetMember( this Schema schema, SchemaCoordinate coordinate, [NotNullWhen(true)] out ITypeSystemMember? member) { - ArgumentNullException.ThrowIfNull(schema); - if (coordinate.OfDirective) { if (schema.DirectiveTypes.TryGetDirective(coordinate.Name, out var directive)) @@ -178,9 +170,6 @@ public static bool TryGetMember( /// /// Returns the resolved type system member. /// - /// - /// is null. - /// /// /// The has invalid syntax. /// @@ -205,9 +194,6 @@ public static ITypeSystemMember GetMember( /// /// Returns the resolved type system member. /// - /// - /// is null. - /// /// /// Unable to resolve a type system member with the /// specified . @@ -216,8 +202,6 @@ public static ITypeSystemMember GetMember( this Schema schema, SchemaCoordinate coordinate) { - ArgumentNullException.ThrowIfNull(schema); - if (coordinate.OfDirective) { if (schema.DirectiveTypes.TryGetDirective(coordinate.Name, out var directive)) diff --git a/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs index 6acc7d516d7..b2487a09c38 100644 --- a/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs @@ -12,8 +12,6 @@ public static class DataLoaderResolverContextExtensions public static T DataLoader(this IResolverContext context) where T : IDataLoader { - ArgumentNullException.ThrowIfNull(context); - var services = context.RequestServices; var reg = services.GetRequiredService(); return reg.GetDataLoader(); diff --git a/src/HotChocolate/Core/src/Types/Internal/TypeInfoExtensions.cs b/src/HotChocolate/Core/src/Types/Internal/TypeInfoExtensions.cs index 4d74b04267f..cd40ef9723d 100644 --- a/src/HotChocolate/Core/src/Types/Internal/TypeInfoExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Internal/TypeInfoExtensions.cs @@ -33,7 +33,6 @@ internal static IType CreateType(this ITypeInfo typeInfo, ITypeDefinition typeDe /// public static TypeReference CreateTypeReference(this ITypeInfo typeInfo, NamedTypeNode namedType) { - ArgumentNullException.ThrowIfNull(typeInfo); ArgumentNullException.ThrowIfNull(namedType); ITypeNode type = namedType; diff --git a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/InternalDirectiveExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/InternalDirectiveExtensions.cs index 5568f9d79e6..a98a9ba00f6 100644 --- a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/InternalDirectiveExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/InternalDirectiveExtensions.cs @@ -26,10 +26,7 @@ public static class InternalDirectiveExtensions /// /// public static IObjectTypeDescriptor Internal(this IObjectTypeDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - return descriptor.Directive(Composite.Internal.Instance); - } + => descriptor.Directive(Composite.Internal.Instance); /// /// @@ -52,8 +49,5 @@ public static IObjectTypeDescriptor Internal(this IObjectTypeDescriptor descript /// /// public static IObjectFieldDescriptor Internal(this IObjectFieldDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - return descriptor.Directive(Composite.Internal.Instance); - } + => descriptor.Directive(Composite.Internal.Instance); } diff --git a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/LookupDirectiveExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/LookupDirectiveExtensions.cs index d8990b5ce08..69d988acb54 100644 --- a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/LookupDirectiveExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/LookupDirectiveExtensions.cs @@ -23,8 +23,5 @@ public static class LookupDirectiveExtensions /// /// public static IObjectFieldDescriptor Lookup(this IObjectFieldDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - return descriptor.Directive(Composite.Lookup.Instance); - } + => descriptor.Directive(Composite.Lookup.Instance); } diff --git a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/RequireDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/RequireDescriptorExtensions.cs index 4dd41df7462..1b9865e740e 100644 --- a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/RequireDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/RequireDescriptorExtensions.cs @@ -23,16 +23,11 @@ public static class RequireDescriptorExtensions /// The argument descriptor. /// The field selection map. /// The argument descriptor with the @require directive applied. - /// - /// The is null. - /// /// /// The syntax used in the parameter is invalid. /// public static IArgumentDescriptor Require(this IArgumentDescriptor descriptor, string field) { - ArgumentNullException.ThrowIfNull(descriptor); - IValueSelectionNode valueSelection; try diff --git a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/ShareableDirectiveExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/ShareableDirectiveExtensions.cs index 9fa833ee517..93d5eb8846a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Composite/Directives/ShareableDirectiveExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Composite/Directives/ShareableDirectiveExtensions.cs @@ -46,15 +46,10 @@ public static class ShareableDirectiveExtensions /// fields of the type sharable. /// /// The object type descriptor with the directive applied. - /// - /// The is null. - /// public static IObjectTypeDescriptor Shareable( this IObjectTypeDescriptor descriptor, bool scoped = false) { - ArgumentNullException.ThrowIfNull(descriptor); - if (scoped) { // The @sharable directive on a type is meant as a helper to apply it to all fields within its scope. @@ -117,12 +112,6 @@ public static IObjectTypeDescriptor Shareable( /// /// The object field descriptor. /// The object field descriptor with the directive applied. - /// - /// The is null. - /// public static IObjectFieldDescriptor Shareable(this IObjectFieldDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - return descriptor.Directive(Composite.Shareable.Instance); - } + => descriptor.Directive(Composite.Shareable.Instance); } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Configurations/BindableListExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Configurations/BindableListExtensions.cs index e23c9d3d122..87684e4dc06 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Configurations/BindableListExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Configurations/BindableListExtensions.cs @@ -3,9 +3,5 @@ namespace HotChocolate.Types.Descriptors.Configurations; public static class BindableListExtensions { public static bool IsImplicitBinding(this IBindableList list) - { - ArgumentNullException.ThrowIfNull(list); - - return list.BindingBehavior == BindingBehavior.Implicit; - } + => list.BindingBehavior == BindingBehavior.Implicit; } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/ArgumentDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/ArgumentDescriptorExtensions.cs index 8c355e36286..8fa5fc4408b 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/ArgumentDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/ArgumentDescriptorExtensions.cs @@ -23,7 +23,6 @@ public static class ArgumentDescriptorExtensions /// Returns the input field descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -33,9 +32,7 @@ public static IArgumentDescriptor DefaultValueSyntax( this IArgumentDescriptor descriptor, [StringSyntax("graphql")] string syntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(syntax); - var value = Utf8GraphQLParser.Syntax.ParseValueLiteral(syntax); return descriptor.DefaultValue(value); } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveTypeDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveTypeDescriptorExtensions.cs index a657798315e..50c86c59cfa 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveTypeDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveTypeDescriptorExtensions.cs @@ -9,9 +9,7 @@ public static IDirectiveTypeDescriptor Ignore( this IDirectiveTypeDescriptor descriptor, Expression> property) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(property); - descriptor.Argument(property).Ignore(); return descriptor; } @@ -29,7 +27,6 @@ public static IDirectiveTypeDescriptor Ignore( /// Returns the directive argument descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -39,9 +36,7 @@ public static IDirectiveArgumentDescriptor Type( this IDirectiveArgumentDescriptor descriptor, string typeSyntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(typeSyntax); - return descriptor.Type(Utf8GraphQLParser.Syntax.ParseTypeReference(typeSyntax)); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/InputObjectTypeDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/InputObjectTypeDescriptorExtensions.cs index cbe781a1857..50bdd7826db 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/InputObjectTypeDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/InputObjectTypeDescriptorExtensions.cs @@ -25,16 +25,13 @@ public static class InputObjectTypeDescriptorExtensions /// Returns the descriptor for configuration chaining. /// /// - /// The is null or - /// the is null. + /// The is null. /// public static IInputObjectTypeDescriptor Ignore( this IInputObjectTypeDescriptor descriptor, Expression> property) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(property); - descriptor.Field(property).Ignore(); return descriptor; } @@ -49,15 +46,8 @@ public static IInputObjectTypeDescriptor Ignore( /// /// Returns the descriptor for configuration chaining. /// - /// - /// The is null. - /// public static IInputObjectTypeDescriptor OneOf(this IInputObjectTypeDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - - return descriptor.Directive(DirectiveNames.OneOf.Name); - } + => descriptor.Directive(DirectiveNames.OneOf.Name); /// /// Defines an input object type as a OneOf input object type @@ -72,16 +62,9 @@ public static IInputObjectTypeDescriptor OneOf(this IInputObjectTypeDescriptor d /// /// Returns the descriptor for configuration chaining. /// - /// - /// The is null. - /// public static IInputObjectTypeDescriptor OneOf( this IInputObjectTypeDescriptor descriptor) - { - ArgumentNullException.ThrowIfNull(descriptor); - - return descriptor.Directive(DirectiveNames.OneOf.Name); - } + => descriptor.Directive(DirectiveNames.OneOf.Name); /// /// Specifies the type of an input field with GraphQL SDL type syntax. @@ -96,7 +79,6 @@ public static IInputObjectTypeDescriptor OneOf( /// Returns the input field descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -106,9 +88,7 @@ public static IInputFieldDescriptor Type( this IInputFieldDescriptor descriptor, string typeSyntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(typeSyntax); - return descriptor.Type(Utf8GraphQLParser.Syntax.ParseTypeReference(typeSyntax)); } @@ -125,7 +105,6 @@ public static IInputFieldDescriptor Type( /// Returns the input field descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -135,9 +114,7 @@ public static IInputFieldDescriptor DefaultValueSyntax( this IInputFieldDescriptor descriptor, [StringSyntax("graphql")] string syntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(syntax); - var value = Utf8GraphQLParser.Syntax.ParseValueLiteral(syntax); return descriptor.DefaultValue(value); } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/InterfaceTypeDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/InterfaceTypeDescriptorExtensions.cs index 58223d2e9f4..ce65ff90a3b 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/InterfaceTypeDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/InterfaceTypeDescriptorExtensions.cs @@ -9,9 +9,7 @@ public static IInterfaceTypeDescriptor Ignore( this IInterfaceTypeDescriptor descriptor, Expression> propertyOrMethod) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(propertyOrMethod); - descriptor.Field(propertyOrMethod).Ignore(); return descriptor; } @@ -29,7 +27,6 @@ public static IInterfaceTypeDescriptor Ignore( /// Returns the interface field descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -39,9 +36,7 @@ public static IInterfaceFieldDescriptor Type( this IInterfaceFieldDescriptor descriptor, string typeSyntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(typeSyntax); - return descriptor.Type(Utf8GraphQLParser.Syntax.ParseTypeReference(typeSyntax)); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectFieldDescriptorExtensions.cs index 8a2e3c96d49..708ff4b135d 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectFieldDescriptorExtensions.cs @@ -14,8 +14,6 @@ public static class ObjectFieldDescriptorExtensions /// public static IObjectFieldDescriptor Serial(this IObjectFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - descriptor.Extend().OnBeforeCreate(c => c.IsParallelExecutable = false); return descriptor; } @@ -26,8 +24,6 @@ public static IObjectFieldDescriptor Serial(this IObjectFieldDescriptor descript /// public static IObjectFieldDescriptor Parallel(this IObjectFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - descriptor.Extend().OnBeforeCreate(c => c.IsParallelExecutable = true); return descriptor; } @@ -45,7 +41,6 @@ public static IObjectFieldDescriptor Parallel(this IObjectFieldDescriptor descri /// Returns the object field descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -55,9 +50,7 @@ public static IObjectFieldDescriptor Type( this IObjectFieldDescriptor descriptor, string typeSyntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(typeSyntax); - return descriptor.Type(Utf8GraphQLParser.Syntax.ParseTypeReference(typeSyntax)); } @@ -74,7 +67,6 @@ public static IObjectFieldDescriptor Type( /// Returns the argument descriptor for configuration chaining. /// /// - /// is null. /// is null. /// /// @@ -84,9 +76,7 @@ public static IArgumentDescriptor Type( this IArgumentDescriptor descriptor, string typeSyntax) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(typeSyntax); - return descriptor.Type(Utf8GraphQLParser.Syntax.ParseTypeReference(typeSyntax)); } @@ -99,14 +89,9 @@ public static IArgumentDescriptor Type( /// /// Returns the object field descriptor for configuration chaining. /// - /// - /// is null. - /// public static IObjectFieldDescriptor UseRequestScope( this IObjectFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - descriptor.Extend().Configuration.DependencyInjectionScope = DependencyInjectionScope.Request; return descriptor; } @@ -120,14 +105,9 @@ public static IObjectFieldDescriptor UseRequestScope( /// /// Returns the object field descriptor for configuration chaining. /// - /// - /// is null. - /// public static IObjectFieldDescriptor UseResolverScope( this IObjectFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - descriptor.Extend().Configuration.DependencyInjectionScope = DependencyInjectionScope.Resolver; return descriptor; } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectTypeDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectTypeDescriptorExtensions.cs index 4aad269bb55..e80c9ff6d31 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectTypeDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectTypeDescriptorExtensions.cs @@ -12,9 +12,7 @@ public static IObjectTypeDescriptor Ignore( this IObjectTypeDescriptor descriptor, Expression> propertyOrMethod) { - ArgumentNullException.ThrowIfNull(descriptor); ArgumentNullException.ThrowIfNull(propertyOrMethod); - descriptor.Field(propertyOrMethod).Ignore(); return descriptor; } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs index eac5e96bdf8..4751b4964cf 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs @@ -11,8 +11,6 @@ public static class TypeExtensions { internal static IInputType EnsureInputType(this IType type) { - ArgumentNullException.ThrowIfNull(type); - if (type.NamedType() is not IInputType) { throw InputTypeExpected(type); @@ -23,8 +21,6 @@ internal static IInputType EnsureInputType(this IType type) internal static IOutputType EnsureOutputType(this IType type) { - ArgumentNullException.ThrowIfNull(type); - if (type.NamedType() is not IOutputType) { throw OutputTypeExpected(type); @@ -34,16 +30,10 @@ internal static IOutputType EnsureOutputType(this IType type) } public static string TypeName(this IType type) - { - ArgumentNullException.ThrowIfNull(type); - - return type.NamedType().Name; - } + => type.NamedType().Name; public static Type ToRuntimeType(this IType type) { - ArgumentNullException.ThrowIfNull(type); - if (type.IsListType()) { var elementType = ToRuntimeType(type.ElementType()); @@ -94,7 +84,6 @@ public static ITypeNode RenameName(this ITypeNode typeNode, string name) public static bool IsInstanceOfType(this IInputType type, IValueNode literal) { - ArgumentNullException.ThrowIfNull(type); ArgumentNullException.ThrowIfNull(literal); while (true) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/NodeTypeFeatureExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/NodeTypeFeatureExtensions.cs index 1fa86b4ecea..e7486276a7a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/NodeTypeFeatureExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/NodeTypeFeatureExtensions.cs @@ -15,8 +15,6 @@ internal static class NodeTypeFeatureExtensions /// True if the node resolver was found, false otherwise. public static bool TryGetNodeResolver(this ObjectType type, [NotNullWhen(true)] out NodeResolverInfo? nodeResolver) { - ArgumentNullException.ThrowIfNull(type); - if (type.Features.TryGet(out var feature) && feature.NodeResolver is not null) { nodeResolver = feature.NodeResolver; @@ -39,7 +37,6 @@ public static void SetNodeResolver( this ObjectType type, NodeResolverInfo nodeResolver) { - ArgumentNullException.ThrowIfNull(type); ArgumentNullException.ThrowIfNull(nodeResolver); var feature = type.Features.Get(); diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs index c7319f40361..fb39ff1f0d8 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs @@ -67,10 +67,7 @@ public static IInputFieldDescriptor ID( this IInputFieldDescriptor descriptor, string? typeName = null) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor, typeName); - return descriptor; } @@ -80,10 +77,7 @@ public static IInputFieldDescriptor ID( /// public static IInputFieldDescriptor ID(this IInputFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor); - return descriptor; } @@ -96,10 +90,7 @@ public static IArgumentDescriptor ID( this IArgumentDescriptor descriptor, string? typeName = null) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor, typeName); - return descriptor; } @@ -109,10 +100,7 @@ public static IArgumentDescriptor ID( /// public static IArgumentDescriptor ID(this IArgumentDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor); - return descriptor; } @@ -125,10 +113,7 @@ public static IObjectFieldDescriptor ID( this IObjectFieldDescriptor descriptor, string? typeName = null) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor, typeName); - return descriptor; } @@ -138,10 +123,7 @@ public static IObjectFieldDescriptor ID( /// public static IObjectFieldDescriptor ID(this IObjectFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor); - return descriptor; } @@ -149,10 +131,7 @@ public static IObjectFieldDescriptor ID(this IObjectFieldDescriptor descripto /// the descriptor public static IInterfaceFieldDescriptor ID(this IInterfaceFieldDescriptor descriptor) { - ArgumentNullException.ThrowIfNull(descriptor); - RelayIdFieldHelpers.ApplyIdToField(descriptor); - return descriptor; } } diff --git a/src/HotChocolate/Core/src/Types/Types/TypeNamePrinter.cs b/src/HotChocolate/Core/src/Types/Types/TypeNamePrinter.cs index 39ff7debc72..aff5cae7b6c 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeNamePrinter.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeNamePrinter.cs @@ -8,8 +8,6 @@ public static class TypeNamePrinter private static string Print(IType type, int count) { - ArgumentNullException.ThrowIfNull(type); - if (count > MaxTypeDepth) { throw new InvalidOperationException( diff --git a/src/HotChocolate/Core/src/Types/Utilities/ReflectionUtils.cs b/src/HotChocolate/Core/src/Types/Utilities/ReflectionUtils.cs index 73e4ec4aab7..7cb3f36bd38 100644 --- a/src/HotChocolate/Core/src/Types/Utilities/ReflectionUtils.cs +++ b/src/HotChocolate/Core/src/Types/Utilities/ReflectionUtils.cs @@ -15,11 +15,7 @@ public static class ReflectionUtils public static MemberInfo TryExtractMember( this Expression> memberExpression, bool allowStatic = false) - { - ArgumentNullException.ThrowIfNull(memberExpression); - - return TryExtractMemberInternal(UnwrapFunc(memberExpression), allowStatic); - } + => TryExtractMemberInternal(UnwrapFunc(memberExpression), allowStatic); internal static MemberInfo TryExtractCallMember( this Expression expression) @@ -56,20 +52,12 @@ public static MethodInfo ExtractMethod( public static MemberInfo ExtractMember( this Expression> memberExpression, bool allowStatic = false) - { - ArgumentNullException.ThrowIfNull(memberExpression); - - return ExtractMemberInternal(UnwrapAction(memberExpression), allowStatic); - } + => ExtractMemberInternal(UnwrapAction(memberExpression), allowStatic); public static MemberInfo ExtractMember( this Expression> memberExpression, bool allowStatic = false) - { - ArgumentNullException.ThrowIfNull(memberExpression); - - return ExtractMemberInternal(UnwrapFunc(memberExpression), allowStatic); - } + => ExtractMemberInternal(UnwrapFunc(memberExpression), allowStatic); private static MemberInfo ExtractMemberInternal( Expression expression, @@ -184,13 +172,9 @@ private static bool IsStaticMethod(MethodInfo method) => method.IsStatic; public static string GetTypeName(this Type type) - { - ArgumentNullException.ThrowIfNull(type); - - return type.IsGenericType + => type.IsGenericType ? CreateGenericTypeName(type) : CreateTypeName(type, type.Name); - } private static string CreateGenericTypeName(Type type) { From be3c2a5bdc36d1bafd587f9f8959cd4e4ea8af95 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 21:57:05 +0100 Subject: [PATCH 21/30] wip --- .../CacheControlConstraintsOptimizer.cs | 16 +-- .../src/Caching/ImmutableCacheConstraints.cs | 12 +++ .../src/Caching/QueryCacheMiddleware.cs | 11 +-- .../Processing/OperationFeatureCollection.cs | 13 ++- .../Types/Execution/Processing/Selection.cs | 99 ++++++++++++++++++- .../Processing/SelectionFeatureCollection.cs | 28 ++++++ .../SelectionSetOptimizerContext.cs | 8 ++ .../Core/src/Types/HotChocolate.Types.csproj | 3 +- .../Extensions/FilterFeatureExtensions.cs | 4 +- .../HotChocolateDataQueryableExtensions.cs | 16 ++- .../Data/src/Data/HotChocolate.Data.csproj | 1 + ...gingArgumentsParameterExpressionBuilder.cs | 2 +- .../Projections/Context/ISelectedField.cs | 4 +- .../Data/Projections/Context/SelectedField.cs | 29 +++--- .../ProjectionProviderDescriptorExtensions.cs | 1 + .../Convention/IProjectionFieldInterceptor.cs | 2 +- .../IProjectionFieldInterceptor`1.cs | 4 +- .../ProjectionInterceptorCombinator.cs | 6 +- .../Convention/ProjectionProvider.cs | 5 +- .../Convention/ProjectionSelection.cs | 23 ----- .../ProjectionSelectionExtensions.cs | 12 +++ .../Handlers/ProjectionFieldHandler.cs | 16 +-- .../Handlers/ProjectionFieldWrapper.cs | 14 +-- .../QueryableProjectionFieldHandler.cs | 6 +- .../QueryableProjectionHandlerBase.cs | 4 +- .../QueryableProjectionListHandler.cs | 8 +- .../QueryableProjectionScalarHandler.cs | 6 +- .../Interceptor/QueryableFilterInterceptor.cs | 11 +-- .../Interceptor/QueryableSortInterceptor.cs | 18 ++-- .../QueryableTakeHandlerInterceptor.cs | 6 +- .../QueryableFilterProjectionOptimizer.cs | 2 +- .../QueryablePagingProjectionOptimizer.cs | 51 +++++----- .../QueryableSortProjectionOptimizer.cs | 6 +- .../Expressions/QueryableProjectionContext.cs | 22 +++-- .../Expressions/QueryableProjectionVisitor.cs | 6 +- ...ojectionObjectFieldDescriptorExtensions.cs | 49 +++------ .../Extensions/SelectionFlagsExtensions.cs | 16 +-- .../Data/Projections/IProjectionSelection.cs | 8 -- .../Projections/ISelectionVisitorContext.cs | 13 ++- .../IsProjectedProjectionOptimizer.cs | 33 +++---- .../Optimizers/NodeSelectionSetOptimizer.cs | 4 +- .../src/Data/Projections/ProjectionFeature.cs | 5 - .../Data/Projections/ProjectionOptimizer.cs | 6 +- .../Data/Projections/ProjectionTypeFeature.cs | 6 ++ .../Projections/ProjectionTypeInterceptor.cs | 1 + .../src/Data/Projections/ProjectionVisitor.cs | 66 ++++++------- .../src/Data/Projections/SelectionVisitor.cs | 4 +- .../Projections/SelectionVisitorContext.cs | 10 +- .../Data/Projections/SelectionVisitor`1.cs | 76 +++++++------- .../Visitor/IProjectionFieldHandler.cs | 9 +- .../Visitor/IProjectionFieldHandler~1.cs | 16 ++- .../Projections/Visitor/ProjectionScope.cs | 7 +- .../Visitor/ProjectionVisitorContext.cs | 9 +- ...SortingContextResolverContextExtensions.cs | 2 +- .../Extensions/SortingFeatureExtensions.cs | 29 +++--- .../src/Diagnostics/ActivityEnricher.cs | 10 +- .../Fusion.Execution.Benchmarks.csproj | 1 + .../GraphQLQueryBenchmark.cs | 9 +- ...bProjectionProviderDescriptorExtensions.cs | 1 + .../HotChocolate.Utilities.Buffers.csproj | 1 + 60 files changed, 480 insertions(+), 386 deletions(-) delete mode 100644 src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelection.cs create mode 100644 src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelectionExtensions.cs delete mode 100644 src/HotChocolate/Data/src/Data/Projections/IProjectionSelection.cs create mode 100644 src/HotChocolate/Data/src/Data/Projections/ProjectionTypeFeature.cs diff --git a/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs b/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs index 2fa588c8518..a12a2c80b87 100644 --- a/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs +++ b/src/HotChocolate/Caching/src/Caching/CacheControlConstraintsOptimizer.cs @@ -38,20 +38,8 @@ public void OptimizeOperation(OperationOptimizerContext context) : null }; - context.ContextData.Add( - ExecutionContextData.CacheControlConstraints, - constraints); - - context.ContextData.Add( - ExecutionContextData.CacheControlHeaderValue, - headerValue); - } - - if (constraints.Vary is { Length: > 0 }) - { - context.ContextData.Add( - ExecutionContextData.VaryHeaderValue, - string.Join(", ", constraints.Vary)); + context.Operation.Features.SetSafe(constraints); + context.Operation.Features.SetSafe(headerValue); } } diff --git a/src/HotChocolate/Caching/src/Caching/ImmutableCacheConstraints.cs b/src/HotChocolate/Caching/src/Caching/ImmutableCacheConstraints.cs index 97472f0697e..91a1f454ef5 100644 --- a/src/HotChocolate/Caching/src/Caching/ImmutableCacheConstraints.cs +++ b/src/HotChocolate/Caching/src/Caching/ImmutableCacheConstraints.cs @@ -16,4 +16,16 @@ internal sealed class ImmutableCacheConstraints( public CacheControlScope Scope { get; } = scope; public ImmutableArray Vary { get; } = vary; + + public string VaryString + { + get + { + field ??= Vary.Length is 0 + ? string.Empty + : string.Join(", ", Vary); + + return field; + } + } } diff --git a/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs b/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs index 190acf51ae9..889d3476e28 100644 --- a/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs +++ b/src/HotChocolate/Caching/src/Caching/QueryCacheMiddleware.cs @@ -30,7 +30,8 @@ public async ValueTask InvokeAsync(RequestContext context) } if (!context.TryGetOperation(out var operation) - || !operation.Features.TryGet(out var cacheControlHeaderValue)) + || !operation.Features.TryGet(out var headerValue) + || !operation.Features.TryGet(out var constraints)) { return; } @@ -45,13 +46,11 @@ operationResult.ContextData is not null ? new ExtensionData(operationResult.ContextData) : []; - contextData.Add(ExecutionContextData.CacheControlHeaderValue, cacheControlHeaderValue); + contextData.Add(ExecutionContextData.CacheControlHeaderValue, headerValue); - if (operation.ContextData.TryGetValue(ExecutionContextData.VaryHeaderValue, out var varyValue) - && varyValue is string varyHeaderValue - && !string.IsNullOrEmpty(varyHeaderValue)) + if (constraints.Vary.Length > 0) { - contextData.Add(ExecutionContextData.VaryHeaderValue, varyHeaderValue); + contextData.Add(ExecutionContextData.VaryHeaderValue, constraints.Vary); } context.Result = operationResult.WithContextData(contextData); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs index 69972333267..dfbf1426fb2 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs @@ -123,11 +123,14 @@ public bool TryGet([NotNullWhen(true)] out TFeature? feature) return false; } - /// - public void Set(TFeature? instance) - { - this[typeof(TFeature)] = instance; - } + /// + /// Sets a feature instance for this selection. + /// + /// The type of the feature. + /// The feature instance to set, or null to remove. + /// This method is thread-safe. + public void SetSafe(TFeature? instance) + => this[typeof(TFeature)] = instance; /// public IEnumerator> GetEnumerator() diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index cef3f42b1cd..d47b00eb706 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -24,10 +24,10 @@ internal Selection( ObjectField field, FieldSelectionNode[] syntaxNodes, ulong[] includeFlags, - bool isInternal, - ArgumentMap? arguments, - FieldDelegate? resolverPipeline, - PureFieldDelegate? pureResolver) + bool isInternal = false, + ArgumentMap? arguments = null, + FieldDelegate? resolverPipeline = null, + PureFieldDelegate? pureResolver = null) { ArgumentNullException.ThrowIfNull(field); @@ -41,6 +41,7 @@ internal Selection( Id = id; ResponseName = responseName; Field = field; + Type = field.Type; Arguments = arguments ?? s_emptyArguments; ResolverPipeline = resolverPipeline; PureResolver = pureResolver; @@ -64,6 +65,34 @@ internal Selection( _utf8ResponseName = Utf8StringCache.GetUtf8String(responseName); } + private Selection( + int id, + string responseName, + byte[] utf8ResponseName, + ObjectField field, + IType type, + FieldSelectionNode[] syntaxNodes, + ulong[] includeFlags, + Flags flags, + ArgumentMap? arguments, + SelectionExecutionStrategy strategy, + FieldDelegate? resolverPipeline, + PureFieldDelegate? pureResolver) + { + Id = id; + ResponseName = responseName; + Field = field; + Type = type; + Arguments = arguments ?? s_emptyArguments; + ResolverPipeline = resolverPipeline; + PureResolver = pureResolver; + Strategy = strategy; + _syntaxNodes = syntaxNodes; + _includeFlags = includeFlags; + _flags = flags; + _utf8ResponseName = utf8ResponseName; + } + /// public int Id { get; } @@ -100,7 +129,7 @@ internal Selection( IOutputFieldDefinition ISelection.Field => Field; /// - public IType Type => Field.Type; + public IType Type { get; } /// /// Gets the object type that declares the field being selected. @@ -161,6 +190,28 @@ IEnumerable ISelection.GetSyntaxNodes() } } + /// + /// Gets the selection set for this selection resolved against the specified object type. + /// + /// + /// The object type context to resolve the selection set against. + /// + /// + /// The selection set containing the child selections for the specified type context. + /// + /// + /// Thrown when this selection is a leaf selection (scalar or enum) which does not have child selections. + /// + public SelectionSet GetSelectionSet(ObjectType typeContext) + { + if (IsLeaf) + { + throw new InvalidOperationException("Leaf selections do not have a selection set."); + } + + return DeclaringOperation.GetSelectionSet(this, typeContext); + } + /// /// Determines whether this selection should be skipped based on conditional flags. /// @@ -215,6 +266,44 @@ public bool IsIncluded(ulong includeFlags) return false; } + public Selection WithField(ObjectField field) + { + ArgumentNullException.ThrowIfNull(field); + + return new Selection( + Id, + ResponseName, + _utf8ResponseName, + field, + field.Type, + _syntaxNodes, + _includeFlags, + _flags, + Arguments, + Strategy, + ResolverPipeline, + PureResolver); + } + + public Selection WithType(IType type) + { + ArgumentNullException.ThrowIfNull(type); + + return new Selection( + Id, + ResponseName, + _utf8ResponseName, + Field, + type, + _syntaxNodes, + _includeFlags, + _flags, + Arguments, + Strategy, + ResolverPipeline, + PureResolver); + } + public override string ToString() { if (SyntaxNodes[0].Node.Alias is not null) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs index b1fe6773b6e..f14458099ba 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs @@ -46,6 +46,34 @@ public object? this[Type key] set => _parent[_selectionId, key] = value; } + /// + /// Gets a feature of the specified type. + /// + /// The type of the feature. + /// The feature instance, or null if not found. + /// + /// Thrown when is a non-nullable value type and the feature does not exist. + /// + public TFeature? Get() + { + if (typeof(TFeature).IsValueType) + { + var feature = this[typeof(TFeature)]; + if (feature is null && Nullable.GetUnderlyingType(typeof(TFeature)) is null) + { + throw new InvalidOperationException( + $"{typeof(TFeature).FullName} does not exist in the feature collection " + + "and because it is a struct the method can't return null. " + + $"Use 'featureCollection[typeof({typeof(TFeature).FullName})] is not null' " + + "to check if the feature exists."); + } + + return (TFeature?)feature; + } + + return (TFeature?)this[typeof(TFeature)]; + } + /// /// Sets a feature instance for this selection. /// diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs index afb94e02f92..c956a0fa9a0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs @@ -50,6 +50,8 @@ internal SelectionSetOptimizerContext( /// public ObjectType TypeContext { get; } + public ImmutableArray Selections => _selections; + public bool ContainsField(string fieldName) => _selections.Any(t => t.Field.Name.EqualsOrdinal(fieldName)); @@ -65,6 +67,12 @@ public bool TryGetSelection(string responseName, [MaybeNullWhen(false)] out Sele return _selectionMap.TryGetValue(responseName, out value); } + public Selection GetSelection(string responseName) + { + _selectionMap ??= _selections.ToDictionary(t => t.ResponseName); + return _selectionMap[responseName]; + } + public SelectionFeatureCollection Features => new(_features, _selectionSetId); /// diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index a36c9b2e24c..d17deed0210 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -35,8 +35,7 @@ - - + diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterFeatureExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterFeatureExtensions.cs index e1873fe3f38..cb7cde8e8da 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterFeatureExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterFeatureExtensions.cs @@ -15,7 +15,7 @@ public static class FilterFeatureExtensions /// true if the selection has a filtering enabled; /// otherwise, false. /// - public static bool HasFilterFeature(this ISelection selection) + public static bool HasFilterFeature(this Selection selection) { ArgumentNullException.ThrowIfNull(selection); @@ -30,7 +30,7 @@ public static bool HasFilterFeature(this ISelection selection) /// The filter feature from the selection; /// otherwise, null. /// - public static FilterFeature? GetFilterFeature(this ISelection selection) + public static FilterFeature? GetFilterFeature(this Selection selection) { ArgumentNullException.ThrowIfNull(selection); diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs index d48cdbd9598..7d7fb6188ab 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs @@ -1,5 +1,6 @@ using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; // ReSharper disable once CheckNamespace @@ -7,7 +8,7 @@ namespace System.Linq; /// /// Provides extension methods to integrate -/// with and . +/// with and . /// public static class HotChocolateDataQueryableExtensions { @@ -27,13 +28,11 @@ public static class HotChocolateDataQueryableExtensions /// Returns a queryable that has the selection applied. /// /// - /// Throws if is null or if is null. + /// Throws if is null. /// - public static IQueryable Select(this IQueryable queryable, ISelection selection) + public static IQueryable Select(this IQueryable queryable, Selection selection) { - ArgumentNullException.ThrowIfNull(queryable); ArgumentNullException.ThrowIfNull(selection); - return queryable.Select(selection.AsSelector()); } @@ -53,13 +52,11 @@ public static IQueryable Select(this IQueryable queryable, ISelection s /// Returns a queryable that has the filter applied. /// /// - /// Throws if is null or if is null. + /// Throws if is null. /// public static IQueryable Where(this IQueryable queryable, IFilterContext filter) { - ArgumentNullException.ThrowIfNull(queryable); ArgumentNullException.ThrowIfNull(filter); - var predicate = filter.AsPredicate(); return predicate is null ? queryable : queryable.Where(predicate); } @@ -80,11 +77,10 @@ public static IQueryable Where(this IQueryable queryable, IFilterContex /// Returns a queryable that has the sorting applied. /// /// - /// Throws if is null or if is null. + /// Throws if is null. /// public static IQueryable Order(this IQueryable queryable, ISortingContext sorting) { - ArgumentNullException.ThrowIfNull(queryable); ArgumentNullException.ThrowIfNull(sorting); var sortDefinition = sorting.AsSortDefinition(); diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index f5f21fbc643..a8f0d569b50 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -12,6 +12,7 @@ + diff --git a/src/HotChocolate/Data/src/Data/PagingArgumentsParameterExpressionBuilder.cs b/src/HotChocolate/Data/src/Data/PagingArgumentsParameterExpressionBuilder.cs index 0bba80e0794..a9ad284273b 100644 --- a/src/HotChocolate/Data/src/Data/PagingArgumentsParameterExpressionBuilder.cs +++ b/src/HotChocolate/Data/src/Data/PagingArgumentsParameterExpressionBuilder.cs @@ -42,6 +42,6 @@ private static PagingArguments MapArguments(IResolverContext context) private static PagingArguments MapArguments(CursorPagingArguments arguments, bool includeTotalCount) => new(arguments.First, arguments.After, arguments.Last, arguments.Before, includeTotalCount); - private static bool IncludeTotalCount(ISelection selection) + private static bool IncludeTotalCount(Selection selection) => selection.Field.Features.Get()?.IncludeTotalCount is true; } diff --git a/src/HotChocolate/Data/src/Data/Projections/Context/ISelectedField.cs b/src/HotChocolate/Data/src/Data/Projections/Context/ISelectedField.cs index a2c98a8aa24..048cf55079b 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Context/ISelectedField.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Context/ISelectedField.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Data.Projections.Context; /// /// Represents a field that is selected in a query in the context of an operation. /// -/// The difference between and is that +/// The difference between and is that /// is specific to the current request/operation and not cached. This /// makes it possible to recursively iterate through the selected fields by calling /// . @@ -35,7 +35,7 @@ public interface ISelectedField /// Gets the field selection for which a field resolver is /// being executed. /// - ISelection Selection { get; } + Selection Selection { get; } /// /// Gets the field on which the field resolver is being executed. diff --git a/src/HotChocolate/Data/src/Data/Projections/Context/SelectedField.cs b/src/HotChocolate/Data/src/Data/Projections/Context/SelectedField.cs index 3f47dc6b21a..825c0c0db55 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Context/SelectedField.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Context/SelectedField.cs @@ -1,3 +1,4 @@ +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -13,14 +14,14 @@ public sealed class SelectedField : ISelectedField /// /// Creates a new instance of /// - internal SelectedField(IResolverContext resolverContext, ISelection selection) + internal SelectedField(IResolverContext resolverContext, Selection selection) { _resolverContext = resolverContext; Selection = selection; } /// - public ISelection Selection { get; } + public Selection Selection { get; } /// public IOutputFieldDefinition Field => Selection.Field; @@ -36,21 +37,21 @@ public IReadOnlyList GetFields( ObjectType? type = null, bool allowInternals = false) { - var fields = GetFieldSelections(type, allowInternals); + var selections = GetSelections(type, allowInternals); - if (fields is null) + if (selections is null) { return []; } - var finalFields = new SelectedField[fields.Count]; + var selectedFields = new List(); - for (var i = 0; i < fields.Count; i++) + foreach (var selection in selections) { - finalFields[i] = new SelectedField(_resolverContext, fields[i]); + selectedFields.Add(new SelectedField(_resolverContext, selection)); } - return finalFields; + return selectedFields; } /// @@ -59,16 +60,16 @@ public bool IsSelected( ObjectType? type = null, bool allowInternals = false) { - var fields = GetFieldSelections(type, allowInternals); + var selections = GetSelections(type, allowInternals); - if (fields is null) + if (selections is null) { return false; } - for (var i = 0; i < fields.Count; i++) + foreach (var selection in selections) { - if (fields[i].Field.Name == fieldName) + if (selection.Field.Name == fieldName) { return true; } @@ -77,13 +78,13 @@ public bool IsSelected( return false; } - private IReadOnlyList? GetFieldSelections( + private SelectionEnumerator? GetSelections( ObjectType? type = null, bool allowInternals = false) { var namedType = Field.Type.NamedType(); - if (Selection.SelectionSet is null) + if (Selection.IsLeaf) { return null; } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/Extensions/ProjectionProviderDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/Extensions/ProjectionProviderDescriptorExtensions.cs index 536e7fa6047..03dca1a4cf9 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/Extensions/ProjectionProviderDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/Extensions/ProjectionProviderDescriptorExtensions.cs @@ -1,6 +1,7 @@ using HotChocolate.Data.Projections; using HotChocolate.Data.Projections.Expressions.Handlers; using HotChocolate.Data.Projections.Handlers; +using HotChocolate.Data.Projections.Optimizers; namespace HotChocolate.Data; diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs index f20404790c6..5643a3e185a 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs @@ -11,5 +11,5 @@ public interface IProjectionFieldInterceptor /// /// The selection to test for /// Returns true if the selection can be handled - bool CanHandle(ISelection selection); + bool CanHandle(Selection selection); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor`1.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor`1.cs index a83fe0cde84..607cf11369e 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor`1.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor`1.cs @@ -14,7 +14,7 @@ public interface IProjectionFieldInterceptor /// The current selection void BeforeProjection( TContext context, - ISelection selection); + Selection selection); /// /// This method is called after the enter and leave methods of a @@ -24,5 +24,5 @@ void BeforeProjection( /// The current selection void AfterProjection( TContext context, - ISelection selection); + Selection selection); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionInterceptorCombinator.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionInterceptorCombinator.cs index 2fbac715201..72064262abc 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionInterceptorCombinator.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionInterceptorCombinator.cs @@ -20,11 +20,11 @@ public ProjectionInterceptorCombinator( _next = next; } - public bool CanHandle(ISelection selection) => true; + public bool CanHandle(Selection selection) => true; public void BeforeProjection( T context, - ISelection selection) + Selection selection) { if (_current is IProjectionFieldInterceptor currentHandler) { @@ -37,7 +37,7 @@ public void BeforeProjection( } } - public void AfterProjection(T context, ISelection selection) + public void AfterProjection(T context, Selection selection) { if (_next is IProjectionFieldInterceptor nextHandler) { diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs index f74cea5be9d..0ea678db3a1 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionProvider.cs @@ -154,9 +154,8 @@ public Selection RewriteSelection( } } - return ProjectionSelection.From( - selection, - fieldHandler); + selection.Features.SetSafe(fieldHandler); + return selection; } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelection.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelection.cs deleted file mode 100644 index de4997ce000..00000000000 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelection.cs +++ /dev/null @@ -1,23 +0,0 @@ -using HotChocolate.Execution.Processing; - -namespace HotChocolate.Data.Projections; - -public class ProjectionSelection - : Selection - , IProjectionSelection -{ - public ProjectionSelection( - IProjectionFieldHandler handler, - Selection selection) - : base(selection) - { - Handler = handler; - } - - public IProjectionFieldHandler Handler { get; } - - public static ProjectionSelection From( - Selection selection, - IProjectionFieldHandler handler) => - new(handler, selection); -} diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelectionExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelectionExtensions.cs new file mode 100644 index 00000000000..fc66246730e --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/ProjectionSelectionExtensions.cs @@ -0,0 +1,12 @@ +using HotChocolate.Execution.Processing; +using HotChocolate.Features; + +namespace HotChocolate.Data.Projections; + +public static class ProjectionSelectionExtensions +{ + extension(Selection selection) + { + public IProjectionFieldHandler? ProjectionHandler { get => selection.Features.Get(); } + } +} diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldHandler.cs index c9fb8e6c66c..967b3dfeacc 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldHandler.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; /// -/// A handler that can intersect a and optimize the selection set for +/// A handler that can intersect a and optimize the selection set for /// projections. /// public abstract class ProjectionFieldHandler @@ -23,35 +23,35 @@ public virtual IProjectionFieldHandler Wrap(IProjectionFieldInterceptor intercep } /// - public abstract bool CanHandle(ISelection selection); + public abstract bool CanHandle(Selection selection); /// - public virtual T OnBeforeEnter(T context, ISelection selection) => context; + public virtual T OnBeforeEnter(T context, Selection selection) => context; /// public abstract bool TryHandleEnter( T context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action); /// public virtual T OnAfterEnter( T context, - ISelection selection, + Selection selection, ISelectionVisitorAction action) => context; /// - public virtual T OnBeforeLeave(T context, ISelection selection) => context; + public virtual T OnBeforeLeave(T context, Selection selection) => context; /// public abstract bool TryHandleLeave( T context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action); /// public virtual T OnAfterLeave( T context, - ISelection selection, + Selection selection, ISelectionVisitorAction action) => context; } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldWrapper.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldWrapper.cs index af0626e2e02..62bde2f0f59 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldWrapper.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/ProjectionFieldWrapper.cs @@ -19,33 +19,33 @@ public ProjectionFieldWrapper( _interceptor = interceptor; } - public bool CanHandle(ISelection selection) => + public bool CanHandle(Selection selection) => _handler.CanHandle(selection); public IProjectionFieldHandler Wrap(IProjectionFieldInterceptor interceptor) => _handler.Wrap(new ProjectionInterceptorCombinator(_interceptor, interceptor)); - public T OnBeforeEnter(T context, ISelection selection) => + public T OnBeforeEnter(T context, Selection selection) => _handler.OnBeforeEnter(context, selection); public bool TryHandleEnter( T context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { _interceptor.BeforeProjection(context, selection); return _handler.TryHandleEnter(context, selection, out action); } - public T OnAfterEnter(T context, ISelection selection, ISelectionVisitorAction result) => + public T OnAfterEnter(T context, Selection selection, ISelectionVisitorAction result) => _handler.OnAfterEnter(context, selection, result); - public T OnBeforeLeave(T context, ISelection selection) => + public T OnBeforeLeave(T context, Selection selection) => _handler.OnBeforeLeave(context, selection); public bool TryHandleLeave( T context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { _handler.TryHandleLeave(context, selection, out action); @@ -53,6 +53,6 @@ public bool TryHandleLeave( return action is not null; } - public T OnAfterLeave(T context, ISelection selection, ISelectionVisitorAction result) => + public T OnAfterLeave(T context, Selection selection, ISelectionVisitorAction result) => _handler.OnAfterLeave(context, selection, result); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs index 4851a43e156..dc13c5b0c68 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs @@ -9,13 +9,13 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; public class QueryableProjectionFieldHandler : QueryableProjectionHandlerBase { - public override bool CanHandle(ISelection selection) => + public override bool CanHandle(Selection selection) => selection.Field.Member is { } && selection.SelectionSet is not null; public override bool TryHandleEnter( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; @@ -48,7 +48,7 @@ public override bool TryHandleEnter( public override bool TryHandleLeave( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionHandlerBase.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionHandlerBase.cs index 2b45b6b5c1d..44937250621 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionHandlerBase.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionHandlerBase.cs @@ -8,7 +8,7 @@ public abstract class QueryableProjectionHandlerBase { public override bool TryHandleEnter( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { action = SelectionVisitor.Continue; @@ -17,7 +17,7 @@ public override bool TryHandleEnter( public override bool TryHandleLeave( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { action = SelectionVisitor.Continue; diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionListHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionListHandler.cs index b2bd688a0b9..9466a674a2c 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionListHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionListHandler.cs @@ -9,13 +9,13 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; public class QueryableProjectionListHandler : QueryableProjectionHandlerBase { - public override bool CanHandle(ISelection selection) => + public override bool CanHandle(Selection selection) => selection.Field.Member is { } && (selection.IsList || selection.IsMemberIsList()); public override QueryableProjectionContext OnBeforeEnter( QueryableProjectionContext context, - ISelection selection) + Selection selection) { var field = selection.Field; if (field.Member is PropertyInfo { CanWrite: true }) @@ -30,7 +30,7 @@ public override QueryableProjectionContext OnBeforeEnter( public override bool TryHandleEnter( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; @@ -58,7 +58,7 @@ public override bool TryHandleEnter( public override bool TryHandleLeave( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs index 7d5be358f91..594df9df6c3 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs @@ -8,13 +8,13 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; public class QueryableProjectionScalarHandler : QueryableProjectionHandlerBase { - public override bool CanHandle(ISelection selection) => + public override bool CanHandle(Selection selection) => selection.Field.Member is { } && selection.SelectionSet is null; public override bool TryHandleEnter( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { if (selection.Field.Member is PropertyInfo { CanWrite: true }) @@ -29,7 +29,7 @@ public override bool TryHandleEnter( public override bool TryHandleLeave( QueryableProjectionContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs index 3fba65b481a..ce707b212db 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs @@ -14,21 +14,20 @@ namespace HotChocolate.Data.Projections.Handlers; public class QueryableFilterInterceptor : IProjectionFieldInterceptor { - public bool CanHandle(ISelection selection) => + public bool CanHandle(Selection selection) => selection.Field.Member is PropertyInfo propertyInfo && propertyInfo.CanWrite && selection.HasFilterFeature(); public void BeforeProjection( QueryableProjectionContext context, - ISelection selection) + Selection selection) { - var field = selection.Field; var filterFeature = selection.GetFilterFeature(); if (filterFeature is not null - && context.Selection.Count > 0 - && context.Selection.Peek().Arguments.TryCoerceArguments(context.ResolverContext, out var coercedArgs) + && context.Selections.Count > 0 + && context.Selections.Peek().Arguments.TryCoerceArguments(context.ResolverContext, out var coercedArgs) && coercedArgs.TryGetValue(filterFeature.ArgumentName, out var argumentValue) && argumentValue.Type is IFilterInputType filterInputType && argumentValue.ValueLiteral is { } valueNode and not NullValueNode) @@ -64,7 +63,7 @@ public void BeforeProjection( } } - public void AfterProjection(QueryableProjectionContext context, ISelection selection) + public void AfterProjection(QueryableProjectionContext context, Selection selection) { } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs index 3898b8ac62a..2953c81bf7a 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs @@ -4,7 +4,7 @@ using HotChocolate.Data.Projections.Expressions.Handlers; using HotChocolate.Data.Sorting; using HotChocolate.Data.Sorting.Expressions; -using HotChocolate.Execution.Internal; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.Types; @@ -15,20 +15,20 @@ namespace HotChocolate.Data.Projections.Handlers; public class QueryableSortInterceptor : IProjectionFieldInterceptor { - public bool CanHandle(ISelection selection) => - selection.Field.Member is PropertyInfo propertyInfo - && propertyInfo.CanWrite - && selection.HasSortingFeature(); + public bool CanHandle(Selection selection) + => selection.Field.Member is PropertyInfo propertyInfo + && propertyInfo.CanWrite + && selection.HasSortingFeature; public void BeforeProjection( QueryableProjectionContext context, - ISelection selection) + Selection selection) { var field = selection.Field; if (field.Features.TryGet(out SortingFeature? feature) - && context.Selection.Count > 0 - && context.Selection.Peek().Arguments.TryCoerceArguments(context.ResolverContext, out var coercedArgs) + && context.Selections.Count > 0 + && context.Selections.Peek().Arguments.TryCoerceArguments(context.ResolverContext, out var coercedArgs) && coercedArgs.TryGetValue(feature.ArgumentName, out var argumentValue) && argumentValue.Type is ListType lt && lt.ElementType is NonNullType nn @@ -51,7 +51,7 @@ public void BeforeProjection( } } - public void AfterProjection(QueryableProjectionContext context, ISelection selection) + public void AfterProjection(QueryableProjectionContext context, Selection selection) { } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableTakeHandlerInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableTakeHandlerInterceptor.cs index 540f0d2da23..5b50413b9fa 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableTakeHandlerInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableTakeHandlerInterceptor.cs @@ -20,13 +20,13 @@ protected QueryableTakeHandlerInterceptor(SelectionFlags selectionFlags, int tak _take = take; } - public bool CanHandle(ISelection selection) => + public bool CanHandle(Selection selection) => selection.Field.Member is PropertyInfo { CanWrite: true } && selection.IsSelectionFlags(_selectionFlags); public void BeforeProjection( QueryableProjectionContext context, - ISelection selection) + Selection selection) { if (selection.IsSelectionFlags(_selectionFlags)) { @@ -42,7 +42,7 @@ public void BeforeProjection( public void AfterProjection( QueryableProjectionContext context, - ISelection selection) + Selection selection) { } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs index 59bfd237377..286acd8f250 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs @@ -8,7 +8,7 @@ namespace HotChocolate.Data.Projections.Handlers; public sealed class QueryableFilterProjectionOptimizer : IProjectionOptimizer { - public bool CanHandle(ISelection field) + public bool CanHandle(Selection field) => field.Field.Member is { } && field.HasFilterFeature(); public Selection RewriteSelection( diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs index d75c48d9c51..7b4ef8f5ef2 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs @@ -9,7 +9,7 @@ namespace HotChocolate.Data.Projections.Handlers; public sealed class QueryablePagingProjectionOptimizer : IProjectionOptimizer { - public bool CanHandle(ISelection field) => + public bool CanHandle(Selection field) => field.DeclaringType is IPageType && field.Field.Name is "edges" or "items" or "nodes"; @@ -19,12 +19,12 @@ public Selection RewriteSelection( { // The selection optimizer will also process the field we just added // we have to avoid processing this field twice. - if (context.Selections.ContainsKey(CombinedEdgeField)) + if (context.ContainsResponseName(CombinedEdgeField)) { return selection; } - if (context.Type.NamedType() is not IPageType pageType) + if (context.TypeContext is not IPageType pageType) { throw ThrowHelper.PagingProjectionOptimizer_NotAPagingField( selection.DeclaringType, @@ -48,7 +48,7 @@ public Selection RewriteSelection( private Selection CreateCombinedSelection( SelectionSetOptimizerContext context, - ISelection selection, + Selection selection, ObjectType declaringType, IPageType pageType, IReadOnlyList selections) @@ -110,20 +110,22 @@ private static void CollectSelectionOfEdges( SelectionSetOptimizerContext context, List selections) { - if (context.Selections.Values.FirstOrDefault( - x => x.Field.Name == "edges") is { } edgeSelection) + if (context.Selections.FirstOrDefault(t => t.Field.Name == "edges") is { } edgeSelection) { - foreach (var edgeSubField in edgeSelection.SelectionSet!.Selections) + foreach (var fieldNode in edgeSelection.SyntaxNodes) { - if (edgeSubField is FieldNode edgeSubFieldNode - && edgeSubFieldNode.Name.Value is "node" - && edgeSubFieldNode.SelectionSet?.Selections is not null) + foreach (var edgeSubField in fieldNode.Node.SelectionSet!.Selections) { - foreach (var nodeField in edgeSubFieldNode.SelectionSet.Selections) + if (edgeSubField is FieldNode edgeSubFieldNode + && edgeSubFieldNode.Name.Value is "node" + && edgeSubFieldNode.SelectionSet?.Selections is not null) { - selections.Add( - s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? + foreach (var nodeField in edgeSubFieldNode.SelectionSet.Selections) + { + selections.Add( + s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? throw new SyntaxNodeCannotBeNullException(nodeField)); + } } } } @@ -134,14 +136,16 @@ private static void CollectSelectionOfItems( SelectionSetOptimizerContext context, List selections) { - if (context.Selections.Values - .FirstOrDefault(x => x.Field.Name == "items") is { } itemSelection) + if (context.Selections.FirstOrDefault(x => x.Field.Name == "items") is { } itemSelection) { - foreach (var nodeField in itemSelection.SelectionSet!.Selections) + foreach (var fieldNode in itemSelection.SyntaxNodes) { - selections.Add( - s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? + foreach (var nodeField in fieldNode.Node.SelectionSet!.Selections) + { + selections.Add( + s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? throw new SyntaxNodeCannotBeNullException(nodeField)); + } } } } @@ -150,13 +154,16 @@ private static void CollectSelectionOfNodes( SelectionSetOptimizerContext context, List selections) { - if (context.Selections.Values.FirstOrDefault(x => x.Field.Name == "nodes") is { } nodeSelection) + if (context.Selections.FirstOrDefault(x => x.Field.Name == "nodes") is { } nodeSelection) { - foreach (var nodeField in nodeSelection.SelectionSet!.Selections) + foreach (var fieldNode in nodeSelection.SyntaxNodes) { - selections.Add( - s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? + foreach (var nodeField in fieldNode.Node.SelectionSet!.Selections) + { + selections.Add( + s_cloneSelectionSetRewriter.Rewrite(nodeField) ?? throw new SyntaxNodeCannotBeNullException(nodeField)); + } } } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableSortProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableSortProjectionOptimizer.cs index c242f3373cb..d8e2e96e6a1 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableSortProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableSortProjectionOptimizer.cs @@ -7,8 +7,8 @@ namespace HotChocolate.Data.Projections.Handlers; public sealed class QueryableSortProjectionOptimizer : IProjectionOptimizer { - public bool CanHandle(ISelection field) - => field.Field.Member is { } && field.HasSortingFeature(); + public bool CanHandle(Selection field) + => field.Field.Member is { } && field.HasSortingFeature; public Selection RewriteSelection( SelectionSetOptimizerContext context, @@ -16,7 +16,7 @@ public Selection RewriteSelection( { var resolverPipeline = selection.ResolverPipeline ?? - context.CompileResolverPipeline(selection.Field, selection.SyntaxNode); + context.CompileResolverPipeline(selection.Field, selection.SyntaxNodes[0].Node); static FieldDelegate WrappedPipeline(FieldDelegate next) => ctx => diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionContext.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionContext.cs index 4d09ce1d3c8..a700222c976 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionContext.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionContext.cs @@ -4,15 +4,17 @@ namespace HotChocolate.Data.Projections.Expressions; -public class QueryableProjectionContext( - IResolverContext context, - IOutputType initialType, - Type runtimeType, - bool inMemory) - : ProjectionVisitorContext( - context, - initialType, - new QueryableProjectionScope(runtimeType, "_s1")) +public class QueryableProjectionContext : ProjectionVisitorContext { - public bool InMemory { get; } = inMemory; + public QueryableProjectionContext( + IResolverContext context, + IOutputType initialType, + Type runtimeType, + bool inMemory) + : base(context, initialType, new QueryableProjectionScope(runtimeType, "_s1")) + { + InMemory = inMemory; + } + + public bool InMemory { get; } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs index a2a9adce4e8..5db151a404d 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs @@ -10,7 +10,7 @@ public class QueryableProjectionVisitor : ProjectionVisitor(IProjectionConvention convention) => new ProjectionQueryBuilder(convention.CreateBuilder()); - private static Selection UnwrapMutationPayloadSelection(ISelectionSet selectionSet, ObjectField field) + private static Selection UnwrapMutationPayloadSelection(SelectionSet selectionSet, ObjectField field) { - ref var selection = ref Unsafe.As(selectionSet).GetSelectionsReference(); - ref var end = ref Unsafe.Add(ref selection, selectionSet.Selections.Count); - - while (Unsafe.IsAddressLessThan(ref selection, ref end)) + foreach (var selection in selectionSet.Selections) { if (ReferenceEquals(selection.Field, field)) { return selection; } - - selection = ref Unsafe.Add(ref selection, 1)!; } throw new InvalidOperationException( @@ -226,12 +220,12 @@ public void Prepare(IMiddlewareContext context) && objectType.RuntimeType != typeof(object)) { var fieldProxy = new ObjectField(context.Selection.Field, objectType); - var selection = CreateProxySelection(context.Selection, fieldProxy); + var selection = context.Selection.WithField(fieldProxy); context = new MiddlewareContextProxy(context, selection, objectType); } //for use case when projection is used with Mutation Conventions - else if (context.Operation.Type is OperationType.Mutation + else if (context.Operation.Kind is OperationType.Mutation && context.Selection.Type.NamedType() is ObjectType mutationPayloadType && mutationPayloadType.Features.TryGet(out MutationPayloadInfo? mutationInfo)) { @@ -254,7 +248,7 @@ public void Apply(IMiddlewareContext context) private sealed class MiddlewareContextProxy( IMiddlewareContext context, - ISelection selection, + Selection selection, ObjectType objectType) : IMiddlewareContext { @@ -266,16 +260,18 @@ private sealed class MiddlewareContextProxy( public ObjectType ObjectType { get; } = objectType; - public IOperation Operation => _context.Operation; + public Operation Operation => _context.Operation; public IOperationResultBuilder OperationResult => _context.OperationResult; - public ISelection Selection { get; } = selection; + public Selection Selection { get; } = selection; public IVariableValueCollection Variables => _context.Variables; public Path Path => _context.Path; + public ulong IncludeFlags => _context.IncludeFlags; + public IServiceProvider RequestServices => _context.RequestServices; public string ResponseName => _context.ResponseName; @@ -336,9 +332,9 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV public void ReportError(Exception exception, Action? configure = null) => _context.ReportError(exception, configure); - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) => _context.GetSelections(typeContext, selection, allowInternals); @@ -372,27 +368,4 @@ public ArgumentValue ReplaceArgument(string argumentName, ArgumentValue newArgum IResolverContext IResolverContext.Clone() => _context.Clone(); } - - private static Selection.Sealed CreateProxySelection(ISelection selection, ObjectField field) - { - var includeConditionsSource = ((Selection)selection).IncludeConditions; - var includeConditions = new long[includeConditionsSource.Length]; - includeConditionsSource.CopyTo(includeConditions); - - var proxy = new Selection.Sealed(selection.Id, - selection.DeclaringType, - field, - field.Type, - selection.SyntaxNode, - selection.ResponseName, - selection.Arguments, - includeConditions, - selection.IsInternal, - selection.Strategy != SelectionExecutionStrategy.Serial, - selection.ResolverPipeline, - selection.PureResolver); - proxy.SetSelectionSetId(((Selection)selection).SelectionSetId); - proxy.Seal(selection.DeclaringOperation, selection.DeclaringSelectionSet); - return proxy; - } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionFlagsExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionFlagsExtensions.cs index 9869506075d..2a3d70a5010 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionFlagsExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionFlagsExtensions.cs @@ -15,10 +15,8 @@ public static class SelectionFlagsExtensions /// The selection. /// /// true if the field has a first or default middleware; otherwise, false. - public static bool IsFirstOrDefault(this ISelection selection) + public static bool IsFirstOrDefault(this Selection selection) { - ArgumentNullException.ThrowIfNull(selection); - var flags = selection.Field.Features.Get() ?? SelectionFlags.None; return (flags & SelectionFlags.FirstOrDefault) == SelectionFlags.FirstOrDefault; } @@ -29,10 +27,8 @@ public static bool IsFirstOrDefault(this ISelection selection) /// The selection. /// /// true if the field has a single or default middleware; otherwise, false. - public static bool IsSingleOrDefault(this ISelection selection) + public static bool IsSingleOrDefault(this Selection selection) { - ArgumentNullException.ThrowIfNull(selection); - var flags = selection.Field.Features.Get() ?? SelectionFlags.None; return (flags & SelectionFlags.SingleOrDefault) == SelectionFlags.SingleOrDefault; } @@ -43,10 +39,8 @@ public static bool IsSingleOrDefault(this ISelection selection) /// The selection. /// /// true if the field is a list; otherwise, false. - public static bool IsMemberIsList(this ISelection selection) + public static bool IsMemberIsList(this Selection selection) { - ArgumentNullException.ThrowIfNull(selection); - var flags = selection.Field.Features.Get() ?? SelectionFlags.None; return (flags & SelectionFlags.MemberIsList) == SelectionFlags.MemberIsList; } @@ -58,10 +52,8 @@ public static bool IsMemberIsList(this ISelection selection) /// The flags. /// /// true if the specified are set on the field; otherwise, false. - public static bool IsSelectionFlags(this ISelection selection, SelectionFlags flags) + public static bool IsSelectionFlags(this Selection selection, SelectionFlags flags) { - ArgumentNullException.ThrowIfNull(selection); - var actualFlags = selection.Field.Features.Get() ?? SelectionFlags.None; return (actualFlags & flags) == flags; } diff --git a/src/HotChocolate/Data/src/Data/Projections/IProjectionSelection.cs b/src/HotChocolate/Data/src/Data/Projections/IProjectionSelection.cs deleted file mode 100644 index f1201812a9e..00000000000 --- a/src/HotChocolate/Data/src/Data/Projections/IProjectionSelection.cs +++ /dev/null @@ -1,8 +0,0 @@ -using HotChocolate.Execution.Processing; - -namespace HotChocolate.Data.Projections; - -public interface IProjectionSelection : ISelection -{ - IProjectionFieldHandler Handler { get; } -} diff --git a/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs b/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs index a9f81f88691..3986a83d9a6 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs @@ -6,9 +6,18 @@ namespace HotChocolate.Data.Projections; public interface ISelectionVisitorContext { - Stack Selection { get; } + ulong IncludeFlags => ResolverContext.IncludeFlags; - Stack ResolvedType { get; } + Operation Operation => ResolverContext.Operation; + + Stack Selections { get; } + + Stack ResolvedTypes { get; } IResolverContext ResolverContext { get; } + + SelectionEnumerator GetSelections( + ObjectType typeContext, + Selection? selection = null, + bool allowInternals = false); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs index c89ef8cbfe2..36a108fe8a9 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs @@ -1,7 +1,7 @@ using HotChocolate.Execution.Processing; using HotChocolate.Language; -namespace HotChocolate.Data.Projections.Handlers; +namespace HotChocolate.Data.Projections.Optimizers; public class IsProjectedProjectionOptimizer : IProjectionOptimizer { @@ -13,8 +13,7 @@ public Selection RewriteSelection( SelectionSetOptimizerContext context, Selection selection) { - if (context.Type is not { } type - || !type.Features.TryGet(out ProjectionTypeFeature? feature)) + if (!context.TypeContext.Features.TryGet(out ProjectionTypeFeature? feature)) { return selection; } @@ -22,41 +21,39 @@ public Selection RewriteSelection( for (var i = 0; i < feature.AlwaysProjectedFields.Length; i++) { var alias = "__projection_alias_" + i; - var alwaysProjectedField = feature.AlwaysProjectedFields[i]; + var fieldName = feature.AlwaysProjectedFields[i]; // if the field is already in the selection set we do not need to project it - if (context.Selections.TryGetValue(alwaysProjectedField, out var field) - && field.Field.Name == alwaysProjectedField) + if (context.TryGetSelection(fieldName, out var otherSelection) + && otherSelection.Field.Name == fieldName) { continue; } // if the field is already added as an alias we do not need to add it - if (context.Selections.TryGetValue(alias, out field) - && field.Field.Name == alwaysProjectedField) + if (context.TryGetSelection(alias, out otherSelection) + && otherSelection.Field.Name == fieldName) { continue; } - var nodesField = type.Fields[alwaysProjectedField]; - var nodesFieldNode = new FieldNode( + var field = context.TypeContext.Fields[fieldName]; + var fieldNode = new FieldNode( null, - new NameNode(alwaysProjectedField), + new NameNode(fieldName), new NameNode(alias), [], [], null); - var nodesPipeline = context.CompileResolverPipeline(nodesField, nodesFieldNode); + var nodesPipeline = context.CompileResolverPipeline(field, fieldNode); - var compiledSelection = new Selection.Sealed( + var compiledSelection = new Selection( context.NewSelectionId(), - context.Type, - nodesField, - nodesField.Type, - nodesFieldNode, alias, - arguments: selection.Arguments, + field, + [new FieldSelectionNode(fieldNode, 0)], + [], isInternal: true, resolverPipeline: nodesPipeline); diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/NodeSelectionSetOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/NodeSelectionSetOptimizer.cs index 75be2f45836..a386eb80d27 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/NodeSelectionSetOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/NodeSelectionSetOptimizer.cs @@ -1,13 +1,13 @@ using HotChocolate.Execution.Processing; using HotChocolate.Types.Relay; -namespace HotChocolate.Data.Projections; +namespace HotChocolate.Data.Projections.Optimizers; internal sealed class NodeSelectionSetOptimizer(ISelectionSetOptimizer optimizer) : ISelectionSetOptimizer { public void OptimizeSelectionSet(SelectionSetOptimizerContext context) { - if (context.Type.Features.TryGet(out var feature) + if (context.TypeContext.Features.TryGet(out var feature) && feature.NodeResolver is { } nodeResolverInfo && nodeResolverInfo.QueryField?.HasProjectionMiddleware() == true) { diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionFeature.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionFeature.cs index 27a92191716..baa0ce03083 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionFeature.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionFeature.cs @@ -1,10 +1,5 @@ -using System.Collections.Immutable; - namespace HotChocolate.Data.Projections; public record ProjectionFeature( bool AlwaysProjected = false, bool HasProjectionMiddleware = false); - -public record ProjectionTypeFeature( - ImmutableArray AlwaysProjectedFields); diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs index 2030c3d416a..6e6d1fe04bd 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs @@ -8,16 +8,16 @@ internal sealed class ProjectionOptimizer(IProjectionProvider provider) public void OptimizeSelectionSet(SelectionSetOptimizerContext context) { var processedSelections = new HashSet(); - while (!processedSelections.SetEquals(context.Selections.Keys)) + while (!processedSelections.SetEquals(context.Selections.Select(t => t.ResponseName))) { - var selectionToProcess = new HashSet(context.Selections.Keys); + var selectionToProcess = new HashSet(context.Selections.Select(t => t.ResponseName)); selectionToProcess.ExceptWith(processedSelections); foreach (var responseName in selectionToProcess) { var rewrittenSelection = provider.RewriteSelection( context, - context.Selections[responseName]); + context.GetSelection(responseName)); context.ReplaceSelection(rewrittenSelection); diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeFeature.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeFeature.cs new file mode 100644 index 00000000000..4e2b61e1c7e --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeFeature.cs @@ -0,0 +1,6 @@ +using System.Collections.Immutable; + +namespace HotChocolate.Data.Projections; + +public record ProjectionTypeFeature( + ImmutableArray AlwaysProjectedFields); diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs index 1724e17488b..0136708102a 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs @@ -1,4 +1,5 @@ using HotChocolate.Configuration; +using HotChocolate.Data.Projections.Optimizers; using HotChocolate.Features; using HotChocolate.Language; using HotChocolate.Types; diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs index c50346ddd28..c10436726a2 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs @@ -14,16 +14,15 @@ public virtual void Visit(TContext context) Visit(context, context.ResolverContext.Selection); } - public virtual void Visit(TContext context, ISelection selection) + public virtual void Visit(TContext context, Selection selection) { - context.Selection.Push(selection); + context.Selections.Push(selection); Visit(selection.Field, context); } - protected override TContext OnBeforeLeave(ISelection selection, TContext localContext) + protected override TContext OnBeforeLeave(Selection selection, TContext localContext) { - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler) + if (selection.ProjectionHandler is IProjectionFieldHandler handler) { return handler.OnBeforeLeave(localContext, selection); } @@ -32,12 +31,11 @@ protected override TContext OnBeforeLeave(ISelection selection, TContext localCo } protected override TContext OnAfterLeave( - ISelection selection, + Selection selection, TContext localContext, ISelectionVisitorAction result) { - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler) + if (selection.ProjectionHandler is IProjectionFieldHandler handler) { return handler.OnAfterLeave(localContext, selection, result); } @@ -46,12 +44,11 @@ protected override TContext OnAfterLeave( } protected override TContext OnAfterEnter( - ISelection selection, + Selection selection, TContext localContext, ISelectionVisitorAction result) { - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler) + if (selection.ProjectionHandler is IProjectionFieldHandler handler) { return handler.OnAfterEnter(localContext, selection, result); } @@ -59,10 +56,9 @@ protected override TContext OnAfterEnter( return localContext; } - protected override TContext OnBeforeEnter(ISelection selection, TContext context) + protected override TContext OnBeforeEnter(Selection selection, TContext context) { - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler) + if (selection.ProjectionHandler is IProjectionFieldHandler handler) { return handler.OnBeforeEnter(context, selection); } @@ -71,17 +67,13 @@ protected override TContext OnBeforeEnter(ISelection selection, TContext context } protected override ISelectionVisitorAction Enter( - ISelection selection, + Selection selection, TContext context) { base.Enter(selection, context); - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler - && handler.TryHandleEnter( - context, - selection, - out var handlerResult)) + if (selection.ProjectionHandler is IProjectionFieldHandler handler + && handler.TryHandleEnter(context, selection, out var handlerResult)) { return handlerResult; } @@ -90,17 +82,13 @@ protected override ISelectionVisitorAction Enter( } protected override ISelectionVisitorAction Leave( - ISelection selection, + Selection selection, TContext context) { base.Leave(selection, context); - if (selection is IProjectionSelection projectionSelection - && projectionSelection.Handler is IProjectionFieldHandler handler - && handler.TryHandleLeave( - context, - selection, - out var handlerResult)) + if (selection.ProjectionHandler is IProjectionFieldHandler handler + && handler.TryHandleLeave(context, selection, out var handlerResult)) { return handlerResult; } @@ -108,7 +96,7 @@ protected override ISelectionVisitorAction Leave( return SkipAndLeave; } - protected override ISelectionVisitorAction Visit(ISelection selection, TContext context) + protected override ISelectionVisitorAction Visit(Selection selection, TContext context) { if (selection.Field.IsNotProjected()) { @@ -120,21 +108,29 @@ protected override ISelectionVisitorAction Visit(ISelection selection, TContext protected override ISelectionVisitorAction Visit(IOutputFieldDefinition field, TContext context) { - if (context.Selection.Count > 1 && field.IsNotProjected()) + if (context.Selections.Count > 1 && field.IsNotProjected()) { return Skip; } if (field.Type.NamedType() is IPageType and ObjectType pageType - && context.Selection.Peek() is { } pagingFieldSelection) + && context.Selections.Peek() is { } pagingFieldSelection) { - var selections = context.ResolverContext.GetSelections(pageType, pagingFieldSelection, true); + var includeFlags = context.IncludeFlags; + var selections = context.Operation.GetSelectionSet(pagingFieldSelection, pageType).Selections; - for (var index = selections.Count - 1; index >= 0; index--) + for (var i = selections.Length - 1; i >= 0; i--) { - if (selections[index] is { ResponseName: CombinedEdgeField } selection) + var selection = selections[i]; + + if (selection.IsSkipped(includeFlags)) + { + continue; + } + + if (selection is { ResponseName: CombinedEdgeField }) { - context.Selection.Push(selection); + context.Selections.Push(selection); return base.Visit(selection.Field, context); } diff --git a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor.cs b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor.cs index b851405ab16..3b8520b7461 100644 --- a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor.cs @@ -1,12 +1,12 @@ namespace HotChocolate.Data.Projections; -public class SelectionVisitor +public abstract class SelectionVisitor { /// /// The visitor default action. /// /// - protected virtual ISelectionVisitorAction DefaultAction => Continue; + protected ISelectionVisitorAction DefaultAction => Continue; /// /// Ends traversing the graph. diff --git a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs index 2fb0e328214..016c478ec86 100644 --- a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs +++ b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs @@ -7,11 +7,17 @@ namespace HotChocolate.Data.Projections; public class SelectionVisitorContext(IResolverContext context) : ISelectionVisitorContext { - public Stack Selection { get; } = new(); + public Stack Selections { get; } = new(); public Stack SelectionSetNodes { get; } = new(); - public Stack ResolvedType { get; } = new(); + public Stack ResolvedTypes { get; } = new(); public IResolverContext ResolverContext { get; } = context; + + public SelectionEnumerator GetSelections( + ObjectType typeContext, + Selection? selection = null, + bool allowInternals = false) + => ResolverContext.GetSelections(typeContext, selection, allowInternals); } diff --git a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs index 8ca14d0bfaf..00f814fdb9c 100644 --- a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs +++ b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs @@ -3,9 +3,7 @@ namespace HotChocolate.Data.Projections; -public class SelectionVisitor - : SelectionVisitor - where TContext : ISelectionVisitorContext +public class SelectionVisitor : SelectionVisitor where TContext : ISelectionVisitorContext { protected virtual ISelectionVisitorAction Visit( IOutputFieldDefinition field, @@ -15,16 +13,15 @@ protected virtual ISelectionVisitorAction Visit( var result = Enter(field, localContext); localContext = OnAfterEnter(field, localContext, result); - if (result.Kind == SelectionVisitorActionKind.Continue) + if (result.Kind is SelectionVisitorActionKind.Continue) { - if (VisitChildren(field, context).Kind == SelectionVisitorActionKind.Break) + if (VisitChildren(field, context).Kind is SelectionVisitorActionKind.Break) { return Break; } } - if (result.Kind == SelectionVisitorActionKind.Continue - || result.Kind == SelectionVisitorActionKind.SkipAndLeave) + if (result.Kind is SelectionVisitorActionKind.Continue or SelectionVisitorActionKind.SkipAndLeave) { localContext = OnBeforeLeave(field, localContext); result = Leave(field, localContext); @@ -36,42 +33,41 @@ protected virtual ISelectionVisitorAction Visit( protected virtual TContext OnBeforeLeave( IOutputFieldDefinition field, - TContext localContext) => - localContext; + TContext localContext) + => localContext; protected virtual TContext OnAfterLeave( IOutputFieldDefinition field, TContext localContext, - ISelectionVisitorAction result) => - localContext; + ISelectionVisitorAction result) + => localContext; protected virtual TContext OnAfterEnter( IOutputFieldDefinition field, TContext localContext, - ISelectionVisitorAction result) => - localContext; + ISelectionVisitorAction result) + => localContext; protected virtual TContext OnBeforeEnter( IOutputFieldDefinition field, - TContext context) => - context; + TContext context) + => context; protected virtual ISelectionVisitorAction Visit( - ISelection selection, + Selection selection, TContext context) { var localContext = OnBeforeEnter(selection, context); var result = Enter(selection, localContext); localContext = OnAfterEnter(selection, localContext, result); - if (result.Kind == SelectionVisitorActionKind.Continue + if (result.Kind is SelectionVisitorActionKind.Continue && VisitChildren(selection, context).Kind == SelectionVisitorActionKind.Break) { return Break; } - if (result.Kind == SelectionVisitorActionKind.Continue - || result.Kind == SelectionVisitorActionKind.SkipAndLeave) + if (result.Kind is SelectionVisitorActionKind.Continue or SelectionVisitorActionKind.SkipAndLeave) { localContext = OnBeforeLeave(selection, localContext); result = Leave(selection, localContext); @@ -82,31 +78,31 @@ protected virtual ISelectionVisitorAction Visit( } protected virtual TContext OnBeforeLeave( - ISelection selection, + Selection selection, TContext localContext) => localContext; protected virtual TContext OnAfterLeave( - ISelection selection, + Selection selection, TContext localContext, ISelectionVisitorAction result) => localContext; protected virtual TContext OnAfterEnter( - ISelection selection, + Selection selection, TContext localContext, ISelectionVisitorAction result) => localContext; protected virtual TContext OnBeforeEnter( - ISelection selection, + Selection selection, TContext context) => context; protected virtual ISelectionVisitorAction VisitChildren(IOutputFieldDefinition field, TContext context) { var type = field.Type; - var selection = context.Selection.Peek(); + var selection = context.Selections.Peek(); var namedType = type.NamedType(); if (namedType.IsAbstractType()) @@ -133,19 +129,24 @@ protected virtual ISelectionVisitorAction VisitChildren(IOutputFieldDefinition f protected virtual ISelectionVisitorAction VisitObjectType( IOutputFieldDefinition field, ObjectType objectType, - ISelection selection, + Selection selection, TContext context) { - context.ResolvedType.Push(field.Type.NamedType().IsAbstractType() ? objectType : null); + context.ResolvedTypes.Push(field.Type.NamedType().IsAbstractType() ? objectType : null); try { - var selections = context.ResolverContext.GetSelections(objectType, selection, true); + var selectionSet = selection.GetSelectionSet(objectType); + var includeFlags = context.ResolverContext.IncludeFlags; - for (var i = 0; i < selections.Count; i++) + foreach (var childSelection in selectionSet.Selections) { - var result = Visit(selections[i], context); - if (result.Kind is SelectionVisitorActionKind.Break) + if (childSelection.IsSkipped(includeFlags)) + { + continue; + } + + if (Visit(selection, context).Kind is SelectionVisitorActionKind.Break) { return Break; } @@ -153,18 +154,17 @@ protected virtual ISelectionVisitorAction VisitObjectType( } finally { - context.ResolvedType.Pop(); + context.ResolvedTypes.Pop(); } return DefaultAction; } protected virtual ISelectionVisitorAction VisitChildren( - ISelection selection, + Selection selection, TContext context) { - var field = selection.Field; - return Visit(field, context); + return Visit(selection.Field, context); } protected virtual ISelectionVisitorAction Enter( @@ -178,18 +178,18 @@ protected virtual ISelectionVisitorAction Leave( DefaultAction; protected virtual ISelectionVisitorAction Enter( - ISelection selection, + Selection selection, TContext context) { - context.Selection.Push(selection); + context.Selections.Push(selection); return DefaultAction; } protected virtual ISelectionVisitorAction Leave( - ISelection selection, + Selection selection, TContext context) { - context.Selection.Pop(); + context.Selections.Pop(); return DefaultAction; } } diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs index 6edbc0e0d95..14d8dc98451 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs @@ -1,21 +1,22 @@ +using HotChocolate.Execution; using HotChocolate.Execution.Processing; namespace HotChocolate.Data.Projections; /// -/// A handler that can intersect a and optimize the selection set for +/// A handler that can intersect a and optimize the selection set for /// projections. /// public interface IProjectionFieldHandler { /// - /// Tests if this field handle can handle a selection. If it can handle the selection it + /// Tests if this field can handle a selection. If it can handle the selection it /// will be attached to the compiled selection set on the - /// type + /// type /// /// The selection to test for /// Returns true if the selection can be handled - bool CanHandle(ISelection selection); + bool CanHandle(Selection selection); /// /// Wrapped this field handler with a type interceptor diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler~1.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler~1.cs index aa2d9f68e78..64c69b5200e 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler~1.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler~1.cs @@ -4,9 +4,7 @@ namespace HotChocolate.Data.Projections; /// -public interface IProjectionFieldHandler - : IProjectionFieldHandler - where TContext : IProjectionVisitorContext +public interface IProjectionFieldHandler : IProjectionFieldHandler where TContext : IProjectionVisitorContext { /// /// This method is called before the visitor calls @@ -17,7 +15,7 @@ public interface IProjectionFieldHandler /// /// The instance of that is used in TryHandleEnter /// - TContext OnBeforeEnter(TContext context, ISelection selection); + TContext OnBeforeEnter(TContext context, Selection selection); /// /// Tries to apply projection to the field. This method is called after @@ -33,7 +31,7 @@ public interface IProjectionFieldHandler /// If true is returned the action is used for further processing bool TryHandleEnter( TContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action); /// @@ -48,7 +46,7 @@ bool TryHandleEnter( /// TContext OnAfterEnter( TContext context, - ISelection selection, + Selection selection, ISelectionVisitorAction result); /// @@ -60,7 +58,7 @@ TContext OnAfterEnter( /// /// The instance of that is used in TryHandleLeave /// - TContext OnBeforeLeave(TContext context, ISelection selection); + TContext OnBeforeLeave(TContext context, Selection selection); /// /// Tries to apply projection to the field. This method is called after @@ -76,7 +74,7 @@ TContext OnAfterEnter( /// If true is returned the action is used for further processing bool TryHandleLeave( TContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action); /// @@ -91,6 +89,6 @@ bool TryHandleLeave( /// TContext OnAfterLeave( TContext context, - ISelection selection, + Selection selection, ISelectionVisitorAction result); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionScope.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionScope.cs index 106e24f094c..858d8b87ce9 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionScope.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionScope.cs @@ -6,13 +6,8 @@ namespace HotChocolate.Data.Projections; /// The type of the filter configuration public class ProjectionScope { - public ProjectionScope() - { - Instance = new Stack(); - } - /// /// Stores the current instance. In case of an expression this would be x.Foo.Bar /// - public Stack Instance { get; } + public Stack Instance { get; } = []; } diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionVisitorContext.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionVisitorContext.cs index 76bf3664a8f..53b83b233eb 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionVisitorContext.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/ProjectionVisitorContext.cs @@ -4,13 +4,14 @@ namespace HotChocolate.Data.Projections; public abstract class ProjectionVisitorContext - : SelectionVisitorContext, - IProjectionVisitorContext + : SelectionVisitorContext + , IProjectionVisitorContext { protected ProjectionVisitorContext( IResolverContext context, IOutputType initialType, - ProjectionScope projectionScope) : base(context) + ProjectionScope projectionScope) + : base(context) { ArgumentNullException.ThrowIfNull(initialType); @@ -21,7 +22,7 @@ protected ProjectionVisitorContext( public Stack> Scopes { get; } - public Stack Types { get; } = new Stack(); + public Stack Types { get; } = []; public IList Errors { get; } = []; } diff --git a/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContextResolverContextExtensions.cs b/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContextResolverContextExtensions.cs index 50209933f4b..687ba355cce 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContextResolverContextExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContextResolverContextExtensions.cs @@ -16,7 +16,7 @@ public static class SortingContextResolverContextExtensions { ArgumentNullException.ThrowIfNull(context); - var argumentName = context.Selection.GetSortingArgumentName(); + var argumentName = context.Selection.SortingArgumentName; if (string.IsNullOrEmpty(argumentName)) { return null; diff --git a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortingFeatureExtensions.cs b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortingFeatureExtensions.cs index caefe88366c..6f46ec06bdc 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortingFeatureExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortingFeatureExtensions.cs @@ -7,19 +7,20 @@ namespace HotChocolate.Data.Sorting; /// public static class SortingFeatureExtensions { - /// - /// Gets the sorting argument name from the selection. - /// - /// The selection. - /// The sorting argument name. - public static string? GetSortingArgumentName(this ISelection selection) - => selection.Field.Features.Get()?.ArgumentName; + extension(Selection selection) + { + /// + /// Gets the sorting argument name from the selection. + /// + /// The sorting argument name. + public string? SortingArgumentName + => selection.Field.Features.Get()?.ArgumentName; - /// - /// Checks if the selection has a sorting feature. - /// - /// The selection. - /// True if the selection has a sorting feature, otherwise false. - public static bool HasSortingFeature(this ISelection selection) - => selection.Field.Features.Get() is not null; + /// + /// Checks if the selection has a sorting feature. + /// + /// True if the selection has a sorting feature, otherwise false. + public bool HasSortingFeature + => selection.Field.Features.Get() is not null; + } } diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs index 531406760fa..988c5231a84 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/ActivityEnricher.cs @@ -336,7 +336,7 @@ public virtual void EnrichExecuteRequest(RequestContext context, Activity activi activity.SetTag("graphql.document.hash", documentInfo.Hash.Value); activity.SetTag("graphql.document.valid", documentInfo.IsValidated); activity.SetTag("graphql.operation.id", operation?.Id); - activity.SetTag("graphql.operation.kind", operation?.Type); + activity.SetTag("graphql.operation.kind", operation?.Kind); activity.SetTag("graphql.operation.name", operation?.Name); if (_options.IncludeDocument && documentInfo.Document is not null) @@ -351,7 +351,7 @@ public virtual void EnrichExecuteRequest(RequestContext context, Activity activi } } - protected virtual string? CreateOperationDisplayName(RequestContext context, IOperation? operation) + protected virtual string? CreateOperationDisplayName(RequestContext context, Operation? operation) { if (operation is null) { @@ -367,7 +367,7 @@ public virtual void EnrichExecuteRequest(RequestContext context, Activity activi displayName.Append('{'); displayName.Append(' '); - foreach (var selection in rootSelectionSet.Selections.Take(3)) + foreach (var selection in rootSelectionSet.Selections[..3]) { if (displayName.Length > 2) { @@ -377,7 +377,7 @@ public virtual void EnrichExecuteRequest(RequestContext context, Activity activi displayName.Append(selection.ResponseName); } - if (rootSelectionSet.Selections.Count > 3) + if (rootSelectionSet.Selections.Length > 3) { displayName.Append(' '); displayName.Append('.'); @@ -667,6 +667,8 @@ private static ISyntaxNode CreateVariablesNode( items[i] = new ObjectValueNode(fields); } + + return new ListValueNode(items); } throw new InvalidOperationException(); diff --git a/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/Fusion.Execution.Benchmarks.csproj b/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/Fusion.Execution.Benchmarks.csproj index 61d4ec3dfb3..293f667f6ee 100644 --- a/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/Fusion.Execution.Benchmarks.csproj +++ b/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/Fusion.Execution.Benchmarks.csproj @@ -6,6 +6,7 @@ net10.0 true Preview + HotChocolate.Fusion.Execution.Benchmarks diff --git a/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/GraphQLQueryBenchmark.cs b/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/GraphQLQueryBenchmark.cs index 3704e00135e..581d0fc787f 100644 --- a/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/GraphQLQueryBenchmark.cs +++ b/src/HotChocolate/Fusion-vnext/benchmarks/Fusion.Execution.Benchmarks/GraphQLQueryBenchmark.cs @@ -14,6 +14,7 @@ using BenchmarkDotNet.Columns; using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Exporters; +using HotChocolate.Buffers; namespace Fusion.Execution.Benchmarks; @@ -61,8 +62,12 @@ public async Task GlobalSetup() _transportFewItemsRequest = new TransportGraphQLHttpRequest(fewItems, _requestUri); _transportClient = new TransportClient(_client); - JsonMemory.Return(JsonMemory.Rent()); - MetaDbMemory.Return(MetaDbMemory.Rent()); + JsonMemory.Reconfigure( + static () => new FixedSizeArrayPool( + FixedSizeArrayPoolKinds.JsonMemory, + JsonMemory.BufferSize, + 128, + preAllocate: true)); } [GlobalCleanup] diff --git a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs index 6a5714432a8..829ac6daf61 100644 --- a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs @@ -1,6 +1,7 @@ using HotChocolate.Data.MongoDb.Projections; using HotChocolate.Data.Projections; using HotChocolate.Data.Projections.Handlers; +using HotChocolate.Data.Projections.Optimizers; namespace HotChocolate.Data; diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj index 8bde094eee5..b767d7e82a9 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj @@ -16,6 +16,7 @@ + From 76ff6cc367d00276fb9c7b37e470d06d9b3b7024 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 22:41:23 +0100 Subject: [PATCH 22/30] wip --- .../OperationCompiler.CoerceArgumentValues.cs | 2 +- .../OperationCompiler.CompileResolver.cs | 2 +- .../Execution/Processing/OperationCompiler.cs | 23 ++++++++++++++-- .../Types/Resolvers/SelectionEnumerator.cs | 11 +++++++- .../QueryableCursorPagingProviderTests.cs | 26 +++++++++---------- .../Convention/IProjectionFieldInterceptor.cs | 2 +- .../QueryableProjectionFieldHandler.cs | 5 ++-- .../QueryableProjectionScalarHandler.cs | 5 ++-- .../Interceptor/QueryableSortInterceptor.cs | 2 +- .../QueryableFilterProjectionOptimizer.cs | 2 +- .../QueryablePagingProjectionOptimizer.cs | 18 +++++-------- .../Visitor/IProjectionFieldHandler.cs | 2 +- .../Visitor/IProjectionOptimizer.cs | 2 +- .../Handlers/MongoDbProjectionFieldHandler.cs | 8 +++--- ...QueryableSpatialProjectionScalarHandler.cs | 4 +-- ...ocumentAnalyzer.CollectInputObjectTypes.cs | 4 +-- .../Analyzers/FieldCollector.cs | 9 ++++--- .../Mappers/TypeDescriptorMapper.cs | 5 ++-- 18 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs index f601fa279a6..bcfc1ce0212 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs @@ -10,7 +10,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler +public sealed partial class OperationCompiler { private ArgumentMap? CoerceArgumentValues( ObjectField field, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs index 372d52fd5b7..ef36b6e2d52 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler +public sealed partial class OperationCompiler { internal static FieldDelegate CreateFieldPipeline(Schema schema, ObjectField field, FieldNode selection) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 2565aa7b5fd..ff196c9d4d4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -9,7 +9,7 @@ namespace HotChocolate.Execution.Processing; -internal sealed partial class OperationCompiler +public sealed partial class OperationCompiler { private readonly Schema _schema; private readonly ObjectPool>> _fieldsPool; @@ -18,7 +18,7 @@ internal sealed partial class OperationCompiler private readonly InputParser _inputValueParser; private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; - public OperationCompiler( + internal OperationCompiler( Schema schema, InputParser inputValueParser, ObjectPool>> fieldsPool, @@ -34,6 +34,25 @@ public OperationCompiler( _optimizers = optimizers; } + public static Operation Compile( + string id, + OperationDefinitionNode operationDefinition, + Schema schema) + => Compile(id, id, operationDefinition, schema); + + public static Operation Compile( + string id, + string hash, + OperationDefinitionNode operationDefinition, + Schema schema) + => new OperationCompiler( + schema, + new InputParser(), + new DefaultObjectPool>>( + new DefaultPooledObjectPolicy>>()), + new OperationCompilerOptimizers()) + .Compile(id, hash, operationDefinition); + public Operation Compile( string id, string hash, diff --git a/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs index aed4577252b..23347abd46b 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs @@ -12,7 +12,16 @@ public struct SelectionEnumerator : IEnumerable, IEnumerator + /// Initializes a new instance of + /// + /// + /// The selection set to enumerate on. + /// + /// + /// The include flags representing the selections that shall be included. + /// + public SelectionEnumerator(SelectionSet selectionSet, ulong includeFlags) { _selectionSet = selectionSet; _includeFlags = includeFlags; diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs index 5c417d78101..7555d0f1906 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs @@ -493,7 +493,7 @@ private sealed class MockContext : IResolverContext { private readonly CursorPagingArguments _arguments; - private MockContext(CursorPagingArguments arguments, IOperation operation, ISelection selection) + private MockContext(CursorPagingArguments arguments, Operation operation, Selection selection) { _arguments = arguments; Operation = operation; @@ -519,14 +519,10 @@ public static async Task CreateContextAsync(CursorPagingArgume } """); - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "abc", - document, - document.Definitions.OfType().First(), - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "abc", + document.Definitions.OfType().First(), + schema); return new MockContext(arguments, operation, operation.RootSelectionSet.Selections[0]); } @@ -574,13 +570,13 @@ public void ReportError(Exception exception, Action? configure = n throw new NotImplementedException(); } - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) { var selectionSet = Operation.GetSelectionSet(selection ?? Selection, typeContext); - return selectionSet.Selections; + return new SelectionEnumerator(selectionSet, IncludeFlags); } public ISelectionCollection Select() @@ -607,14 +603,16 @@ public IResolverContext Clone() public ObjectType ObjectType => throw new NotImplementedException(); - public IOperation Operation { get; } + public Operation Operation { get; } - public ISelection Selection { get; } + public Selection Selection { get; } public IVariableValueCollection Variables => throw new NotImplementedException(); public Path Path => throw new NotImplementedException(); + public ulong IncludeFlags => 0; + public T Parent() { throw new NotImplementedException(); diff --git a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs index 5643a3e185a..0c455a1e94c 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Convention/IProjectionFieldInterceptor.cs @@ -7,7 +7,7 @@ public interface IProjectionFieldInterceptor /// /// Tests if this interceptor can handle a selection If it can handle the selection it /// will be attached to the compiled selection set on the - /// type + /// type ProjectionSelection. /// /// The selection to test for /// Returns true if the selection can be handled diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs index dc13c5b0c68..96557f400f5 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs @@ -9,9 +9,8 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; public class QueryableProjectionFieldHandler : QueryableProjectionHandlerBase { - public override bool CanHandle(Selection selection) => - selection.Field.Member is { } - && selection.SelectionSet is not null; + public override bool CanHandle(Selection selection) + => selection.Field.Member is not null && !selection.IsLeaf; public override bool TryHandleEnter( QueryableProjectionContext context, diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs index 594df9df6c3..e16fef75060 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionScalarHandler.cs @@ -8,9 +8,8 @@ namespace HotChocolate.Data.Projections.Expressions.Handlers; public class QueryableProjectionScalarHandler : QueryableProjectionHandlerBase { - public override bool CanHandle(Selection selection) => - selection.Field.Member is { } - && selection.SelectionSet is null; + public override bool CanHandle(Selection selection) + => selection.Field.Member is not null && selection.IsLeaf; public override bool TryHandleEnter( QueryableProjectionContext context, diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs index 2953c81bf7a..3051f34d423 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs @@ -4,7 +4,7 @@ using HotChocolate.Data.Projections.Expressions.Handlers; using HotChocolate.Data.Sorting; using HotChocolate.Data.Sorting.Expressions; -using HotChocolate.Execution; +using HotChocolate.Execution.Internal; using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.Types; diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs index 286acd8f250..19eec26c72d 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryableFilterProjectionOptimizer.cs @@ -17,7 +17,7 @@ public Selection RewriteSelection( { var resolverPipeline = selection.ResolverPipeline - ?? context.CompileResolverPipeline(selection.Field, selection.SyntaxNode); + ?? context.CompileResolverPipeline(selection.Field, selection.SyntaxNodes[0].Node); static FieldDelegate WrappedPipeline(FieldDelegate next) => ctx => diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs index 7b4ef8f5ef2..4026a494ce0 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Optimizers/QueryablePagingProjectionOptimizer.cs @@ -9,9 +9,8 @@ namespace HotChocolate.Data.Projections.Handlers; public sealed class QueryablePagingProjectionOptimizer : IProjectionOptimizer { - public bool CanHandle(Selection field) => - field.DeclaringType is IPageType - && field.Field.Name is "edges" or "items" or "nodes"; + public bool CanHandle(Selection field) + => field is { DeclaringType: IPageType, Field.Name: "edges" or "items" or "nodes" }; public Selection RewriteSelection( SelectionSetOptimizerContext context, @@ -37,7 +36,6 @@ public Selection RewriteSelection( CreateCombinedSelection( context, selection, - selection.DeclaringType, pageType, selections); @@ -49,7 +47,6 @@ public Selection RewriteSelection( private Selection CreateCombinedSelection( SelectionSetOptimizerContext context, Selection selection, - ObjectType declaringType, IPageType pageType, IReadOnlyList selections) { @@ -67,15 +64,14 @@ private Selection CreateCombinedSelection( selection.ResolverPipeline ?? context.CompileResolverPipeline(nodesField, combinedField); - return new Selection.Sealed( + return new Selection( context.NewSelectionId(), - declaringType, - nodesField, - nodesField.Type, - combinedField, CombinedEdgeField, - arguments: selection.Arguments, + nodesField, + [new FieldSelectionNode(combinedField, 0)], + [], isInternal: true, + arguments: selection.Arguments, resolverPipeline: nodesPipeline); } diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs index 14d8dc98451..0d939dee70e 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionFieldHandler.cs @@ -12,7 +12,7 @@ public interface IProjectionFieldHandler /// /// Tests if this field can handle a selection. If it can handle the selection it /// will be attached to the compiled selection set on the - /// type + /// type IProjectionSelection. /// /// The selection to test for /// Returns true if the selection can be handled diff --git a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs index 53d5cfc8c2d..1bd4518490d 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Visitor/IProjectionOptimizer.cs @@ -7,7 +7,7 @@ public interface IProjectionOptimizer /// /// Tests if this optimizer can handle a selection If it can handle the selection it /// will be attached to the compiled selection set on the - /// type + /// type ProjectionSelection. /// /// The selection to test for /// Returns true if the selection can be handled diff --git a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionFieldHandler.cs b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionFieldHandler.cs index b089ef4ca76..92b3f6a017a 100644 --- a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionFieldHandler.cs +++ b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionFieldHandler.cs @@ -9,13 +9,13 @@ public class MongoDbProjectionFieldHandler : MongoDbProjectionHandlerBase { /// - public override bool CanHandle(ISelection selection) => - selection.SelectionSet is not null; + public override bool CanHandle(Selection selection) + => !selection.IsLeaf; /// public override bool TryHandleEnter( MongoDbProjectionVisitorContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; @@ -27,7 +27,7 @@ public override bool TryHandleEnter( /// public override bool TryHandleLeave( MongoDbProjectionVisitorContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { context.Path.Pop(); diff --git a/src/HotChocolate/Spatial/src/Data/Projections/Extensions/Extensions/QueryableSpatialProjectionScalarHandler.cs b/src/HotChocolate/Spatial/src/Data/Projections/Extensions/Extensions/QueryableSpatialProjectionScalarHandler.cs index a2c57a634d5..48fd21c4b2a 100644 --- a/src/HotChocolate/Spatial/src/Data/Projections/Extensions/Extensions/QueryableSpatialProjectionScalarHandler.cs +++ b/src/HotChocolate/Spatial/src/Data/Projections/Extensions/Extensions/QueryableSpatialProjectionScalarHandler.cs @@ -8,9 +8,9 @@ namespace HotChocolate.Data.Projections.Spatial; public class QueryableSpatialProjectionScalarHandler : QueryableProjectionScalarHandler { - public override bool CanHandle(ISelection selection) => + public override bool CanHandle(Selection selection) => selection.Field.Member is not null && typeof(Geometry).IsAssignableFrom(selection.Field.Member.GetReturnType()); - public static new QueryableSpatialProjectionScalarHandler Create(ProjectionProviderContext context) => new(); + public new static QueryableSpatialProjectionScalarHandler Create(ProjectionProviderContext context) => new(); } diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/DocumentAnalyzer.CollectInputObjectTypes.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/DocumentAnalyzer.CollectInputObjectTypes.cs index 4ad3991107b..1ad24060502 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/DocumentAnalyzer.CollectInputObjectTypes.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/DocumentAnalyzer.CollectInputObjectTypes.cs @@ -41,7 +41,7 @@ private static void RegisterInputObjectType( foreach (var inputField in inputObjectType.Fields) { - rename = inputField.Directives.SingleOrDefault(); + rename = inputField.Directives.FirstOrDefault()?.ToValue(); fields.Add(new InputFieldModel( GetClassName(rename?.Name ?? inputField.Name), @@ -55,7 +55,7 @@ inputField.DefaultValue is not null context.RegisterType(inputField.Type.NamedType()); } - rename = inputObjectType.Directives.SingleOrDefault(); + rename = inputObjectType.Directives.FirstOrDefault()?.ToValue(); var typeName = context.ResolveTypeName( GetClassName(rename?.Name ?? inputObjectType.Name)); diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs index e1390615d65..6fdf84008ea 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs @@ -207,9 +207,10 @@ private void ResolveFragmentSpread( if (DoesTypeApply(fragment.TypeCondition, type)) { - var deferDirective = fragmentSpreadSyntax.Directives.GetDeferDirectiveNode(); + // TODO : DEFER + // var deferDirective = fragmentSpreadSyntax.Directives.GetDeferDirectiveNode(); var nodes = new List(); - var fragmentNode = new FragmentNode(fragment, nodes, deferDirective); + var fragmentNode = new FragmentNode(fragment, nodes, null); fragmentNodes.Add(fragmentNode); CollectFields( @@ -232,9 +233,9 @@ private void ResolveInlineFragment( if (DoesTypeApply(fragment.TypeCondition, type)) { - var deferDirective = inlineFragmentSyntax.Directives.GetDeferDirectiveNode(); + // var deferDirective = inlineFragmentSyntax.Directives.GetDeferDirectiveNode(); var nodes = new List(); - var fragmentNode = new FragmentNode(fragment, nodes, deferDirective); + var fragmentNode = new FragmentNode(fragment, nodes, null); fragmentNodes.Add(fragmentNode); CollectFields( diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs index 4bf5c476ae1..bada4a415c8 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs @@ -375,8 +375,9 @@ private static void CollectClassesThatImplementInterface( private static bool IncludeOrSkipDirective(OutputFieldModel field) { - return field.SyntaxNode.Directives.GetIncludeDirectiveNode() is not null - || field.SyntaxNode.Directives.GetSkipDirectiveNode() is not null; + return field.SyntaxNode.Directives.Any( + t => t.Name.Value.Equals(DirectiveNames.Include.Name) + || t.Name.Value.Equals(DirectiveNames.Skip.Name)); } private static void AddProperties( From 9d8eeb9daff3697ad975a2312d3d451810840d09 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 22:41:30 +0100 Subject: [PATCH 23/30] wip --- src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj | 1 + .../AutomaticMocking/MockFieldMiddleware.cs | 2 +- .../Convention/Handlers/MongoDbProjectionHandlerBase.cs | 6 +++--- .../Convention/Handlers/MongoDbProjectionScalarHandler.cs | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index d17deed0210..30ebd2b0c99 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -22,6 +22,7 @@ + diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Tests.Shared/AutomaticMocking/MockFieldMiddleware.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Tests.Shared/AutomaticMocking/MockFieldMiddleware.cs index 617211388ea..bd0251809d2 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Tests.Shared/AutomaticMocking/MockFieldMiddleware.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Tests.Shared/AutomaticMocking/MockFieldMiddleware.cs @@ -312,7 +312,7 @@ private static IError CreateError(IResolverContext context, int? index = null) return ErrorBuilder.New() .SetMessage("Unexpected Execution Error") .SetPath(path) - .AddLocation(context.Selection.SyntaxNode) + .AddLocations(context.Selection) .Build(); } diff --git a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionHandlerBase.cs b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionHandlerBase.cs index b9baa931b05..b38b1abf437 100644 --- a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionHandlerBase.cs +++ b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionHandlerBase.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Data.MongoDb.Projections; /// -/// A handler that can intersect a and optimize the selection set for +/// A handler that can intersect a and optimize the selection set for /// mongodb projections. /// public abstract class MongoDbProjectionHandlerBase @@ -15,7 +15,7 @@ public abstract class MongoDbProjectionHandlerBase /// public override bool TryHandleEnter( MongoDbProjectionVisitorContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { action = SelectionVisitor.Continue; @@ -25,7 +25,7 @@ public override bool TryHandleEnter( /// public override bool TryHandleLeave( MongoDbProjectionVisitorContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { action = SelectionVisitor.Continue; diff --git a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionScalarHandler.cs b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionScalarHandler.cs index e093ef61513..161bcd5aece 100644 --- a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionScalarHandler.cs +++ b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Handlers/MongoDbProjectionScalarHandler.cs @@ -10,13 +10,13 @@ public class MongoDbProjectionScalarHandler : MongoDbProjectionHandlerBase { /// - public override bool CanHandle(ISelection selection) => - selection.SelectionSet is null; + public override bool CanHandle(Selection selection) => + selection.IsLeaf; /// public override bool TryHandleEnter( MongoDbProjectionVisitorContext context, - ISelection selection, + Selection selection, [NotNullWhen(true)] out ISelectionVisitorAction? action) { var field = selection.Field; From cdfbd3fa6e96bbee772c24cd2d4aa8efedaa700b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 23:53:38 +0100 Subject: [PATCH 24/30] wip --- .../Execution/Processing/OperationCompiler.cs | 12 +- .../Execution/Processing/SelectionSet.cs | 15 +- .../SelectionSetOptimizerContext.cs | 7 + .../Extensions/ErrorBuilderExtensions.cs | 21 +- .../Execution.Tests/MiddlewareContextTests.cs | 13 +- .../Processing/OperationCompilerTests.cs | 411 ++++++------------ .../Processing/VisibilityTests.cs | 68 +-- .../Resolvers/ResolverCompilerTests.cs | 136 ++++-- .../Types/Brands/BrandNode.cs | 1 + 9 files changed, 339 insertions(+), 345 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index ff196c9d4d4..1aa47e7467e 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -85,6 +85,7 @@ public Operation Compile( includeConditions); var selectionSet = BuildSelectionSet( + SelectionPath.Root, fields, rootType, compilationContext, @@ -167,7 +168,8 @@ internal SelectionSet CompileSelectionSet( } } - var selectionSet = BuildSelectionSet(fields, objectType, compilationContext, optimizers, ref lastId); + var path = selection.DeclaringSelectionSet.Path.Append(selection.ResponseName); + var selectionSet = BuildSelectionSet(path, fields, objectType, compilationContext, optimizers, ref lastId); compilationContext.Register(selectionSet, selectionSet.Id); elementsById = compilationContext.ElementsById; selectionSet.Complete(operation); @@ -232,6 +234,7 @@ private void CollectFields( } private SelectionSet BuildSelectionSet( + SelectionPath path, OrderedDictionary> fieldMap, ObjectType typeContext, CompilationContext compilationContext, @@ -324,7 +327,7 @@ private SelectionSet BuildSelectionSet( // if there are no optimizers registered for this selection we exit early. if (optimizers.Length == 0) { - return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + return new SelectionSet(selectionSetId, path, typeContext, selections, isConditional); } var current = ImmutableCollectionsMarshal.AsImmutableArray(selections); @@ -332,6 +335,7 @@ private SelectionSet BuildSelectionSet( var optimizerContext = new SelectionSetOptimizerContext( selectionSetId, + path, typeContext, ref rewritten, compilationContext.Features, @@ -349,7 +353,7 @@ private SelectionSet BuildSelectionSet( // This mean we can simply construct the SelectionSet. if (current == rewritten) { - return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + return new SelectionSet(selectionSetId, path, typeContext, selections, isConditional); } if (current.Length < rewritten.Length) @@ -368,7 +372,7 @@ private SelectionSet BuildSelectionSet( } selections = ImmutableCollectionsMarshal.AsArray(rewritten)!; - return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + return new SelectionSet(selectionSetId, path, typeContext, selections, isConditional); } private static void CollapseIncludeFlags(List includeFlags) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index 228f15f6261..b27ea77f5b5 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -18,7 +18,12 @@ public sealed class SelectionSet : ISelectionSet private Flags _flags; private Operation? _declaringOperation; - internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) + internal SelectionSet( + int id, + SelectionPath path, + IObjectTypeDefinition type, + Selection[] selections, + bool isConditional) { ArgumentNullException.ThrowIfNull(selections); @@ -28,6 +33,7 @@ internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections } Id = id; + Path = path; Type = type; _flags = isConditional ? Flags.Conditional : Flags.None; _selections = selections; @@ -40,13 +46,18 @@ internal SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections /// public int Id { get; } + /// + /// Gets the path where this selection set is located within the GraphQL operation document. + /// + public SelectionPath Path { get; } + /// /// Defines if this list needs post-processing for skip and include. /// public bool IsConditional => (_flags & Flags.Conditional) == Flags.Conditional; /// - /// Gets the type that declares this selection set. + /// Gets the type context of this selection set. /// public IObjectTypeDefinition Type { get; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs index c956a0fa9a0..17d94802be4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs @@ -24,6 +24,7 @@ public ref struct SelectionSetOptimizerContext /// internal SelectionSetOptimizerContext( int selectionSetId, + SelectionPath path, ObjectType typeContext, ref ImmutableArray selections, OperationFeatureCollection features, @@ -36,6 +37,7 @@ internal SelectionSetOptimizerContext( _features = features; _lastSelectionId = ref lastSelectionId; _createFieldPipeline = createFieldPipeline; + Path = path; TypeContext = typeContext; Schema = schema; } @@ -45,6 +47,11 @@ internal SelectionSetOptimizerContext( /// public Schema Schema { get; } + /// + /// Gets the path where this selection set is located within the GraphQL operation document. + /// + public SelectionPath Path { get; } + /// /// Gets the type context of the current selection-set. /// diff --git a/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs index 7a6362d85c2..db9ded8259a 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ErrorBuilderExtensions.cs @@ -1,14 +1,27 @@ -using System.Diagnostics; using HotChocolate.Execution.Processing; namespace HotChocolate; -internal static class ErrorBuilderExtensions +/// +/// Provides extension methods for . +/// +public static class ErrorBuilderExtensions { + /// + /// Adds all syntax node locations from a selection to the error builder. + /// + /// + /// The error builder to which locations will be added. + /// + /// + /// The selection containing the syntax nodes whose locations will be added to the error. + /// + /// + /// The for method chaining. + /// public static ErrorBuilder AddLocations(this ErrorBuilder errorBuilder, Selection selection) { - Debug.Assert(errorBuilder is not null); - Debug.Assert(selection is not null); + ArgumentNullException.ThrowIfNull(selection); foreach (var syntaxNode in selection.SyntaxNodes) { diff --git a/src/HotChocolate/Core/test/Execution.Tests/MiddlewareContextTests.cs b/src/HotChocolate/Core/test/Execution.Tests/MiddlewareContextTests.cs index 7d294bf4644..a798cdb7f4f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/MiddlewareContextTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/MiddlewareContextTests.cs @@ -18,7 +18,7 @@ public async Task AccessVariables() .AddResolver( "Query", "foo", - ctx => ctx.Variables.GetValue("abc")?.Value) + ctx => ctx.Variables.GetValue("abc").Value) .Create(); var request = OperationRequestBuilder.New() @@ -43,7 +43,7 @@ public async Task AccessVariables_Fails_When_Variable_Not_Exists() .AddResolver( "Query", "foo", - ctx => ctx.Variables.GetValue("abc")?.Value) + ctx => ctx.Variables.GetValue("abc").Value) .Create(); var request = OperationRequestBuilder.New() @@ -63,7 +63,7 @@ public async Task AccessVariables_Fails_When_Variable_Not_Exists() public async Task CollectFields() { // arrange - var list = new List(); + var list = new List(); var schema = SchemaBuilder.New() .AddDocumentFromString( @@ -105,7 +105,7 @@ await schema.MakeExecutable().ExecuteAsync( }"); // assert - list.Select(t => t.SyntaxNode.Name.Value).ToList().MatchSnapshot(); + list.Select(t => t.SyntaxNodes[0].Node.Name.Value).ToList().MatchSnapshot(); } [Fact] @@ -627,8 +627,8 @@ public async Task SetResultExtensionData_With_ObjectValue_WhenDeferred() private static void CollectSelections( IResolverContext context, - ISelection selection, - ICollection collected) + Selection selection, + ICollection collected) { if (selection.Type.IsLeafType()) { @@ -644,5 +644,6 @@ private static void CollectSelections( } } + // ReSharper disable once NotAccessedPositionalProperty.Local private record SomeData(string SomeField); } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs index 1274cf5d887..09311f012ba 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs @@ -27,14 +27,10 @@ public void Prepare_One_Field() var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -59,14 +55,10 @@ public void Prepare_Duplicate_Field() document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -91,14 +83,10 @@ public void Prepare_Empty_Operation_SelectionSet() document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -129,14 +117,10 @@ ... on Human { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -172,14 +156,10 @@ fragment def on Human { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -242,14 +222,10 @@ fragment Human3 on Human { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -274,14 +250,10 @@ public void Prepare_Duplicate_Field_With_Skip() var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -308,14 +280,10 @@ public void Field_Does_Not_Exist() // act void Action() { - var compiler = new OperationCompiler(new InputParser()); - compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + OperationCompiler.Compile( + "opid", + operationDefinition, + schema); } // assert @@ -348,14 +316,10 @@ fragment abc on Droid { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -384,14 +348,10 @@ fragment abc on Droid { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -420,14 +380,10 @@ fragment abc on Droid { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -460,14 +416,10 @@ fragment abc on Droid { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -508,14 +460,10 @@ ... @include(if: $v) { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -580,14 +528,10 @@ fragment Human3 on Human { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -622,14 +566,10 @@ ... @include(if: $v) { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -653,14 +593,10 @@ name @include(if: $q) var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -693,14 +629,10 @@ public void Field_Based_Optimizers() var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -728,14 +660,10 @@ ... @defer { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -765,14 +693,10 @@ fragment Foo on Droid { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -802,14 +726,10 @@ friends @include(if: $withFriends) { document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -836,14 +756,10 @@ ... abc var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -858,24 +774,22 @@ public void InlineFragment_SelectionsSet_Empty() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean){ - hero(episode: EMPIRE) { - name @include(if: $v) - ... on Droid { } - } - }"); + """ + query foo($v: Boolean){ + hero(episode: EMPIRE) { + name @include(if: $v) + ... on Droid { } + } + } + """); var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -899,14 +813,10 @@ query foo($v: Boolean) { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -929,14 +839,10 @@ public async Task Large_Query_Test() document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -960,14 +866,10 @@ public async Task Crypto_Details_Test() document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1005,14 +907,10 @@ fragment PriceInfo on Asset { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1050,14 +948,10 @@ fragment PriceInfo on Asset { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1096,14 +990,10 @@ fragment PriceInfo on Asset { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1127,14 +1017,10 @@ public async Task Crypto_List_Test() document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1215,14 +1101,10 @@ type OrganizationUnit2 implements OrganizationUnit { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1303,19 +1185,13 @@ type OrganizationUnit2 implements OrganizationUnit { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert - Assert.Equal(29, compiler.Metrics.Selections); - Assert.Equal(7, compiler.Metrics.SelectionSetVariants); - Assert.Equal(4, compiler.Metrics.BacklogMaxSize); + // Note: Metrics are no longer accessible with static method } [Fact] @@ -1352,14 +1228,10 @@ fragment TypeTwoParts on TypeTwo { var operationDefinition = document.Definitions.OfType().Single(); // act - var compiler = new OperationCompiler(new InputParser()); - var operation = compiler.Compile( - new OperationCompilerRequest( - "opid", - document, - operationDefinition, - schema.QueryType, - schema)); + var operation = OperationCompiler.Compile( + "opid", + operationDefinition, + schema); // assert MatchSnapshot(document, operation); @@ -1436,17 +1308,16 @@ public void OptimizeSelectionSet(SelectionSetOptimizerContext context) { if (context.Path is { Name: "bar" }) { - var baz = context.Type.Fields["baz"]; + var baz = context.TypeContext.Fields["baz"]; var bazSelection = Utf8GraphQLParser.Syntax.ParseField("baz { text }"); var bazPipeline = context.CompileResolverPipeline(baz, bazSelection); var compiledSelection = new Selection( context.NewSelectionId(), - context.Type, - baz, - baz.Type, - bazSelection, "someName", + baz, + [new FieldSelectionNode(bazSelection, 0)], + [], isInternal: true, resolverPipeline: bazPipeline); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/VisibilityTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/VisibilityTests.cs index 2342c35e52c..13a0bee45dd 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/VisibilityTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/VisibilityTests.cs @@ -13,9 +13,10 @@ public void TryExtract_Skip_With_Literal() var field = Utf8GraphQLParser.Syntax.ParseField("field @skip(if: true)"); // act - var includeCondition = IncludeCondition.FromSelection(field); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert + Assert.True(hasIncludeCondition); Assert.False(includeCondition.IsIncluded(variables.Object)); } @@ -27,10 +28,12 @@ public void Equals_Skip_With_Literal_True() var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: true)"); // act - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.True(includeConditionA.Equals(includeConditionB)); } @@ -42,10 +45,12 @@ public void Equals_Skip_With_Variable_True() var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: $a)"); // act - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.True(includeConditionA.Equals(includeConditionB)); } @@ -57,10 +62,12 @@ public void Equals_Skip_With_Literal_False() var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: false)"); // act - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.False(includeConditionA.Equals(includeConditionB)); } @@ -72,10 +79,12 @@ public void Equals_Skip_With_Variable_False() var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: $a)"); // act - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.False(includeConditionA.Equals(includeConditionB)); } @@ -87,10 +96,10 @@ public void TryExtract_False() var field = Utf8GraphQLParser.Syntax.ParseField("field @test(test: true)"); // act - var includeCondition = IncludeCondition.FromSelection(field); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert - Assert.True(includeCondition.IsIncluded(variables.Object)); + Assert.False(hasIncludeCondition); } [Fact] @@ -101,10 +110,10 @@ public void TryExtract_False_2() var field = Utf8GraphQLParser.Syntax.ParseField("field"); // act - var includeCondition = IncludeCondition.FromSelection(field); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert - Assert.True(includeCondition.IsIncluded(variables.Object)); + Assert.False(hasIncludeCondition); } [Fact] @@ -115,9 +124,10 @@ public void TryExtract_True() var field = Utf8GraphQLParser.Syntax.ParseField("field @skip(if: true)"); // act - var includeCondition = IncludeCondition.FromSelection(field); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert + Assert.True(hasIncludeCondition); Assert.False(includeCondition.IsIncluded(variables.Object)); } @@ -128,14 +138,16 @@ public void GetHashCode_Skip_With_Literal_Equal() var fieldA = Utf8GraphQLParser.Syntax.ParseField("fieldA @skip(if: true)"); var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: true)"); - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // act var hashCodeA = includeConditionA.GetHashCode(); var hashCodeB = includeConditionB.GetHashCode(); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.Equal(hashCodeA, hashCodeB); } @@ -146,14 +158,16 @@ public void GetHashCode_Skip_With_Literal_NotEqual() var fieldA = Utf8GraphQLParser.Syntax.ParseField("fieldA @skip(if: true)"); var fieldB = Utf8GraphQLParser.Syntax.ParseField("fieldB @skip(if: false)"); - var includeConditionA = IncludeCondition.FromSelection(fieldA); - var includeConditionB = IncludeCondition.FromSelection(fieldB); + var hasIncludeConditionA = IncludeCondition.TryCreate(fieldA, out var includeConditionA); + var hasIncludeConditionB = IncludeCondition.TryCreate(fieldB, out var includeConditionB); // act var hashCodeA = includeConditionA.GetHashCode(); var hashCodeB = includeConditionB.GetHashCode(); // assert + Assert.True(hasIncludeConditionA); + Assert.True(hasIncludeConditionB); Assert.NotEqual(hashCodeA, hashCodeB); } @@ -164,13 +178,13 @@ public void IsVisible_Skip_Variables_True() var variables = new Mock(); variables.Setup(t => t.GetValue(It.IsAny())).Returns(BooleanValueNode.False); var field = Utf8GraphQLParser.Syntax.ParseField("field @skip(if: $a)"); - var includeCondition = IncludeCondition.FromSelection(field); // act - var visible = includeCondition.IsIncluded(variables.Object); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert - Assert.True(visible); + Assert.True(hasIncludeCondition); + Assert.True(includeCondition.IsIncluded(variables.Object)); } [Fact] @@ -180,13 +194,13 @@ public void IsVisible_Include_Variables_True() var variables = new Mock(); variables.Setup(t => t.GetValue(It.IsAny())).Returns(BooleanValueNode.True); var field = Utf8GraphQLParser.Syntax.ParseField("field @include(if: $a)"); - var includeCondition = IncludeCondition.FromSelection(field); // act - var visible = includeCondition.IsIncluded(variables.Object); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert - Assert.True(visible); + Assert.True(hasIncludeCondition); + Assert.True(includeCondition.IsIncluded(variables.Object)); } [Fact] @@ -195,12 +209,12 @@ public void IsVisible_Include_Literal_True() // arrange var variables = new Mock(); var field = Utf8GraphQLParser.Syntax.ParseField("field @include(if: true)"); - var includeCondition = IncludeCondition.FromSelection(field); // act - var visible = includeCondition.IsIncluded(variables.Object); + var hasIncludeCondition = IncludeCondition.TryCreate(field, out var includeCondition); // assert - Assert.True(visible); + Assert.True(hasIncludeCondition); + Assert.True(includeCondition.IsIncluded(variables.Object)); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs index 90ee7a94b30..1ae88b29256 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs @@ -467,11 +467,29 @@ public async Task Compile_Arguments_FieldSelection() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert - var selection = new Mock(); + var fieldSyntax = new FieldNode( + null, + new NameNode("foo"), + null, + Array.Empty(), + Array.Empty(), + null); + + var schema = + SchemaBuilder.New() + .AddQueryType(c => c.Name("Query").Field("abc").Resolve("def")) + .Create(); + + var selection = new Selection( + id: 1, + "abc", + schema.Types.GetType("Query").Fields["abc"], + [new FieldSelectionNode(fieldSyntax, 1)], + []); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Selection).Returns(selection.Object); + context.SetupGet(t => t.Selection).Returns(selection); var result = (bool)(await resolver(context.Object))!; Assert.True(result); @@ -491,11 +509,29 @@ public async Task Compile_Arguments_Selection() var pure = compiler.CompileResolve(member, type).PureResolver!; // assert - var selection = new Mock(); + var fieldSyntax = new FieldNode( + null, + new NameNode("foo"), + null, + Array.Empty(), + Array.Empty(), + null); + + var schema = + SchemaBuilder.New() + .AddQueryType(c => c.Name("Query").Field("abc").Resolve("def")) + .Create(); + + var selection = new Selection( + id: 1, + "abc", + schema.Types.GetType("Query").Fields["abc"], + [new FieldSelectionNode(fieldSyntax, 1)], + []); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Selection).Returns(selection.Object); + context.SetupGet(t => t.Selection).Returns(selection); var result = (bool)(await resolver(context.Object))!; Assert.True(result, "Standard Resolver"); @@ -526,12 +562,21 @@ public async Task Compile_Arguments_FieldSyntax() Array.Empty(), null); - var selection = new Mock(); - selection.SetupGet(t => t.SyntaxNode).Returns(fieldSyntax); + var schema = + SchemaBuilder.New() + .AddQueryType(c => c.Name("Query").Field("abc").Resolve("def")) + .Create(); + + var selection = new Selection( + id: 1, + "abc", + schema.Types.GetType("Query").Fields["abc"], + [new FieldSelectionNode(fieldSyntax, 1)], + []); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Selection).Returns(selection.Object); + context.SetupGet(t => t.Selection).Returns(selection); var result = (bool)(await resolver(context.Object))!; Assert.True(result, "Standard Resolver"); @@ -579,24 +624,20 @@ public async Task Compile_Arguments_Operation() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert - var operationDefinition = - new OperationDefinitionNode( - null, - null, - null, - OperationType.Query, - Array.Empty(), - Array.Empty(), - new SelectionSetNode( - null, - Array.Empty())); - - var operation = new Mock(); - operation.Setup(t => t.Definition).Returns(operationDefinition); + var schema = + SchemaBuilder.New() + .AddQueryType(c => c.Name("Query").Field("abc").Resolve("def")) + .Create(); + + var document = Utf8GraphQLParser.Parse("{ abc }"); + var operation = OperationCompiler.Compile( + "abc", + document.Definitions.OfType().First(), + schema); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Operation).Returns(operation.Object); + context.SetupGet(t => t.Operation).Returns(operation); var result = (bool)(await resolver(context.Object))!; Assert.True(result); } @@ -614,6 +655,14 @@ public async Task Compile_Arguments_ObjectField() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert + var fieldSyntax = new FieldNode( + null, + new NameNode("a"), + null, + Array.Empty(), + Array.Empty(), + null); + var schema = SchemaBuilder.New() .AddDocumentFromString("type Query { a: String }") .Use(next => next) @@ -621,12 +670,16 @@ public async Task Compile_Arguments_ObjectField() var queryType = schema.Types.GetType("Query"); - var selection = new Mock(); - selection.SetupGet(t => t.Field).Returns(queryType.Fields.First()); + var selection = new Selection( + id: 1, + "a", + queryType.Fields.First(), + [new FieldSelectionNode(fieldSyntax, 1)], + []); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Selection).Returns(selection.Object); + context.SetupGet(t => t.Selection).Returns(selection); var result = (bool)(await resolver(context.Object))!; Assert.True(result); @@ -645,6 +698,14 @@ public async Task Compile_Arguments_IOutputField() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert + var fieldSyntax = new FieldNode( + null, + new NameNode("a"), + null, + Array.Empty(), + Array.Empty(), + null); + var schema = SchemaBuilder.New() .AddDocumentFromString("type Query { a: String }") .Use(next => next) @@ -652,12 +713,16 @@ public async Task Compile_Arguments_IOutputField() var queryType = schema.Types.GetType("Query"); - var selection = new Mock(); - selection.SetupGet(t => t.Field).Returns(queryType.Fields.First()); + var selection = new Selection( + id: 1, + "a", + queryType.Fields.First(), + [new FieldSelectionNode(fieldSyntax, 1)], + []); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Selection).Returns(selection.Object); + context.SetupGet(t => t.Selection).Returns(selection); var result = (bool)(await resolver(context.Object))!; Assert.True(result); @@ -676,13 +741,20 @@ public async Task Compile_Arguments_Document() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert - var document = new DocumentNode(Array.Empty()); - var operation = new Mock(); - operation.Setup(t => t.Document).Returns(document); + var schema = + SchemaBuilder.New() + .AddQueryType(c => c.Name("Query").Field("abc").Resolve("def")) + .Create(); + + var document = Utf8GraphQLParser.Parse("{ abc }"); + var operation = OperationCompiler.Compile( + "abc", + document.Definitions.OfType().First(), + schema); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.SetupGet(t => t.Operation).Returns(operation.Object); + context.SetupGet(t => t.Operation).Returns(operation); var result = (bool)(await resolver(context.Object))!; Assert.True(result); } diff --git a/src/HotChocolate/Data/test/Data.PostgreSQL.Tests/Types/Brands/BrandNode.cs b/src/HotChocolate/Data/test/Data.PostgreSQL.Tests/Types/Brands/BrandNode.cs index 7eee8c9b14b..122ef0ab407 100644 --- a/src/HotChocolate/Data/test/Data.PostgreSQL.Tests/Types/Brands/BrandNode.cs +++ b/src/HotChocolate/Data/test/Data.PostgreSQL.Tests/Types/Brands/BrandNode.cs @@ -1,6 +1,7 @@ using GreenDonut.Data; using HotChocolate.Data.Models; using HotChocolate.Data.Services; +using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Types; using HotChocolate.Types.Pagination; From 722e60f5dea17ffe4c7f5ed1774b3b8f4709031a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 4 Dec 2025 23:58:48 +0100 Subject: [PATCH 25/30] Fixed middleware --- .../Execution/Pipeline/OperationResolverMiddleware.cs | 9 ++------- .../src/Types/Execution/Processing/OperationCompiler.cs | 1 + 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs index 9c85a312c1d..0a301750db7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs @@ -9,7 +9,6 @@ internal sealed class OperationResolverMiddleware { private readonly RequestDelegate _next; private readonly OperationCompiler _operationPlanner; - private readonly DocumentRewriter _documentRewriter; private readonly IExecutionDiagnosticEvents _diagnosticEvents; private OperationResolverMiddleware( @@ -25,7 +24,6 @@ private OperationResolverMiddleware( _next = next; _operationPlanner = operationPlanner; - _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); _diagnosticEvents = diagnosticEvents; } @@ -42,13 +40,10 @@ public async ValueTask InvokeAsync(RequestContext context) { using (_diagnosticEvents.CompileOperation(context)) { - // Before we can plan an operation, we must de-fragmentize it and remove static include conditions. - var operationDocument = documentInfo.Document; var operationName = context.Request.OperationName; - operationDocument = _documentRewriter.RewriteDocument(operationDocument, operationName); + var operationDocument = documentInfo.Document; var operationNode = operationDocument.GetOperation(operationName); - // After optimizing the query structure we can begin the planning process. operation = _operationPlanner.Compile( operationId ?? Guid.NewGuid().ToString("N"), documentInfo.Hash.Value, @@ -69,7 +64,7 @@ public static RequestMiddlewareConfiguration Create() (core, next) => { var schema = core.Schema; - var operationCompiler = core.Services.GetRequiredService(); + var operationCompiler = core.SchemaServices.GetRequiredService(); var diagnosticEvents = core.SchemaServices.GetRequiredService(); var middleware = new OperationResolverMiddleware( diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 1aa47e7467e..49a07b7e4e9 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -61,6 +61,7 @@ public Operation Compile( ArgumentException.ThrowIfNullOrWhiteSpace(id); ArgumentNullException.ThrowIfNull(operationDefinition); + // Before we can plan an operation, we must de-fragmentize it and remove static include conditions. var document = new DocumentNode([operationDefinition]); document = _documentRewriter.RewriteDocument(document); operationDefinition = (OperationDefinitionNode)document.Definitions[0]; From 133f6942efba492f489153f7563c745040bbabff Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 5 Dec 2025 14:40:05 +0100 Subject: [PATCH 26/30] wip --- ...estExecutorBuilderExtensions.Convention.cs | 2 +- .../Core/src/Types/Execution/ErrorHelper.cs | 2 +- .../Extensions/ExecutionResultExtensions.cs | 2 +- .../Processing/MiddlewareContext.Arguments.cs | 2 +- .../Types/Execution/Processing/Operation.cs | 2 +- .../OperationCompiler.CoerceArgumentValues.cs | 6 - .../Execution/Processing/OperationCompiler.cs | 2 +- .../Processing/OperationContext.Operation.cs | 2 +- .../Processing/Result/ResultBuilder.cs | 2 +- .../Execution/Processing/RootValueResolver.cs | 3 +- .../Types/Execution/Processing/Selection.cs | 2 +- .../Processing/Tasks/ResolverTask.Pooling.cs | 1 - .../Processing/Tasks/ResolverTask.cs | 4 +- .../Processing/VariableValueOrLiteral.cs | 2 +- .../Types/Execution/RequestExecutorManager.cs | 17 +- .../Core/src/Types/Execution/ThrowHelper.cs | 2 +- .../Types/Fetching/BatchDispatcherResult.cs | 2 +- .../Fetching/ExecutionDataLoaderScope.cs | 3 +- .../Types/Fetching/Utilities/ThrowHelper.cs | 2 +- .../Core/src/Types/HotChocolate.Types.csproj | 23 + .../Properties/FetchingResources.Designer.cs | 34 +- .../Types/Properties/FetchingResources.resx | 112 +---- .../Types/Properties/Resources.Designer.cs | 470 +++++------------- .../Conventions/DescriptorContext.cs | 8 +- .../Execution/FusionRequestExecutorManager.cs | 2 +- 25 files changed, 215 insertions(+), 494 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs index a2c6eab4b96..bc762ba1c1c 100644 --- a/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs @@ -1,6 +1,6 @@ using HotChocolate; using HotChocolate.Execution.Configuration; -using HotChocolate.Execution.Properties; +using HotChocolate.Properties; using HotChocolate.Types.Descriptors; using static HotChocolate.Execution.ThrowHelper; diff --git a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs index abcb9c0ca53..2507ff251a6 100644 --- a/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs @@ -2,7 +2,7 @@ using System.Net; using HotChocolate.Execution.Processing; using HotChocolate.Language; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; namespace HotChocolate.Execution; diff --git a/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs index 8e0e5c914f2..08931bcac27 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs @@ -3,7 +3,7 @@ using HotChocolate.Buffers; using HotChocolate.Execution; using HotChocolate.Transport.Formatters; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; // ReSharper disable once CheckNamespace namespace HotChocolate; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs index e69ab937ba7..16bca1ca7b7 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs @@ -2,7 +2,7 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; using static HotChocolate.Execution.ThrowHelper; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs index c88f27dda86..f62474155e8 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -32,7 +32,7 @@ internal Operation( SelectionSet rootSelectionSet, OperationCompiler compiler, IncludeConditionCollection includeConditions, - OperationFeatureCollection features, + OperationFeatureCollection features, int lastId, object[] elementsById) { diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs index bcfc1ce0212..62b09006798 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs @@ -1,12 +1,6 @@ -using System.Buffers; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using HotChocolate.Fusion.Rewriters; using HotChocolate.Language; -using HotChocolate.Language.Visitors; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.Extensions.ObjectPool; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 49a07b7e4e9..e417997b6ae 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -294,7 +294,7 @@ private SelectionSet BuildSelectionSet( var pureFieldDelegate = TryCreatePureField(_schema, field, first.Node); var arguments = ArgumentMap.Empty; - if (first.Node.Arguments.Count > 0) + if (field.Arguments.Count > 0) { arguments = CoerceArgumentValues(field, first.Node); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs index 8921485ebd5..ab9451f9a43 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs @@ -1,4 +1,4 @@ -using HotChocolate.Execution.Properties; +using HotChocolate.Properties; using HotChocolate.Types; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs index 82f764c4804..653ea7c7d95 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs @@ -1,6 +1,6 @@ using System.Buffers; using System.Runtime.CompilerServices; -using HotChocolate.Execution.Properties; +using HotChocolate.Properties; using HotChocolate.Resolvers; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs index eb1c09b379b..d0d6524aa18 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs @@ -1,6 +1,5 @@ using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs index d47b00eb706..194fa17715d 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -1,4 +1,4 @@ -using HotChocolate.Execution.Properties; +using HotChocolate.Properties; using HotChocolate.Features; using HotChocolate.Language; using HotChocolate.Resolvers; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs index 5bd5f9edf88..a5d30fe42a4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs @@ -40,7 +40,6 @@ internal bool Reset() Next = null; Previous = null; State = null; - _task = null; _taskBuffer.Clear(); _args.Clear(); return true; diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs index 4b19453a6e7..277a00bd565 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs @@ -1,5 +1,6 @@ using HotChocolate.Execution.Instrumentation; using HotChocolate.Resolvers; +using HotChocolate.Utilities; using Microsoft.Extensions.ObjectPool; namespace HotChocolate.Execution.Processing.Tasks; @@ -12,7 +13,6 @@ internal sealed partial class ResolverTask(ObjectPool objectPool) private OperationContext _operationContext = null!; private Selection _selection = null!; private ExecutionTaskStatus _completionStatus = ExecutionTaskStatus.Completed; - private Task? _task; /// /// Gets or sets the internal execution id. @@ -71,6 +71,6 @@ public ExecutionTaskKind Kind public void BeginExecute(CancellationToken cancellationToken) { Status = ExecutionTaskStatus.Running; - _task = ExecuteAsync(cancellationToken); + ExecuteAsync(cancellationToken).FireAndForget(); } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs index 4b82070912f..61844bacb3e 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs @@ -1,6 +1,6 @@ using HotChocolate.Language; using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; namespace HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs index a3ebe5b7ee4..56e873af829 100644 --- a/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs +++ b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs @@ -35,18 +35,19 @@ internal sealed partial class RequestExecutorManager private readonly IServiceProvider _applicationServices; private readonly EventObservable _events = new(); private readonly ChannelWriter _executorEvictionChannelWriter; + private ulong _version; private bool _disposed; public RequestExecutorManager( IOptionsMonitor optionsMonitor, - IServiceProvider serviceProvider) + IServiceProvider applicationServices) { ArgumentNullException.ThrowIfNull(optionsMonitor); - ArgumentNullException.ThrowIfNull(serviceProvider); + ArgumentNullException.ThrowIfNull(applicationServices); _optionsMonitor = optionsMonitor; - _applicationServices = serviceProvider; + _applicationServices = applicationServices; var executorEvictionChannel = Channel.CreateUnbounded(); _executorEvictionChannelWriter = executorEvictionChannel.Writer; @@ -89,8 +90,9 @@ public async ValueTask GetExecutorAsync( throw new InvalidOperationException($"The requested schema '{schemaName}' does not exist."); } - var registeredExecutor = await CreateRequestExecutorAsync(schemaName, true, cancellationToken) - .ConfigureAwait(false); + var registeredExecutor = + await CreateRequestExecutorAsync(schemaName, true, cancellationToken) + .ConfigureAwait(false); return registeredExecutor.Executor; } @@ -266,7 +268,8 @@ await typeModuleChangeMonitor.ConfigureAsync(context, cancellationToken) serviceCollection.AddSingleton(new SchemaVersionInfo(version)); - serviceCollection.AddSingleton(context.DescriptorContext.TypeConverter); + serviceCollection.AddSingleton( + _ => context.DescriptorContext.TypeConverter); serviceCollection.AddSingleton( static sp => new InputParser(sp.GetRequiredService())); @@ -282,6 +285,8 @@ await typeModuleChangeMonitor.ConfigureAsync(context, cancellationToken) static _ => new DefaultObjectPoolProvider()); serviceCollection.AddSingleton( static sp => sp.GetRequiredService().CreateStringBuilderPool()); + serviceCollection.AddSingleton( + static sp => sp.GetRequiredService().CreateFieldMapPool()); serviceCollection.AddSingleton(executorOptions); serviceCollection.AddSingleton( diff --git a/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs index d2ac4820927..81cd8a172c0 100644 --- a/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs +++ b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs @@ -1,6 +1,6 @@ using HotChocolate.Language; using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Properties.Resources; namespace HotChocolate.Execution; diff --git a/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs index 96e1ef89d5b..409662c4ca2 100644 --- a/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs +++ b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs @@ -1,4 +1,4 @@ -using HotChocolate.Fetching.Properties; +using HotChocolate.Properties; namespace HotChocolate.Fetching; diff --git a/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs index ebf55abd726..737e035edfc 100644 --- a/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs +++ b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs @@ -1,8 +1,7 @@ using System.Collections.Concurrent; using GreenDonut; using GreenDonut.DependencyInjection; -using HotChocolate.Fetching.Properties; -using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Properties; namespace HotChocolate.Fetching; diff --git a/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs index 51281d7e99e..bb7a836dc5f 100644 --- a/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs +++ b/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs @@ -1,4 +1,4 @@ -using static HotChocolate.Fetching.Properties.FetchingResources; +using static HotChocolate.Properties.FetchingResources; namespace HotChocolate.Fetching.Utilities; diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 30ebd2b0c99..17b5240fe90 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -221,6 +221,24 @@ OperationFeatureCollection.cs + + + RequestExecutorManager.cs + + + + RequestExecutorManager.cs + + + + RequestExecutorManager.cs + + + + True + True + FetchingResources.resx + @@ -322,6 +340,11 @@ ResXFileCodeGenerator TextJsonResources.Designer.cs + + + ResXFileCodeGenerator + FetchingResources.Designer.cs + diff --git a/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs index 83d091f7900..a7beaa7e6ea 100644 --- a/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs @@ -7,34 +7,34 @@ // //------------------------------------------------------------------------------ -namespace HotChocolate.Fetching.Properties { +namespace HotChocolate.Properties { using System; - - + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class FetchingResources { - + private static System.Resources.ResourceManager resourceMan; - + private static System.Globalization.CultureInfo resourceCulture; - + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal FetchingResources() { } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Resources.ResourceManager ResourceManager { get { if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Fetching.Properties.FetchingResources", typeof(FetchingResources).Assembly); + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Properties.FetchingResources", typeof(FetchingResources).Assembly); resourceMan = temp; } return resourceMan; } } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Globalization.CultureInfo Culture { get { @@ -44,49 +44,49 @@ internal static System.Globalization.CultureInfo Culture { resourceCulture = value; } } - + internal static string DataLoaderRegistry_KeyNullOrEmpty { get { return ResourceManager.GetString("DataLoaderRegistry_KeyNullOrEmpty", resourceCulture); } } - + internal static string DefaultDataLoaderRegistry_GetOrRegister { get { return ResourceManager.GetString("DefaultDataLoaderRegistry_GetOrRegister", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_RegistryIsNull { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_RegistryIsNull", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_UnableToRegister { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_UnableToRegister", resourceCulture); } } - + internal static string ThrowHelper_DataLoader_InvalidType { get { return ResourceManager.GetString("ThrowHelper_DataLoader_InvalidType", resourceCulture); } } - + internal static string BatchDispatcherResult_NoExceptions { get { return ResourceManager.GetString("BatchDispatcherResult_NoExceptions", resourceCulture); diff --git a/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx index 054ca8f64cb..67e73398d1e 100644 --- a/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx @@ -1,122 +1,24 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + text/microsoft-resx - 2.0 + 1.3 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + The DataLoader key cannot be null or empty. @@ -136,7 +38,7 @@ Unable to register a DataLoader with your DataLoader registry. - The provided type {0} is not a dataloader + The provided type {0} is not a DataLoader. There must be at least one exception to create an error result. diff --git a/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs index bf137efe381..89f003a93dd 100644 --- a/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs @@ -7,50 +7,36 @@ // //------------------------------------------------------------------------------ -namespace HotChocolate.Execution.Properties { +namespace HotChocolate.Properties { using System; - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { - private static global::System.Resources.ResourceManager resourceMan; + private static System.Resources.ResourceManager resourceMan; - private static global::System.Globalization.CultureInfo resourceCulture; + private static System.Globalization.CultureInfo resourceCulture; - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HotChocolate.Execution.Properties.Resources", typeof(Resources).Assembly); + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -59,561 +45,375 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to Serial execution tasks cannot be batched.. - /// - internal static string BatchExecutionTask_AddExecutionTask_SerialTasksNotAllowed { + internal static string ThrowHelper_FieldDoesNotExistOnType { get { - return ResourceManager.GetString("BatchExecutionTask_AddExecutionTask_SerialTasksNotAllowed", resourceCulture); + return ResourceManager.GetString("ThrowHelper_FieldDoesNotExistOnType", resourceCulture); } } - /// - /// Looks up a localized string similar to You can only read a response stream once.. - /// - internal static string DeferredResult_ReadResultsAsync_ReadOnlyOnce { + internal static string ObjectBuffer_IsEmpty { get { - return ResourceManager.GetString("DeferredResult_ReadResultsAsync_ReadOnlyOnce", resourceCulture); + return ResourceManager.GetString("ObjectBuffer_IsEmpty", resourceCulture); } } - /// - /// Looks up a localized string similar to Detected a non-null violation in argument `{0}`.. - /// - internal static string ErrorHelper_ArgumentNonNullError_Message { + internal static string ObjectBuffer_IsUsedUp { get { - return ResourceManager.GetString("ErrorHelper_ArgumentNonNullError_Message", resourceCulture); + return ResourceManager.GetString("ObjectBuffer_IsUsedUp", resourceCulture); } } - /// - /// Looks up a localized string similar to The type `{0}` is not supported as list value.. - /// - internal static string ErrorHelper_ListValueIsNotSupported_Message { + internal static string PreparedSelection_ReadOnly { get { - return ResourceManager.GetString("ErrorHelper_ListValueIsNotSupported_Message", resourceCulture); + return ResourceManager.GetString("PreparedSelection_ReadOnly", resourceCulture); } } - /// - /// Looks up a localized string similar to The request exceeded the configured timeout of `{0}`.. - /// - internal static string ErrorHelper_RequestTimeout { + internal static string DeferredResult_ReadResultsAsync_ReadOnlyOnce { get { - return ResourceManager.GetString("ErrorHelper_RequestTimeout", resourceCulture); + return ResourceManager.GetString("DeferredResult_ReadResultsAsync_ReadOnlyOnce", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified root type `{0}` is not supported by this server.. - /// - internal static string ErrorHelper_RootTypeNotFound_Message { + internal static string ErrorHelper_ArgumentNonNullError_Message { get { - return ResourceManager.GetString("ErrorHelper_RootTypeNotFound_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_ArgumentNonNullError_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Either no compiled operation was found or the variables have not been coerced.. - /// - internal static string ErrorHelper_StateInvalidForOperationExecution_Message { + internal static string ErrorHelper_UnableToResolveTheAbstractType_Message { get { - return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationExecution_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_UnableToResolveTheAbstractType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Either no query document exists or the document validation result is invalid.. - /// - internal static string ErrorHelper_StateInvalidForOperationResolver_Message { + internal static string ErrorHelper_ListValueIsNotSupported_Message { get { - return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationResolver_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_ListValueIsNotSupported_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to There is no operation on the context which can be used to coerce variables.. - /// - internal static string ErrorHelper_StateInvalidForOperationVariableCoercion_Message { + internal static string ErrorHelper_UnexpectedValueCompletionError_Message { get { - return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationVariableCoercion_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_UnexpectedValueCompletionError_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Unable to resolve the abstract type `{0}`.. - /// - internal static string ErrorHelper_UnableToResolveTheAbstractType_Message { + internal static string ErrorHelper_RootTypeNotFound_Message { get { - return ResourceManager.GetString("ErrorHelper_UnableToResolveTheAbstractType_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_RootTypeNotFound_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Unexpected error during value completion.. - /// - internal static string ErrorHelper_UnexpectedValueCompletionError_Message { + internal static string ErrorHelper_StateInvalidForOperationResolver_Message { get { - return ResourceManager.GetString("ErrorHelper_UnexpectedValueCompletionError_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationResolver_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Could not resolve the actual object type from `{0}` for the abstract type `{1}`.. - /// - internal static string ErrorHelper_ValueCompletion_CouldNotResolveAbstractType_Message { + internal static string ErrorHelper_StateInvalidForOperationVariableCoercion_Message { get { - return ResourceManager.GetString("ErrorHelper_ValueCompletion_CouldNotResolveAbstractType_Message", resourceCulture); + return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationVariableCoercion_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The query cannot be null or empty.. - /// - internal static string ExecutionRequestExecutorExtensions_ExecuteAsync_QueryCannotBeNullOrEmpty { + internal static string ErrorHelper_StateInvalidForOperationExecution_Message { get { - return ResourceManager.GetString("ExecutionRequestExecutorExtensions_ExecuteAsync_QueryCannotBeNullOrEmpty", resourceCulture); + return ResourceManager.GetString("ErrorHelper_StateInvalidForOperationExecution_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Only query results are supported.. - /// - internal static string ExecutionResultExtensions_OnlyQueryResults { + internal static string ErrorHelper_ValueCompletion_CouldNotResolveAbstractType_Message { get { - return ResourceManager.GetString("ExecutionResultExtensions_OnlyQueryResults", resourceCulture); + return ResourceManager.GetString("ErrorHelper_ValueCompletion_CouldNotResolveAbstractType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to There is no argument with the name `{0}`.. - /// - internal static string MiddlewareContext_ReplaceArgument_InvalidKey { + internal static string ThrowHelper_VariableIsNotAnInputType_Message { get { - return ResourceManager.GetString("MiddlewareContext_ReplaceArgument_InvalidKey", resourceCulture); + return ResourceManager.GetString("ThrowHelper_VariableIsNotAnInputType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to It is not allowed to replace the arguments with null.. - /// - internal static string MiddlewareContext_ReplaceArguments_NullNotAllowed { + internal static string ThrowHelper_NonNullVariableIsNull_Message { get { - return ResourceManager.GetString("MiddlewareContext_ReplaceArguments_NullNotAllowed", resourceCulture); + return ResourceManager.GetString("ThrowHelper_NonNullVariableIsNull_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Buffer is full.. - /// - internal static string ObjectBuffer_IsEmpty { + internal static string ThrowHelper_VariableValueInvalidType_Message { get { - return ResourceManager.GetString("ObjectBuffer_IsEmpty", resourceCulture); + return ResourceManager.GetString("ThrowHelper_VariableValueInvalidType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Buffer is used up.. - /// - internal static string ObjectBuffer_IsUsedUp { + internal static string ThrowHelper_VariableNotFound_Message { get { - return ResourceManager.GetString("ObjectBuffer_IsUsedUp", resourceCulture); + return ResourceManager.GetString("ThrowHelper_VariableNotFound_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified selection does not have a selection set.. - /// - internal static string Operation_GetPossibleTypes_NoSelectionSet { + internal static string ThrowHelper_VariableNotOfType_Message { get { - return ResourceManager.GetString("Operation_GetPossibleTypes_NoSelectionSet", resourceCulture); + return ResourceManager.GetString("ThrowHelper_VariableNotOfType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The operation selection set is empty.. - /// - internal static string OperationCompiler_Compile_SelectionSetIsEmpty { + internal static string ThrowHelper_RootTypeNotSupported_Message { get { - return ResourceManager.GetString("OperationCompiler_Compile_SelectionSetIsEmpty", resourceCulture); + return ResourceManager.GetString("ThrowHelper_RootTypeNotSupported_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The document did not contain a fragment definition with the name `{0}`.. - /// - internal static string OperationCompiler_FragmentNotFound { + internal static string ThrowHelper_SubscriptionExecutor_ContextInvalidState_Message { get { - return ResourceManager.GetString("OperationCompiler_FragmentNotFound", resourceCulture); + return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_ContextInvalidState_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The operation compiler only allows for 64 unique include conditions.. - /// - internal static string OperationCompiler_ToManyIncludeConditions { + internal static string ThrowHelper_SubscriptionExecutor_SubscriptionsMustHaveOneField_Message { get { - return ResourceManager.GetString("OperationCompiler_ToManyIncludeConditions", resourceCulture); + return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_SubscriptionsMustHaveOneField_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The query type could not be casted to {0}.. - /// - internal static string OperationContext_GetQueryRoot_InvalidCast { + internal static string ThrowHelper_SubscriptionExecutor_NoSubscribeResolver_Message { get { - return ResourceManager.GetString("OperationContext_GetQueryRoot_InvalidCast", resourceCulture); + return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_NoSubscribeResolver_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The selection is read-only.. - /// - internal static string PreparedSelection_ReadOnly { + internal static string ThrowHelper_ResolverContext_LiteralsNotSupported_Message { get { - return ResourceManager.GetString("PreparedSelection_ReadOnly", resourceCulture); + return ResourceManager.GetString("ThrowHelper_ResolverContext_LiteralsNotSupported_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Invalid fragment id.. - /// - internal static string QueryPlan_InvalidFragmentId { + internal static string ThrowHelper_ResolverContext_CannotConvertArgument_Message { get { - return ResourceManager.GetString("QueryPlan_InvalidFragmentId", resourceCulture); + return ResourceManager.GetString("ThrowHelper_ResolverContext_CannotConvertArgument_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified convention type is not supported.. - /// - internal static string RequestExecutorBuilder_Convention_NotSupported { + internal static string ThrowHelper_ResolverContext_LiteralNotCompatible_Message { get { - return ResourceManager.GetString("RequestExecutorBuilder_Convention_NotSupported", resourceCulture); + return ResourceManager.GetString("ThrowHelper_ResolverContext_LiteralNotCompatible_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Its not allowed to set `items` and `data` at the same time.. - /// - internal static string ResultBuilder_DataAndItemsNotAllowed { + internal static string ThrowHelper_ResolverContext_ArgumentDoesNotExist_Message { get { - return ResourceManager.GetString("ResultBuilder_DataAndItemsNotAllowed", resourceCulture); + return ResourceManager.GetString("ThrowHelper_ResolverContext_ArgumentDoesNotExist_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to A GraphQL result must have data, errors or both.. - /// - internal static string ResultHelper_BuildResult_InvalidResult { + internal static string ThrowHelper_OperationResolverHelper_NoOperationFound_Message { get { - return ResourceManager.GetString("ResultHelper_BuildResult_InvalidResult", resourceCulture); + return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_NoOperationFound_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Unable to create the operation type `{0}` instance. Try adding the following service: `services.AddScoped<{1}>();`. - /// - internal static string RootValueResolver_Resolve_CannotCreateInstance { + internal static string ThrowHelper_OperationResolverHelper_MultipleOperation_Message { get { - return ResourceManager.GetString("RootValueResolver_Resolve_CannotCreateInstance", resourceCulture); + return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_MultipleOperation_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The provided response name must be the same as on the selection.. - /// - internal static string SelectionSetOptimizerContext_AddSelection_ResponseNameNotTheSame { + internal static string ThrowHelper_OperationResolverHelper_InvalidOperationName_Message { get { - return ResourceManager.GetString("SelectionSetOptimizerContext_AddSelection_ResponseNameNotTheSame", resourceCulture); + return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_InvalidOperationName_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The `{0}` is not a valid GraphQL field name.. - /// - internal static string SelectionSetOptimizerContext_InvalidFieldName { + internal static string ThrowHelper_BatchExecutor_CannotSerializeVariable_Message { get { - return ResourceManager.GetString("SelectionSetOptimizerContext_InvalidFieldName", resourceCulture); + return ResourceManager.GetString("ThrowHelper_BatchExecutor_CannotSerializeVariable_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to This SelectionVariants instance is read-only.. - /// - internal static string SelectionVariants_ReadOnly { + internal static string ThrowHelper_CollectVariablesVisitor_NoCompatibleType_Message { get { - return ResourceManager.GetString("SelectionVariants_ReadOnly", resourceCulture); + return ResourceManager.GetString("ThrowHelper_CollectVariablesVisitor_NoCompatibleType_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified type `{0}` is not a possible type to this selection set.. - /// - internal static string SelectionVariants_TypeContextInvalid { + internal static string ThrowHelper_FieldVisibility_ValueNotSupported_Message { get { - return ResourceManager.GetString("SelectionVariants_TypeContextInvalid", resourceCulture); + return ResourceManager.GetString("ThrowHelper_FieldVisibility_ValueNotSupported_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Could not serialize the specified variable `{0}`.. - /// - internal static string ThrowHelper_BatchExecutor_CannotSerializeVariable_Message { + internal static string ThrowHelper_QueryCompiler_CompositeTypeSelectionSet_Message { get { - return ResourceManager.GetString("ThrowHelper_BatchExecutor_CannotSerializeVariable_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_QueryCompiler_CompositeTypeSelectionSet_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Unable to find a compatible input type for the exported object type.. - /// - internal static string ThrowHelper_CollectVariablesVisitor_NoCompatibleType_Message { + internal static string ThrowHelper_OperationExecutionMiddleware_NoBatchDispatcher_Message { get { - return ResourceManager.GetString("ThrowHelper_CollectVariablesVisitor_NoCompatibleType_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_OperationExecutionMiddleware_NoBatchDispatcher_Message", resourceCulture); } } - /// - /// Looks up a localized string similar to Field `{0}` does not exist on type `{1}`.. - /// - internal static string ThrowHelper_FieldDoesNotExistOnType { + internal static string OperationCompiler_Compile_SelectionSetIsEmpty { get { - return ResourceManager.GetString("ThrowHelper_FieldDoesNotExistOnType", resourceCulture); + return ResourceManager.GetString("OperationCompiler_Compile_SelectionSetIsEmpty", resourceCulture); } } - /// - /// Looks up a localized string similar to The skip/include if-argument value has to be a 'Boolean'.. - /// - internal static string ThrowHelper_FieldVisibility_ValueNotSupported_Message { + internal static string ExecutionRequestExecutorExtensions_ExecuteAsync_QueryCannotBeNullOrEmpty { get { - return ResourceManager.GetString("ThrowHelper_FieldVisibility_ValueNotSupported_Message", resourceCulture); + return ResourceManager.GetString("ExecutionRequestExecutorExtensions_ExecuteAsync_QueryCannotBeNullOrEmpty", resourceCulture); } } - /// - /// Looks up a localized string similar to The {0} only supports formatting `IQueryResult`.. - /// - internal static string ThrowHelper_JsonFormatter_ResultNotSupported { + internal static string RequestExecutorBuilder_Convention_NotSupported { get { - return ResourceManager.GetString("ThrowHelper_JsonFormatter_ResultNotSupported", resourceCulture); + return ResourceManager.GetString("RequestExecutorBuilder_Convention_NotSupported", resourceCulture); } } - /// - /// Looks up a localized string similar to Variable `{0}` is required.. - /// - internal static string ThrowHelper_NonNullVariableIsNull_Message { + internal static string RootValueResolver_Resolve_CannotCreateInstance { get { - return ResourceManager.GetString("ThrowHelper_NonNullVariableIsNull_Message", resourceCulture); + return ResourceManager.GetString("RootValueResolver_Resolve_CannotCreateInstance", resourceCulture); } } - /// - /// Looks up a localized string similar to Value for OneOf field {0} must be non-null.. - /// - internal static string ThrowHelper_OneOfFieldMustBeNonNull { + internal static string OperationContext_GetQueryRoot_InvalidCast { get { - return ResourceManager.GetString("ThrowHelper_OneOfFieldMustBeNonNull", resourceCulture); + return ResourceManager.GetString("OperationContext_GetQueryRoot_InvalidCast", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified selection does not have a selection set.. - /// - internal static string ThrowHelper_Operation_NoSelectionSet { + internal static string ErrorHelper_RequestTimeout { get { - return ResourceManager.GetString("ThrowHelper_Operation_NoSelectionSet", resourceCulture); + return ResourceManager.GetString("ErrorHelper_RequestTimeout", resourceCulture); } } - /// - /// Looks up a localized string similar to Make sure that you have registered an IBatchDispatcher with your scoped request services.. - /// - internal static string ThrowHelper_OperationExecutionMiddleware_NoBatchDispatcher_Message { + internal static string ResultHelper_BuildResult_InvalidResult { get { - return ResourceManager.GetString("ThrowHelper_OperationExecutionMiddleware_NoBatchDispatcher_Message", resourceCulture); + return ResourceManager.GetString("ResultHelper_BuildResult_InvalidResult", resourceCulture); } } - /// - /// Looks up a localized string similar to The specified operation `{0}` cannot be found.. - /// - internal static string ThrowHelper_OperationResolverHelper_InvalidOperationName_Message { + internal static string BatchExecutionTask_AddExecutionTask_SerialTasksNotAllowed { get { - return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_InvalidOperationName_Message", resourceCulture); + return ResourceManager.GetString("BatchExecutionTask_AddExecutionTask_SerialTasksNotAllowed", resourceCulture); } } - /// - /// Looks up a localized string similar to The operation name can only be omitted if there is just one operation in a GraphQL document.. - /// - internal static string ThrowHelper_OperationResolverHelper_MultipleOperation_Message { + internal static string QueryPlan_InvalidFragmentId { get { - return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_MultipleOperation_Message", resourceCulture); + return ResourceManager.GetString("QueryPlan_InvalidFragmentId", resourceCulture); } } - /// - /// Looks up a localized string similar to There are no operations in the GraphQL document.. - /// - internal static string ThrowHelper_OperationResolverHelper_NoOperationFound_Message { + internal static string WorkBacklog_NotFullyInitialized { get { - return ResourceManager.GetString("ThrowHelper_OperationResolverHelper_NoOperationFound_Message", resourceCulture); + return ResourceManager.GetString("WorkBacklog_NotFullyInitialized", resourceCulture); } } - /// - /// Looks up a localized string similar to A composite type always needs to specify a selection set.. - /// - internal static string ThrowHelper_QueryCompiler_CompositeTypeSelectionSet_Message { + internal static string ThrowHelper_ResolverContext_CannotCastParent { get { - return ResourceManager.GetString("ThrowHelper_QueryCompiler_CompositeTypeSelectionSet_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_ResolverContext_CannotCastParent", resourceCulture); } } - /// - /// Looks up a localized string similar to There was no argument with the name `{0}` found on the field `{1}`.. - /// - internal static string ThrowHelper_ResolverContext_ArgumentDoesNotExist_Message { + internal static string ExecutionResultExtensions_OnlyQueryResults { get { - return ResourceManager.GetString("ThrowHelper_ResolverContext_ArgumentDoesNotExist_Message", resourceCulture); + return ResourceManager.GetString("ExecutionResultExtensions_OnlyQueryResults", resourceCulture); } } - /// - /// Looks up a localized string similar to The resolver parent type of field `{0}` is `{1}` but the resolver requested the type `{2}`. The resolver was unable to cast the parent type to the requested type.. - /// - internal static string ThrowHelper_ResolverContext_CannotCastParent { + internal static string SelectionVariants_ReadOnly { get { - return ResourceManager.GetString("ThrowHelper_ResolverContext_CannotCastParent", resourceCulture); + return ResourceManager.GetString("SelectionVariants_ReadOnly", resourceCulture); } } - /// - /// Looks up a localized string similar to Unable to convert the value of the argument `{0}` to `{1}`. Check if the requested type is correct or register a custom type converter.. - /// - internal static string ThrowHelper_ResolverContext_CannotConvertArgument_Message { + internal static string OperationCompiler_FragmentNotFound { get { - return ResourceManager.GetString("ThrowHelper_ResolverContext_CannotConvertArgument_Message", resourceCulture); + return ResourceManager.GetString("OperationCompiler_FragmentNotFound", resourceCulture); } } - /// - /// Looks up a localized string similar to The argument literal representation is `{0}` which is not compatible with the request literal type `{1}`.. - /// - internal static string ThrowHelper_ResolverContext_LiteralNotCompatible_Message { + internal static string SelectionSetOptimizerContext_InvalidFieldName { get { - return ResourceManager.GetString("ThrowHelper_ResolverContext_LiteralNotCompatible_Message", resourceCulture); + return ResourceManager.GetString("SelectionSetOptimizerContext_InvalidFieldName", resourceCulture); } } - /// - /// Looks up a localized string similar to The ArgumentValue method on the resolver context only allows for runtime values. If you want to retrieve the argument value as GraphQL literal use the ArgumentLiteral method instead.. - /// - internal static string ThrowHelper_ResolverContext_LiteralsNotSupported_Message { + internal static string OperationCompiler_ToManyIncludeConditions { get { - return ResourceManager.GetString("ThrowHelper_ResolverContext_LiteralsNotSupported_Message", resourceCulture); + return ResourceManager.GetString("OperationCompiler_ToManyIncludeConditions", resourceCulture); } } - /// - /// Looks up a localized string similar to The root type `{0}` is not supported.. - /// - internal static string ThrowHelper_RootTypeNotSupported_Message { + internal static string SelectionVariants_TypeContextInvalid { get { - return ResourceManager.GetString("ThrowHelper_RootTypeNotSupported_Message", resourceCulture); + return ResourceManager.GetString("SelectionVariants_TypeContextInvalid", resourceCulture); } } - /// - /// Looks up a localized string similar to The type {0} was already added.. - /// internal static string ThrowHelper_SelectionSet_TypeAlreadyAdded { get { return ResourceManager.GetString("ThrowHelper_SelectionSet_TypeAlreadyAdded", resourceCulture); } } - /// - /// Looks up a localized string similar to The request context is in an invalid state for subscriptions.. - /// - internal static string ThrowHelper_SubscriptionExecutor_ContextInvalidState_Message { + internal static string ThrowHelper_OneOfFieldMustBeNonNull { get { - return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_ContextInvalidState_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_OneOfFieldMustBeNonNull", resourceCulture); } } - /// - /// Looks up a localized string similar to You must declare a subscribe resolver for subscription fields.. - /// - internal static string ThrowHelper_SubscriptionExecutor_NoSubscribeResolver_Message { + internal static string ThrowHelper_Operation_NoSelectionSet { get { - return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_NoSubscribeResolver_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_Operation_NoSelectionSet", resourceCulture); } } - /// - /// Looks up a localized string similar to Subscription queries must have exactly one root field.. - /// - internal static string ThrowHelper_SubscriptionExecutor_SubscriptionsMustHaveOneField_Message { + internal static string ThrowHelper_JsonFormatter_ResultNotSupported { get { - return ResourceManager.GetString("ThrowHelper_SubscriptionExecutor_SubscriptionsMustHaveOneField_Message", resourceCulture); + return ResourceManager.GetString("ThrowHelper_JsonFormatter_ResultNotSupported", resourceCulture); } } - /// - /// Looks up a localized string similar to Variable `{0}` is not an input type.. - /// - internal static string ThrowHelper_VariableIsNotAnInputType_Message { + internal static string ResultBuilder_DataAndItemsNotAllowed { get { - return ResourceManager.GetString("ThrowHelper_VariableIsNotAnInputType_Message", resourceCulture); + return ResourceManager.GetString("ResultBuilder_DataAndItemsNotAllowed", resourceCulture); } } - /// - /// Looks up a localized string similar to The variable with the name `{0}` does not exist.. - /// - internal static string ThrowHelper_VariableNotFound_Message { + internal static string MiddlewareContext_ReplaceArgument_InvalidKey { get { - return ResourceManager.GetString("ThrowHelper_VariableNotFound_Message", resourceCulture); + return ResourceManager.GetString("MiddlewareContext_ReplaceArgument_InvalidKey", resourceCulture); } } - /// - /// Looks up a localized string similar to The variable with the name `{0}` is not of the requested type `{1}`.. - /// - internal static string ThrowHelper_VariableNotOfType_Message { + internal static string VariableValueOrLiteral_NullNotAllowed { get { - return ResourceManager.GetString("ThrowHelper_VariableNotOfType_Message", resourceCulture); + return ResourceManager.GetString("VariableValueOrLiteral_NullNotAllowed", resourceCulture); } } - /// - /// Looks up a localized string similar to Variable `{0}` got an invalid value.. - /// - internal static string ThrowHelper_VariableValueInvalidType_Message { + internal static string Operation_GetPossibleTypes_NoSelectionSet { get { - return ResourceManager.GetString("ThrowHelper_VariableValueInvalidType_Message", resourceCulture); + return ResourceManager.GetString("Operation_GetPossibleTypes_NoSelectionSet", resourceCulture); } } - /// - /// Looks up a localized string similar to The runtime value can only be null if the literal is also null.. - /// - internal static string VariableValueOrLiteral_NullNotAllowed { + internal static string MiddlewareContext_ReplaceArguments_NullNotAllowed { get { - return ResourceManager.GetString("VariableValueOrLiteral_NullNotAllowed", resourceCulture); + return ResourceManager.GetString("MiddlewareContext_ReplaceArguments_NullNotAllowed", resourceCulture); } } - /// - /// Looks up a localized string similar to The WorkBacklog is not fully initialized.. - /// - internal static string WorkBacklog_NotFullyInitialized { + internal static string SelectionSetOptimizerContext_AddSelection_ResponseNameNotTheSame { get { - return ResourceManager.GetString("WorkBacklog_NotFullyInitialized", resourceCulture); + return ResourceManager.GetString("SelectionSetOptimizerContext_AddSelection_ResponseNameNotTheSame", resourceCulture); } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs index 8d3a58a703d..a4eaa83a427 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs @@ -267,26 +267,26 @@ private TypeDiscoveryHandler[] CreateTypeDiscoveryHandlers() internal static DescriptorContext Create( IReadOnlySchemaOptions? options = null, - IServiceProvider? services = null, + IServiceProvider? schemaServices = null, IFeatureCollection? features = null, SchemaBuilder.LazySchema? schema = null, TypeInterceptor? typeInterceptor = null) => new DescriptorContext( () => options ??= new SchemaOptions(), - services ?? EmptyServiceProvider.Instance, + schemaServices ?? EmptyServiceProvider.Instance, features ?? new FeatureCollection(), schema ?? new SchemaBuilder.LazySchema(), typeInterceptor ?? new AggregateTypeInterceptor()); internal static DescriptorContext Create( Func options, - IServiceProvider services, + IServiceProvider schemaServices, IFeatureCollection? features = null, SchemaBuilder.LazySchema? schema = null, TypeInterceptor? typeInterceptor = null) => new DescriptorContext( options, - services, + schemaServices, features ?? new FeatureCollection(), schema ?? new SchemaBuilder.LazySchema(), typeInterceptor ?? new AggregateTypeInterceptor()); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/FusionRequestExecutorManager.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/FusionRequestExecutorManager.cs index 45e2670f7ce..091741d21e4 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/FusionRequestExecutorManager.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/FusionRequestExecutorManager.cs @@ -560,7 +560,7 @@ private sealed class SchemaDefinitionAccessor public FusionSchemaDefinition Schema { get; set; } = null!; } - public sealed class RequestExecutorRegistration : IAsyncDisposable + private sealed class RequestExecutorRegistration : IAsyncDisposable { private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly CancellationToken _cancellationToken; From 1b44d4591dd370b4ac4fcff484268f6739fe101b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 5 Dec 2025 19:20:19 +0100 Subject: [PATCH 27/30] wip --- .../Pipeline/OperationResolverMiddleware.cs | 8 +- .../Processing/MiddlewareContext.Global.cs | 17 +-- .../Execution/Processing/OperationCompiler.cs | 26 ++-- .../Extensions/ResolverContextExtensions.cs | 16 +- ...cMethod_Await_Return_ApplicationError.json | 6 - ...ncMethod_Await_Throw_ApplicationError.json | 6 - ...yncMethod_Await_Throw_UnexpectedError.json | 6 - ...ethod_NoAwait_Return_ApplicationError.json | 6 - ...Method_NoAwait_Throw_ApplicationError.json | 6 - ...cMethod_NoAwait_Throw_UnexpectedError.json | 6 - ...haviorTests.Error_Filter_Adds_Details.json | 6 - ...viorTests.Error_On_NonNull_Root_Field.json | 6 - ...ests.Property_Return_ApplicationError.json | 6 - ...Tests.Property_Throw_ApplicationError.json | 6 - ...rTests.Property_Throw_UnexpectedError.json | 6 - ...ts.SyncMethod_Return_ApplicationError.json | 6 - ...sts.SyncMethod_Throw_ApplicationError.json | 6 - ...ests.SyncMethod_Throw_UnexpectedError.json | 6 - ...ErrorHandlerTests.AddClassErrorFilter.json | 6 - ...ViaServiceExtensions_ErrorFilterWorks.json | 6 - ...ViaServiceExtensions_ErrorFilterWorks.json | 6 - ...rTests.AddClassErrorFilterWithFactory.json | 6 - ...ViaServiceExtensions_ErrorFilterWorks.json | 6 - .../ErrorHandlerTests.AddFuncErrorFilter.json | 6 - ...dlerTests.FilterOnlyNullRefExceptions.json | 12 -- ...ests.ReportAggregateError_In_Resolver.json | 12 -- ...ests.UseAggregateError_In_ErrorFilter.json | 12 -- ...HasError_nonnull_list_nonnull_element.json | 6 - ...asError_nonnull_list_nullable_element.json | 6 - ...asError_nullable_list_nonnull_element.json | 6 - ...sError_nullable_list_nullable_element.json | 6 - ...ntIsNull_nonnull_list_nonnull_element.json | 6 - ...tIsNull_nonnull_list_nullable_element.json | 6 - ...tIsNull_nullable_list_nonnull_element.json | 6 - ...IsNull_nullable_list_nullable_element.json | 6 - ...t_NonNullElementHasError_nonnull_prop.json | 6 - ..._NonNullElementHasError_nullable_prop.json | 6 - ...ect_NonNullElementIsNull_nonnull_prop.json | 6 - ...ct_NonNullElementIsNull_nullable_prop.json | 6 - ...ld_ThrowException_When_NotADataLoader.snap | 2 +- ..._To_NonNullArgument_With_DefaultValue.snap | 8 +- .../StarWarsCodeFirstTests.cs | 66 ++++---- .../Processing/OperationCompilerTests.cs | 144 ++++-------------- ...iables_Fails_When_Variable_Not_Exists.snap | 6 - ...s_Delegate_ReplaceWithNull_ShouldFail.snap | 6 - ...sure_Errors_Do_Not_Result_In_Timeouts.snap | 8 +- ...OutputType_ClrValue_CannotBeConverted.snap | 8 +- ...ts.OutputType_ClrValue_CannotBeParsed.snap | 6 - ...t_Item_Returns_Null_Should_Error_Item.snap | 6 - ...m_Throwing_Should_Null_And_Error_Item.snap | 12 -- ...Object_List_Returns_Null_Should_Error.snap | 6 - ..._List_Throwing_Should_Null_FAnd_Error.snap | 6 - ...sync_Object_Returns_Null_Should_Error.snap | 6 - ...Object_Throwing_Should_Null_And_Error.snap | 6 - ...t_Item_Returns_Null_Should_Error_Item.snap | 6 - ...m_Throwing_Should_Null_And_Error_Item.snap | 12 -- ...Scalar_List_Returns_Null_Should_Error.snap | 6 - ...r_List_Throwing_Should_Null_And_Error.snap | 6 - ...sync_Scalar_Returns_Null_Should_Error.snap | 6 - ...Scalar_Throwing_Should_Null_And_Error.snap | 6 - ...t_Item_Returns_Null_Should_Error_Item.snap | 6 - ...m_Throwing_Should_Null_And_Error_Item.snap | 12 -- ...Object_List_Returns_Null_Should_Error.snap | 6 - ..._List_Throwing_Should_Null_FAnd_Error.snap | 6 - ...Pure_Object_Returns_Null_Should_Error.snap | 6 - ...Object_Throwing_Should_Null_And_Error.snap | 6 - ...ner_Return_Null_Should_Null_And_Error.snap | 12 -- ...le_Returns_Null_Should_Null_And_Error.snap | 6 - ...t_Item_Returns_Null_Should_Error_Item.snap | 6 - ...m_Throwing_Should_Null_And_Error_Item.snap | 12 -- ...Scalar_List_Returns_Null_Should_Error.snap | 6 - ...r_List_Throwing_Should_Null_And_Error.snap | 6 - ...Pure_Scalar_Returns_Null_Should_Error.snap | 6 - ...Scalar_Throwing_Should_Null_And_Error.snap | 6 - ...ticNonNullTests.Query_With_Connection.snap | 6 - ...SchemaBuilderExtensions.Resolvers.Tests.cs | 10 +- .../Descriptors/ArgumentDescriptorTests.cs | 2 +- .../DirectiveArgumentDescriptorTests.cs | 2 +- .../Descriptors/InputFieldDescriptorTests.cs | 2 +- .../InterfaceFieldDescriptorTests.cs | 2 +- .../Descriptors/ObjectFieldDescriptorTests.cs | 2 +- .../Types.Tests/Types/DirectiveTypeTests.cs | 2 +- .../Types.Tests/Types/InputObjectTypeTests.cs | 6 +- .../Types.Tests/Types/InterfaceTypeTests.cs | 2 +- .../test/Types.Tests/Types/ObjectTypeTests.cs | 2 +- .../Execution/Nodes/OperationCompiler.cs | 2 +- 86 files changed, 116 insertions(+), 659 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs index 0a301750db7..d67c0373e4d 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs @@ -1,6 +1,5 @@ using HotChocolate.Execution.Instrumentation; using HotChocolate.Execution.Processing; -using HotChocolate.Fusion.Rewriters; using static HotChocolate.Execution.ErrorHelper; namespace HotChocolate.Execution.Pipeline; @@ -40,14 +39,11 @@ public async ValueTask InvokeAsync(RequestContext context) { using (_diagnosticEvents.CompileOperation(context)) { - var operationName = context.Request.OperationName; - var operationDocument = documentInfo.Document; - var operationNode = operationDocument.GetOperation(operationName); - operation = _operationPlanner.Compile( operationId ?? Guid.NewGuid().ToString("N"), documentInfo.Hash.Value, - operationNode); + context.Request.OperationName, + documentInfo.Document); context.SetOperation(operation); } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs index d1ba41b2545..e6dbd0f01c9 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs @@ -49,7 +49,6 @@ public void ReportError(string errorMessage) ErrorBuilder.New() .SetMessage(errorMessage) .SetPath(Path) - .AddLocations(_selection) .Build()); } @@ -75,8 +74,7 @@ public void ReportError(Exception exception, Action? configure = n { var errorBuilder = ErrorBuilder .FromException(exception) - .SetPath(Path) - .AddLocations(_selection); + .SetPath(Path); configure?.Invoke(errorBuilder); @@ -291,16 +289,3 @@ c is null s))); } } - -file static class Extensions -{ - public static ErrorBuilder AddLocations(this ErrorBuilder errorBuilder, Selection selection) - { - foreach (var (node, _) in selection.SyntaxNodes) - { - errorBuilder.AddLocation(node); - } - - return errorBuilder; - } -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index e417997b6ae..5448655c3ae 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -36,14 +36,22 @@ internal OperationCompiler( public static Operation Compile( string id, - OperationDefinitionNode operationDefinition, + DocumentNode document, Schema schema) - => Compile(id, id, operationDefinition, schema); + => Compile(id, id, null, document, schema); + + public static Operation Compile( + string id, + string? operationName, + DocumentNode document, + Schema schema) + => Compile(id, id, operationName, document, schema); public static Operation Compile( string id, string hash, - OperationDefinitionNode operationDefinition, + string? operationName, + DocumentNode document, Schema schema) => new OperationCompiler( schema, @@ -51,20 +59,20 @@ public static Operation Compile( new DefaultObjectPool>>( new DefaultPooledObjectPolicy>>()), new OperationCompilerOptimizers()) - .Compile(id, hash, operationDefinition); + .Compile(id, hash, operationName, document); public Operation Compile( string id, string hash, - OperationDefinitionNode operationDefinition) + string? operationName, + DocumentNode document) { ArgumentException.ThrowIfNullOrWhiteSpace(id); - ArgumentNullException.ThrowIfNull(operationDefinition); + ArgumentNullException.ThrowIfNull(document); // Before we can plan an operation, we must de-fragmentize it and remove static include conditions. - var document = new DocumentNode([operationDefinition]); document = _documentRewriter.RewriteDocument(document); - operationDefinition = (OperationDefinitionNode)document.Definitions[0]; + var operationDefinition = document.GetOperation(operationName); var includeConditions = new IncludeConditionCollection(); IncludeConditionVisitor.Instance.Visit(operationDefinition, includeConditions); @@ -319,7 +327,7 @@ private SelectionSet BuildSelectionSet( compilationContext.Register(selection, selection.Id); selections[i++] = selection; - if (includeFlags.Count > 1) + if (includeFlags.Count > 0) { isConditional = true; } diff --git a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs index 9a886885497..2865bfed608 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs @@ -438,7 +438,7 @@ public bool IsSelected(string fieldName) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsIncluded(includeFlags) && selection.Field.Name.Equals(fieldName)) @@ -453,7 +453,7 @@ public bool IsSelected(string fieldName) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsIncluded(includeFlags) && selection.Field.Name.Equals(fieldName)) @@ -501,7 +501,7 @@ public bool IsSelected(string fieldName1, string fieldName2) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsSkipped(includeFlags)) { @@ -520,7 +520,7 @@ public bool IsSelected(string fieldName1, string fieldName2) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsSkipped(includeFlags)) { @@ -577,7 +577,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsSkipped(includeFlags)) { @@ -598,7 +598,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsSkipped(includeFlags)) { @@ -647,7 +647,7 @@ public bool IsSelected(ISet fieldNames) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, possibleType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsIncluded(includeFlags) && fieldNames.Contains(selection.Field.Name)) @@ -662,7 +662,7 @@ public bool IsSelected(ISet fieldNames) var includeFlags = context.IncludeFlags; var selectionSet = context.Operation.GetSelectionSet(context.Selection, (ObjectType)namedType); - foreach(var selection in selectionSet.Selections) + foreach (var selection in selectionSet.Selections) { if (selection.IsIncluded(includeFlags) && fieldNames.Contains(selection.Field.Name)) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Return_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Return_ApplicationError.json index b850c73db2b..6b78593e0be 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Return_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Return_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 6", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error6" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_ApplicationError.json index 7b000a4d97e..789fcde3231 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 4", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error4" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_UnexpectedError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_UnexpectedError.json index afbf6e89d41..4c878b126c4 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_UnexpectedError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_Await_Throw_UnexpectedError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error5" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Return_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Return_ApplicationError.json index fdefe0720fc..7bfbbfb8e89 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Return_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Return_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 3", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error3" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_ApplicationError.json index 4e2e87ae3a4..0d87efd2255 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 1", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error1" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_UnexpectedError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_UnexpectedError.json index d664a0c23c0..3fc814a785f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_UnexpectedError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.AsyncMethod_NoAwait_Throw_UnexpectedError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error2" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_Filter_Adds_Details.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_Filter_Adds_Details.json index 1c8564fb675..a23da695a95 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_Filter_Adds_Details.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_Filter_Adds_Details.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error14" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_On_NonNull_Root_Field.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_On_NonNull_Root_Field.json index 0e2677a1dae..c556743fc23 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_On_NonNull_Root_Field.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Error_On_NonNull_Root_Field.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error15" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_ApplicationError.json index 1e0b49ac42f..96fe813df42 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 12", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error12" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_ApplicationError.json index 75de3a1df02..37bb2829107 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 10", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error10" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_UnexpectedError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_UnexpectedError.json index 97b6b55fea8..7d17b7ccf14 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_UnexpectedError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Throw_UnexpectedError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error11" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Return_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Return_ApplicationError.json index 19eb7bc4683..651f04b8fe5 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Return_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Return_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 9", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error9" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_ApplicationError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_ApplicationError.json index 33700a3ac45..976e35bf402 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_ApplicationError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_ApplicationError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "query error 7", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error7" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_UnexpectedError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_UnexpectedError.json index c80eaa0aa71..c0c6aa99577 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_UnexpectedError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyncMethod_Throw_UnexpectedError.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "error8" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter.json index ef697553c2f..4b69180ef2e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingDI_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingDI_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json index 20e2cfaaf20..9a8d330d490 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingDI_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingDI_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingFactory_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingFactory_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json index 20e2cfaaf20..9a8d330d490 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingFactory_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterUsingFactory_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterWithFactory.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterWithFactory.json index ef697553c2f..4b69180ef2e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterWithFactory.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilterWithFactory.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json index 20e2cfaaf20..9a8d330d490 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddClassErrorFilter_SchemaBuiltViaServiceExtensions_ErrorFilterWorks.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddFuncErrorFilter.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddFuncErrorFilter.json index ef697553c2f..4b69180ef2e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddFuncErrorFilter.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.AddFuncErrorFilter.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.FilterOnlyNullRefExceptions.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.FilterOnlyNullRefExceptions.json index 8e67f4315f2..4cfef53f3ea 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.FilterOnlyNullRefExceptions.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.FilterOnlyNullRefExceptions.json @@ -2,24 +2,12 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ] }, { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 7 - } - ], "path": [ "bar" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.ReportAggregateError_In_Resolver.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.ReportAggregateError_In_Resolver.json index 8bce9986d95..07034838f59 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.ReportAggregateError_In_Resolver.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.ReportAggregateError_In_Resolver.json @@ -2,24 +2,12 @@ "errors": [ { "message": "abc", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ] }, { "message": "def", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.UseAggregateError_In_ErrorFilter.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.UseAggregateError_In_ErrorFilter.json index 550bc931dd5..a0f0b7e4f95 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.UseAggregateError_In_ErrorFilter.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorHandlerTests.UseAggregateError_In_ErrorFilter.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], @@ -17,12 +11,6 @@ }, { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nonnull_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nonnull_element.json index 40a93880bd1..d12e8f2d757 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nonnull_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nonnull_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 40 - } - ], "path": [ "foo", "nonnull_list_nonnull_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nullable_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nullable_element.json index 31644d0b788..af36a82c07b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nullable_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nonnull_list_nullable_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 41 - } - ], "path": [ "foo", "nonnull_list_nullable_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nonnull_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nonnull_element.json index 2329e540d0f..265459aee68 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nonnull_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nonnull_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 41 - } - ], "path": [ "foo", "nullable_list_nonnull_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nullable_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nullable_element.json index 9575ba78887..3a8291a85f3 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nullable_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementHasError_nullable_list_nullable_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 42 - } - ], "path": [ "foo", "nullable_list_nullable_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nonnull_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nonnull_element.json index 431b16bef0c..353787c469a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nonnull_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nonnull_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 40 - } - ], "path": [ "foo", "nonnull_list_nonnull_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nullable_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nullable_element.json index 6befba572a7..c2a87772b88 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nullable_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nonnull_list_nullable_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 41 - } - ], "path": [ "foo", "nonnull_list_nullable_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nonnull_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nonnull_element.json index cb65fd9194d..12fd757bf13 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nonnull_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nonnull_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 41 - } - ], "path": [ "foo", "nullable_list_nonnull_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nullable_element.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nullable_element.json index 1cb53765ff2..82339e96531 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nullable_element.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.List_NonNullElementIsNull_nullable_list_nullable_element.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 42 - } - ], "path": [ "foo", "nullable_list_nullable_element", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nonnull_prop.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nonnull_prop.json index 40d979abf24..2de6c622f64 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nonnull_prop.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nonnull_prop.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 24 - } - ], "path": [ "foo", "nonnull_prop", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nullable_prop.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nullable_prop.json index ee6b4a00fa1..fca3c8ee191 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nullable_prop.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementHasError_nullable_prop.json @@ -2,12 +2,6 @@ "errors": [ { "message": "ERROR", - "locations": [ - { - "line": 1, - "column": 25 - } - ], "path": [ "foo", "nullable_prop", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nonnull_prop.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nonnull_prop.json index add4b91ff56..1a5bb81281e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nonnull_prop.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nonnull_prop.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 24 - } - ], "path": [ "foo", "nonnull_prop", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nullable_prop.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nullable_prop.json index 77c93273c7d..ec5ad8f3cb9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nullable_prop.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/NullErrorPropagationTests.Object_NonNullElementIsNull_nullable_prop.json @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 25 - } - ], "path": [ "foo", "nullable_prop", diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/UseDataLoaderTests.UseDataLoader_Should_ThrowException_When_NotADataLoader.snap b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/UseDataLoaderTests.UseDataLoader_Should_ThrowException_When_NotADataLoader.snap index f8943c78f19..31655be4474 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/UseDataLoaderTests.UseDataLoader_Should_ThrowException_When_NotADataLoader.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/UseDataLoaderTests.UseDataLoader_Should_ThrowException_When_NotADataLoader.snap @@ -2,7 +2,7 @@ For more details look at the `Errors` property. 1. For more details look at the `Errors` property. -1. The provided type HotChocolate.Execution.Integration.DataLoader.UseDataLoaderTests+Foo is not a dataloader +1. The provided type HotChocolate.Execution.Integration.DataLoader.UseDataLoaderTests+Foo is not a DataLoader. (HotChocolate.Types.ObjectType) at HotChocolate.Types.DataLoaderObjectFieldExtensions.UseDataLoader(IObjectFieldDescriptor descriptor, Type dataLoaderType) in ObjectFieldDataLoaderExtensions.cs:line 28 diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap b/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap index 55c5694d490..de63f0ffdb4 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "Detected a non-null violation in argument `name`.", - "locations": [ - { - "line": 1, - "column": 12 - } - ], "path": [ "sayHello" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs index ff4017a85bf..431abffa852 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs @@ -110,24 +110,26 @@ await ExpectValid( public async Task GraphQLOrgFragmentExample() { await ExpectValid( - @"{ - leftComparison: hero(episode: EMPIRE) { - ...comparisonFields - } - rightComparison: hero(episode: JEDI) { - ...comparisonFields + """ + { + leftComparison: hero(episode: EMPIRE) { + ...comparisonFields + } + rightComparison: hero(episode: JEDI) { + ...comparisonFields + } } - } - fragment comparisonFields on Character { - name - appearsIn - friends { - nodes { - name + fragment comparisonFields on Character { + name + appearsIn + friends { + nodes { + name + } } } - }") + """) .MatchSnapshotAsync(); } @@ -191,23 +193,24 @@ query HeroNameAndFriends($episode: Episode = JEDI) { public async Task GraphQLOrgDirectiveIncludeExample1() { await ExpectValid( - @" - query Hero($episode: Episode, $withFriends: Boolean!) { - hero(episode: $episode) { - name - friends @include(if: $withFriends) { - nodes { - name - } + """ + query Hero($episode: Episode, $withFriends: Boolean!) { + hero(episode: $episode) { + name + friends @include(if: $withFriends) { + nodes { + name } } - }", - request: c => c.SetVariableValues( - new Dictionary - { - { "episode", new EnumValueNode("JEDI") }, - { "withFriends", new BooleanValueNode(false) } - })) + } + } + """, + request: c => c.SetVariableValues( + new Dictionary + { + { "episode", new EnumValueNode("JEDI") }, + { "withFriends", new BooleanValueNode(false) } + })) .MatchSnapshotAsync(); } @@ -263,7 +266,7 @@ friends @skip(if: $withFriends) { public async Task GraphQLOrgDirectiveSkipExample2() { await ExpectValid( - @" + """ query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name @@ -273,7 +276,8 @@ friends @skip(if: $withFriends) { } } } - }", + } + """, request: r => r.SetVariableValues( new Dictionary { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs index 09311f012ba..103dee2fb3c 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs @@ -24,12 +24,10 @@ public void Prepare_One_Field() var document = Utf8GraphQLParser.Parse("{ foo }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -51,13 +49,10 @@ public void Prepare_Duplicate_Field() var document = Utf8GraphQLParser.Parse("{ foo foo }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -79,13 +74,10 @@ public void Prepare_Empty_Operation_SelectionSet() var document = Utf8GraphQLParser.Parse("{ }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -113,13 +105,10 @@ ... on Human { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -152,13 +141,10 @@ fragment def on Human { } "); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -218,13 +204,10 @@ fragment Human3 on Human { } """); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -247,12 +230,10 @@ public void Prepare_Duplicate_Field_With_Skip() var document = Utf8GraphQLParser.Parse( "{ foo @skip(if: true) foo @skip(if: false) }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -274,15 +255,12 @@ public void Field_Does_Not_Exist() var document = Utf8GraphQLParser.Parse("{ foo bar }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act void Action() { OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); } @@ -312,13 +290,10 @@ fragment abc on Droid { name }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -345,12 +320,10 @@ fragment abc on Droid { name }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -377,12 +350,10 @@ fragment abc on Droid { name }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -412,13 +383,10 @@ fragment abc on Droid { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -456,13 +424,10 @@ ... @include(if: $v) { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -525,12 +490,10 @@ fragment Human3 on Human { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -562,13 +525,10 @@ ... @include(if: $v) { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -590,12 +550,10 @@ name @include(if: $q) } }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -626,12 +584,10 @@ public void Field_Based_Optimizers() } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -656,13 +612,10 @@ ... @defer { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -689,13 +642,10 @@ fragment Foo on Droid { id }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -722,13 +672,10 @@ friends @include(if: $withFriends) { } }"); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -753,12 +700,10 @@ ... abc fragment abc on Droid { }"); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -783,12 +728,10 @@ ... on Droid { } } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -810,12 +753,10 @@ query foo($v: Boolean) { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -835,13 +776,10 @@ public async Task Large_Query_Test() var document = Utf8GraphQLParser.Parse( FileResource.Open("LargeQuery.graphql")); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -862,13 +800,10 @@ public async Task Crypto_Details_Test() var document = Utf8GraphQLParser.Parse( FileResource.Open("CryptoDetailQuery.graphql")); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -904,12 +839,10 @@ fragment PriceInfo on Asset { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -945,12 +878,10 @@ fragment PriceInfo on Asset { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -987,12 +918,10 @@ fragment PriceInfo on Asset { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -1013,13 +942,10 @@ public async Task Crypto_List_Test() var document = Utf8GraphQLParser.Parse( FileResource.Open("CryptoQuery.graphql")); - var operationDefinition = - document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -1098,12 +1024,10 @@ type OrganizationUnit2 implements OrganizationUnit { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -1182,12 +1106,10 @@ type OrganizationUnit2 implements OrganizationUnit { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert @@ -1225,12 +1147,10 @@ fragment TypeTwoParts on TypeTwo { } """); - var operationDefinition = document.Definitions.OfType().Single(); - // act var operation = OperationCompiler.Compile( "opid", - operationDefinition, + document, schema); // assert diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.AccessVariables_Fails_When_Variable_Not_Exists.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.AccessVariables_Fails_When_Variable_Not_Exists.snap index 9c1f3b954d6..c96c7071efb 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.AccessVariables_Fails_When_Variable_Not_Exists.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.AccessVariables_Fails_When_Variable_Not_Exists.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The variable with the name `abc` does not exist.", - "locations": [ - { - "line": 1, - "column": 26 - } - ], "path": [ "foo" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.ReplaceArguments_Delegate_ReplaceWithNull_ShouldFail.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.ReplaceArguments_Delegate_ReplaceWithNull_ShouldFail.snap index c04812ef5e3..081fea4c8ec 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.ReplaceArguments_Delegate_ReplaceWithNull_ShouldFail.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/MiddlewareContextTests.ReplaceArguments_Delegate_ReplaceWithNull_ShouldFail.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "abc" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/RequestExecutorTests.Ensure_Errors_Do_Not_Result_In_Timeouts.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/RequestExecutorTests.Ensure_Errors_Do_Not_Result_In_Timeouts.snap index 92153eb8136..f9b707d7a51 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/RequestExecutorTests.Ensure_Errors_Do_Not_Result_In_Timeouts.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/RequestExecutorTests.Ensure_Errors_Do_Not_Result_In_Timeouts.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 4, - "column": 25 - } - ], "path": [ "test" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeConverted.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeConverted.snap index 80077d78462..f4c607274eb 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeConverted.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeConverted.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "Name cannot serialize the given value.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "stringToName" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeParsed.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeParsed.snap index 8291e303e9b..0f73e3da82c 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeParsed.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/ScalarExecutionErrorTests.OutputType_ClrValue_CannotBeParsed.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Foo cannot serialize the given value.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "stringToFoo" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap index cef5849184d..e64c9d4a214 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectListItemReturningNull", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap index c136aaacabf..d578768ba80 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Another error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectListItemThrowingError", 1 @@ -15,12 +9,6 @@ }, { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectListItemThrowingError", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap index 5272b1193ac..cd74246f7cc 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectListReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap index 584bc579f4d..1a8e1b1447f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectListThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap index fa19729099c..de90deed3b8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap index de64fcf0f25..57ccb939ca0 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "objectThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap index 6e3b5178b29..f4a6d5a8478 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarListItemReturningNull", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap index 87f194c0037..464d5cdb295 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Another error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarListItemThrowingError", 1 @@ -15,12 +9,6 @@ }, { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarListItemThrowingError", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap index 136f20e321e..7c3567e8c3f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarListReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap index fe169a3cc33..a406ecdc3d9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarListThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap index d6f046b374e..d12cef73fdc 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap index f992ef3496b..ced147f1349 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "scalarThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap index 20741ef4898..702c8496c8a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectListItemReturningNull", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap index 338ea110d60..561c8238624 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Another error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectListItemThrowingError", 1 @@ -15,12 +9,6 @@ }, { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectListItemThrowingError", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap index dd9c523f560..2262faed20a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectListReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap index 89587618619..42516bd3c4b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectListThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap index 81b0b6ca595..c15611a44a1 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap index 76230f78282..63eeb3487ae 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureObjectThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap index 7c42ba3a2a3..7bf76b7e4c0 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "nestedScalarArrayNullableMiddleItem", 0, @@ -19,12 +13,6 @@ }, { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "nestedScalarArrayNullableMiddleItem", 2, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap index 5b68ea3fafb..bf9a690b5f8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "nestedScalarArrayNullableOuterItems", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap index f3a0b0bb1ca..ed0a4dd9a70 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarListItemReturningNull", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap index e5ecb022752..cdb53b6f7ff 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Another error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarListItemThrowingError", 1 @@ -15,12 +9,6 @@ }, { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarListItemThrowingError", 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap index e6c89302539..465984d2f34 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarListReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap index 87eb04d1690..44c06c8d747 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarListThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap index 4ddf157a011..d2e3122ede9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Cannot return null for semantic non-null field.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarReturningNull" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap index a1980169f92..1cbac92d059 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "pureScalarThrowingError" ] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap index af087216eda..e0e253ee4af 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 4, - "column": 7 - } - ], "path": [ "scalarConnection", "edges", diff --git a/src/HotChocolate/Core/test/Types.Tests/Extensions/SchemaBuilderExtensions.Resolvers.Tests.cs b/src/HotChocolate/Core/test/Types.Tests/Extensions/SchemaBuilderExtensions.Resolvers.Tests.cs index 3763da8f045..cc4c61b9e7f 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Extensions/SchemaBuilderExtensions.Resolvers.Tests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Extensions/SchemaBuilderExtensions.Resolvers.Tests.cs @@ -21,7 +21,7 @@ public void AddResolverContextObject_BuilderIsNull_ArgNullExcept() new Func(c => new object())); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] @@ -78,7 +78,7 @@ public void AddResolverContextTaskObject_BuilderIsNull_ArgNullExcept() c => Task.FromResult(new object()))); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] @@ -137,7 +137,7 @@ public void AddResolverContextTResult_BuilderIsNull_ArgNullExcept() c => "abc")); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] @@ -197,7 +197,7 @@ public void AddResolverContextTaskTResult_BuilderIsNull_ArgNullExcept() c => Task.FromResult("abc"))); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] @@ -256,7 +256,7 @@ public void AddResolverObject_BuilderIsNull_ArgNullExcept() new Func(() => "abc")); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ArgumentDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ArgumentDescriptorTests.cs index ad52c19a3b4..7ad560d1e7f 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ArgumentDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ArgumentDescriptorTests.cs @@ -254,6 +254,6 @@ public void Type_Syntax_Type_Null() public void Type_Syntax_Descriptor_Null() { void Error() => default(ArgumentDescriptor)!.Type("foo"); - Assert.Throws(Error); + Assert.Throws(Error); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/DirectiveArgumentDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/DirectiveArgumentDescriptorTests.cs index 0d2960e0a16..5a0c82a5207 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/DirectiveArgumentDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/DirectiveArgumentDescriptorTests.cs @@ -16,7 +16,7 @@ public void Type_Syntax_Type_Null() public void Type_Syntax_Descriptor_Null() { void Error() => default(DirectiveArgumentDescriptor)!.Type("foo"); - Assert.Throws(Error); + Assert.Throws(Error); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InputFieldDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InputFieldDescriptorTests.cs index 4f3a202a4c5..a8ae1bfe3a0 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InputFieldDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InputFieldDescriptorTests.cs @@ -275,6 +275,6 @@ public void Type_Syntax_Type_Null() public void Type_Syntax_Descriptor_Null() { void Error() => default(InputFieldDescriptor)!.Type("foo"); - Assert.Throws(Error); + Assert.Throws(Error); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InterfaceFieldDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InterfaceFieldDescriptorTests.cs index 32fe4edbef0..15fdd5ba3b9 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InterfaceFieldDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/InterfaceFieldDescriptorTests.cs @@ -15,6 +15,6 @@ public void Type_Syntax_Type_Null() public void Type_Syntax_Descriptor_Null() { void Error() => default(InterfaceFieldDescriptor)!.Type("foo"); - Assert.Throws(Error); + Assert.Throws(Error); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs index 60012d8f644..9bfdc1e5b6e 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/ObjectFieldDescriptorTests.cs @@ -200,7 +200,7 @@ public void Type_Syntax_Type_Null() public void Type_Syntax_Descriptor_Null() { void Error() => default(IObjectFieldDescriptor)!.Type("foo"); - Assert.Throws(Error); + Assert.Throws(Error); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/DirectiveTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/DirectiveTypeTests.cs index 29302077107..10d61b736cc 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/DirectiveTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/DirectiveTypeTests.cs @@ -224,7 +224,7 @@ public void Ignore_DescriptorIsNull_ArgumentNullException() .Ignore(null!, t => t.Argument2); // assert - Assert.Throws(action); + Assert.Throws(action); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/InputObjectTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/InputObjectTypeTests.cs index d5c75930696..9950a885fc9 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/InputObjectTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/InputObjectTypeTests.cs @@ -370,7 +370,7 @@ void Action() => InputObjectTypeDescriptorExtensions.Ignore(null!, t => t.Id); // assert - Assert.Throws(Action); + Assert.Throws(Action); } [Fact] @@ -723,7 +723,7 @@ public void OneOf_descriptor_is_null() { void Fail() => InputObjectTypeDescriptorExtensions.OneOf(null!); - Assert.Throws(Fail); + Assert.Throws(Fail); } [Fact] @@ -731,7 +731,7 @@ public void OneOf_generic_descriptor_is_null() { void Fail() => InputObjectTypeDescriptorExtensions.OneOf(null!); - Assert.Throws(Fail); + Assert.Throws(Fail); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/InterfaceTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/InterfaceTypeTests.cs index 5ce75ba4a04..d4b1590cfa3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/InterfaceTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/InterfaceTypeTests.cs @@ -439,7 +439,7 @@ public void Ignore_DescriptorIsNull_ArgumentNullException() void Action() => InterfaceTypeDescriptorExtensions.Ignore(null!, t => t.Bar); // assert - Assert.Throws(Action); + Assert.Throws(Action); } [Fact] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs index f8ef72e86dc..4d625b68366 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs @@ -1236,7 +1236,7 @@ public void IgnoreField_DescriptorIsNull_ArgumentNullException() void Action() => ObjectTypeDescriptorExtensions.Ignore(null!, t => t.Description); // assert - Assert.Throws(Action); + Assert.Throws(Action); } [Fact] diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationCompiler.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationCompiler.cs index bfabcec6ed3..f61b6edcba4 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationCompiler.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationCompiler.cs @@ -254,7 +254,7 @@ private SelectionSet BuildSelectionSet( compilationContext.Register(selection, selection.Id); selections[i++] = selection; - if (includeFlags.Count > 1) + if (includeFlags.Count > 0) { isConditional = true; } From e4180e61d0349d0d91d38b78081b632dd7cb0207 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 9 Dec 2025 12:06:02 +0100 Subject: [PATCH 28/30] Updated Snapshots --- .../Execution/Processing/OperationCompiler.cs | 4 +- .../Processing/SelectionFeatureCollection.cs | 2 +- .../Processing/Tasks/ResolverTaskFactory.cs | 1 + ..._To_NonNullArgument_With_DefaultValue.snap | 6 +++ .../StarWarsCodeFirstTests.cs | 16 ++++---- .../QueryableCursorPagingProviderTests.cs | 5 +-- .../IntegrationTests.First_Value_Not_Set.snap | 6 --- ...ationTests.Invalid_After_Index_Cursor.snap | 6 --- ...tionTests.Invalid_Before_Index_Cursor.snap | 6 --- ...nvalid_EmptyString_After_Index_Cursor.snap | 6 --- ...valid_EmptyString_Before_Index_Cursor.snap | 6 --- ...grationTests.MaxPageSizeReached_First.snap | 6 --- ...egrationTests.MaxPageSizeReached_Last.snap | 6 --- ...grationTests.MinPageSizeReached_First.snap | 6 --- ...egrationTests.MinPageSizeReached_Last.snap | 6 --- .../IntegrationTests.No_Boundaries_Set.snap | 6 --- ...s.Mutation_Aggregate_Error_Not_Mapped.snap | 6 --- .../IntegrationTests.MaxPageSizeReached.snap | 8 +--- ...IntegrationTests.No_Paging_Boundaries.snap | 8 +--- ...esult_And_Exceptions_Unexpected_Error.snap | 6 --- ...esult_And_Exceptions_Unexpected_Error.snap | 6 --- .../Resolvers/ResolverCompilerTests.cs | 10 +---- ...ptorTests.Input_Should_EmptySelection.snap | 8 +--- ...ceptorTests.Input_Should_UnknownValue.snap | 8 +--- ...ttributeTests.Id_On_Objects_InvalidId.snap | 4 +- ...ributeTests.Id_On_Objects_InvalidType.snap | 4 +- ...g_Throws_On_InvalidInputs_InvalidArgs.snap | 24 ------------ ....Node_Resolve_Implicit_Custom_IdField.snap | 6 --- .../NodeFieldSupportTests.Tow_Many_Nodes.snap | 6 --- ...houldReturnNonNullError_When_IdIsNull.snap | 8 +--- ..._Against_Interface_Without_Impl_Field.snap | 6 --- .../Rewriters/DocumentRewriter.cs | 2 +- .../InlineFragmentOperationRewriter.cs | 22 ++++++----- ...ault_ErrorHandlingMode_Can_Be_Changed.yaml | 6 --- ...Http_Request_In_Node_Is_Still_Ongoing.yaml | 6 --- ...d_While_Subscription_Is_Still_Ongoing.yaml | 6 --- ..._Source_Schema_Are_Properly_Forwarded.yaml | 6 --- ..._On_Lookup_Field_In_List_OnError_Halt.yaml | 18 --------- ..._On_Lookup_Field_In_List_OnError_Null.yaml | 18 --------- ...ookup_Field_In_List_OnError_Propagate.yaml | 18 --------- ...ts.Error_On_Lookup_Field_OnError_Halt.yaml | 6 --- ...ts.Error_On_Lookup_Field_OnError_Null.yaml | 6 --- ...ror_On_Lookup_Field_OnError_Propagate.yaml | 6 --- ...kup_Leaf_In_List_NonNull_OnError_Halt.yaml | 18 --------- ...kup_Leaf_In_List_NonNull_OnError_Null.yaml | 18 --------- ...eaf_In_List_NonNull_OnError_Propagate.yaml | 18 --------- ...r_On_Lookup_Leaf_In_List_OnError_Halt.yaml | 18 --------- ...r_On_Lookup_Leaf_In_List_OnError_Null.yaml | 18 --------- ...Lookup_Leaf_In_List_OnError_Propagate.yaml | 18 --------- ...r_On_Lookup_Leaf_NonNull_OnError_Halt.yaml | 6 --- ...r_On_Lookup_Leaf_NonNull_OnError_Null.yaml | 6 --- ...Lookup_Leaf_NonNull_OnError_Propagate.yaml | 6 --- ...sts.Error_On_Lookup_Leaf_OnError_Halt.yaml | 6 --- ...sts.Error_On_Lookup_Leaf_OnError_Null.yaml | 6 --- ...rror_On_Lookup_Leaf_OnError_Propagate.yaml | 6 --- ...ests.Error_On_Root_Field_OnError_Halt.yaml | 6 --- ...ests.Error_On_Root_Field_OnError_Null.yaml | 6 --- ...Error_On_Root_Field_OnError_Propagate.yaml | 6 --- ...Tests.Error_On_Root_Leaf_OnError_Halt.yaml | 6 --- ...Tests.Error_On_Root_Leaf_OnError_Null.yaml | 6 --- ....Error_On_Root_Leaf_OnError_Propagate.yaml | 6 --- ...For_Lookup_Field_NonNull_OnError_Halt.yaml | 6 --- ...For_Lookup_Field_NonNull_OnError_Null.yaml | 6 --- ...ookup_Field_NonNull_OnError_Propagate.yaml | 6 --- ..._For_Lookup_Leaf_NonNull_OnError_Halt.yaml | 6 --- ..._For_Lookup_Leaf_NonNull_OnError_Null.yaml | 6 --- ...Lookup_Leaf_NonNull_OnError_Propagate.yaml | 6 --- ...h_For_Root_Field_NonNull_OnError_Halt.yaml | 6 --- ...h_For_Root_Field_NonNull_OnError_Null.yaml | 6 --- ..._Root_Field_NonNull_OnError_Propagate.yaml | 6 --- ...NonNull_One_Service_Errors_EntryField.yaml | 6 --- ...ullable_One_Service_Errors_EntryField.yaml | 6 --- ...d_NonNull_One_Service_Errors_SubField.yaml | 6 --- ..._Nullable_One_Service_Errors_SubField.yaml | 6 --- ..._Nullable_One_Service_Errors_SubField.yaml | 6 --- ...NonNull_One_Service_Errors_EntryField.yaml | 6 --- ...t_NonNull_One_Service_Errors_SubField.yaml | 6 --- ...ullable_One_Service_Errors_EntryField.yaml | 6 --- ..._Nullable_One_Service_Errors_SubField.yaml | 6 --- ...ullable_One_Service_Errors_EntryField.yaml | 6 --- ..._Nullable_One_Service_Errors_SubField.yaml | 6 --- .../InlineFragmentOperationRewriterTests.cs | 37 +++++++++++++++++++ 82 files changed, 80 insertions(+), 589 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs index 5448655c3ae..b9d6de55af4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -14,7 +14,7 @@ public sealed partial class OperationCompiler private readonly Schema _schema; private readonly ObjectPool>> _fieldsPool; private readonly OperationCompilerOptimizers _optimizers; - private readonly DocumentRewriter _documentRewriter; + private readonly InlineFragmentOperationRewriter _documentRewriter; private readonly InputParser _inputValueParser; private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; @@ -30,7 +30,7 @@ internal OperationCompiler( _schema = schema; _inputValueParser = inputValueParser; _fieldsPool = fieldsPool; - _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); + _documentRewriter = new InlineFragmentOperationRewriter(schema, removeStaticallyExcludedSelections: true); _optimizers = optimizers; } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs index f14458099ba..ce8d18f2e11 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionFeatureCollection.cs @@ -101,7 +101,7 @@ public bool TryGet([NotNullWhen(true)] out TFeature? feature) /// The existing or newly created feature instance. /// This method is thread-safe. public TFeature GetOrSetSafe() where TFeature : new() - => GetOrSetSafe(static () => new TFeature()); + => GetOrSetSafe(static () => new TFeature()); internal TFeature GetOrSetSafe(Func factory) => _parent.GetOrSetSafe(_selectionId, factory); diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs index c54b36a56f5..d48b6592620 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs @@ -76,6 +76,7 @@ public static ObjectResult EnqueueResolverTasks( } } + // TODO : remove ? public static ResolverTask EnqueueElementTasks( OperationContext operationContext, Selection selection, diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap b/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap index de63f0ffdb4..5644d15eb0b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/Spec/__snapshots__/ArgumentCoercionTests.Pass_In_Null_To_NonNullArgument_With_DefaultValue.snap @@ -2,6 +2,12 @@ "errors": [ { "message": "Detected a non-null violation in argument `name`.", + "locations": [ + { + "line": 1, + "column": 12 + } + ], "path": [ "sayHello" ], diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs index 431abffa852..610a421e5ba 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs @@ -1101,15 +1101,15 @@ name @include(if: {{ifValue}}) public async Task Include_With_Variable(bool ifValue) { await ExpectValid( - """ - query ($if: Boolean!) { - human(id: "1000") { - name @include(if: $if) - height - } + """ + query ($if: Boolean!) { + human(id: "1000") { + name @include(if: $if) + height } - """, - request: r => r.SetVariableValues(new Dictionary { { "if", ifValue } })) + } + """, + request: r => r.SetVariableValues(new Dictionary { { "if", ifValue } })) .MatchSnapshotAsync(postFix: ifValue); } diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs index 7555d0f1906..8b4a846d4c4 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs @@ -519,10 +519,7 @@ public static async Task CreateContextAsync(CursorPagingArgume } """); - var operation = OperationCompiler.Compile( - "abc", - document.Definitions.OfType().First(), - schema); + var operation = OperationCompiler.Compile("abc", document, schema); return new MockContext(arguments, operation, operation.RootSelectionSet.Selections[0]); } diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.First_Value_Not_Set.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.First_Value_Not_Set.snap index d7a9894ef12..a1203609ca9 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.First_Value_Not_Set.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.First_Value_Not_Set.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "You must provide a `first` value to properly paginate the `LettersConnection`.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_After_Index_Cursor.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_After_Index_Cursor.snap index 826e7a0cb9b..7aa38b4514b 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_After_Index_Cursor.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_After_Index_Cursor.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The cursor specified in `after` has an invalid format.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_Before_Index_Cursor.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_Before_Index_Cursor.snap index cd2cb54e714..dd21d96ff6d 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_Before_Index_Cursor.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_Before_Index_Cursor.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The cursor specified in `before` has an invalid format.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_After_Index_Cursor.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_After_Index_Cursor.snap index c15f8a3745b..deeb2a245cc 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_After_Index_Cursor.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_After_Index_Cursor.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The cursor specified in `after` has an invalid format.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_Before_Index_Cursor.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_Before_Index_Cursor.snap index f130bc24d0f..d10fd0432e0 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_Before_Index_Cursor.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Invalid_EmptyString_Before_Index_Cursor.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The cursor specified in `before` has an invalid format.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_First.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_First.snap index aff5a792fec..b48fc9361dd 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_First.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_First.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The maximum allowed items per page were exceeded.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_Last.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_Last.snap index aff5a792fec..b48fc9361dd 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_Last.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached_Last.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The maximum allowed items per page were exceeded.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_First.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_First.snap index 2f4bd3fe3d0..e3b36abf61b 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_First.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_First.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The requested number of values per page must be at least 0.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_Last.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_Last.snap index 2f4bd3fe3d0..e3b36abf61b 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_Last.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.MinPageSizeReached_Last.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The requested number of values per page must be at least 0.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap index 9ff705486c9..5c3f2ffa91f 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "You must provide a `first` or `last` value to properly paginate the `LettersConnection`.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.Mutation_Aggregate_Error_Not_Mapped.snap b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.Mutation_Aggregate_Error_Not_Mapped.snap index 76b40b4a801..6a0504ad745 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.Mutation_Aggregate_Error_Not_Mapped.snap +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.Mutation_Aggregate_Error_Not_Mapped.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "doSomething2" ] diff --git a/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached.snap b/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached.snap index 4201ec8fb7f..2dad32b0401 100644 --- a/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached.snap +++ b/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.MaxPageSizeReached.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "The maximum allowed items per page were exceeded.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.No_Paging_Boundaries.snap b/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.No_Paging_Boundaries.snap index 3320ff188f4..d516032374a 100644 --- a/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.No_Paging_Boundaries.snap +++ b/src/HotChocolate/Core/test/Types.OffsetPagination.Tests/__snapshots__/IntegrationTests.No_Paging_Boundaries.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "You must provide take to properly paginate the `LettersCollectionSegment`.", - "locations": [ - { - "line": 3, - "column": 21 - } - ], "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/AnnotationBasedSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/AnnotationBasedSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap index 13609ca9403..884083d29d0 100644 --- a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/AnnotationBasedSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/AnnotationBasedSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "userById" ] diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap index 13609ca9403..884083d29d0 100644 --- a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Execute_Query_With_FieldResult_And_Exceptions_Unexpected_Error.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "userById" ] diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs index 1ae88b29256..329c2ae5357 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs @@ -630,10 +630,7 @@ public async Task Compile_Arguments_Operation() .Create(); var document = Utf8GraphQLParser.Parse("{ abc }"); - var operation = OperationCompiler.Compile( - "abc", - document.Definitions.OfType().First(), - schema); + var operation = OperationCompiler.Compile("abc", document, schema); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); @@ -747,10 +744,7 @@ public async Task Compile_Arguments_Document() .Create(); var document = Utf8GraphQLParser.Parse("{ abc }"); - var operation = OperationCompiler.Compile( - "abc", - document.Definitions.OfType().First(), - schema); + var operation = OperationCompiler.Compile("abc", document, schema); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_EmptySelection.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_EmptySelection.snap index dbfb030642e..a5a60149dd2 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_EmptySelection.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_EmptySelection.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "Flags need to have at least one selection. Type: FlagsEnumFlagsInput", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "test" ] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_UnknownValue.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_UnknownValue.snap index 163f6e8b341..e4b071e30f2 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_UnknownValue.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Interceptors/__snapshots__/FlagEnumInterceptorTests.Input_Should_UnknownValue.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "The value isAsd is not known for type FlagsEnumFlagsInput", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "test" ] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidId.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidId.snap index 3e0559c0364..e4427c22fe6 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidId.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidId.snap @@ -1,4 +1,4 @@ -{ - "result": "{\n \"errors\": [\n {\n \"message\": \"The node ID string has an invalid format.\",\n \"locations\": [\n {\n \"line\": 2,\n \"column\": 5\n }\n ],\n \"path\": [\n \"foo\"\n ],\n \"extensions\": {\n \"originalValue\": \"abc\"\n }\n }\n ],\n \"data\": null\n}", +{ + "result": "{\n \"errors\": [\n {\n \"message\": \"The node ID string has an invalid format.\",\n \"path\": [\n \"foo\"\n ],\n \"extensions\": {\n \"originalValue\": \"abc\"\n }\n }\n ],\n \"data\": null\n}", "someId": "abc" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidType.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidType.snap index b1a75519cee..2377ccf1b7d 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidType.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_InvalidType.snap @@ -1,4 +1,4 @@ -{ - "result": "{\n \"errors\": [\n {\n \"message\": \"The node id type name `Query` does not match the expected type name `Some`.\",\n \"locations\": [\n {\n \"line\": 2,\n \"column\": 5\n }\n ],\n \"path\": [\n \"foo\"\n ]\n }\n ],\n \"data\": null\n}", +{ + "result": "{\n \"errors\": [\n {\n \"message\": \"The node id type name `Query` does not match the expected type name `Some`.\",\n \"path\": [\n \"foo\"\n ]\n }\n ],\n \"data\": null\n}", "someId": "UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA==" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Honors_CustomTypeNaming_Throws_On_InvalidInputs_InvalidArgs.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Honors_CustomTypeNaming_Throws_On_InvalidInputs_InvalidArgs.snap index dd746ccc21a..696c3baa90b 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Honors_CustomTypeNaming_Throws_On_InvalidInputs_InvalidArgs.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Honors_CustomTypeNaming_Throws_On_InvalidInputs_InvalidArgs.snap @@ -2,48 +2,24 @@ "errors": [ { "message": "The node id type name `FooFoo` does not match the expected type name `RenamedUser`.", - "locations": [ - { - "line": 2, - "column": 5 - } - ], "path": [ "validUserIdInput" ] }, { "message": "The node id type name `FooFooFluent` does not match the expected type name `FooFoo`.", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "validFooIdInput" ] }, { "message": "The node id type name `FooFooFluentSingle` does not match the expected type name `FooFooFluent`.", - "locations": [ - { - "line": 4, - "column": 5 - } - ], "path": [ "validFluentFooIdInput" ] }, { "message": "The node id type name `RenamedUser` does not match the expected type name `FooFooFluentSingle`.", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "validSingleTypeFluentFooIdInput" ] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Node_Resolve_Implicit_Custom_IdField.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Node_Resolve_Implicit_Custom_IdField.snap index f375a3abe1c..1473ba45ce5 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Node_Resolve_Implicit_Custom_IdField.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Node_Resolve_Implicit_Custom_IdField.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Could not resolve the actual object type from `HotChocolate.Types.Relay.NodeFieldSupportTests+Bar2` for the abstract type `node`.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "node" ] diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Tow_Many_Nodes.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Tow_Many_Nodes.snap index 06301c15d06..c5202261061 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Tow_Many_Nodes.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/NodeFieldSupportTests.Tow_Many_Nodes.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "The maximum number of nodes that can be fetched at once is 1. This selection tried to fetch 2 nodes that exceeded the maximum allowed amount.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "nodes" ], diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/RelaySchemaTests.Relay_ShouldReturnNonNullError_When_IdIsNull.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/RelaySchemaTests.Relay_ShouldReturnNonNullError_When_IdIsNull.snap index fa2eb7d3585..a4d5f0b6cfa 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/RelaySchemaTests.Relay_ShouldReturnNonNullError_When_IdIsNull.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/RelaySchemaTests.Relay_ShouldReturnNonNullError_When_IdIsNull.snap @@ -1,13 +1,7 @@ -{ +{ "errors": [ { "message": "Cannot return null for non-nullable field.", - "locations": [ - { - "line": 1, - "column": 16 - } - ], "path": [ "user", "id" diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaBuilderTests.Execute_Against_Interface_Without_Impl_Field.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaBuilderTests.Execute_Against_Interface_Without_Impl_Field.snap index 981d9a23dc4..1e5d92424a3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaBuilderTests.Execute_Against_Interface_Without_Impl_Field.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaBuilderTests.Execute_Against_Interface_Without_Impl_Field.snap @@ -2,12 +2,6 @@ "errors": [ { "message": "Could not resolve the actual object type from `System.String` for the abstract type `foo`.", - "locations": [ - { - "line": 1, - "column": 3 - } - ], "path": [ "foo" ] diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/DocumentRewriter.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/DocumentRewriter.cs index 689620e30dc..d4aea762a95 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/DocumentRewriter.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/DocumentRewriter.cs @@ -828,7 +828,7 @@ private sealed class Context( /// /// Provides a fast way to get all FieldNodes for the same response name. - /// The key is the respones name. + /// The key is the response name. /// public Dictionary>? Fields { get; private set; } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/InlineFragmentOperationRewriter.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/InlineFragmentOperationRewriter.cs index 7ea0e766b4f..07b5f8a91de 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/InlineFragmentOperationRewriter.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Utilities/Rewriters/InlineFragmentOperationRewriter.cs @@ -568,19 +568,21 @@ private static IReadOnlyList RemoveStaticIncludeConditions( static bool IsStaticIncludeCondition(DirectiveNode directive, ref bool skipChecked, ref bool includeChecked) { - if (directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal) - && directive.Arguments.Count == 1 - && directive.Arguments[0].Value is BooleanValueNode skipConstant - && !skipConstant.Value) + if(directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal)) { - return true; + skipChecked = true; + if (directive.Arguments is [{ Value: BooleanValueNode }]) + { + return true; + } } - else if (directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal) - && (directive.Arguments.Count != 1 - || directive.Arguments[0].Value is not BooleanValueNode includeConstant - || includeConstant.Value)) + else if(directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal)) { - return true; + includeChecked = true; + if (directive.Arguments is [{ Value: BooleanValueNode }]) + { + return true; + } } return false; diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Default_ErrorHandlingMode_Can_Be_Changed.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Default_ErrorHandlingMode_Can_Be_Changed.yaml index 74b61133c8e..a16305dda21 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Default_ErrorHandlingMode_Can_Be_Changed.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Default_ErrorHandlingMode_Can_Be_Changed.yaml @@ -47,12 +47,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve reviews", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "reviews" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Http_Request_In_Node_Is_Still_Ongoing.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Http_Request_In_Node_Is_Still_Ongoing.yaml index 142c5dedb94..d43e5fef38d 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Http_Request_In_Node_Is_Still_Ongoing.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Http_Request_In_Node_Is_Still_Ongoing.yaml @@ -72,12 +72,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve reviews", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "reviews" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Subscription_Is_Still_Ongoing.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Subscription_Is_Still_Ongoing.yaml index c56a349960c..39cb784934f 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Subscription_Is_Still_Ongoing.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/CancellationTests.Execution_Is_Halted_While_Subscription_Is_Still_Ongoing.yaml @@ -68,12 +68,6 @@ sourceSchemas: "errors": [ { "message": "Could not produce review", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "onReviewCreated" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml index ffd7c58bb2c..3936766b644 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml @@ -46,12 +46,6 @@ sourceSchemas: "errors": [ { "message": "Something went wrong", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "someField" ], diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Halt.yaml index ad5d46a25f4..86f6bbda802 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Halt.yaml @@ -113,12 +113,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -133,12 +127,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -153,12 +141,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Null.yaml index 71e8d18bf91..3fc85073acc 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Null.yaml @@ -145,12 +145,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -165,12 +159,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -185,12 +173,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Propagate.yaml index e68f2836769..1a913d705ee 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_In_List_OnError_Propagate.yaml @@ -144,12 +144,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -164,12 +158,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -184,12 +172,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Halt.yaml index 1101ad8bd1a..94720026ab0 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Halt.yaml @@ -94,12 +94,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Null.yaml index a2fd147935b..d0e7c4a9957 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Null.yaml @@ -100,12 +100,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Propagate.yaml index ba31c74d2db..7850f33c18e 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Field_OnError_Propagate.yaml @@ -99,12 +99,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Halt.yaml index 012eaad7478..cb5f798e19d 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Halt.yaml @@ -113,12 +113,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -133,12 +127,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -153,12 +141,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Null.yaml index 588e98daca9..ba8a5029d94 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Null.yaml @@ -145,12 +145,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -165,12 +159,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -185,12 +173,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Propagate.yaml index ead712a5f2f..18552f06227 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_NonNull_OnError_Propagate.yaml @@ -112,12 +112,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -132,12 +126,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] @@ -152,12 +140,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Halt.yaml index 25b8242856b..dec776c96b4 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Halt.yaml @@ -113,12 +113,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -136,12 +130,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -159,12 +147,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Null.yaml index 159f5a363f4..467ce3b9060 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Null.yaml @@ -145,12 +145,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -168,12 +162,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -191,12 +179,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Propagate.yaml index 5e1e3ea6e5c..f2e9911e655 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_In_List_OnError_Propagate.yaml @@ -144,12 +144,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -167,12 +161,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" @@ -190,12 +178,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Halt.yaml index 25ebf37958f..157973a0108 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Halt.yaml @@ -94,12 +94,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Null.yaml index 82e0e776c41..a66d94e7cdf 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Null.yaml @@ -100,12 +100,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Propagate.yaml index e6b5913d04c..7a4186d81ec 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_NonNull_OnError_Propagate.yaml @@ -93,12 +93,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Halt.yaml index 2b35f43081d..2b40e33950a 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Halt.yaml @@ -94,12 +94,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Null.yaml index cdfb5bfee91..41e32d1c801 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Null.yaml @@ -100,12 +100,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Propagate.yaml index 37913ca0554..852d595015b 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Lookup_Leaf_OnError_Propagate.yaml @@ -99,12 +99,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Halt.yaml index 2e63fcd8853..1c5a760f448 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Halt.yaml @@ -49,12 +49,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Null.yaml index 28f1bb1aa06..2025c497028 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Null.yaml @@ -52,12 +52,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Propagate.yaml index 4ab469854c4..cb08cd00a61 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Field_OnError_Propagate.yaml @@ -51,12 +51,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Halt.yaml index ace8a030e95..65b5c9895d3 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Halt.yaml @@ -50,12 +50,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Null.yaml index f26ed11ca88..c445fb8baaf 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Null.yaml @@ -55,12 +55,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Propagate.yaml index dfed1bc3ec2..fd07ff284ef 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_On_Root_Leaf_OnError_Propagate.yaml @@ -54,12 +54,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Halt.yaml index 80d61079dd3..de2886aecd4 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Halt.yaml @@ -94,12 +94,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Null.yaml index b5808323917..b798d10242b 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Null.yaml @@ -100,12 +100,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Propagate.yaml index 5a6ef6d3431..0ce71a9e555 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Field_NonNull_OnError_Propagate.yaml @@ -93,12 +93,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Halt.yaml index ee505e22443..889bd2c60a6 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Halt.yaml @@ -94,12 +94,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Null.yaml index 65e4bfa011b..34802735c60 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Null.yaml @@ -100,12 +100,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Propagate.yaml index 965f0f72366..870450722b8 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Lookup_Leaf_NonNull_OnError_Propagate.yaml @@ -93,12 +93,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product.name", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "productById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Halt.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Halt.yaml index e78b20a3a68..2f39e67321a 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Halt.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Halt.yaml @@ -49,12 +49,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Null.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Null.yaml index ebd23e3d03e..fcf5004f6a9 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Null.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Null.yaml @@ -52,12 +52,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Propagate.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Propagate.yaml index 7969aba8bc1..0ad108fc55f 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Propagate.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.No_Data_And_Error_With_Path_For_Root_Field_NonNull_OnError_Propagate.yaml @@ -48,12 +48,6 @@ sourceSchemas: "errors": [ { "message": "Could not resolve Product", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "productById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_NonNull_One_Service_Errors_EntryField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_NonNull_One_Service_Errors_EntryField.yaml index b63f5d8c073..b64c80f82a4 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_NonNull_One_Service_Errors_EntryField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_NonNull_One_Service_Errors_EntryField.yaml @@ -72,12 +72,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "other" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_Nullable_One_Service_Errors_EntryField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_Nullable_One_Service_Errors_EntryField.yaml index a1741c8cd93..e1d8b1e02fc 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_Nullable_One_Service_Errors_EntryField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_EntryField_Nullable_One_Service_Errors_EntryField.yaml @@ -87,12 +87,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], "path": [ "other" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_NonNull_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_NonNull_One_Service_Errors_SubField.yaml index 0fa953035e8..a98f1e81628 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_NonNull_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_NonNull_One_Service_Errors_SubField.yaml @@ -72,12 +72,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "other", "userId" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_Nullable_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_Nullable_One_Service_Errors_SubField.yaml index 24d0820925f..eff02802c8c 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_Nullable_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_NonNull_EntryField_Nullable_One_Service_Errors_SubField.yaml @@ -87,12 +87,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "other", "userId" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_Nullable_EntryField_Nullable_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_Nullable_EntryField_Nullable_One_Service_Errors_SubField.yaml index df4dff0c19b..85438db1290 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_Nullable_EntryField_Nullable_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Parallel_SubField_Nullable_EntryField_Nullable_One_Service_Errors_SubField.yaml @@ -90,12 +90,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 3, - "column": 5 - } - ], "path": [ "other", "userId" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_EntryField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_EntryField.yaml index 60d3fb6a170..1b3d0f292f6 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_EntryField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_EntryField.yaml @@ -105,12 +105,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "brandById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_SubField.yaml index 25eac97730e..a595dc063b5 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_NonNull_One_Service_Errors_SubField.yaml @@ -105,12 +105,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "brandById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_EntryField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_EntryField.yaml index 767440f9dce..7d0ef901476 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_EntryField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_EntryField.yaml @@ -108,12 +108,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "brandById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_SubField.yaml index 82f7987e4c1..8730cfc995b 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_NonNull_Parent_Nullable_One_Service_Errors_SubField.yaml @@ -108,12 +108,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "brandById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.yaml index f0a77593a12..e0d03c5fccb 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.yaml @@ -111,12 +111,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 4, - "column": 3 - } - ], "path": [ "brandById" ] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.yaml index f7a3aa98214..63be5799b16 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/v15/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.yaml @@ -111,12 +111,6 @@ sourceSchemas: "errors": [ { "message": "Unexpected Execution Error", - "locations": [ - { - "line": 5, - "column": 5 - } - ], "path": [ "brandById", "name" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/Rewriters/InlineFragmentOperationRewriterTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/Rewriters/InlineFragmentOperationRewriterTests.cs index 8c66269a3ed..4a03c817556 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/Rewriters/InlineFragmentOperationRewriterTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Utilities.Tests/Rewriters/InlineFragmentOperationRewriterTests.cs @@ -699,4 +699,41 @@ ... UnknownFragment "A fragment with the name 'UnknownFragment' does not exist.", Assert.Throws(Action).Message); } + + [Fact] + public void Single_Include_With_Variable() + { + // arrange + var sourceText = FileResource.Open("schema1.graphql"); + var schemaDefinition = SchemaParser.Parse(sourceText); + + var doc = Utf8GraphQLParser.Parse( + """ + query($skip: Boolean!) { + productById(id: 1) { + name @include(if: $skip) + id + } + } + """); + + // act + var rewriter = new InlineFragmentOperationRewriter( + schemaDefinition, + removeStaticallyExcludedSelections: true); + var rewritten = rewriter.RewriteDocument(doc); + + // assert + rewritten.MatchInlineSnapshot( + """ + query( + $skip: Boolean! + ) { + productById(id: 1) { + name @include(if: $skip) + id + } + } + """); + } } From 183bce543a7b745e34bcc0a5a824ddfac0a7db9a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 9 Dec 2025 12:17:47 +0100 Subject: [PATCH 29/30] fixed tests --- .../Types/Execution/Processing/OperationCompilerOptimizers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs index e60919fe435..855c089dfd4 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs @@ -7,8 +7,8 @@ namespace HotChocolate.Execution.Processing; // TODO : We might remove this internal sealed class OperationCompilerOptimizers { - private ImmutableArray _operationOptimizers; - private ImmutableArray _selectionSetOptimizers; + private ImmutableArray _operationOptimizers = []; + private ImmutableArray _selectionSetOptimizers = []; private PropertyInitFlags _initFlags; public ImmutableArray OperationOptimizers From 11075e61ff83f2546392271bc94392e94ef0a8db Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 12 Dec 2025 09:50:52 +0100 Subject: [PATCH 30/30] wip --- .../Processing/OperationCompilerTests.cs | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs index 103dee2fb3c..a5d11e907b5 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs @@ -544,11 +544,13 @@ public void Object_Field_Visibility_Is_Correctly_Inherited_3() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean!, $q: Boolean!) { - hero(episode: EMPIRE) @include(if: $v) { - name @include(if: $q) - } - }"); + """ + query foo($v: Boolean!, $q: Boolean!) { + hero(episode: EMPIRE) @include(if: $v) { + name @include(if: $q) + } + } + """); // act var operation = OperationCompiler.Compile( @@ -603,14 +605,16 @@ public void Defer_Inline_Fragment() .Create(); var document = Utf8GraphQLParser.Parse( - @"{ - hero(episode: EMPIRE) { - name - ... @defer { - id - } + """ + { + hero(episode: EMPIRE) { + name + ... @defer { + id } - }"); + } + } + """); // act var operation = OperationCompiler.Compile( @@ -631,16 +635,18 @@ public void Defer_Fragment_Spread() .Create(); var document = Utf8GraphQLParser.Parse( - @"{ - hero(episode: EMPIRE) { - name - ... Foo @defer - } + """ + { + hero(episode: EMPIRE) { + name + ... Foo @defer + } } fragment Foo on Droid { - id - }"); + id + } + """); // act var operation = OperationCompiler.Compile( @@ -691,14 +697,16 @@ public void FragmentSpread_SelectionsSet_Empty() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean){ - hero(episode: EMPIRE) { - name @include(if: $v) - ... abc - } + """ + query foo($v: Boolean){ + hero(episode: EMPIRE) { + name @include(if: $v) + ... abc + } } - fragment abc on Droid { }"); + fragment abc on Droid { } + """); // act var operation = OperationCompiler.Compile(