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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Unit F64
)>
{
public static readonly AlgebraicType Unit = new Product([]);
public const string QueryBuilderProductTypeTag = "__query__";

// Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as an Option<T>.
internal static AlgebraicType MakeOption(AlgebraicType someType) =>
Expand All @@ -47,4 +48,8 @@ internal static AlgebraicType MakeOption(AlgebraicType someType) =>
// Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as a Result<T, E>.
internal static AlgebraicType MakeResult(AlgebraicType okType, AlgebraicType errType) =>
new Sum([new("ok", okType), new("err", errType)]);

// Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as Query<T>.
public static AlgebraicType MakeQueryBuilderProductType(Ref rowProductTypeRef) =>
new Product([new(QueryBuilderProductTypeTag, rowProductTypeRef)]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1636,10 +1636,9 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar
IsPublic: true,
IsAnonymous: false,
Params: [],
ReturnType: new SpacetimeDB.BSATN.ValueOption<
PublicTable,
PublicTable.BSATN
>().GetAlgebraicType(registrar)
ReturnType: global::SpacetimeDB.BSATN.AlgebraicType.MakeQueryBuilderProductType(
new PublicTable.BSATN().GetAlgebraicType(registrar)
)
);

public byte[] Invoke(
Expand Down
26 changes: 15 additions & 11 deletions crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ record ViewDeclaration
public readonly bool IsPublic;
public readonly bool ReturnsQuery;
public readonly TypeUse ReturnType;
public readonly TypeUse? QueryRowType;
public readonly EquatableArray<MemberDeclaration> Parameters;
public readonly Scope Scope;

Expand Down Expand Up @@ -1186,15 +1187,12 @@ method.ReturnType is INamedTypeSymbol
{
ReturnsQuery = true;
var rowType = TypeUse.Parse(method, queryRowType, diag);
var optType = queryRowType.IsValueType
? "SpacetimeDB.BSATN.ValueOption"
: "SpacetimeDB.BSATN.RefOption";
var opt = $"{optType}<{rowType.Name}, {rowType.BSATNName}>";
// Match Rust semantics: Query<T> is described as a nullable row (T?).
ReturnType = new ReferenceUse(opt, opt);
QueryRowType = rowType;
ReturnType = rowType;
}
else
{
QueryRowType = null;
ReturnType = TypeUse.Parse(method, method.ReturnType, diag);
}
Scope = new Scope(methodSyntax.Parent as MemberDeclarationSyntax);
Expand All @@ -1211,9 +1209,10 @@ method.ReturnType is INamedTypeSymbol
diag.Report(ErrorDescriptor.ViewContextParam, methodSyntax);
}

// Validate return type: must be List<T> or T?
// Validate return type: must be List<T>, T?, or IQuery<T>.
if (
!ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.ValueOption")
!ReturnsQuery
&& !ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.ValueOption")
&& !ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.RefOption")
&& !ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.List")
)
Expand All @@ -1229,17 +1228,22 @@ method.ReturnType is INamedTypeSymbol
);
}

