From 8c438d81bed979d6949c4403c46b09bb456c1d0a Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sun, 2 Aug 2026 18:27:06 +0500 Subject: [PATCH 01/23] bump substrait submodule to v0.99.0 --- substrait | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrait b/substrait index d9b9672..49e37e3 160000 --- a/substrait +++ b/substrait @@ -1 +1 @@ -Subproject commit d9b9672fd3c24285afdee9344fc2f4f7fcd70afb +Subproject commit 49e37e34312d1c133ecafa6e5df3cd8353a89632 From 79234170bcbd861a0f27dbc8df72f3148e49acb8 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sun, 2 Aug 2026 18:27:30 +0500 Subject: [PATCH 02/23] target net10, update Protobuf and Grpc packages --- src/Substrait.Core/Substrait.Core.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Substrait.Core/Substrait.Core.csproj b/src/Substrait.Core/Substrait.Core.csproj index 3b23017..40bd707 100644 --- a/src/Substrait.Core/Substrait.Core.csproj +++ b/src/Substrait.Core/Substrait.Core.csproj @@ -1,7 +1,7 @@  - net6.0 + net10.0 enable enable True @@ -13,8 +13,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 8407535e399cb300618b27cc4a007ba28ee7051b Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sun, 2 Aug 2026 18:27:44 +0500 Subject: [PATCH 03/23] add .editorconfig --- .editorconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..853ce91 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.cs] +charset = utf-8-bom +indent_style = space +indent_size = 2 + +[*.{csproj,sln,json,yml,yaml}] +indent_style = space +indent_size = 2 From 4445c4d900867e4364ca6be8a09b4366f06cc68f Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sun, 2 Aug 2026 18:42:34 +0500 Subject: [PATCH 04/23] migrate sln to slnx, add test project, add ci workflow --- .github/workflows/ci.yml | 31 +++++++++++++++++++ substrait-csharp.sln | 25 --------------- substrait-csharp.slnx | 13 ++++++++ .../Substrait.Core.Tests.csproj | 25 +++++++++++++++ .../SubstraitRelVisitorTests.cs | 27 ++++++++++++++++ 5 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 substrait-csharp.sln create mode 100644 substrait-csharp.slnx create mode 100644 test/Substrait.Core.Tests/Substrait.Core.Tests.csproj create mode 100644 test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f90e049 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: "10.0.x" + + - name: Restore + run: dotnet restore substrait-csharp.slnx + + - name: Check code style + run: dotnet format substrait-csharp.slnx --verify-no-changes + + - name: Build + run: dotnet build substrait-csharp.slnx --no-restore --configuration Release + + - name: Test + run: dotnet test substrait-csharp.slnx --no-build --configuration Release diff --git a/substrait-csharp.sln b/substrait-csharp.sln deleted file mode 100644 index 7efe320..0000000 --- a/substrait-csharp.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.33027.108 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Substrait.Core", "src\Substrait.Core\Substrait.Core.csproj", "{C33A4305-4A80-4A92-AEF5-FB304D8BA6C7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C33A4305-4A80-4A92-AEF5-FB304D8BA6C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C33A4305-4A80-4A92-AEF5-FB304D8BA6C7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C33A4305-4A80-4A92-AEF5-FB304D8BA6C7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C33A4305-4A80-4A92-AEF5-FB304D8BA6C7}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {671E873C-513C-474D-AA51-3532134EC36E} - EndGlobalSection -EndGlobal diff --git a/substrait-csharp.slnx b/substrait-csharp.slnx new file mode 100644 index 0000000..5c723fc --- /dev/null +++ b/substrait-csharp.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj b/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj new file mode 100644 index 0000000..41948f6 --- /dev/null +++ b/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs new file mode 100644 index 0000000..1e8699d --- /dev/null +++ b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs @@ -0,0 +1,27 @@ +using Substrait.Core; +using Substrait.Relation; + +namespace Substrait.Core.Tests; + +public class SubstraitRelVisitorTests +{ + private sealed class NoopVisitor : SubstraitRelVisitor + { + } + + [Fact] + public void Visit_WithNullRelation_ThrowsArgumentNullException() + { + var visitor = new NoopVisitor(); + + Assert.Throws(() => visitor.Visit((Read)null!)); + } + + [Fact] + public void Visit_WithUnhandledRelation_FallsBackAndThrows() + { + var visitor = new NoopVisitor(); + + Assert.Throws(() => visitor.Visit(new Read())); + } +} From 11e7189995012f9b481415c1ff528eb7afb27b9e Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 14:15:24 +0500 Subject: [PATCH 05/23] Fix namespace in Relation/* --- src/Substrait.Core/Relation/Aggregate.cs | 15 +++++++-------- src/Substrait.Core/Relation/Fetch.cs | 15 +++++++-------- src/Substrait.Core/Relation/Filter.cs | 15 +++++++-------- src/Substrait.Core/Relation/Join.cs | 15 +++++++-------- src/Substrait.Core/Relation/Project.cs | 15 +++++++-------- src/Substrait.Core/Relation/Read.cs | 15 +++++++-------- src/Substrait.Core/Relation/Rel.cs | 15 +++++++-------- src/Substrait.Core/Relation/Sort.cs | 15 +++++++-------- 8 files changed, 56 insertions(+), 64 deletions(-) diff --git a/src/Substrait.Core/Relation/Aggregate.cs b/src/Substrait.Core/Relation/Aggregate.cs index f53af86..2524d35 100644 --- a/src/Substrait.Core/Relation/Aggregate.cs +++ b/src/Substrait.Core/Relation/Aggregate.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The AGGREGATE relational operator representing GROUP BY semantics, +/// +public class Aggregate : Rel { - /// - /// The AGGREGATE relational operator representing GROUP BY semantics, - /// - public class Aggregate : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Fetch.cs b/src/Substrait.Core/Relation/Fetch.cs index 4ce5e36..a256595 100644 --- a/src/Substrait.Core/Relation/Fetch.cs +++ b/src/Substrait.Core/Relation/Fetch.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The FETCH relational operator representing LIMIT or TOP semantics, +/// +public class Fetch : Rel { - /// - /// The FETCH relational operator representing LIMIT or TOP semantics, - /// - public class Fetch : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Filter.cs b/src/Substrait.Core/Relation/Filter.cs index d649ddc..8d2d82c 100644 --- a/src/Substrait.Core/Relation/Filter.cs +++ b/src/Substrait.Core/Relation/Filter.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The FILTER relational operator, +/// +public class Filter : Rel { - /// - /// The FILTER relational operator, - /// - public class Filter : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Join.cs b/src/Substrait.Core/Relation/Join.cs index 0289d8a..e9287ca 100644 --- a/src/Substrait.Core/Relation/Join.cs +++ b/src/Substrait.Core/Relation/Join.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The binary JOIN relational operator, +/// +public class Join : Rel { - /// - /// The binary JOIN relational operator, - /// - public class Join : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Project.cs b/src/Substrait.Core/Relation/Project.cs index a4e5d2a..036ab38 100644 --- a/src/Substrait.Core/Relation/Project.cs +++ b/src/Substrait.Core/Relation/Project.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The PROJECT relational operator representing calculated expressions of fields, +/// +public class Project : Rel { - /// - /// The PROJECT relational operator representing calculated expressions of fields, - /// - public class Project : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Read.cs b/src/Substrait.Core/Relation/Read.cs index 869d288..0743ddb 100644 --- a/src/Substrait.Core/Relation/Read.cs +++ b/src/Substrait.Core/Relation/Read.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The READ relational operator representing data scan, +/// +public class Read : Rel { - /// - /// The READ relational operator representing data scan, - /// - public class Read : Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Rel.cs b/src/Substrait.Core/Relation/Rel.cs index 8730c30..fb2dfa9 100644 --- a/src/Substrait.Core/Relation/Rel.cs +++ b/src/Substrait.Core/Relation/Rel.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// Base type for all relational operators, +/// +abstract public class Rel { - /// - /// Base type for all relational operators, - /// - abstract public class Rel - { - } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Relation/Sort.cs b/src/Substrait.Core/Relation/Sort.cs index 991f4b9..cfe83b7 100644 --- a/src/Substrait.Core/Relation/Sort.cs +++ b/src/Substrait.Core/Relation/Sort.cs @@ -1,9 +1,8 @@ -namespace Substrait.Relation +namespace Substrait.Core.Relation; + +/// +/// The SORT relational operator representing ORDER BY semantics, +/// +public class Sort : Rel { - /// - /// The SORT relational operator representing ORDER BY semantics, - /// - public class Sort : Rel - { - } -} +} \ No newline at end of file From 8667ae53b9b28b774df82becab88f979c25768eb Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 15:12:51 +0500 Subject: [PATCH 06/23] Filescoped namespace and fixed import --- src/Substrait.Core/SubstraitRelVisitor.cs | 157 +++++++++++----------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/src/Substrait.Core/SubstraitRelVisitor.cs b/src/Substrait.Core/SubstraitRelVisitor.cs index 3a48f12..42fe229 100644 --- a/src/Substrait.Core/SubstraitRelVisitor.cs +++ b/src/Substrait.Core/SubstraitRelVisitor.cs @@ -1,107 +1,106 @@ -using Substrait.Relation; +using Substrait.Core.Relation; -namespace Substrait.Core +namespace Substrait.Core; + +/// +/// Visitor to transform, compile, and/or process SQL logical operators represented using Substrait. The visitor has +/// methods for visiting relation operator objects as input and provides concrete implementation to meet its goals. +/// +public abstract class SubstraitRelVisitor { /// - /// Visitor to transform, compile, and/or process SQL logical operators represented using Substrait. The visitor has - /// methods for visiting relation operator objects as input and provides concrete implementation to meet its goals. + /// Visit relational operator of type AGGREGATE /// - public abstract class SubstraitRelVisitor + public void Visit(Aggregate aggregate) { - /// - /// Visit relational operator of type AGGREGATE - /// - public void Visit(Aggregate aggregate) + if (aggregate is null) { - if (aggregate is null) - { - throw new ArgumentNullException(nameof(aggregate)); - } - - Fallback(aggregate); + throw new ArgumentNullException(nameof(aggregate)); } - /// - /// Visit relational operator of type FETCH - /// - public void Visit(Fetch fetch) - { - if (fetch is null) - { - throw new ArgumentNullException(nameof(fetch)); - } - - Fallback(fetch); - } + Fallback(aggregate); + } - /// - /// Visit relational operator of type FILTER - /// - public void Visit(Filter filter) + /// + /// Visit relational operator of type FETCH + /// + public void Visit(Fetch fetch) + { + if (fetch is null) { - if (filter is null) - { - throw new ArgumentNullException(nameof(filter)); - } - - Fallback(filter); + throw new ArgumentNullException(nameof(fetch)); } - /// - /// Visit relational operator of type JOIN - /// - public void Visit(Join join) - { - if (join is null) - { - throw new ArgumentNullException(nameof(join)); - } + Fallback(fetch); + } - Fallback(join); + /// + /// Visit relational operator of type FILTER + /// + public void Visit(Filter filter) + { + if (filter is null) + { + throw new ArgumentNullException(nameof(filter)); } - /// - /// Visit relational operator of type PROJECT - /// - public void Visit(Project project) - { - if (project is null) - { - throw new ArgumentNullException(nameof(project)); - } + Fallback(filter); + } - Fallback(project); + /// + /// Visit relational operator of type JOIN + /// + public void Visit(Join join) + { + if (join is null) + { + throw new ArgumentNullException(nameof(join)); } - /// - /// Visit relational operator of type READ - /// - public void Visit(Read read) - { - if (read is null) - { - throw new ArgumentNullException(nameof(read)); - } + Fallback(join); + } - Fallback(read); + /// + /// Visit relational operator of type PROJECT + /// + public void Visit(Project project) + { + if (project is null) + { + throw new ArgumentNullException(nameof(project)); } - /// - /// Visit relational operator of type SORT - /// - public void Visit(Sort sort) - { - if (sort is null) - { - throw new ArgumentNullException(nameof(sort)); - } + Fallback(project); + } - Fallback(sort); + /// + /// Visit relational operator of type READ + /// + public void Visit(Read read) + { + if (read is null) + { + throw new ArgumentNullException(nameof(read)); } - public void Fallback(Rel _) + Fallback(read); + } + + /// + /// Visit relational operator of type SORT + /// + public void Visit(Sort sort) + { + if (sort is null) { - throw new InvalidOperationException(); + throw new ArgumentNullException(nameof(sort)); } + + Fallback(sort); + } + + public void Fallback(Rel _) + { + throw new InvalidOperationException(); } } From 6fc9661c45f85f27800e58682c2d25d556f2f592 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 17:01:02 +0500 Subject: [PATCH 07/23] Simple and Compound Types --- src/Substrait.Core/Type/Compound/Decimal.cs | 10 +++ .../Type/Compound/FixedBinary.cs | 8 ++ src/Substrait.Core/Type/Compound/FixedChar.cs | 8 ++ src/Substrait.Core/Type/Compound/Func.cs | 29 ++++++ .../Type/Compound/IntervalCompound.cs | 8 ++ .../Type/Compound/IntervalDay.cs | 8 ++ src/Substrait.Core/Type/Compound/List.cs | 8 ++ src/Substrait.Core/Type/Compound/Map.cs | 10 +++ .../Type/Compound/PrecisionTime.cs | 8 ++ .../Type/Compound/PrecisionTimestamp.cs | 8 ++ .../Type/Compound/PrecisionTimestampTz.cs | 8 ++ src/Substrait.Core/Type/Compound/Struct.cs | 23 +++++ src/Substrait.Core/Type/Compound/VarChar.cs | 8 ++ src/Substrait.Core/Type/ITypeVisitor.cs | 59 ++++++++++++ src/Substrait.Core/Type/Simple/Binary.cs | 6 ++ src/Substrait.Core/Type/Simple/Bool.cs | 6 ++ src/Substrait.Core/Type/Simple/Date.cs | 6 ++ src/Substrait.Core/Type/Simple/Fp32.cs | 6 ++ src/Substrait.Core/Type/Simple/Fp64.cs | 6 ++ src/Substrait.Core/Type/Simple/I16.cs | 6 ++ src/Substrait.Core/Type/Simple/I32.cs | 6 ++ src/Substrait.Core/Type/Simple/I64.cs | 6 ++ src/Substrait.Core/Type/Simple/I8.cs | 6 ++ .../Type/Simple/IntervalYear.cs | 6 ++ src/Substrait.Core/Type/Simple/String.cs | 6 ++ src/Substrait.Core/Type/Simple/Uuid.cs | 6 ++ src/Substrait.Core/Type/TypeClass.cs | 8 ++ src/Substrait.Core/Type/TypeCreator.cs | 90 +++++++++++++++++++ 28 files changed, 373 insertions(+) create mode 100644 src/Substrait.Core/Type/Compound/Decimal.cs create mode 100644 src/Substrait.Core/Type/Compound/FixedBinary.cs create mode 100644 src/Substrait.Core/Type/Compound/FixedChar.cs create mode 100644 src/Substrait.Core/Type/Compound/Func.cs create mode 100644 src/Substrait.Core/Type/Compound/IntervalCompound.cs create mode 100644 src/Substrait.Core/Type/Compound/IntervalDay.cs create mode 100644 src/Substrait.Core/Type/Compound/List.cs create mode 100644 src/Substrait.Core/Type/Compound/Map.cs create mode 100644 src/Substrait.Core/Type/Compound/PrecisionTime.cs create mode 100644 src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs create mode 100644 src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs create mode 100644 src/Substrait.Core/Type/Compound/Struct.cs create mode 100644 src/Substrait.Core/Type/Compound/VarChar.cs create mode 100644 src/Substrait.Core/Type/ITypeVisitor.cs create mode 100644 src/Substrait.Core/Type/Simple/Binary.cs create mode 100644 src/Substrait.Core/Type/Simple/Bool.cs create mode 100644 src/Substrait.Core/Type/Simple/Date.cs create mode 100644 src/Substrait.Core/Type/Simple/Fp32.cs create mode 100644 src/Substrait.Core/Type/Simple/Fp64.cs create mode 100644 src/Substrait.Core/Type/Simple/I16.cs create mode 100644 src/Substrait.Core/Type/Simple/I32.cs create mode 100644 src/Substrait.Core/Type/Simple/I64.cs create mode 100644 src/Substrait.Core/Type/Simple/I8.cs create mode 100644 src/Substrait.Core/Type/Simple/IntervalYear.cs create mode 100644 src/Substrait.Core/Type/Simple/String.cs create mode 100644 src/Substrait.Core/Type/Simple/Uuid.cs create mode 100644 src/Substrait.Core/Type/TypeClass.cs create mode 100644 src/Substrait.Core/Type/TypeCreator.cs diff --git a/src/Substrait.Core/Type/Compound/Decimal.cs b/src/Substrait.Core/Type/Compound/Decimal.cs new file mode 100644 index 0000000..f5091bb --- /dev/null +++ b/src/Substrait.Core/Type/Compound/Decimal.cs @@ -0,0 +1,10 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record Decimal : TypeClass +{ + public required int Precision { get; init; } + + public required int Scale { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Compound/FixedBinary.cs b/src/Substrait.Core/Type/Compound/FixedBinary.cs new file mode 100644 index 0000000..68d1678 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/FixedBinary.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record FixedBinary : TypeClass +{ + public required int Length { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/FixedChar.cs b/src/Substrait.Core/Type/Compound/FixedChar.cs new file mode 100644 index 0000000..ac98dc7 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/FixedChar.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record FixedChar : TypeClass +{ + public required int Length { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/Func.cs b/src/Substrait.Core/Type/Compound/Func.cs new file mode 100644 index 0000000..d1525b2 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/Func.cs @@ -0,0 +1,29 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record Func : TypeClass +{ + public required IReadOnlyList ParameterTypes { get; init; } + + public required TypeClass ReturnType { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); + + public bool Equals(Func? other) => + other is not null + && Nullable == other.Nullable + && ReturnType.Equals(other.ReturnType) + && ParameterTypes.SequenceEqual(other.ParameterTypes); + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Nullable); + hash.Add(ReturnType); + foreach (var parameterType in ParameterTypes) + { + hash.Add(parameterType); + } + + return hash.ToHashCode(); + } +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/IntervalCompound.cs b/src/Substrait.Core/Type/Compound/IntervalCompound.cs new file mode 100644 index 0000000..930352f --- /dev/null +++ b/src/Substrait.Core/Type/Compound/IntervalCompound.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record IntervalCompound : TypeClass +{ + public required int Precision { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/IntervalDay.cs b/src/Substrait.Core/Type/Compound/IntervalDay.cs new file mode 100644 index 0000000..7bd3b5d --- /dev/null +++ b/src/Substrait.Core/Type/Compound/IntervalDay.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record IntervalDay : TypeClass +{ + public required int Precision { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/List.cs b/src/Substrait.Core/Type/Compound/List.cs new file mode 100644 index 0000000..8b8bcdd --- /dev/null +++ b/src/Substrait.Core/Type/Compound/List.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record List : TypeClass +{ + public required TypeClass ElementType { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Compound/Map.cs b/src/Substrait.Core/Type/Compound/Map.cs new file mode 100644 index 0000000..64c4f19 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/Map.cs @@ -0,0 +1,10 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record Map : TypeClass +{ + public required TypeClass Key { get; init; } + + public required TypeClass Value { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/PrecisionTime.cs b/src/Substrait.Core/Type/Compound/PrecisionTime.cs new file mode 100644 index 0000000..c337d74 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/PrecisionTime.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record PrecisionTime : TypeClass +{ + public required int Precision { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs b/src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs new file mode 100644 index 0000000..33a65ad --- /dev/null +++ b/src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record PrecisionTimestamp : TypeClass +{ + public required int Precision { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs b/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs new file mode 100644 index 0000000..22e1890 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record PrecisionTimestampTz : TypeClass +{ + public required int Precision { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Compound/Struct.cs b/src/Substrait.Core/Type/Compound/Struct.cs new file mode 100644 index 0000000..a945862 --- /dev/null +++ b/src/Substrait.Core/Type/Compound/Struct.cs @@ -0,0 +1,23 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record Struct : TypeClass +{ + public required IReadOnlyList Fields { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); + + public bool Equals(Struct? other) => + other is not null && Nullable == other.Nullable && Fields.SequenceEqual(other.Fields); + + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Nullable); + foreach (var field in Fields) + { + hash.Add(field); + } + + return hash.ToHashCode(); + } +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/VarChar.cs b/src/Substrait.Core/Type/Compound/VarChar.cs new file mode 100644 index 0000000..b126a9e --- /dev/null +++ b/src/Substrait.Core/Type/Compound/VarChar.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type.Compound; + +public sealed record VarChar : TypeClass +{ + public required int Length { get; init; } + + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/ITypeVisitor.cs b/src/Substrait.Core/Type/ITypeVisitor.cs new file mode 100644 index 0000000..f77c5a0 --- /dev/null +++ b/src/Substrait.Core/Type/ITypeVisitor.cs @@ -0,0 +1,59 @@ +using Substrait.Core.Type.Compound; +using Substrait.Core.Type.Simple; +using Decimal = Substrait.Core.Type.Compound.Decimal; +using String = Substrait.Core.Type.Simple.String; + +namespace Substrait.Core.Type; + +public interface ITypeVisitor +{ + TResult Visit(Bool type); + + TResult Visit(I8 type); + + TResult Visit(I16 type); + + TResult Visit(I32 type); + + TResult Visit(I64 type); + + TResult Visit(Fp32 type); + + TResult Visit(Fp64 type); + + TResult Visit(String type); + + TResult Visit(Binary type); + + TResult Visit(Date type); + + TResult Visit(IntervalYear type); + + TResult Visit(IntervalDay type); + + TResult Visit(IntervalCompound type); + + TResult Visit(Uuid type); + + TResult Visit(FixedChar type); + + TResult Visit(VarChar type); + + TResult Visit(FixedBinary type); + + TResult Visit(Decimal type); + + TResult Visit(PrecisionTime type); + + TResult Visit(PrecisionTimestamp type); + + TResult Visit(PrecisionTimestampTz type); + + TResult Visit(Func type); + + TResult Visit(Struct type); + + TResult Visit(List type); + + TResult Visit(Map type); +} diff --git a/src/Substrait.Core/Type/Simple/Binary.cs b/src/Substrait.Core/Type/Simple/Binary.cs new file mode 100644 index 0000000..1d0f688 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Binary.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Binary : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Simple/Bool.cs b/src/Substrait.Core/Type/Simple/Bool.cs new file mode 100644 index 0000000..4338009 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Bool.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Bool : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/Date.cs b/src/Substrait.Core/Type/Simple/Date.cs new file mode 100644 index 0000000..f0fe47f --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Date.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Date : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/Fp32.cs b/src/Substrait.Core/Type/Simple/Fp32.cs new file mode 100644 index 0000000..8add30e --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Fp32.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Fp32 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Simple/Fp64.cs b/src/Substrait.Core/Type/Simple/Fp64.cs new file mode 100644 index 0000000..33549d0 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Fp64.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Fp64 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Simple/I16.cs b/src/Substrait.Core/Type/Simple/I16.cs new file mode 100644 index 0000000..28a8210 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/I16.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record I16 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/I32.cs b/src/Substrait.Core/Type/Simple/I32.cs new file mode 100644 index 0000000..047a9f3 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/I32.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record I32 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/I64.cs b/src/Substrait.Core/Type/Simple/I64.cs new file mode 100644 index 0000000..20b1804 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/I64.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record I64 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/I8.cs b/src/Substrait.Core/Type/Simple/I8.cs new file mode 100644 index 0000000..4492559 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/I8.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record I8 : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/IntervalYear.cs b/src/Substrait.Core/Type/Simple/IntervalYear.cs new file mode 100644 index 0000000..4aea60e --- /dev/null +++ b/src/Substrait.Core/Type/Simple/IntervalYear.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record IntervalYear : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/String.cs b/src/Substrait.Core/Type/Simple/String.cs new file mode 100644 index 0000000..0a5ab01 --- /dev/null +++ b/src/Substrait.Core/Type/Simple/String.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record String : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} diff --git a/src/Substrait.Core/Type/Simple/Uuid.cs b/src/Substrait.Core/Type/Simple/Uuid.cs new file mode 100644 index 0000000..79ca4ef --- /dev/null +++ b/src/Substrait.Core/Type/Simple/Uuid.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Type.Simple; + +public sealed record Uuid : TypeClass +{ + public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/TypeClass.cs b/src/Substrait.Core/Type/TypeClass.cs new file mode 100644 index 0000000..f81f167 --- /dev/null +++ b/src/Substrait.Core/Type/TypeClass.cs @@ -0,0 +1,8 @@ +namespace Substrait.Core.Type; + +public abstract record TypeClass +{ + public required bool Nullable { get; init; } + + public abstract TResult Accept(ITypeVisitor visitor); +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/TypeCreator.cs b/src/Substrait.Core/Type/TypeCreator.cs new file mode 100644 index 0000000..8c69618 --- /dev/null +++ b/src/Substrait.Core/Type/TypeCreator.cs @@ -0,0 +1,90 @@ +using Substrait.Core.Type.Compound; +using Substrait.Core.Type.Simple; +using Decimal = Substrait.Core.Type.Compound.Decimal; +using String = Substrait.Core.Type.Simple.String; + +namespace Substrait.Core.Type; + +public sealed class TypeCreator +{ + public static readonly TypeCreator Required = new(nullable: false); + + public static readonly TypeCreator Nullable = new(nullable: true); + + private readonly bool _nullable; + + private TypeCreator(bool nullable) + { + _nullable = nullable; + } + + public TypeClass Bool => new Bool { Nullable = _nullable }; + + public TypeClass I8 => new I8 { Nullable = _nullable }; + + public TypeClass I16 => new I16 { Nullable = _nullable }; + + public TypeClass I32 => new I32 { Nullable = _nullable }; + + public TypeClass I64 => new I64 { Nullable = _nullable }; + + public TypeClass Fp32 => new Fp32 { Nullable = _nullable }; + + public TypeClass Fp64 => new Fp64 { Nullable = _nullable }; + + public TypeClass String => new String { Nullable = _nullable }; + + public TypeClass Binary => new Binary { Nullable = _nullable }; + + public TypeClass Date => new Date { Nullable = _nullable }; + + public TypeClass IntervalYear => new IntervalYear { Nullable = _nullable }; + + public TypeClass Uuid => new Uuid { Nullable = _nullable }; + + public TypeClass FixedChar(int length) => new FixedChar { Nullable = _nullable, Length = length }; + + public TypeClass VarChar(int length) => new VarChar { Nullable = _nullable, Length = length }; + + public TypeClass FixedBinary(int length) => + new FixedBinary { Nullable = _nullable, Length = length }; + + public TypeClass Decimal(int precision, int scale) => + new Decimal { Nullable = _nullable, Precision = precision, Scale = scale }; + + public TypeClass PrecisionTime(int precision) => + new PrecisionTime { Nullable = _nullable, Precision = precision }; + + public TypeClass PrecisionTimestamp(int precision) => + new PrecisionTimestamp { Nullable = _nullable, Precision = precision }; + + public TypeClass PrecisionTimestampTz(int precision) => + new PrecisionTimestampTz { Nullable = _nullable, Precision = precision }; + + public TypeClass IntervalDay(int precision) => + new IntervalDay { Nullable = _nullable, Precision = precision }; + + public TypeClass IntervalCompound(int precision) => + new IntervalCompound { Nullable = _nullable, Precision = precision }; + + public TypeClass Func(IReadOnlyList parameterTypes, TypeClass returnType) => + new Func { Nullable = _nullable, ParameterTypes = parameterTypes, ReturnType = returnType }; + + public Struct Struct(params TypeClass[] fields) => + new() { Nullable = _nullable, Fields = fields }; + + public Struct Struct(IEnumerable fields) => + new() { Nullable = _nullable, Fields = fields.ToList() }; + + public List List(TypeClass elementType) => + new() { Nullable = _nullable, ElementType = elementType }; + + public Map Map(TypeClass key, TypeClass value) => + new() { Nullable = _nullable, Key = key, Value = value }; + + public static TypeCreator Of(bool nullable) => nullable ? Nullable : Required; + + public static TypeClass AsNullable(TypeClass type) => type with { Nullable = true }; + + public static TypeClass AsNotNullable(TypeClass type) => type with { Nullable = false }; +} From 5c7abea1347c5995065045eddbb950bb7f7fa6c6 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 17:36:40 +0500 Subject: [PATCH 08/23] trailing newline false --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 853ce91..987ec23 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] end_of_line = lf -insert_final_newline = true +insert_final_newline = false trim_trailing_whitespace = true [*.cs] From dc9755a09db08fef7691a4e499b12be21950a49e Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 18:10:55 +0500 Subject: [PATCH 09/23] remove trailing /n --- src/Substrait.Core/SubstraitRelVisitor.cs | 2 +- src/Substrait.Core/Type/Compound/Decimal.cs | 2 +- src/Substrait.Core/Type/Compound/List.cs | 2 +- src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs | 2 +- src/Substrait.Core/Type/ITypeVisitor.cs | 2 +- src/Substrait.Core/Type/Simple/Binary.cs | 2 +- src/Substrait.Core/Type/Simple/Fp32.cs | 2 +- src/Substrait.Core/Type/Simple/Fp64.cs | 2 +- src/Substrait.Core/Type/Simple/String.cs | 2 +- src/Substrait.Core/Type/TypeCreator.cs | 2 +- test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Substrait.Core/SubstraitRelVisitor.cs b/src/Substrait.Core/SubstraitRelVisitor.cs index 42fe229..f002cd1 100644 --- a/src/Substrait.Core/SubstraitRelVisitor.cs +++ b/src/Substrait.Core/SubstraitRelVisitor.cs @@ -103,4 +103,4 @@ public void Fallback(Rel _) { throw new InvalidOperationException(); } -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/Decimal.cs b/src/Substrait.Core/Type/Compound/Decimal.cs index f5091bb..98e7dcd 100644 --- a/src/Substrait.Core/Type/Compound/Decimal.cs +++ b/src/Substrait.Core/Type/Compound/Decimal.cs @@ -7,4 +7,4 @@ public sealed record Decimal : TypeClass public required int Scale { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/List.cs b/src/Substrait.Core/Type/Compound/List.cs index 8b8bcdd..2c50024 100644 --- a/src/Substrait.Core/Type/Compound/List.cs +++ b/src/Substrait.Core/Type/Compound/List.cs @@ -5,4 +5,4 @@ public sealed record List : TypeClass public required TypeClass ElementType { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs b/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs index 22e1890..e43260e 100644 --- a/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs +++ b/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs @@ -5,4 +5,4 @@ public sealed record PrecisionTimestampTz : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/ITypeVisitor.cs b/src/Substrait.Core/Type/ITypeVisitor.cs index f77c5a0..7e78f49 100644 --- a/src/Substrait.Core/Type/ITypeVisitor.cs +++ b/src/Substrait.Core/Type/ITypeVisitor.cs @@ -56,4 +56,4 @@ public interface ITypeVisitor TResult Visit(List type); TResult Visit(Map type); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/Binary.cs b/src/Substrait.Core/Type/Simple/Binary.cs index 1d0f688..27b6841 100644 --- a/src/Substrait.Core/Type/Simple/Binary.cs +++ b/src/Substrait.Core/Type/Simple/Binary.cs @@ -3,4 +3,4 @@ public sealed record Binary : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/Fp32.cs b/src/Substrait.Core/Type/Simple/Fp32.cs index 8add30e..c7ef00e 100644 --- a/src/Substrait.Core/Type/Simple/Fp32.cs +++ b/src/Substrait.Core/Type/Simple/Fp32.cs @@ -3,4 +3,4 @@ public sealed record Fp32 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/Fp64.cs b/src/Substrait.Core/Type/Simple/Fp64.cs index 33549d0..eb2f855 100644 --- a/src/Substrait.Core/Type/Simple/Fp64.cs +++ b/src/Substrait.Core/Type/Simple/Fp64.cs @@ -3,4 +3,4 @@ public sealed record Fp64 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/Simple/String.cs b/src/Substrait.Core/Type/Simple/String.cs index 0a5ab01..76bc2d6 100644 --- a/src/Substrait.Core/Type/Simple/String.cs +++ b/src/Substrait.Core/Type/Simple/String.cs @@ -3,4 +3,4 @@ public sealed record String : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} +} \ No newline at end of file diff --git a/src/Substrait.Core/Type/TypeCreator.cs b/src/Substrait.Core/Type/TypeCreator.cs index 8c69618..2f266ac 100644 --- a/src/Substrait.Core/Type/TypeCreator.cs +++ b/src/Substrait.Core/Type/TypeCreator.cs @@ -87,4 +87,4 @@ public Map Map(TypeClass key, TypeClass value) => public static TypeClass AsNullable(TypeClass type) => type with { Nullable = true }; public static TypeClass AsNotNullable(TypeClass type) => type with { Nullable = false }; -} +} \ No newline at end of file diff --git a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs index 1e8699d..dfec2ca 100644 --- a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs +++ b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs @@ -24,4 +24,4 @@ public void Visit_WithUnhandledRelation_FallsBackAndThrows() Assert.Throws(() => visitor.Visit(new Read())); } -} +} \ No newline at end of file From 8195f41ed936a0fd1f0a06f9da51d91d91ba370c Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Mon, 3 Aug 2026 20:09:26 +0500 Subject: [PATCH 10/23] Fix bad namespace import in SubstraitRelVisitorTests.cs --- test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs index dfec2ca..56036bd 100644 --- a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs +++ b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs @@ -1,5 +1,4 @@ -using Substrait.Core; -using Substrait.Relation; +using Substrait.Core.Relation; namespace Substrait.Core.Tests; From 627cf913bbdf001155aee50cbbba4c5945954683 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Fri, 7 Aug 2026 16:49:15 +0500 Subject: [PATCH 11/23] Add default implementations in ITypeVisitor Co-authored-by: Niels Pardon --- src/Substrait.Core/Type/ITypeVisitor.cs | 58 ++++++++++++++----------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/Substrait.Core/Type/ITypeVisitor.cs b/src/Substrait.Core/Type/ITypeVisitor.cs index 7e78f49..2cc6b20 100644 --- a/src/Substrait.Core/Type/ITypeVisitor.cs +++ b/src/Substrait.Core/Type/ITypeVisitor.cs @@ -7,53 +7,61 @@ namespace Substrait.Core.Type; public interface ITypeVisitor { - TResult Visit(Bool type); + /// + /// Invoked for any type kind the implementation does not handle explicitly. Override to supply a + /// default result, or to throw a domain-specific error. + /// + TResult VisitFallback(TypeClass type) => + throw new NotSupportedException( + $"{GetType().Name} does not handle type kind '{type.GetType().Name}'."); - TResult Visit(I8 type); + TResult Visit(Bool type) => VisitFallback(type); - TResult Visit(I16 type); + TResult Visit(I8 type) => VisitFallback(type); - TResult Visit(I32 type); + TResult Visit(I16 type) => VisitFallback(type); - TResult Visit(I64 type); + TResult Visit(I32 type) => VisitFallback(type); - TResult Visit(Fp32 type); + TResult Visit(I64 type) => VisitFallback(type); - TResult Visit(Fp64 type); + TResult Visit(Fp32 type) => VisitFallback(type); - TResult Visit(String type); + TResult Visit(Fp64 type) => VisitFallback(type); - TResult Visit(Binary type); + TResult Visit(String type) => VisitFallback(type); - TResult Visit(Date type); + TResult Visit(Binary type) => VisitFallback(type); - TResult Visit(IntervalYear type); + TResult Visit(Date type) => VisitFallback(type); - TResult Visit(IntervalDay type); + TResult Visit(IntervalYear type) => VisitFallback(type); - TResult Visit(IntervalCompound type); + TResult Visit(IntervalDay type) => VisitFallback(type); - TResult Visit(Uuid type); + TResult Visit(IntervalCompound type) => VisitFallback(type); - TResult Visit(FixedChar type); + TResult Visit(Uuid type) => VisitFallback(type); - TResult Visit(VarChar type); + TResult Visit(FixedChar type) => VisitFallback(type); - TResult Visit(FixedBinary type); + TResult Visit(VarChar type) => VisitFallback(type); - TResult Visit(Decimal type); + TResult Visit(FixedBinary type) => VisitFallback(type); - TResult Visit(PrecisionTime type); + TResult Visit(Decimal type) => VisitFallback(type); - TResult Visit(PrecisionTimestamp type); + TResult Visit(PrecisionTime type) => VisitFallback(type); - TResult Visit(PrecisionTimestampTz type); + TResult Visit(PrecisionTimestamp type) => VisitFallback(type); - TResult Visit(Func type); + TResult Visit(PrecisionTimestampTz type) => VisitFallback(type); - TResult Visit(Struct type); + TResult Visit(Func type) => VisitFallback(type); - TResult Visit(List type); + TResult Visit(Struct type) => VisitFallback(type); - TResult Visit(Map type); + TResult Visit(List type) => VisitFallback(type); + + TResult Visit(Map type) => VisitFallback(type); } \ No newline at end of file From 6ec4b90864d29de6cfa867b29ec40d3529ffed37 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Fri, 7 Aug 2026 16:53:40 +0500 Subject: [PATCH 12/23] Rename namespaces from Substrait.Core.Type to Substrait.Core.Types for consistency --- src/Substrait.Core/{Type => Types}/Compound/Decimal.cs | 2 +- .../{Type => Types}/Compound/FixedBinary.cs | 2 +- .../{Type => Types}/Compound/FixedChar.cs | 2 +- src/Substrait.Core/{Type => Types}/Compound/Func.cs | 2 +- .../{Type => Types}/Compound/IntervalCompound.cs | 2 +- .../{Type => Types}/Compound/IntervalDay.cs | 2 +- src/Substrait.Core/{Type => Types}/Compound/List.cs | 2 +- src/Substrait.Core/{Type => Types}/Compound/Map.cs | 2 +- .../{Type => Types}/Compound/PrecisionTime.cs | 2 +- .../{Type => Types}/Compound/PrecisionTimestamp.cs | 2 +- .../{Type => Types}/Compound/PrecisionTimestampTz.cs | 2 +- src/Substrait.Core/{Type => Types}/Compound/Struct.cs | 2 +- src/Substrait.Core/{Type => Types}/Compound/VarChar.cs | 2 +- src/Substrait.Core/{Type => Types}/ITypeVisitor.cs | 10 +++++----- src/Substrait.Core/{Type => Types}/Simple/Binary.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/Bool.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/Date.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/Fp32.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/Fp64.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/I16.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/I32.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/I64.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/I8.cs | 2 +- .../{Type => Types}/Simple/IntervalYear.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/String.cs | 2 +- src/Substrait.Core/{Type => Types}/Simple/Uuid.cs | 2 +- src/Substrait.Core/{Type => Types}/TypeClass.cs | 2 +- src/Substrait.Core/{Type => Types}/TypeCreator.cs | 10 +++++----- 28 files changed, 36 insertions(+), 36 deletions(-) rename src/Substrait.Core/{Type => Types}/Compound/Decimal.cs (84%) rename src/Substrait.Core/{Type => Types}/Compound/FixedBinary.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/FixedChar.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/Func.cs (94%) rename src/Substrait.Core/{Type => Types}/Compound/IntervalCompound.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/IntervalDay.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/List.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/Map.cs (84%) rename src/Substrait.Core/{Type => Types}/Compound/PrecisionTime.cs (81%) rename src/Substrait.Core/{Type => Types}/Compound/PrecisionTimestamp.cs (82%) rename src/Substrait.Core/{Type => Types}/Compound/PrecisionTimestampTz.cs (82%) rename src/Substrait.Core/{Type => Types}/Compound/Struct.cs (92%) rename src/Substrait.Core/{Type => Types}/Compound/VarChar.cs (80%) rename src/Substrait.Core/{Type => Types}/ITypeVisitor.cs (89%) rename src/Substrait.Core/{Type => Types}/Simple/Binary.cs (77%) rename src/Substrait.Core/{Type => Types}/Simple/Bool.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/Date.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/Fp32.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/Fp64.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/I16.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/I32.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/I64.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/I8.cs (76%) rename src/Substrait.Core/{Type => Types}/Simple/IntervalYear.cs (77%) rename src/Substrait.Core/{Type => Types}/Simple/String.cs (77%) rename src/Substrait.Core/{Type => Types}/Simple/Uuid.cs (76%) rename src/Substrait.Core/{Type => Types}/TypeClass.cs (81%) rename src/Substrait.Core/{Type => Types}/TypeCreator.cs (93%) diff --git a/src/Substrait.Core/Type/Compound/Decimal.cs b/src/Substrait.Core/Types/Compound/Decimal.cs similarity index 84% rename from src/Substrait.Core/Type/Compound/Decimal.cs rename to src/Substrait.Core/Types/Compound/Decimal.cs index 98e7dcd..74be20b 100644 --- a/src/Substrait.Core/Type/Compound/Decimal.cs +++ b/src/Substrait.Core/Types/Compound/Decimal.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record Decimal : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/FixedBinary.cs b/src/Substrait.Core/Types/Compound/FixedBinary.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/FixedBinary.cs rename to src/Substrait.Core/Types/Compound/FixedBinary.cs index 68d1678..d342c7a 100644 --- a/src/Substrait.Core/Type/Compound/FixedBinary.cs +++ b/src/Substrait.Core/Types/Compound/FixedBinary.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record FixedBinary : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/FixedChar.cs b/src/Substrait.Core/Types/Compound/FixedChar.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/FixedChar.cs rename to src/Substrait.Core/Types/Compound/FixedChar.cs index ac98dc7..aad83e4 100644 --- a/src/Substrait.Core/Type/Compound/FixedChar.cs +++ b/src/Substrait.Core/Types/Compound/FixedChar.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record FixedChar : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/Func.cs b/src/Substrait.Core/Types/Compound/Func.cs similarity index 94% rename from src/Substrait.Core/Type/Compound/Func.cs rename to src/Substrait.Core/Types/Compound/Func.cs index d1525b2..96b2ddd 100644 --- a/src/Substrait.Core/Type/Compound/Func.cs +++ b/src/Substrait.Core/Types/Compound/Func.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record Func : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/IntervalCompound.cs b/src/Substrait.Core/Types/Compound/IntervalCompound.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/IntervalCompound.cs rename to src/Substrait.Core/Types/Compound/IntervalCompound.cs index 930352f..36a4ed9 100644 --- a/src/Substrait.Core/Type/Compound/IntervalCompound.cs +++ b/src/Substrait.Core/Types/Compound/IntervalCompound.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record IntervalCompound : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/IntervalDay.cs b/src/Substrait.Core/Types/Compound/IntervalDay.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/IntervalDay.cs rename to src/Substrait.Core/Types/Compound/IntervalDay.cs index 7bd3b5d..b02c531 100644 --- a/src/Substrait.Core/Type/Compound/IntervalDay.cs +++ b/src/Substrait.Core/Types/Compound/IntervalDay.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record IntervalDay : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/List.cs b/src/Substrait.Core/Types/Compound/List.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/List.cs rename to src/Substrait.Core/Types/Compound/List.cs index 2c50024..f1bc487 100644 --- a/src/Substrait.Core/Type/Compound/List.cs +++ b/src/Substrait.Core/Types/Compound/List.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record List : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/Map.cs b/src/Substrait.Core/Types/Compound/Map.cs similarity index 84% rename from src/Substrait.Core/Type/Compound/Map.cs rename to src/Substrait.Core/Types/Compound/Map.cs index 64c4f19..e8fb6c0 100644 --- a/src/Substrait.Core/Type/Compound/Map.cs +++ b/src/Substrait.Core/Types/Compound/Map.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record Map : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/PrecisionTime.cs b/src/Substrait.Core/Types/Compound/PrecisionTime.cs similarity index 81% rename from src/Substrait.Core/Type/Compound/PrecisionTime.cs rename to src/Substrait.Core/Types/Compound/PrecisionTime.cs index c337d74..ee5e138 100644 --- a/src/Substrait.Core/Type/Compound/PrecisionTime.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTime.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record PrecisionTime : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs b/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs similarity index 82% rename from src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs rename to src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs index 33a65ad..4a4dfbf 100644 --- a/src/Substrait.Core/Type/Compound/PrecisionTimestamp.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record PrecisionTimestamp : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs b/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs similarity index 82% rename from src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs rename to src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs index e43260e..c4f0558 100644 --- a/src/Substrait.Core/Type/Compound/PrecisionTimestampTz.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record PrecisionTimestampTz : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/Struct.cs b/src/Substrait.Core/Types/Compound/Struct.cs similarity index 92% rename from src/Substrait.Core/Type/Compound/Struct.cs rename to src/Substrait.Core/Types/Compound/Struct.cs index a945862..f832cb4 100644 --- a/src/Substrait.Core/Type/Compound/Struct.cs +++ b/src/Substrait.Core/Types/Compound/Struct.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record Struct : TypeClass { diff --git a/src/Substrait.Core/Type/Compound/VarChar.cs b/src/Substrait.Core/Types/Compound/VarChar.cs similarity index 80% rename from src/Substrait.Core/Type/Compound/VarChar.cs rename to src/Substrait.Core/Types/Compound/VarChar.cs index b126a9e..9873143 100644 --- a/src/Substrait.Core/Type/Compound/VarChar.cs +++ b/src/Substrait.Core/Types/Compound/VarChar.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Compound; +namespace Substrait.Core.Types.Compound; public sealed record VarChar : TypeClass { diff --git a/src/Substrait.Core/Type/ITypeVisitor.cs b/src/Substrait.Core/Types/ITypeVisitor.cs similarity index 89% rename from src/Substrait.Core/Type/ITypeVisitor.cs rename to src/Substrait.Core/Types/ITypeVisitor.cs index 2cc6b20..9b91188 100644 --- a/src/Substrait.Core/Type/ITypeVisitor.cs +++ b/src/Substrait.Core/Types/ITypeVisitor.cs @@ -1,9 +1,9 @@ -using Substrait.Core.Type.Compound; -using Substrait.Core.Type.Simple; -using Decimal = Substrait.Core.Type.Compound.Decimal; -using String = Substrait.Core.Type.Simple.String; +using Substrait.Core.Types.Compound; +using Substrait.Core.Types.Simple; +using Decimal = Substrait.Core.Types.Compound.Decimal; +using String = Substrait.Core.Types.Simple.String; -namespace Substrait.Core.Type; +namespace Substrait.Core.Types; public interface ITypeVisitor { diff --git a/src/Substrait.Core/Type/Simple/Binary.cs b/src/Substrait.Core/Types/Simple/Binary.cs similarity index 77% rename from src/Substrait.Core/Type/Simple/Binary.cs rename to src/Substrait.Core/Types/Simple/Binary.cs index 27b6841..dfdcfa4 100644 --- a/src/Substrait.Core/Type/Simple/Binary.cs +++ b/src/Substrait.Core/Types/Simple/Binary.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Binary : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/Bool.cs b/src/Substrait.Core/Types/Simple/Bool.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/Bool.cs rename to src/Substrait.Core/Types/Simple/Bool.cs index 4338009..b73e2cf 100644 --- a/src/Substrait.Core/Type/Simple/Bool.cs +++ b/src/Substrait.Core/Types/Simple/Bool.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Bool : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/Date.cs b/src/Substrait.Core/Types/Simple/Date.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/Date.cs rename to src/Substrait.Core/Types/Simple/Date.cs index f0fe47f..8c6c110 100644 --- a/src/Substrait.Core/Type/Simple/Date.cs +++ b/src/Substrait.Core/Types/Simple/Date.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Date : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/Fp32.cs b/src/Substrait.Core/Types/Simple/Fp32.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/Fp32.cs rename to src/Substrait.Core/Types/Simple/Fp32.cs index c7ef00e..630cb1f 100644 --- a/src/Substrait.Core/Type/Simple/Fp32.cs +++ b/src/Substrait.Core/Types/Simple/Fp32.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Fp32 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/Fp64.cs b/src/Substrait.Core/Types/Simple/Fp64.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/Fp64.cs rename to src/Substrait.Core/Types/Simple/Fp64.cs index eb2f855..42c48e2 100644 --- a/src/Substrait.Core/Type/Simple/Fp64.cs +++ b/src/Substrait.Core/Types/Simple/Fp64.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Fp64 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/I16.cs b/src/Substrait.Core/Types/Simple/I16.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/I16.cs rename to src/Substrait.Core/Types/Simple/I16.cs index 28a8210..9a0d3a0 100644 --- a/src/Substrait.Core/Type/Simple/I16.cs +++ b/src/Substrait.Core/Types/Simple/I16.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record I16 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/I32.cs b/src/Substrait.Core/Types/Simple/I32.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/I32.cs rename to src/Substrait.Core/Types/Simple/I32.cs index 047a9f3..7e6d1c6 100644 --- a/src/Substrait.Core/Type/Simple/I32.cs +++ b/src/Substrait.Core/Types/Simple/I32.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record I32 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/I64.cs b/src/Substrait.Core/Types/Simple/I64.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/I64.cs rename to src/Substrait.Core/Types/Simple/I64.cs index 20b1804..9ae1374 100644 --- a/src/Substrait.Core/Type/Simple/I64.cs +++ b/src/Substrait.Core/Types/Simple/I64.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record I64 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/I8.cs b/src/Substrait.Core/Types/Simple/I8.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/I8.cs rename to src/Substrait.Core/Types/Simple/I8.cs index 4492559..78c6bd3 100644 --- a/src/Substrait.Core/Type/Simple/I8.cs +++ b/src/Substrait.Core/Types/Simple/I8.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record I8 : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/IntervalYear.cs b/src/Substrait.Core/Types/Simple/IntervalYear.cs similarity index 77% rename from src/Substrait.Core/Type/Simple/IntervalYear.cs rename to src/Substrait.Core/Types/Simple/IntervalYear.cs index 4aea60e..08cc44b 100644 --- a/src/Substrait.Core/Type/Simple/IntervalYear.cs +++ b/src/Substrait.Core/Types/Simple/IntervalYear.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record IntervalYear : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/String.cs b/src/Substrait.Core/Types/Simple/String.cs similarity index 77% rename from src/Substrait.Core/Type/Simple/String.cs rename to src/Substrait.Core/Types/Simple/String.cs index 76bc2d6..9a468f6 100644 --- a/src/Substrait.Core/Type/Simple/String.cs +++ b/src/Substrait.Core/Types/Simple/String.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record String : TypeClass { diff --git a/src/Substrait.Core/Type/Simple/Uuid.cs b/src/Substrait.Core/Types/Simple/Uuid.cs similarity index 76% rename from src/Substrait.Core/Type/Simple/Uuid.cs rename to src/Substrait.Core/Types/Simple/Uuid.cs index 79ca4ef..68482d7 100644 --- a/src/Substrait.Core/Type/Simple/Uuid.cs +++ b/src/Substrait.Core/Types/Simple/Uuid.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type.Simple; +namespace Substrait.Core.Types.Simple; public sealed record Uuid : TypeClass { diff --git a/src/Substrait.Core/Type/TypeClass.cs b/src/Substrait.Core/Types/TypeClass.cs similarity index 81% rename from src/Substrait.Core/Type/TypeClass.cs rename to src/Substrait.Core/Types/TypeClass.cs index f81f167..40c77a6 100644 --- a/src/Substrait.Core/Type/TypeClass.cs +++ b/src/Substrait.Core/Types/TypeClass.cs @@ -1,4 +1,4 @@ -namespace Substrait.Core.Type; +namespace Substrait.Core.Types; public abstract record TypeClass { diff --git a/src/Substrait.Core/Type/TypeCreator.cs b/src/Substrait.Core/Types/TypeCreator.cs similarity index 93% rename from src/Substrait.Core/Type/TypeCreator.cs rename to src/Substrait.Core/Types/TypeCreator.cs index 2f266ac..b183743 100644 --- a/src/Substrait.Core/Type/TypeCreator.cs +++ b/src/Substrait.Core/Types/TypeCreator.cs @@ -1,9 +1,9 @@ -using Substrait.Core.Type.Compound; -using Substrait.Core.Type.Simple; -using Decimal = Substrait.Core.Type.Compound.Decimal; -using String = Substrait.Core.Type.Simple.String; +using Substrait.Core.Types.Compound; +using Substrait.Core.Types.Simple; +using Decimal = Substrait.Core.Types.Compound.Decimal; +using String = Substrait.Core.Types.Simple.String; -namespace Substrait.Core.Type; +namespace Substrait.Core.Types; public sealed class TypeCreator { From 4b4441e35d347fa701e484acffe3dbc0e7ce5a8a Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 10:21:52 +0500 Subject: [PATCH 13/23] Apply suggestion from @nielspardon Co-authored-by: Niels Pardon --- src/Substrait.Core/Relation/Rel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Substrait.Core/Relation/Rel.cs b/src/Substrait.Core/Relation/Rel.cs index fb2dfa9..3112712 100644 --- a/src/Substrait.Core/Relation/Rel.cs +++ b/src/Substrait.Core/Relation/Rel.cs @@ -3,6 +3,6 @@ /// /// Base type for all relational operators, /// -abstract public class Rel +public abstract class Rel { } \ No newline at end of file From f13942aedb1536e8a76f6bb3ed933c5b1c5bb084 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 10:25:46 +0500 Subject: [PATCH 14/23] Argument Validation in TypeCreator Co-authored-by: Niels Pardon --- src/Substrait.Core/Types/TypeCreator.cs | 50 +++++++++++++++++++------ 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/Substrait.Core/Types/TypeCreator.cs b/src/Substrait.Core/Types/TypeCreator.cs index b183743..167e92f 100644 --- a/src/Substrait.Core/Types/TypeCreator.cs +++ b/src/Substrait.Core/Types/TypeCreator.cs @@ -42,30 +42,58 @@ private TypeCreator(bool nullable) public TypeClass Uuid => new Uuid { Nullable = _nullable }; - public TypeClass FixedChar(int length) => new FixedChar { Nullable = _nullable, Length = length }; + public TypeClass FixedChar(int length) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); + return new FixedChar { Nullable = _nullable, Length = length }; + } - public TypeClass VarChar(int length) => new VarChar { Nullable = _nullable, Length = length }; + public TypeClass VarChar(int length) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); + return new VarChar { Nullable = _nullable, Length = length }; + } - public TypeClass FixedBinary(int length) => - new FixedBinary { Nullable = _nullable, Length = length }; + public TypeClass FixedBinary(int length) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); + return new FixedBinary { Nullable = _nullable, Length = length }; + } - public TypeClass Decimal(int precision, int scale) => - new Decimal { Nullable = _nullable, Precision = precision, Scale = scale }; + public TypeClass Decimal(int precision, int scale) + { + ArgumentOutOfRangeException.ThrowIfNegative(precision); + ArgumentOutOfRangeException.ThrowIfGreaterThan(precision, MaxDecimalPrecision); + ArgumentOutOfRangeException.ThrowIfNegative(scale); + ArgumentOutOfRangeException.ThrowIfGreaterThan(scale, precision); + return new Decimal { Nullable = _nullable, Precision = precision, Scale = scale }; + } public TypeClass PrecisionTime(int precision) => - new PrecisionTime { Nullable = _nullable, Precision = precision }; + new PrecisionTime { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; public TypeClass PrecisionTimestamp(int precision) => - new PrecisionTimestamp { Nullable = _nullable, Precision = precision }; + new PrecisionTimestamp { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; public TypeClass PrecisionTimestampTz(int precision) => - new PrecisionTimestampTz { Nullable = _nullable, Precision = precision }; + new PrecisionTimestampTz { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; public TypeClass IntervalDay(int precision) => - new IntervalDay { Nullable = _nullable, Precision = precision }; + new IntervalDay { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; public TypeClass IntervalCompound(int precision) => - new IntervalCompound { Nullable = _nullable, Precision = precision }; + new IntervalCompound { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + + private const int MaxDecimalPrecision = 38; + + private const int MaxSubsecondPrecision = 9; + + private static int SubsecondPrecision(int precision) + { + ArgumentOutOfRangeException.ThrowIfNegative(precision); + ArgumentOutOfRangeException.ThrowIfGreaterThan(precision, MaxSubsecondPrecision); + return precision; + } public TypeClass Func(IReadOnlyList parameterTypes, TypeClass returnType) => new Func { Nullable = _nullable, ParameterTypes = parameterTypes, ReturnType = returnType }; From 492e47d387a8ae14066ab34b6ad326e535ad2ea3 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Fri, 7 Aug 2026 17:48:24 +0500 Subject: [PATCH 15/23] Update CI configuration and add project files for code style enforcement --- .editorconfig | 4 ++-- .gitattributes | 2 ++ .github/workflows/ci.yml | 14 +++++++++++--- Directory.Build.props | 6 ++++++ global.json | 6 ++++++ substrait-csharp.slnx | 2 -- 6 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 .gitattributes create mode 100644 Directory.Build.props create mode 100644 global.json diff --git a/.editorconfig b/.editorconfig index 987ec23..fb38cd4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] end_of_line = lf -insert_final_newline = false +insert_final_newline = true trim_trailing_whitespace = true [*.cs] @@ -10,6 +10,6 @@ charset = utf-8-bom indent_style = space indent_size = 2 -[*.{csproj,sln,json,yml,yaml}] +[*.{csproj,props,targets,slnx,json,yml,yaml}] indent_style = space indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d0d6371 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto eol=lf +*.png binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f90e049..3ac3c8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,16 +5,24 @@ on: branches: [main] pull_request: branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build-and-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: submodules: recursive - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: dotnet-version: "10.0.x" @@ -28,4 +36,4 @@ jobs: run: dotnet build substrait-csharp.slnx --no-restore --configuration Release - name: Test - run: dotnet test substrait-csharp.slnx --no-build --configuration Release + run: dotnet test substrait-csharp.slnx --no-build --configuration Release --collect:"XPlat Code Coverage" diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..f118034 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,6 @@ + + + true + latest-recommended + + diff --git a/global.json b/global.json new file mode 100644 index 0000000..1e7fdfa --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestMinor" + } +} diff --git a/substrait-csharp.slnx b/substrait-csharp.slnx index 5c723fc..0707f75 100644 --- a/substrait-csharp.slnx +++ b/substrait-csharp.slnx @@ -1,8 +1,6 @@ - - From d5ce4874a4a977d6df69f59f771393c9db330e11 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Fri, 7 Aug 2026 17:49:07 +0500 Subject: [PATCH 16/23] Add missing newlines at the end of multiple files --- src/Substrait.Core/Relation/Aggregate.cs | 2 +- src/Substrait.Core/Relation/Fetch.cs | 2 +- src/Substrait.Core/Relation/Filter.cs | 2 +- src/Substrait.Core/Relation/Join.cs | 2 +- src/Substrait.Core/Relation/Project.cs | 2 +- src/Substrait.Core/Relation/Read.cs | 2 +- src/Substrait.Core/Relation/Rel.cs | 2 +- src/Substrait.Core/Relation/Sort.cs | 2 +- src/Substrait.Core/SubstraitRelVisitor.cs | 2 +- src/Substrait.Core/Types/Compound/Decimal.cs | 2 +- .../Types/Compound/FixedBinary.cs | 2 +- .../Types/Compound/FixedChar.cs | 2 +- src/Substrait.Core/Types/Compound/Func.cs | 2 +- .../Types/Compound/IntervalCompound.cs | 2 +- .../Types/Compound/IntervalDay.cs | 2 +- src/Substrait.Core/Types/Compound/List.cs | 2 +- src/Substrait.Core/Types/Compound/Map.cs | 2 +- .../Types/Compound/PrecisionTime.cs | 2 +- .../Types/Compound/PrecisionTimestamp.cs | 2 +- .../Types/Compound/PrecisionTimestampTz.cs | 2 +- src/Substrait.Core/Types/Compound/Struct.cs | 2 +- src/Substrait.Core/Types/Compound/VarChar.cs | 2 +- .../Types/Extension/IParameter.cs | 5 +++ .../Types/Extension/ParameterBooleanValue.cs | 6 +++ .../Types/Extension/ParameterDataType.cs | 6 +++ .../Types/Extension/ParameterEnumValue.cs | 6 +++ .../Types/Extension/ParameterIntegerValue.cs | 6 +++ .../Types/Extension/ParameterNull.cs | 10 +++++ .../Types/Extension/ParameterStringValue.cs | 6 +++ .../Types/Extension/UserDefined.cs | 37 +++++++++++++++++++ src/Substrait.Core/Types/ITypeVisitor.cs | 2 +- src/Substrait.Core/Types/Simple/Binary.cs | 2 +- src/Substrait.Core/Types/Simple/Bool.cs | 2 +- src/Substrait.Core/Types/Simple/Date.cs | 2 +- src/Substrait.Core/Types/Simple/Fp32.cs | 2 +- src/Substrait.Core/Types/Simple/Fp64.cs | 2 +- src/Substrait.Core/Types/Simple/I16.cs | 2 +- src/Substrait.Core/Types/Simple/I32.cs | 2 +- src/Substrait.Core/Types/Simple/I64.cs | 2 +- src/Substrait.Core/Types/Simple/I8.cs | 2 +- .../Types/Simple/IntervalYear.cs | 2 +- src/Substrait.Core/Types/Simple/String.cs | 2 +- src/Substrait.Core/Types/Simple/Uuid.cs | 2 +- src/Substrait.Core/Types/TypeClass.cs | 2 +- src/Substrait.Core/Types/TypeCreator.cs | 2 +- 45 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 src/Substrait.Core/Types/Extension/IParameter.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterBooleanValue.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterDataType.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterEnumValue.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterIntegerValue.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterNull.cs create mode 100644 src/Substrait.Core/Types/Extension/ParameterStringValue.cs create mode 100644 src/Substrait.Core/Types/Extension/UserDefined.cs diff --git a/src/Substrait.Core/Relation/Aggregate.cs b/src/Substrait.Core/Relation/Aggregate.cs index 2524d35..447729e 100644 --- a/src/Substrait.Core/Relation/Aggregate.cs +++ b/src/Substrait.Core/Relation/Aggregate.cs @@ -5,4 +5,4 @@ /// public class Aggregate : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Fetch.cs b/src/Substrait.Core/Relation/Fetch.cs index a256595..74c458e 100644 --- a/src/Substrait.Core/Relation/Fetch.cs +++ b/src/Substrait.Core/Relation/Fetch.cs @@ -5,4 +5,4 @@ /// public class Fetch : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Filter.cs b/src/Substrait.Core/Relation/Filter.cs index 8d2d82c..33ece77 100644 --- a/src/Substrait.Core/Relation/Filter.cs +++ b/src/Substrait.Core/Relation/Filter.cs @@ -5,4 +5,4 @@ /// public class Filter : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Join.cs b/src/Substrait.Core/Relation/Join.cs index e9287ca..006f2c9 100644 --- a/src/Substrait.Core/Relation/Join.cs +++ b/src/Substrait.Core/Relation/Join.cs @@ -5,4 +5,4 @@ /// public class Join : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Project.cs b/src/Substrait.Core/Relation/Project.cs index 036ab38..4cbde0f 100644 --- a/src/Substrait.Core/Relation/Project.cs +++ b/src/Substrait.Core/Relation/Project.cs @@ -5,4 +5,4 @@ /// public class Project : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Read.cs b/src/Substrait.Core/Relation/Read.cs index 0743ddb..54f59e5 100644 --- a/src/Substrait.Core/Relation/Read.cs +++ b/src/Substrait.Core/Relation/Read.cs @@ -5,4 +5,4 @@ /// public class Read : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Rel.cs b/src/Substrait.Core/Relation/Rel.cs index 3112712..6d8f2c1 100644 --- a/src/Substrait.Core/Relation/Rel.cs +++ b/src/Substrait.Core/Relation/Rel.cs @@ -5,4 +5,4 @@ /// public abstract class Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Relation/Sort.cs b/src/Substrait.Core/Relation/Sort.cs index cfe83b7..41d914e 100644 --- a/src/Substrait.Core/Relation/Sort.cs +++ b/src/Substrait.Core/Relation/Sort.cs @@ -5,4 +5,4 @@ /// public class Sort : Rel { -} \ No newline at end of file +} diff --git a/src/Substrait.Core/SubstraitRelVisitor.cs b/src/Substrait.Core/SubstraitRelVisitor.cs index f002cd1..42fe229 100644 --- a/src/Substrait.Core/SubstraitRelVisitor.cs +++ b/src/Substrait.Core/SubstraitRelVisitor.cs @@ -103,4 +103,4 @@ public void Fallback(Rel _) { throw new InvalidOperationException(); } -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/Decimal.cs b/src/Substrait.Core/Types/Compound/Decimal.cs index 74be20b..0153918 100644 --- a/src/Substrait.Core/Types/Compound/Decimal.cs +++ b/src/Substrait.Core/Types/Compound/Decimal.cs @@ -7,4 +7,4 @@ public sealed record Decimal : TypeClass public required int Scale { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/FixedBinary.cs b/src/Substrait.Core/Types/Compound/FixedBinary.cs index d342c7a..12996d9 100644 --- a/src/Substrait.Core/Types/Compound/FixedBinary.cs +++ b/src/Substrait.Core/Types/Compound/FixedBinary.cs @@ -5,4 +5,4 @@ public sealed record FixedBinary : TypeClass public required int Length { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/FixedChar.cs b/src/Substrait.Core/Types/Compound/FixedChar.cs index aad83e4..0fbfc4b 100644 --- a/src/Substrait.Core/Types/Compound/FixedChar.cs +++ b/src/Substrait.Core/Types/Compound/FixedChar.cs @@ -5,4 +5,4 @@ public sealed record FixedChar : TypeClass public required int Length { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/Func.cs b/src/Substrait.Core/Types/Compound/Func.cs index 96b2ddd..d2b10d5 100644 --- a/src/Substrait.Core/Types/Compound/Func.cs +++ b/src/Substrait.Core/Types/Compound/Func.cs @@ -26,4 +26,4 @@ public override int GetHashCode() return hash.ToHashCode(); } -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/IntervalCompound.cs b/src/Substrait.Core/Types/Compound/IntervalCompound.cs index 36a4ed9..52ec347 100644 --- a/src/Substrait.Core/Types/Compound/IntervalCompound.cs +++ b/src/Substrait.Core/Types/Compound/IntervalCompound.cs @@ -5,4 +5,4 @@ public sealed record IntervalCompound : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/IntervalDay.cs b/src/Substrait.Core/Types/Compound/IntervalDay.cs index b02c531..14cef7a 100644 --- a/src/Substrait.Core/Types/Compound/IntervalDay.cs +++ b/src/Substrait.Core/Types/Compound/IntervalDay.cs @@ -5,4 +5,4 @@ public sealed record IntervalDay : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/List.cs b/src/Substrait.Core/Types/Compound/List.cs index f1bc487..754eba2 100644 --- a/src/Substrait.Core/Types/Compound/List.cs +++ b/src/Substrait.Core/Types/Compound/List.cs @@ -5,4 +5,4 @@ public sealed record List : TypeClass public required TypeClass ElementType { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/Map.cs b/src/Substrait.Core/Types/Compound/Map.cs index e8fb6c0..e309856 100644 --- a/src/Substrait.Core/Types/Compound/Map.cs +++ b/src/Substrait.Core/Types/Compound/Map.cs @@ -7,4 +7,4 @@ public sealed record Map : TypeClass public required TypeClass Value { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/PrecisionTime.cs b/src/Substrait.Core/Types/Compound/PrecisionTime.cs index ee5e138..aa8fe7c 100644 --- a/src/Substrait.Core/Types/Compound/PrecisionTime.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTime.cs @@ -5,4 +5,4 @@ public sealed record PrecisionTime : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs b/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs index 4a4dfbf..13f939c 100644 --- a/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTimestamp.cs @@ -5,4 +5,4 @@ public sealed record PrecisionTimestamp : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs b/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs index c4f0558..74e9b34 100644 --- a/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs +++ b/src/Substrait.Core/Types/Compound/PrecisionTimestampTz.cs @@ -5,4 +5,4 @@ public sealed record PrecisionTimestampTz : TypeClass public required int Precision { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/Struct.cs b/src/Substrait.Core/Types/Compound/Struct.cs index f832cb4..9182911 100644 --- a/src/Substrait.Core/Types/Compound/Struct.cs +++ b/src/Substrait.Core/Types/Compound/Struct.cs @@ -20,4 +20,4 @@ public override int GetHashCode() return hash.ToHashCode(); } -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Compound/VarChar.cs b/src/Substrait.Core/Types/Compound/VarChar.cs index 9873143..d8a2dfe 100644 --- a/src/Substrait.Core/Types/Compound/VarChar.cs +++ b/src/Substrait.Core/Types/Compound/VarChar.cs @@ -5,4 +5,4 @@ public sealed record VarChar : TypeClass public required int Length { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Extension/IParameter.cs b/src/Substrait.Core/Types/Extension/IParameter.cs new file mode 100644 index 0000000..9831bb9 --- /dev/null +++ b/src/Substrait.Core/Types/Extension/IParameter.cs @@ -0,0 +1,5 @@ +namespace Substrait.Core.Types.Extension; + +public interface IParameter +{ +} diff --git a/src/Substrait.Core/Types/Extension/ParameterBooleanValue.cs b/src/Substrait.Core/Types/Extension/ParameterBooleanValue.cs new file mode 100644 index 0000000..e87d2b0 --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterBooleanValue.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterBooleanValue : IParameter +{ + public required bool Value { get; init; } +} diff --git a/src/Substrait.Core/Types/Extension/ParameterDataType.cs b/src/Substrait.Core/Types/Extension/ParameterDataType.cs new file mode 100644 index 0000000..aa06cde --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterDataType.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterDataType : IParameter +{ + public required TypeClass Type { get; init; } +} diff --git a/src/Substrait.Core/Types/Extension/ParameterEnumValue.cs b/src/Substrait.Core/Types/Extension/ParameterEnumValue.cs new file mode 100644 index 0000000..7a0f654 --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterEnumValue.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterEnumValue : IParameter +{ + public required string Value { get; init; } +} diff --git a/src/Substrait.Core/Types/Extension/ParameterIntegerValue.cs b/src/Substrait.Core/Types/Extension/ParameterIntegerValue.cs new file mode 100644 index 0000000..96319ee --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterIntegerValue.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterIntegerValue : IParameter +{ + public required long Value { get; init; } +} diff --git a/src/Substrait.Core/Types/Extension/ParameterNull.cs b/src/Substrait.Core/Types/Extension/ParameterNull.cs new file mode 100644 index 0000000..fe7b324 --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterNull.cs @@ -0,0 +1,10 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterNull : IParameter +{ + public static readonly ParameterNull Instance = new(); + + private ParameterNull() + { + } +} diff --git a/src/Substrait.Core/Types/Extension/ParameterStringValue.cs b/src/Substrait.Core/Types/Extension/ParameterStringValue.cs new file mode 100644 index 0000000..75daf6f --- /dev/null +++ b/src/Substrait.Core/Types/Extension/ParameterStringValue.cs @@ -0,0 +1,6 @@ +namespace Substrait.Core.Types.Extension; + +public sealed record ParameterStringValue : IParameter +{ + public required string Value { get; init; } +} diff --git a/src/Substrait.Core/Types/Extension/UserDefined.cs b/src/Substrait.Core/Types/Extension/UserDefined.cs new file mode 100644 index 0000000..f9f9215 --- /dev/null +++ b/src/Substrait.Core/Types/Extension/UserDefined.cs @@ -0,0 +1,37 @@ +// namespace Substrait.Core.Types.Extension; +// +// public sealed record UserDefined : TypeClass +// { +// public required string Urn { get; init; } +// +// public required string Name { get; init; } +// +// public IReadOnlyList TypeParameters { get; init; } = Array.Empty(); +// +// public int TypeVariationReference { get; init; } +// +// public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); +// +// public bool Equals(UserDefined? other) => +// other is not null +// && Nullable == other.Nullable +// && Urn == other.Urn +// && Name == other.Name +// && TypeVariationReference == other.TypeVariationReference +// && TypeParameters.SequenceEqual(other.TypeParameters); +// +// public override int GetHashCode() +// { +// var hash = new HashCode(); +// hash.Add(Nullable); +// hash.Add(Urn); +// hash.Add(Name); +// hash.Add(TypeVariationReference); +// foreach (var parameter in TypeParameters) +// { +// hash.Add(parameter); +// } +// +// return hash.ToHashCode(); +// } +// } diff --git a/src/Substrait.Core/Types/ITypeVisitor.cs b/src/Substrait.Core/Types/ITypeVisitor.cs index 9b91188..7a05666 100644 --- a/src/Substrait.Core/Types/ITypeVisitor.cs +++ b/src/Substrait.Core/Types/ITypeVisitor.cs @@ -64,4 +64,4 @@ TResult VisitFallback(TypeClass type) => TResult Visit(List type) => VisitFallback(type); TResult Visit(Map type) => VisitFallback(type); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Binary.cs b/src/Substrait.Core/Types/Simple/Binary.cs index dfdcfa4..d7258b8 100644 --- a/src/Substrait.Core/Types/Simple/Binary.cs +++ b/src/Substrait.Core/Types/Simple/Binary.cs @@ -3,4 +3,4 @@ public sealed record Binary : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Bool.cs b/src/Substrait.Core/Types/Simple/Bool.cs index b73e2cf..ca06fd7 100644 --- a/src/Substrait.Core/Types/Simple/Bool.cs +++ b/src/Substrait.Core/Types/Simple/Bool.cs @@ -3,4 +3,4 @@ public sealed record Bool : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Date.cs b/src/Substrait.Core/Types/Simple/Date.cs index 8c6c110..8183b94 100644 --- a/src/Substrait.Core/Types/Simple/Date.cs +++ b/src/Substrait.Core/Types/Simple/Date.cs @@ -3,4 +3,4 @@ public sealed record Date : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Fp32.cs b/src/Substrait.Core/Types/Simple/Fp32.cs index 630cb1f..f530555 100644 --- a/src/Substrait.Core/Types/Simple/Fp32.cs +++ b/src/Substrait.Core/Types/Simple/Fp32.cs @@ -3,4 +3,4 @@ public sealed record Fp32 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Fp64.cs b/src/Substrait.Core/Types/Simple/Fp64.cs index 42c48e2..bfca396 100644 --- a/src/Substrait.Core/Types/Simple/Fp64.cs +++ b/src/Substrait.Core/Types/Simple/Fp64.cs @@ -3,4 +3,4 @@ public sealed record Fp64 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/I16.cs b/src/Substrait.Core/Types/Simple/I16.cs index 9a0d3a0..3aefb36 100644 --- a/src/Substrait.Core/Types/Simple/I16.cs +++ b/src/Substrait.Core/Types/Simple/I16.cs @@ -3,4 +3,4 @@ public sealed record I16 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/I32.cs b/src/Substrait.Core/Types/Simple/I32.cs index 7e6d1c6..0801a38 100644 --- a/src/Substrait.Core/Types/Simple/I32.cs +++ b/src/Substrait.Core/Types/Simple/I32.cs @@ -3,4 +3,4 @@ public sealed record I32 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/I64.cs b/src/Substrait.Core/Types/Simple/I64.cs index 9ae1374..0b9d5c0 100644 --- a/src/Substrait.Core/Types/Simple/I64.cs +++ b/src/Substrait.Core/Types/Simple/I64.cs @@ -3,4 +3,4 @@ public sealed record I64 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/I8.cs b/src/Substrait.Core/Types/Simple/I8.cs index 78c6bd3..f44d86f 100644 --- a/src/Substrait.Core/Types/Simple/I8.cs +++ b/src/Substrait.Core/Types/Simple/I8.cs @@ -3,4 +3,4 @@ public sealed record I8 : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/IntervalYear.cs b/src/Substrait.Core/Types/Simple/IntervalYear.cs index 08cc44b..a6d9eec 100644 --- a/src/Substrait.Core/Types/Simple/IntervalYear.cs +++ b/src/Substrait.Core/Types/Simple/IntervalYear.cs @@ -3,4 +3,4 @@ public sealed record IntervalYear : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/String.cs b/src/Substrait.Core/Types/Simple/String.cs index 9a468f6..25f0c9a 100644 --- a/src/Substrait.Core/Types/Simple/String.cs +++ b/src/Substrait.Core/Types/Simple/String.cs @@ -3,4 +3,4 @@ public sealed record String : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/Simple/Uuid.cs b/src/Substrait.Core/Types/Simple/Uuid.cs index 68482d7..a011f76 100644 --- a/src/Substrait.Core/Types/Simple/Uuid.cs +++ b/src/Substrait.Core/Types/Simple/Uuid.cs @@ -3,4 +3,4 @@ public sealed record Uuid : TypeClass { public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/TypeClass.cs b/src/Substrait.Core/Types/TypeClass.cs index 40c77a6..085d5e5 100644 --- a/src/Substrait.Core/Types/TypeClass.cs +++ b/src/Substrait.Core/Types/TypeClass.cs @@ -5,4 +5,4 @@ public abstract record TypeClass public required bool Nullable { get; init; } public abstract TResult Accept(ITypeVisitor visitor); -} \ No newline at end of file +} diff --git a/src/Substrait.Core/Types/TypeCreator.cs b/src/Substrait.Core/Types/TypeCreator.cs index 167e92f..72b6b14 100644 --- a/src/Substrait.Core/Types/TypeCreator.cs +++ b/src/Substrait.Core/Types/TypeCreator.cs @@ -115,4 +115,4 @@ public Map Map(TypeClass key, TypeClass value) => public static TypeClass AsNullable(TypeClass type) => type with { Nullable = true }; public static TypeClass AsNotNullable(TypeClass type) => type with { Nullable = false }; -} \ No newline at end of file +} From 2dc1d2d29fef8bb48d49cf921e427112cec641ee Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Fri, 7 Aug 2026 18:15:55 +0500 Subject: [PATCH 17/23] upgrade to xunit v3, drop coverlet --- .github/workflows/ci.yml | 2 +- test/Substrait.Core.Tests/Substrait.Core.Tests.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ac3c8d..a6dac3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,4 +36,4 @@ jobs: run: dotnet build substrait-csharp.slnx --no-restore --configuration Release - name: Test - run: dotnet test substrait-csharp.slnx --no-build --configuration Release --collect:"XPlat Code Coverage" + run: dotnet test substrait-csharp.slnx --no-build --configuration Release diff --git a/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj b/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj index 41948f6..0a696fa 100644 --- a/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj +++ b/test/Substrait.Core.Tests/Substrait.Core.Tests.csproj @@ -2,15 +2,15 @@ net10.0 + Exe enable enable false + true - - - + From cfab686c48ee4f215dcbd381f768e9d0da5657c4 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 10:39:32 +0500 Subject: [PATCH 18/23] Make SubstraitRelVisitor's Visit/Fallback overridable and use ArgumentNullException.ThrowIfNull --- src/Substrait.Core/SubstraitRelVisitor.cs | 60 +++++++++-------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/src/Substrait.Core/SubstraitRelVisitor.cs b/src/Substrait.Core/SubstraitRelVisitor.cs index 42fe229..ca0b580 100644 --- a/src/Substrait.Core/SubstraitRelVisitor.cs +++ b/src/Substrait.Core/SubstraitRelVisitor.cs @@ -11,12 +11,9 @@ public abstract class SubstraitRelVisitor /// /// Visit relational operator of type AGGREGATE /// - public void Visit(Aggregate aggregate) + public virtual void Visit(Aggregate aggregate) { - if (aggregate is null) - { - throw new ArgumentNullException(nameof(aggregate)); - } + ArgumentNullException.ThrowIfNull(aggregate); Fallback(aggregate); } @@ -24,12 +21,9 @@ public void Visit(Aggregate aggregate) /// /// Visit relational operator of type FETCH /// - public void Visit(Fetch fetch) + public virtual void Visit(Fetch fetch) { - if (fetch is null) - { - throw new ArgumentNullException(nameof(fetch)); - } + ArgumentNullException.ThrowIfNull(fetch); Fallback(fetch); } @@ -37,12 +31,9 @@ public void Visit(Fetch fetch) /// /// Visit relational operator of type FILTER /// - public void Visit(Filter filter) + public virtual void Visit(Filter filter) { - if (filter is null) - { - throw new ArgumentNullException(nameof(filter)); - } + ArgumentNullException.ThrowIfNull(filter); Fallback(filter); } @@ -50,12 +41,9 @@ public void Visit(Filter filter) /// /// Visit relational operator of type JOIN /// - public void Visit(Join join) + public virtual void Visit(Join join) { - if (join is null) - { - throw new ArgumentNullException(nameof(join)); - } + ArgumentNullException.ThrowIfNull(join); Fallback(join); } @@ -63,12 +51,9 @@ public void Visit(Join join) /// /// Visit relational operator of type PROJECT /// - public void Visit(Project project) + public virtual void Visit(Project project) { - if (project is null) - { - throw new ArgumentNullException(nameof(project)); - } + ArgumentNullException.ThrowIfNull(project); Fallback(project); } @@ -76,12 +61,9 @@ public void Visit(Project project) /// /// Visit relational operator of type READ /// - public void Visit(Read read) + public virtual void Visit(Read read) { - if (read is null) - { - throw new ArgumentNullException(nameof(read)); - } + ArgumentNullException.ThrowIfNull(read); Fallback(read); } @@ -89,18 +71,22 @@ public void Visit(Read read) /// /// Visit relational operator of type SORT /// - public void Visit(Sort sort) + public virtual void Visit(Sort sort) { - if (sort is null) - { - throw new ArgumentNullException(nameof(sort)); - } + ArgumentNullException.ThrowIfNull(sort); Fallback(sort); } - public void Fallback(Rel _) + /// + /// Invoked for any relational operator the visitor does not handle explicitly. Override to supply + /// default behaviour. + /// + protected virtual void Fallback(Rel rel) { - throw new InvalidOperationException(); + ArgumentNullException.ThrowIfNull(rel); + + throw new InvalidOperationException( + $"{GetType().Name} does not handle relational operator '{rel.GetType().Name}'."); } } From 0f6812dd7384d89073a97aaf8e55037de73f7472 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 10:39:39 +0500 Subject: [PATCH 19/23] Return concrete types from every TypeCreator factory method instead of the base TypeClass --- src/Substrait.Core/Types/TypeCreator.cs | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Substrait.Core/Types/TypeCreator.cs b/src/Substrait.Core/Types/TypeCreator.cs index 72b6b14..3962bf0 100644 --- a/src/Substrait.Core/Types/TypeCreator.cs +++ b/src/Substrait.Core/Types/TypeCreator.cs @@ -18,49 +18,49 @@ private TypeCreator(bool nullable) _nullable = nullable; } - public TypeClass Bool => new Bool { Nullable = _nullable }; + public Bool Bool => new() { Nullable = _nullable }; - public TypeClass I8 => new I8 { Nullable = _nullable }; + public I8 I8 => new() { Nullable = _nullable }; - public TypeClass I16 => new I16 { Nullable = _nullable }; + public I16 I16 => new() { Nullable = _nullable }; - public TypeClass I32 => new I32 { Nullable = _nullable }; + public I32 I32 => new() { Nullable = _nullable }; - public TypeClass I64 => new I64 { Nullable = _nullable }; + public I64 I64 => new() { Nullable = _nullable }; - public TypeClass Fp32 => new Fp32 { Nullable = _nullable }; + public Fp32 Fp32 => new() { Nullable = _nullable }; - public TypeClass Fp64 => new Fp64 { Nullable = _nullable }; + public Fp64 Fp64 => new() { Nullable = _nullable }; - public TypeClass String => new String { Nullable = _nullable }; + public String String => new() { Nullable = _nullable }; - public TypeClass Binary => new Binary { Nullable = _nullable }; + public Binary Binary => new() { Nullable = _nullable }; - public TypeClass Date => new Date { Nullable = _nullable }; + public Date Date => new() { Nullable = _nullable }; - public TypeClass IntervalYear => new IntervalYear { Nullable = _nullable }; + public IntervalYear IntervalYear => new() { Nullable = _nullable }; - public TypeClass Uuid => new Uuid { Nullable = _nullable }; + public Uuid Uuid => new() { Nullable = _nullable }; - public TypeClass FixedChar(int length) + public FixedChar FixedChar(int length) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); return new FixedChar { Nullable = _nullable, Length = length }; } - public TypeClass VarChar(int length) + public VarChar VarChar(int length) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); return new VarChar { Nullable = _nullable, Length = length }; } - public TypeClass FixedBinary(int length) + public FixedBinary FixedBinary(int length) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); return new FixedBinary { Nullable = _nullable, Length = length }; } - public TypeClass Decimal(int precision, int scale) + public Decimal Decimal(int precision, int scale) { ArgumentOutOfRangeException.ThrowIfNegative(precision); ArgumentOutOfRangeException.ThrowIfGreaterThan(precision, MaxDecimalPrecision); @@ -69,24 +69,24 @@ public TypeClass Decimal(int precision, int scale) return new Decimal { Nullable = _nullable, Precision = precision, Scale = scale }; } - public TypeClass PrecisionTime(int precision) => - new PrecisionTime { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + public PrecisionTime PrecisionTime(int precision) => + new() { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; - public TypeClass PrecisionTimestamp(int precision) => - new PrecisionTimestamp { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + public PrecisionTimestamp PrecisionTimestamp(int precision) => + new() { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; - public TypeClass PrecisionTimestampTz(int precision) => - new PrecisionTimestampTz { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + public PrecisionTimestampTz PrecisionTimestampTz(int precision) => + new() { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; - public TypeClass IntervalDay(int precision) => - new IntervalDay { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + public IntervalDay IntervalDay(int precision) => + new() { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; - public TypeClass IntervalCompound(int precision) => - new IntervalCompound { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; + public IntervalCompound IntervalCompound(int precision) => + new() { Nullable = _nullable, Precision = SubsecondPrecision(precision) }; private const int MaxDecimalPrecision = 38; - private const int MaxSubsecondPrecision = 9; + private const int MaxSubsecondPrecision = 12; private static int SubsecondPrecision(int precision) { @@ -95,8 +95,8 @@ private static int SubsecondPrecision(int precision) return precision; } - public TypeClass Func(IReadOnlyList parameterTypes, TypeClass returnType) => - new Func { Nullable = _nullable, ParameterTypes = parameterTypes, ReturnType = returnType }; + public Func Func(IReadOnlyList parameterTypes, TypeClass returnType) => + new() { Nullable = _nullable, ParameterTypes = parameterTypes, ReturnType = returnType }; public Struct Struct(params TypeClass[] fields) => new() { Nullable = _nullable, Fields = fields }; @@ -114,5 +114,5 @@ public Map Map(TypeClass key, TypeClass value) => public static TypeClass AsNullable(TypeClass type) => type with { Nullable = true }; - public static TypeClass AsNotNullable(TypeClass type) => type with { Nullable = false }; + public static TypeClass AsRequired(TypeClass type) => type with { Nullable = false }; } From 52ec562f6174c480aee0d314abe85bde370c02aa Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 11:08:08 +0500 Subject: [PATCH 20/23] Enable compilation of Protobuf files in Substrait.Core.csproj --- src/Substrait.Core/Substrait.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Substrait.Core/Substrait.Core.csproj b/src/Substrait.Core/Substrait.Core.csproj index 40bd707..1be2e78 100644 --- a/src/Substrait.Core/Substrait.Core.csproj +++ b/src/Substrait.Core/Substrait.Core.csproj @@ -8,7 +8,7 @@ - + From 0173627a21e510235fe069aedb349bcf2b820441 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 11:09:21 +0500 Subject: [PATCH 21/23] remove trailing /n --- test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs index 56036bd..36f7dfc 100644 --- a/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs +++ b/test/Substrait.Core.Tests/SubstraitRelVisitorTests.cs @@ -23,4 +23,4 @@ public void Visit_WithUnhandledRelation_FallsBackAndThrows() Assert.Throws(() => visitor.Visit(new Read())); } -} \ No newline at end of file +} From fa4793e928cd7927b5a0b562658889ac17f66388 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 11:30:53 +0500 Subject: [PATCH 22/23] =?UTF-8?q?=E2=8F=BA=20Fix=20Struct/Func=20to=20defe?= =?UTF-8?q?nsively=20copy=20Fields/ParameterTypes=20on=20init,=20preventin?= =?UTF-8?q?g=20caller=20array=20mutation=20from=20corrupting=20hash/equali?= =?UTF-8?q?ty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Substrait.Core/Types/Compound/Func.cs | 17 ++++++++++++----- src/Substrait.Core/Types/Compound/Struct.cs | 11 +++++++++-- src/Substrait.Core/Types/TypeCreator.cs | 2 +- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Substrait.Core/Types/Compound/Func.cs b/src/Substrait.Core/Types/Compound/Func.cs index d2b10d5..a569062 100644 --- a/src/Substrait.Core/Types/Compound/Func.cs +++ b/src/Substrait.Core/Types/Compound/Func.cs @@ -2,17 +2,24 @@ public sealed record Func : TypeClass { - public required IReadOnlyList ParameterTypes { get; init; } + private readonly TypeClass[] _parameterTypes = []; + + public required IReadOnlyList ParameterTypes + { + get => _parameterTypes; + init => _parameterTypes = value.ToArray(); + } public required TypeClass ReturnType { get; init; } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); public bool Equals(Func? other) => - other is not null - && Nullable == other.Nullable - && ReturnType.Equals(other.ReturnType) - && ParameterTypes.SequenceEqual(other.ParameterTypes); + ReferenceEquals(this, other) + || (other is not null + && Nullable == other.Nullable + && ReturnType == other.ReturnType + && ParameterTypes.SequenceEqual(other.ParameterTypes)); public override int GetHashCode() { diff --git a/src/Substrait.Core/Types/Compound/Struct.cs b/src/Substrait.Core/Types/Compound/Struct.cs index 9182911..80e3c1d 100644 --- a/src/Substrait.Core/Types/Compound/Struct.cs +++ b/src/Substrait.Core/Types/Compound/Struct.cs @@ -2,12 +2,19 @@ public sealed record Struct : TypeClass { - public required IReadOnlyList Fields { get; init; } + private readonly TypeClass[] _fields = []; + + public required IReadOnlyList Fields + { + get => _fields; + init => _fields = value.ToArray(); + } public override TResult Accept(ITypeVisitor visitor) => visitor.Visit(this); public bool Equals(Struct? other) => - other is not null && Nullable == other.Nullable && Fields.SequenceEqual(other.Fields); + ReferenceEquals(this, other) + || (other is not null && Nullable == other.Nullable && Fields.SequenceEqual(other.Fields)); public override int GetHashCode() { diff --git a/src/Substrait.Core/Types/TypeCreator.cs b/src/Substrait.Core/Types/TypeCreator.cs index 3962bf0..2585c80 100644 --- a/src/Substrait.Core/Types/TypeCreator.cs +++ b/src/Substrait.Core/Types/TypeCreator.cs @@ -102,7 +102,7 @@ public Struct Struct(params TypeClass[] fields) => new() { Nullable = _nullable, Fields = fields }; public Struct Struct(IEnumerable fields) => - new() { Nullable = _nullable, Fields = fields.ToList() }; + new() { Nullable = _nullable, Fields = fields.ToArray() }; public List List(TypeClass elementType) => new() { Nullable = _nullable, ElementType = elementType }; From 82db270db428d462a69c175b1e8194d527539607 Mon Sep 17 00:00:00 2001 From: Anas Ismail Khan Date: Sat, 8 Aug 2026 13:41:07 +0500 Subject: [PATCH 23/23] Add missing type-system tests from review --- .../Type/StructuralEqualityTests.cs | 126 ++++++++++++++++++ .../Type/TypeCreatorTests.cs | 96 +++++++++++++ .../Type/TypeVisitorTests.cs | 120 +++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 test/Substrait.Core.Tests/Type/StructuralEqualityTests.cs create mode 100644 test/Substrait.Core.Tests/Type/TypeCreatorTests.cs create mode 100644 test/Substrait.Core.Tests/Type/TypeVisitorTests.cs diff --git a/test/Substrait.Core.Tests/Type/StructuralEqualityTests.cs b/test/Substrait.Core.Tests/Type/StructuralEqualityTests.cs new file mode 100644 index 0000000..56724dc --- /dev/null +++ b/test/Substrait.Core.Tests/Type/StructuralEqualityTests.cs @@ -0,0 +1,126 @@ +using Substrait.Core.Types; +using Substrait.Core.Types.Compound; + +namespace Substrait.Core.Tests.Type; + +public class StructuralEqualityTests +{ + [Fact] + public void Struct_WithEqualFieldsFromDifferentLists_AreEqualAndHashesEqual() + { + var first = new Struct + { + Nullable = false, + Fields = new List { TypeCreator.Required.I32, TypeCreator.Required.String }, + }; + var second = new Struct + { + Nullable = false, + Fields = [TypeCreator.Required.I32, TypeCreator.Required.String], + }; + + Assert.Equal(first, second); + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + [Fact] + public void Struct_WithDifferentFields_AreNotEqual() + { + var first = TypeCreator.Required.Struct(TypeCreator.Required.I32); + var second = TypeCreator.Required.Struct(TypeCreator.Required.I64); + + Assert.NotEqual(first, second); + } + + [Fact] + public void Func_WithEqualParameterTypesFromDifferentLists_AreEqualAndHashesEqual() + { + var first = new Func + { + Nullable = false, + ParameterTypes = new List { TypeCreator.Required.I32 }, + ReturnType = TypeCreator.Required.Bool, + }; + var second = new Func + { + Nullable = false, + ParameterTypes = [TypeCreator.Required.I32], + ReturnType = TypeCreator.Required.Bool, + }; + + Assert.Equal(first, second); + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + [Fact] + public void Struct_WithReorderedFields_IsNotEqual() + { + var first = TypeCreator.Required.Struct(TypeCreator.Required.I32, TypeCreator.Required.I64); + var second = TypeCreator.Required.Struct(TypeCreator.Required.I64, TypeCreator.Required.I32); + + Assert.NotEqual(first, second); + } + + [Fact] + public void Struct_DifferingOnlyInNullability_IsNotEqual() + { + var required = TypeCreator.Required.Struct(TypeCreator.Required.I32); + var nullable = TypeCreator.Nullable.Struct(TypeCreator.Required.I32); + + Assert.NotEqual(required, nullable); + } + + [Fact] + public void Struct_IsNotMutatedByCallerOwnedArray() + { + var fields = new TypeClass[] { TypeCreator.Required.I32 }; + var type = TypeCreator.Required.Struct(fields); + var hashBefore = type.GetHashCode(); + + fields[0] = TypeCreator.Required.I64; + + Assert.Equal(hashBefore, type.GetHashCode()); + } + + [Fact] + public void Func_WithDifferentReturnType_IsNotEqual() + { + var first = new Func + { + Nullable = false, + ParameterTypes = [TypeCreator.Required.I32], + ReturnType = TypeCreator.Required.Bool, + }; + var second = new Func + { + Nullable = false, + ParameterTypes = [TypeCreator.Required.I32], + ReturnType = TypeCreator.Required.I64, + }; + + Assert.NotEqual(first, second); + } + + [Fact] + public void NestedTypes_CompareStructurally() + { + var first = TypeCreator.Required.Map( + TypeCreator.Required.String, + TypeCreator.Nullable.List(TypeCreator.Required.I32)); + var second = TypeCreator.Required.Map( + TypeCreator.Required.String, + TypeCreator.Nullable.List(TypeCreator.Required.I32)); + + Assert.Equal(first, second); + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + [Fact] + public void DifferentKindsWithIdenticalShape_AreNotEqual() + { + TypeClass first = TypeCreator.Required.PrecisionTime(6); + TypeClass second = TypeCreator.Required.PrecisionTimestamp(6); + + Assert.NotEqual(first, second); + } +} diff --git a/test/Substrait.Core.Tests/Type/TypeCreatorTests.cs b/test/Substrait.Core.Tests/Type/TypeCreatorTests.cs new file mode 100644 index 0000000..e94c422 --- /dev/null +++ b/test/Substrait.Core.Tests/Type/TypeCreatorTests.cs @@ -0,0 +1,96 @@ +using Substrait.Core.Types; +using Substrait.Core.Types.Compound; +using Decimal = Substrait.Core.Types.Compound.Decimal; + +namespace Substrait.Core.Tests.Type; + +public class TypeCreatorTests +{ + [Fact] + public void Required_ProducesNonNullableTypes() + { + Assert.False(TypeCreator.Required.Bool.Nullable); + Assert.False(TypeCreator.Required.FixedChar(10).Nullable); + } + + [Fact] + public void Nullable_ProducesNullableTypes() + { + Assert.True(TypeCreator.Nullable.Bool.Nullable); + Assert.True(TypeCreator.Nullable.FixedChar(10).Nullable); + } + + [Fact] + public void Of_ReturnsTheMatchingSingleton() + { + Assert.Same(TypeCreator.Required, TypeCreator.Of(false)); + Assert.Same(TypeCreator.Nullable, TypeCreator.Of(true)); + } + + [Fact] + public void FixedChar_SetsLength() + { + var type = Assert.IsType(TypeCreator.Required.FixedChar(10)); + + Assert.Equal(10, type.Length); + } + + [Fact] + public void Decimal_SetsPrecisionAndScale() + { + var type = Assert.IsType(TypeCreator.Required.Decimal(precision: 38, scale: 4)); + + Assert.Equal(38, type.Precision); + Assert.Equal(4, type.Scale); + } + + [Fact] + public void Struct_FromParams_BuildsFieldList() + { + var type = TypeCreator.Required.Struct(TypeCreator.Required.I32, TypeCreator.Nullable.String); + + Assert.Equal([TypeCreator.Required.I32, TypeCreator.Nullable.String], type.Fields); + } + + [Fact] + public void List_SetsElementType() + { + var type = TypeCreator.Nullable.List(TypeCreator.Required.I64); + + Assert.Equal(TypeCreator.Required.I64, type.ElementType); + Assert.True(type.Nullable); + } + + [Fact] + public void Map_SetsKeyAndValueTypes() + { + var type = TypeCreator.Required.Map(TypeCreator.Required.String, TypeCreator.Nullable.I32); + + Assert.Equal(TypeCreator.Required.String, type.Key); + Assert.Equal(TypeCreator.Nullable.I32, type.Value); + } + + [Fact] + public void AsNullable_TogglesNullabilityAndPreservesRuntimeType() + { + TypeClass required = TypeCreator.Required.FixedChar(10); + + var nullable = TypeCreator.AsNullable(required); + + var fixedChar = Assert.IsType(nullable); + Assert.True(fixedChar.Nullable); + Assert.Equal(10, fixedChar.Length); + } + + [Fact] + public void AsRequired_TogglesNullabilityAndPreservesRuntimeType() + { + TypeClass nullable = TypeCreator.Nullable.FixedChar(10); + + var required = TypeCreator.AsRequired(nullable); + + var fixedChar = Assert.IsType(required); + Assert.False(fixedChar.Nullable); + Assert.Equal(10, fixedChar.Length); + } +} diff --git a/test/Substrait.Core.Tests/Type/TypeVisitorTests.cs b/test/Substrait.Core.Tests/Type/TypeVisitorTests.cs new file mode 100644 index 0000000..c666517 --- /dev/null +++ b/test/Substrait.Core.Tests/Type/TypeVisitorTests.cs @@ -0,0 +1,120 @@ +using Substrait.Core.Types; +using Substrait.Core.Types.Compound; +using Substrait.Core.Types.Simple; +using Decimal = Substrait.Core.Types.Compound.Decimal; +using String = Substrait.Core.Types.Simple.String; + +namespace Substrait.Core.Tests.Type; + +public class TypeVisitorTests +{ + private sealed class KindNameVisitor : ITypeVisitor + { + public string Visit(Bool type) => nameof(Bool); + + public string Visit(I8 type) => nameof(I8); + + public string Visit(I16 type) => nameof(I16); + + public string Visit(I32 type) => nameof(I32); + + public string Visit(I64 type) => nameof(I64); + + public string Visit(Fp32 type) => nameof(Fp32); + + public string Visit(Fp64 type) => nameof(Fp64); + + public string Visit(String type) => nameof(String); + + public string Visit(Binary type) => nameof(Binary); + + public string Visit(Date type) => nameof(Date); + + public string Visit(IntervalYear type) => nameof(IntervalYear); + + public string Visit(IntervalDay type) => nameof(IntervalDay); + + public string Visit(IntervalCompound type) => nameof(IntervalCompound); + + public string Visit(Uuid type) => nameof(Uuid); + + public string Visit(FixedChar type) => nameof(FixedChar); + + public string Visit(VarChar type) => nameof(VarChar); + + public string Visit(FixedBinary type) => nameof(FixedBinary); + + public string Visit(Decimal type) => nameof(Decimal); + + public string Visit(PrecisionTime type) => nameof(PrecisionTime); + + public string Visit(PrecisionTimestamp type) => nameof(PrecisionTimestamp); + + public string Visit(PrecisionTimestampTz type) => nameof(PrecisionTimestampTz); + + public string Visit(Func type) => nameof(Func); + + public string Visit(Struct type) => nameof(Struct); + + public string Visit(List type) => nameof(List); + + public string Visit(Map type) => nameof(Map); + } + + public static TheoryData TypesAndExpectedKinds() => + new() + { + { TypeCreator.Required.Bool, nameof(Bool) }, + { TypeCreator.Required.I32, nameof(I32) }, + { TypeCreator.Required.String, nameof(String) }, + { TypeCreator.Required.FixedChar(5), nameof(FixedChar) }, + { TypeCreator.Required.Decimal(10, 2), nameof(Decimal) }, + { TypeCreator.Required.Struct(TypeCreator.Required.Bool), nameof(Struct) }, + { TypeCreator.Required.List(TypeCreator.Required.Bool), nameof(List) }, + { TypeCreator.Required.Map(TypeCreator.Required.String, TypeCreator.Required.I32), nameof(Map) }, + }; + + [Theory] + [MemberData(nameof(TypesAndExpectedKinds))] + public void Accept_DispatchesToMatchingVisitMethod(TypeClass type, string expectedKind) + { + var visitor = new KindNameVisitor(); + + var result = type.Accept(visitor); + + Assert.Equal(expectedKind, result); + } + + [Fact] + public void EveryTypeClass_HasAVisitorOverload() + { + var kinds = typeof(TypeClass) + .Assembly.GetTypes() + .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(TypeClass))) + .ToList(); + + var covered = typeof(ITypeVisitor<>) + .GetMethods() + .Where(m => m.Name == "Visit") + .Select(m => m.GetParameters()[0].ParameterType) + .ToHashSet(); + + var missing = kinds.Where(k => !covered.Contains(k)).Select(k => k.Name).Order().ToList(); + + Assert.True(missing.Count == 0, $"No ITypeVisitor overload for: {string.Join(", ", missing)}"); + } + + [Fact] + public void Accept_DispatchesOnTheRuntimeKind() + { + var visitor = new FallbackOnlyVisitor(); + + Assert.Equal(nameof(Decimal), TypeCreator.Required.Decimal(10, 2).Accept(visitor)); + Assert.Equal(nameof(List), TypeCreator.Required.List(TypeCreator.Required.I32).Accept(visitor)); + } + + private sealed class FallbackOnlyVisitor : ITypeVisitor + { + public string VisitFallback(TypeClass type) => type.GetType().Name; + } +}