Skip to content

Commit 5a99700

Browse files
committed
Relations now support computed fields in secondary indexes.
1 parent 7b011b9 commit 5a99700

8 files changed

+258
-44
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//HintName: TestNamespace.Person.g.cs
2+
// <auto-generated/>
3+
#pragma warning disable 612,618
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
7+
namespace TestNamespace;
8+
9+
[CompilerGenerated]
10+
static file class PersonRegistration
11+
{
12+
[ModuleInitializer]
13+
internal static unsafe void Register4BTDB()
14+
{
15+
global::BTDB.IOC.IContainer.RegisterFactory(typeof(global::TestNamespace.Person), (container, ctx) =>
16+
{
17+
return (container2, ctx2) =>
18+
{
19+
var res = new global::TestNamespace.Person();
20+
return res;
21+
};
22+
});
23+
}
24+
}

BTDB.SourceGenerator.Test/MetadataTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ public class Person
2424
""");
2525
}
2626

27+
[Fact]
28+
public Task StaticMembersMustBeSkippedInMetadata()
29+
{
30+
// language=cs
31+
return VerifySourceGenerator("""
32+
namespace TestNamespace;
33+
34+
[BTDB.Generate]
35+
public class Person
36+
{
37+
public static string Name { get; set; } = "";
38+
public static int Age;
39+
}
40+
""");
41+
}
42+
2743
[Fact]
2844
public Task VerifyCollectionMetadata()
2945
{

BTDB/ODBLayer/RelationBuilder.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ RelationVersionInfo CreateVersionInfoByReflection()
130130

131131
if (actualPKAttribute != null)
132132
{
133+
if (!pi.CanWrite)
134+
{
135+
RelationInfoResolver.ActualOptions.ThrowBTDBException(
136+
$"Key field {pi.Name} must have setter, cannot be computed");
137+
}
138+
133139
var fieldInfo = TableFieldInfo.Build(_name, pi, RelationInfoResolver.FieldHandlerFactory,
134140
FieldHandlerOptions.Orderable, actualPKAttribute.InKeyValue);
135141
if (fieldInfo.Handler!.NeedsCtx())
@@ -141,6 +147,12 @@ RelationVersionInfo CreateVersionInfoByReflection()
141147
var sks = pi.GetCustomAttributes(typeof(SecondaryKeyAttribute), true);
142148
var id = (int)(-actualPKAttribute?.Order ?? secondaryKeyFields.Count);
143149
List<SecondaryKeyAttribute> currentList = null;
150+
if (sks.Length == 0 && !pi.CanWrite)
151+
{
152+
RelationInfoResolver.ActualOptions.ThrowBTDBException(
153+
$"Property {pi.Name} because it is not part of some secondary key must have setter, cannot be computed");
154+
}
155+
144156
for (var i = 0; i < sks.Length; i++)
145157
{
146158
if (actualPKAttribute?.InKeyValue ?? false)
@@ -170,7 +182,7 @@ RelationVersionInfo CreateVersionInfoByReflection()
170182
FieldHandlerOptions.None, false));
171183
}
172184

173-
return new RelationVersionInfo(primaryKeys, secondaryKeys, secondaryKeyFields.ToArray(), fields.ToArray());
185+
return new(primaryKeys, secondaryKeys, secondaryKeyFields.ToArray(), fields.ToArray());
174186
}
175187

176188
int RegisterLoadType(Type itemType)

BTDB/ODBLayer/RelationDBManipulator.cs

+89-26
Original file line numberDiff line numberDiff line change
@@ -1423,8 +1423,7 @@ public unsafe object CreateInstanceFromSecondaryKey(RelationInfo.ItemLoaderInfo
14231423
uint remappedSecondaryKeyIndex,
14241424
in ReadOnlySpan<byte> secondaryKey)
14251425
{
1426-
Span<byte> pkBuffer = stackalloc byte[512];
1427-
var pkWriter = MemWriter.CreateFromStackAllocatedSpan(pkBuffer);
1426+
var pkWriter = MemWriter.CreateFromStackAllocatedSpan(stackalloc byte[4096]);
14281427
WriteRelationPKPrefix(ref pkWriter);
14291428
fixed (void* _ = secondaryKey)
14301429
{
@@ -1504,53 +1503,117 @@ void AddIntoSecondaryIndexes(T obj, ref MemWriter writer)
15041503
}
15051504
}
15061505

1506+
[SkipLocalsInit]
15071507
bool UpdateSecondaryIndexes(in ReadOnlySpan<byte> oldKey, in ReadOnlySpan<byte> oldValue,
15081508
in ReadOnlySpan<byte> newValue)
15091509
{
15101510
var changed = false;
1511-
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1511+
if (_relationInfo.ClientRelationVersionInfo.HasComputedField)
15121512
{
1513-
var newKeyBytes = WriteSecondaryKeyKey(key, oldKey, newValue);
1514-
var oldKeyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1515-
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1516-
continue;
1517-
//remove old index
1518-
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1519-
//insert new value
1520-
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1521-
changed = true;
1513+
var writer = MemWriter.CreateFromStackAllocatedSpan(stackalloc byte[4096]);
1514+
var writerOld = MemWriter.CreateFromStackAllocatedSpan(stackalloc byte[4096]);
1515+
var objOld = _relationInfo.ItemLoaderInfos[0].CreateInstance(_transaction, oldKey, oldValue);
1516+
var objNew = _relationInfo.ItemLoaderInfos[0].CreateInstance(_transaction, oldKey, newValue);
1517+
1518+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1519+
{
1520+
writer.Reset();
1521+
writerOld.Reset();
1522+
var newKeyBytes = WriteSecondaryKeyKey(key, Unsafe.As<T>(objNew), ref writer);
1523+
var oldKeyBytes = WriteSecondaryKeyKey(key, Unsafe.As<T>(objOld), ref writerOld);
1524+
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1525+
continue;
1526+
//remove old index
1527+
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1528+
//insert new value
1529+
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1530+
changed = true;
1531+
}
1532+
}
1533+
else
1534+
{
1535+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1536+
{
1537+
var newKeyBytes = WriteSecondaryKeyKey(key, oldKey, newValue);
1538+
var oldKeyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1539+
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1540+
continue;
1541+
//remove old index
1542+
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1543+
//insert new value
1544+
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1545+
changed = true;
1546+
}
15221547
}
15231548

15241549
return changed;
15251550
}
15261551

1552+
[SkipLocalsInit]
15271553
bool UpdateSecondaryIndexes(T newValue, in ReadOnlySpan<byte> oldKey, in ReadOnlySpan<byte> oldValue,
15281554
ref MemWriter writer)
15291555
{
15301556
var changed = false;
1531-
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1557+
if (_relationInfo.ClientRelationVersionInfo.HasComputedField)
15321558
{
1533-
writer.Reset();
1534-
var newKeyBytes = WriteSecondaryKeyKey(key, newValue, ref writer);
1535-
var oldKeyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1536-
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1537-
continue;
1538-
//remove old index
1539-
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1540-
//insert new value
1541-
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1542-
changed = true;
1559+
var writerOld = MemWriter.CreateFromStackAllocatedSpan(stackalloc byte[4096]);
1560+
var obj = _relationInfo.ItemLoaderInfos[0].CreateInstance(_transaction, oldKey, oldValue);
1561+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1562+
{
1563+
writer.Reset();
1564+
writerOld.Reset();
1565+
var newKeyBytes = WriteSecondaryKeyKey(key, newValue, ref writer);
1566+
var oldKeyBytes = WriteSecondaryKeyKey(key, Unsafe.As<T>(obj), ref writerOld);
1567+
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1568+
continue;
1569+
//remove old index
1570+
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1571+
//insert new value
1572+
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1573+
changed = true;
1574+
}
1575+
}
1576+
else
1577+
{
1578+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1579+
{
1580+
writer.Reset();
1581+
var newKeyBytes = WriteSecondaryKeyKey(key, newValue, ref writer);
1582+
var oldKeyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1583+
if (oldKeyBytes.SequenceEqual(newKeyBytes))
1584+
continue;
1585+
//remove old index
1586+
EraseOldSecondaryKey(oldKey, oldKeyBytes, key);
1587+
//insert new value
1588+
_kvtr.CreateOrUpdateKeyValue(newKeyBytes, new());
1589+
changed = true;
1590+
}
15431591
}
15441592

15451593
return changed;
15461594
}
15471595

1596+
[SkipLocalsInit]
15481597
void RemoveSecondaryIndexes(in ReadOnlySpan<byte> oldKey, in ReadOnlySpan<byte> oldValue)
15491598
{
1550-
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1599+
if (_relationInfo.ClientRelationVersionInfo.HasComputedField)
15511600
{
1552-
var keyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1553-
EraseOldSecondaryKey(oldKey, keyBytes, key);
1601+
var writer = MemWriter.CreateFromStackAllocatedSpan(stackalloc byte[4096]);
1602+
var obj = _relationInfo.ItemLoaderInfos[0].CreateInstance(_transaction, oldKey, oldValue);
1603+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1604+
{
1605+
writer.Reset();
1606+
var keyBytes = WriteSecondaryKeyKey(key, Unsafe.As<T>(obj), ref writer);
1607+
EraseOldSecondaryKey(oldKey, keyBytes, key);
1608+
}
1609+
}
1610+
else
1611+
{
1612+
foreach (var (key, _) in _relationInfo.ClientRelationVersionInfo.SecondaryKeys)
1613+
{
1614+
var keyBytes = WriteSecondaryKeyKey(key, oldKey, oldValue);
1615+
EraseOldSecondaryKey(oldKey, keyBytes, key);
1616+
}
15541617
}
15551618
}
15561619

BTDB/ODBLayer/RelationInfo.cs

+37-4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,26 @@ internal unsafe object CreateInstance(IInternalObjectDBTransaction tr, in ReadOn
101101
}
102102
}
103103

104+
internal unsafe object CreateInstance(IInternalObjectDBTransaction tr, in ReadOnlySpan<byte> keyBytes,
105+
in ReadOnlySpan<byte> valueBytes)
106+
{
107+
fixed (void* pKeyBytes = keyBytes)
108+
{
109+
var reader = new MemReader(pKeyBytes, keyBytes.Length);
110+
reader.Skip1Byte(); // 3
111+
reader.SkipVUInt64(); // RelationId
112+
var obj = _primaryKeysLoader(tr, ref reader);
113+
if (_primaryKeyIsEnough) return obj;
114+
fixed (void* _ = valueBytes)
115+
{
116+
reader = MemReader.CreateFromPinnedSpan(valueBytes);
117+
var version = reader.ReadVUInt32();
118+
GetValueLoader(version)(tr, ref reader, obj);
119+
return obj;
120+
}
121+
}
122+
}
123+
104124
internal readonly RelationLoaderFunc _primaryKeysLoader;
105125
internal readonly bool _primaryKeyIsEnough;
106126
readonly RelationLoader?[] _valueLoaders;
@@ -113,8 +133,15 @@ internal RelationLoader GetValueLoader(uint version)
113133
{
114134
res = _valueLoaders[version];
115135
if (res != null) return res;
136+
StructList<TableFieldInfo> filteredFields = new();
137+
foreach (var tableFieldInfo in _owner._relationVersions[version]!.Fields.Span)
138+
{
139+
if (tableFieldInfo.Computed) continue;
140+
filteredFields.Add(tableFieldInfo);
141+
}
142+
116143
res = CreateLoader(_itemType,
117-
_owner._relationVersions[version]!.Fields.Span,
144+
filteredFields,
118145
$"RelationValueLoader_{_owner.Name}_{version}_{_itemType.ToSimpleName()}");
119146
} while (Interlocked.CompareExchange(ref _valueLoaders[version], res, null) != null);
120147

@@ -741,11 +768,10 @@ void CalculateSecondaryKey(IInternalObjectDBTransaction tr,
741768
keyWriter.WriteBlock(PrefixSecondary);
742769
keyWriter.WriteUInt8((byte)indexes[i].Key);
743770
keySavers[i](tr, ref keyWriter, obj!);
744-
var keyBytes = keyWriter.GetSpan();
771+
var keyBytes = keyWriter.GetSpanAndReset();
745772

746773
if (!tr.KeyValueDBTransaction.CreateOrUpdateKeyValue(keyBytes, new ReadOnlySpan<byte>()))
747774
throw new BTDBException("Internal error, secondary key bytes must be always unique.");
748-
keyWriter.Reset();
749775
}
750776
}
751777
}
@@ -809,7 +835,14 @@ void ResolveVersionInfos()
809835

810836
void CreateCreatorLoadersAndSavers()
811837
{
812-
_valueSaver = CreateSaver(ClientRelationVersionInfo.Fields.Span, $"RelationValueSaver_{Name}", false);
838+
StructList<TableFieldInfo> filteredFields = new();
839+
foreach (var tableFieldInfo in ClientRelationVersionInfo.Fields.Span)
840+
{
841+
if (tableFieldInfo.Computed) continue;
842+
filteredFields.Add(tableFieldInfo);
843+
}
844+
845+
_valueSaver = CreateSaver(filteredFields, $"RelationValueSaver_{Name}", false);
813846
_primaryKeysSaver = CreateSaver(ClientRelationVersionInfo.PrimaryKeyFields.Span,
814847
$"RelationKeySaver_{Name}", true);
815848
_beforeRemove = CreateBeforeRemove($"RelationBeforeRemove_{Name}");

BTDB/ODBLayer/RelationVersionInfo.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class RelationVersionInfo
5656
ReadOnlyMemory<TableFieldInfo> _secondaryKeyFields;
5757
IDictionary<string, uint> _secondaryKeysNames;
5858
IDictionary<uint, SecondaryKeyInfo> _secondaryKeys;
59+
public bool HasComputedField;
5960

6061
public RelationVersionInfo(Dictionary<uint, TableFieldInfo> primaryKeyFields, //order -> info
6162
List<Tuple<int, IList<SecondaryKeyAttribute>>>
@@ -82,6 +83,13 @@ public RelationVersionInfo(Dictionary<uint, TableFieldInfo> primaryKeyFields, //
8283
_secondaryKeyFields = secondaryKeyFields;
8384
CreateSecondaryKeyInfo(secondaryKeys, primaryKeyFields);
8485
Fields = fields;
86+
HasComputedField = false;
87+
foreach (var tableFieldInfo in fields.Span)
88+
{
89+
if (!tableFieldInfo.Computed) continue;
90+
HasComputedField = true;
91+
break;
92+
}
8593
}
8694

8795
void CreateSecondaryKeyInfo(List<Tuple<int, IList<SecondaryKeyAttribute>>> attributes,
@@ -168,6 +176,13 @@ internal RelationVersionInfo(ReadOnlyMemory<TableFieldInfo> primaryKeyFields,
168176

169177
_secondaryKeyFields = secondaryKeyFields;
170178
Fields = fields;
179+
HasComputedField = false;
180+
foreach (var tableFieldInfo in fields.Span)
181+
{
182+
if (!tableFieldInfo.Computed) continue;
183+
HasComputedField = true;
184+
break;
185+
}
171186
}
172187

173188
internal TableFieldInfo? this[string name]
@@ -332,7 +347,7 @@ public static RelationVersionInfo LoadUnresolved(ref MemReader reader, string re
332347
fieldInfos[i] = UnresolvedTableFieldInfo.Load(ref reader, relationName, FieldHandlerOptions.None);
333348
}
334349

335-
return new RelationVersionInfo(primaryKeyFields, secondaryKeys, secondaryKeyFields, fieldInfos);
350+
return new(primaryKeyFields, secondaryKeys, secondaryKeyFields, fieldInfos);
336351
}
337352

338353
public void ResolveFieldHandlers(IFieldHandlerFactory fieldHandlerFactory)

0 commit comments

Comments
 (0)