public string GenerateViewDef(uint Index) =>
$$$"""
public string GenerateViewDef(uint Index)
{
var returnTypeExpr = ReturnsQuery
? $"global::SpacetimeDB.BSATN.AlgebraicType.MakeQueryBuilderProductType(new {QueryRowType!.BSATNName}().GetAlgebraicType(registrar))"
: $"new {ReturnType.BSATNName}().GetAlgebraicType(registrar)";
return $$$"""
new global::SpacetimeDB.Internal.RawViewDefV10(
SourceName: "{{{Name}}}",
Index: {{{Index}}},
IsPublic: {{{IsPublic.ToString().ToLower()}}},
IsAnonymous: {{{IsAnonymous.ToString().ToLower()}}},
Params: [{{{MemberDeclaration.GenerateDefs(Parameters)}}}],
ReturnType: new {{{ReturnType.BSATNName}}}().GetAlgebraicType(registrar)
ReturnType: {{{returnTypeExpr}}}
);
""";
}

/// <summary>
/// Generates the class responsible for evaluating a view.
Expand Down
7 changes: 7 additions & 0 deletions crates/codegen/src/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ impl Lang for Csharp<'_> {
}
}
}
for (columns, constraints) in schema.backcompat_column_constraints() {
if constraints.has_indexed() || constraints.has_unique() || constraints.has_primary_key() {
for col_pos in columns.iter() {
ix_col_positions.insert(col_pos.idx());
}
}
}

writeln!(output, "public sealed class {cols_owner_name}Cols");
indented_block(&mut output, |output| {
Expand Down
2 changes: 2 additions & 0 deletions modules/sdk-test-view-pk-cs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin
obj
92 changes: 92 additions & 0 deletions modules/sdk-test-view-pk-cs/Lib.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
namespace SpacetimeDB.Sdk.Test.ViewPk;

using SpacetimeDB;

public static partial class Module
{
[SpacetimeDB.Table(Accessor = "view_pk_player", Public = true)]
public partial struct ViewPkPlayer
{
[SpacetimeDB.PrimaryKey]
public ulong id;
public string name;
}

[SpacetimeDB.Table(Accessor = "view_pk_membership", Public = true)]
public partial struct ViewPkMembership
{
[SpacetimeDB.PrimaryKey]
public ulong id;

[SpacetimeDB.Index.BTree]
public ulong player_id;
}

[SpacetimeDB.Table(Accessor = "view_pk_membership_secondary", Public = true)]
public partial struct ViewPkMembershipSecondary
{
[SpacetimeDB.PrimaryKey]
public ulong id;

[SpacetimeDB.Index.BTree]
public ulong player_id;
}

[SpacetimeDB.Reducer]
public static void insert_view_pk_player(ReducerContext ctx, ulong id, string name)
{
ctx.Db.view_pk_player.Insert(new ViewPkPlayer { id = id, name = name });
}

[SpacetimeDB.Reducer]
public static void update_view_pk_player(ReducerContext ctx, ulong id, string name)
{
ctx.Db.view_pk_player.id.Update(new ViewPkPlayer { id = id, name = name });
}

[SpacetimeDB.Reducer]
public static void insert_view_pk_membership(ReducerContext ctx, ulong id, ulong player_id)
{
ctx.Db.view_pk_membership.Insert(new ViewPkMembership { id = id, player_id = player_id });
}

[SpacetimeDB.Reducer]
public static void insert_view_pk_membership_secondary(
ReducerContext ctx,
ulong id,
ulong player_id
)
{
ctx.Db.view_pk_membership_secondary.Insert(
new ViewPkMembershipSecondary { id = id, player_id = player_id }
);
}

[SpacetimeDB.View(Accessor = "all_view_pk_players", Public = true)]
public static IQuery<ViewPkPlayer> all_view_pk_players(ViewContext ctx)
{
return ctx.From.view_pk_player();
}

[SpacetimeDB.View(Accessor = "sender_view_pk_players_a", Public = true)]
public static IQuery<ViewPkPlayer> sender_view_pk_players_a(ViewContext ctx)
{
return ctx
.From.view_pk_membership()
.RightSemijoin(
ctx.From.view_pk_player(),
(membership, player) => membership.player_id.Eq(player.id)
);
}

[SpacetimeDB.View(Accessor = "sender_view_pk_players_b", Public = true)]
public static IQuery<ViewPkPlayer> sender_view_pk_players_b(ViewContext ctx)
{
return ctx
.From.view_pk_membership_secondary()
.RightSemijoin(
ctx.From.view_pk_player(),
(membership, player) => membership.player_id.Eq(player.id)
);
}
}
8 changes: 8 additions & 0 deletions modules/sdk-test-view-pk-cs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `sdk-test-view-pk-cs` *C#* test

See the [sdk-test-view-pk README](../sdk-test-view-pk/README.md) for more details.

> **WARNING**: This C# source code is manually derived from `../sdk-test-view-pk/src/lib.rs`
> and is supposed to be functionally equivalent.
> Do not add new types or functionality here that are not present in the *Rust* version,
> because they're compared against each other.
13 changes: 13 additions & 0 deletions modules/sdk-test-view-pk-cs/sdk-test-view-pk-cs.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<!--
Use local package sources instead of published ones.
This makes integration test somewhat differ from production configuration, but
at least it simplifies workflow for editing and testing C# code itself.
-->
<ItemGroup>
<ProjectReference Include="../../crates/bindings-csharp/Codegen/Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="../../crates/bindings-csharp/Runtime/Runtime.csproj" />
</ItemGroup>

</Project>
Loading
Loading