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>
53 changes: 4 additions & 49 deletions sdks/csharp/examples~/regression-tests/client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using RegressionTests.Shared;
using SpacetimeDB;
using SpacetimeDB.Types;

Expand All @@ -19,37 +20,6 @@
const string EXPECTED_TEST_EVENT_NAME = "hello";
const ulong EXPECTED_TEST_EVENT_VALUE = 42;

DbConnection ConnectToDB()
{
DbConnection? conn = null;
conn = DbConnection
.Builder()
.WithUri(HOST)
.WithDatabaseName(DBNAME)
.OnConnect(OnConnected)
.OnConnectError(
(err) =>
{
throw err;
}
)
.OnDisconnect(
(conn, err) =>
{
if (err != null)
{
throw err;
}
else
{
throw new Exception("Unexpected disconnect");
}
}
)
.Build();
return conn;
}

uint waiting = 0;
var applied = false;
SubscriptionHandle? handle = null;
Expand Down Expand Up @@ -1129,24 +1099,9 @@ ProcedureCallbackResult<Result<ReturnStruct, string>> result
);
}

System.AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Log.Exception($"Unhandled exception: {sender} {args}");
Environment.Exit(1);
};
var db = ConnectToDB();
Log.Info("Starting timer");
RegressionTestHarness.RegisterUnhandledExceptionExitHandler();
var db = RegressionTestHarness.ConnectToDatabase(HOST, DBNAME, OnConnected);
const int TIMEOUT = 20; // seconds;
var start = DateTime.Now;
while (!applied || waiting > 0)
{
db.FrameTick();
Thread.Sleep(100);
if ((DateTime.Now - start).Seconds > TIMEOUT)
{
Log.Error($"Timeout, all events should have elapsed in {TIMEOUT} seconds!");
Environment.Exit(1);
}
}
RegressionTestHarness.FrameTickUntilComplete(db, () => applied && waiting == 0, TIMEOUT);
Log.Info("Success");
Environment.Exit(0);
4 changes: 4 additions & 0 deletions sdks/csharp/examples~/regression-tests/client/client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
<ProjectReference Include="../../../SpacetimeDB.ClientSDK.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="../shared/RegressionTestHarness.cs" Link="shared/RegressionTestHarness.cs" />
</ItemGroup>

</Project>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading