Make accessor required for table-level index defs in C# (#4541)

# Description of Changes

Make `Accessor` a required argument for table-level index defs in C# to
align with rust and typescript. The same change was done for typescript
in https://github.com/clockworklabs/SpacetimeDB/pull/4525.

# API and ABI breaking changes

Technically breaks the module api, although I believe this is the
behavior that is outlined in the spec, and so the current behavior
should really be considered a bug.

# Expected complexity level and risk

1

# Testing

Added negative compile tests
This commit is contained in:
joshua-spacetime
2026-03-03 15:06:19 -08:00
committed by GitHub
parent 8a82b6d5f6
commit 89fba42165
6 changed files with 234 additions and 5 deletions
@@ -441,10 +441,17 @@ public partial struct MyStruct
[SpacetimeDB.Index.BTree(Accessor = "TestIndexWithoutColumns")]
[SpacetimeDB.Index.BTree(Accessor = "TestIndexWithEmptyColumns", Columns = [])]
[SpacetimeDB.Index.BTree(Accessor = "TestUnknownColumns", Columns = ["UnknownColumn"])]
[SpacetimeDB.Index.BTree(Columns = ["SelfIndexingColumn"])]
[SpacetimeDB.Index.BTree(
Name = "TestCanonicalNameWithoutAccessor",
Columns = ["SecondaryIndexingColumn"]
)]
public partial struct TestIndexIssues
{
[SpacetimeDB.Index.BTree(Accessor = "TestUnexpectedColumns", Columns = ["UnexpectedColumn"])]
public int SelfIndexingColumn;
public int SecondaryIndexingColumn;
}
[SpacetimeDB.Table(
@@ -251,6 +251,10 @@ namespace SpacetimeDB
public readonly struct TestIndexIssuesCols
{
public readonly global::SpacetimeDB.Col<global::TestIndexIssues, int> SelfIndexingColumn;
public readonly global::SpacetimeDB.Col<
global::TestIndexIssues,
int
> SecondaryIndexingColumn;
internal TestIndexIssuesCols(string tableName)
{
@@ -258,12 +262,20 @@ namespace SpacetimeDB
tableName,
"SelfIndexingColumn"
);
SecondaryIndexingColumn = new global::SpacetimeDB.Col<global::TestIndexIssues, int>(
tableName,
"SecondaryIndexingColumn"
);
}
}
public readonly struct TestIndexIssuesIxCols
{
public readonly global::SpacetimeDB.IxCol<global::TestIndexIssues, int> SelfIndexingColumn;
public readonly global::SpacetimeDB.IxCol<
global::TestIndexIssues,
int
> SecondaryIndexingColumn;
internal TestIndexIssuesIxCols(string tableName)
{
@@ -271,6 +283,10 @@ namespace SpacetimeDB
tableName,
"SelfIndexingColumn"
);
SecondaryIndexingColumn = new global::SpacetimeDB.IxCol<global::TestIndexIssues, int>(
tableName,
"SecondaryIndexingColumn"
);
}
}
@@ -1196,6 +1212,16 @@ namespace SpacetimeDB.Internal.TableHandles
AccessorName: "TestUnknownColumns",
Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([])
),
new(
SourceName: "TestIndexIssues_SelfIndexingColumn_idx_btree",
AccessorName: "SelfIndexingColumn",
Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])
),
new(
SourceName: "TestIndexIssues_SecondaryIndexingColumn_idx_btree",
AccessorName: "SecondaryIndexingColumn",
Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])
),
new(
SourceName: "TestIndexIssues_SelfIndexingColumn_idx_btree",
AccessorName: "TestUnexpectedColumns",
@@ -1257,6 +1283,82 @@ namespace SpacetimeDB.Internal.TableHandles
public TestUnknownColumnsIndex TestUnknownColumns => new();
public sealed class SelfIndexingColumnIndex()
: SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(
"TestIndexIssues_SelfIndexingColumn_idx_btree"
)
{
public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>
DoFilter(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
public ulong Delete(int SelfIndexingColumn) =>
DoDelete(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
public IEnumerable<global::TestIndexIssues> Filter(
global::SpacetimeDB.Bound<int> SelfIndexingColumn
) =>
DoFilter(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
public ulong Delete(global::SpacetimeDB.Bound<int> SelfIndexingColumn) =>
DoDelete(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
}
public SelfIndexingColumnIndex SelfIndexingColumn => new();
public sealed class SecondaryIndexingColumnIndex()
: SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(
"TestIndexIssues_SecondaryIndexingColumn_idx_btree"
)
{
public IEnumerable<global::TestIndexIssues> Filter(int SecondaryIndexingColumn) =>
DoFilter(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
public ulong Delete(int SecondaryIndexingColumn) =>
DoDelete(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
public IEnumerable<global::TestIndexIssues> Filter(
global::SpacetimeDB.Bound<int> SecondaryIndexingColumn
) =>
DoFilter(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
public ulong Delete(global::SpacetimeDB.Bound<int> SecondaryIndexingColumn) =>
DoDelete(
new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
}
public SecondaryIndexingColumnIndex SecondaryIndexingColumn => new();
public sealed class TestUnexpectedColumnsIndex()
: SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(
"TestIndexIssues_SelfIndexingColumn_idx_btree"
@@ -2442,6 +2544,56 @@ namespace SpacetimeDB.Internal.ViewHandles
public TestUnknownColumnsIndex TestUnknownColumns => new();
public sealed class SelfIndexingColumnIndex
: global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>
{
internal SelfIndexingColumnIndex()
: base("TestIndexIssues_SelfIndexingColumn_idx_btree") { }
public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>
DoFilter(
new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
public IEnumerable<global::TestIndexIssues> Filter(
global::SpacetimeDB.Bound<int> SelfIndexingColumn
) =>
DoFilter(
new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SelfIndexingColumn
)
);
}
public SelfIndexingColumnIndex SelfIndexingColumn => new();
public sealed class SecondaryIndexingColumnIndex
: global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>
{
internal SecondaryIndexingColumnIndex()
: base("TestIndexIssues_SecondaryIndexingColumn_idx_btree") { }
public IEnumerable<global::TestIndexIssues> Filter(int SecondaryIndexingColumn) =>
DoFilter(
new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
public IEnumerable<global::TestIndexIssues> Filter(
global::SpacetimeDB.Bound<int> SecondaryIndexingColumn
) =>
DoFilter(
new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(
SecondaryIndexingColumn
)
);
}
public SecondaryIndexingColumnIndex SecondaryIndexingColumn => new();
public sealed class TestUnexpectedColumnsIndex
: global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>
{
@@ -2827,6 +2979,11 @@ static class ModuleRegistration
(identity, connectionId, random, time) =>
new SpacetimeDB.ProcedureContext(identity, connectionId, random, time)
);
SpacetimeDB.Internal.Module.RegisterExplicitIndexName(
"TestIndexIssues_SecondaryIndexingColumn_idx_btree",
"TestCanonicalNameWithoutAccessor"
);
var __memoryStream = new MemoryStream();
var __writer = new BinaryWriter(__memoryStream);
@@ -9,11 +9,13 @@ partial struct TestIndexIssues
public void ReadFields(System.IO.BinaryReader reader)
{
SelfIndexingColumn = BSATN.SelfIndexingColumnRW.Read(reader);
SecondaryIndexingColumn = BSATN.SecondaryIndexingColumnRW.Read(reader);
}
public void WriteFields(System.IO.BinaryWriter writer)
{
BSATN.SelfIndexingColumnRW.Write(writer, SelfIndexingColumn);
BSATN.SecondaryIndexingColumnRW.Write(writer, SecondaryIndexingColumn);
}
object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()
@@ -22,11 +24,12 @@ partial struct TestIndexIssues
}
public override string ToString() =>
$"TestIndexIssues {{ SelfIndexingColumn = {SpacetimeDB.BSATN.StringUtil.GenericToString(SelfIndexingColumn)} }}";
$"TestIndexIssues {{ SelfIndexingColumn = {SpacetimeDB.BSATN.StringUtil.GenericToString(SelfIndexingColumn)}, SecondaryIndexingColumn = {SpacetimeDB.BSATN.StringUtil.GenericToString(SecondaryIndexingColumn)} }}";
public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestIndexIssues>
{
internal static readonly SpacetimeDB.BSATN.I32 SelfIndexingColumnRW = new();
internal static readonly SpacetimeDB.BSATN.I32 SecondaryIndexingColumnRW = new();
public TestIndexIssues Read(System.IO.BinaryReader reader)
{
@@ -47,7 +50,11 @@ partial struct TestIndexIssues
_ => new SpacetimeDB.BSATN.AlgebraicType.Product(
new SpacetimeDB.BSATN.AggregateElement[]
{
new("SelfIndexingColumn", SelfIndexingColumnRW.GetAlgebraicType(registrar))
new("SelfIndexingColumn", SelfIndexingColumnRW.GetAlgebraicType(registrar)),
new(
"SecondaryIndexingColumn",
SecondaryIndexingColumnRW.GetAlgebraicType(registrar)
)
}
)
);
@@ -60,14 +67,18 @@ partial struct TestIndexIssues
public override int GetHashCode()
{
var ___hashSelfIndexingColumn = SelfIndexingColumn.GetHashCode();
return ___hashSelfIndexingColumn;
var ___hashSecondaryIndexingColumn = SecondaryIndexingColumn.GetHashCode();
return ___hashSelfIndexingColumn ^ ___hashSecondaryIndexingColumn;
}
#nullable enable
public bool Equals(TestIndexIssues that)
{
var ___eqSelfIndexingColumn = this.SelfIndexingColumn.Equals(that.SelfIndexingColumn);
return ___eqSelfIndexingColumn;
var ___eqSecondaryIndexingColumn = this.SecondaryIndexingColumn.Equals(
that.SecondaryIndexingColumn
);
return ___eqSelfIndexingColumn && ___eqSecondaryIndexingColumn;
}
public override bool Equals(object? that)
@@ -140,7 +140,7 @@ public partial record TestTableTaggedEnum : SpacetimeDB.TaggedEnum<(int X, int Y
[SpacetimeDB.Index.BTree(Accessor = "TestIndexWithEmptyColumns", Columns = [])]
[SpacetimeDB.Index.BTree(Accessor = "TestUnknownColumns", Columns = ["UnknownColumn"])]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
public partial struct TestIndexIssues
[SpacetimeDB.Index.BTree(Columns = ["SelfIndexingColumn"])]
*/
Message: Could not find the specified column UnknownColumn in TestIndexIssues.,
Severity: Error,
@@ -154,6 +154,46 @@ public partial struct TestIndexIssues
}
},
{/*
[SpacetimeDB.Index.BTree(Accessor = "TestUnknownColumns", Columns = ["UnknownColumn"])]
[SpacetimeDB.Index.BTree(Columns = ["SelfIndexingColumn"])]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[SpacetimeDB.Index.BTree(
*/
Message: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,
Severity: Error,
Descriptor: {
Id: STDB0029,
Title: Table-level index attributes must specify Accessor,
MessageFormat: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,
Category: SpacetimeDB,
DefaultSeverity: Error,
IsEnabledByDefault: true
}
},
{/*
[SpacetimeDB.Index.BTree(Columns = ["SelfIndexingColumn"])]
[SpacetimeDB.Index.BTree(
^^^^^^^^^^^^^^^^^^^^^^^^
Name = "TestCanonicalNameWithoutAccessor",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Columns = ["SecondaryIndexingColumn"]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)]
^
public partial struct TestIndexIssues
*/
Message: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,
Severity: Error,
Descriptor: {
Id: STDB0029,
Title: Table-level index attributes must specify Accessor,
MessageFormat: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,
Category: SpacetimeDB,
DefaultSeverity: Error,
IsEnabledByDefault: true
}
},
{/*
[SpacetimeDB.Table(
^^^^^^^^^^^^^^^^^^
+9
View File
@@ -257,4 +257,13 @@ internal static class ErrorDescriptor
$"[SpacetimeDB.Settings] is declared multiple times: {string.Join(", ", fullNames)}",
_ => Location.None
);
public static readonly ErrorDescriptor<AttributeData> TableLevelIndexMissingAccessor =
new(
group,
"Table-level index attributes must specify Accessor",
_ =>
$"Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.",
attr => attr
);
}
+5
View File
@@ -407,6 +407,11 @@ record TableIndex
.ToImmutableArray()
)
{
if (string.IsNullOrWhiteSpace(attr.Accessor))
{
diag.Report(ErrorDescriptor.TableLevelIndexMissingAccessor, data);
}
if (attr.Columns.Length == 0)
{
diag.Report(ErrorDescriptor.EmptyIndexColumns, data);