-
Notifications
You must be signed in to change notification settings - Fork 874
Expand file tree
/
Copy pathObjectConverter.cs
More file actions
102 lines (85 loc) · 4.69 KB
/
ObjectConverter.cs
File metadata and controls
102 lines (85 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Npgsql.Internal.Postgres;
namespace Npgsql.Internal;
sealed class ObjectConverter(PgSerializerOptions options, PgTypeId pgTypeId) : PgStreamingConverter<object>(customDbNullPredicate: true)
{
protected override bool IsDbNullValue(object? value, ref object? writeState)
{
if (value is null or DBNull)
return true;
var typeInfo = GetTypeInfo(value.GetType());
object? effectiveState = null;
var converter = typeInfo.GetObjectResolution(value).Converter;
if (converter.IsDbNullAsObject(value, ref effectiveState))
return true;
writeState = effectiveState is not null ? new WriteState { TypeInfo = typeInfo, EffectiveState = effectiveState } : typeInfo;
return false;
}
public override object Read(PgReader reader) => throw new NotSupportedException();
public override ValueTask<object> ReadAsync(PgReader reader, CancellationToken cancellationToken = default) => throw new NotSupportedException();
public override Size GetSize(SizeContext context, object value, ref object? writeState)
{
var (typeInfo, effectiveState) = writeState switch
{
PgTypeInfo info => (info, null),
WriteState state => (state.TypeInfo, state.EffectiveState),
_ => throw new InvalidOperationException("Invalid state")
};
// We can call GetDefaultResolution here as validation has already happened in IsDbNullValue.
// And we know it was called due to the writeState being filled.
Debug.Assert(typeInfo.PgTypeId is not null);
var converter = typeInfo is PgResolverTypeInfo resolverTypeInfo
? resolverTypeInfo.GetDefaultResolution(null).Converter
: typeInfo.GetResolution().Converter;
if (typeInfo.GetBufferRequirements(converter, context.Format) is not { } bufferRequirements)
{
ThrowHelper.ThrowNotSupportedException($"Resolved converter '{converter.GetType()}' has to support the {context.Format} format to be compatible.");
return default;
}
// Fixed size converters won't have a GetSize implementation.
if (bufferRequirements.Write.Kind is SizeKind.Exact)
return bufferRequirements.Write;
var result = converter.GetSizeAsObject(context, value, ref effectiveState);
if (effectiveState is not null)
{
if (writeState is WriteState state && !ReferenceEquals(state.EffectiveState, effectiveState))
state.EffectiveState = effectiveState;
else
writeState = new WriteState { TypeInfo = typeInfo, EffectiveState = effectiveState };
}
return result;
}
public override void Write(PgWriter writer, object value)
=> Write(async: false, writer, value, CancellationToken.None).GetAwaiter().GetResult();
public override ValueTask WriteAsync(PgWriter writer, object value, CancellationToken cancellationToken = default)
=> Write(async: true, writer, value, cancellationToken);
async ValueTask Write(bool async, PgWriter writer, object value, CancellationToken cancellationToken)
{
var (typeInfo, effectiveState) = writer.Current.WriteState switch
{
PgTypeInfo info => (info, null),
WriteState state => (state.TypeInfo, state.EffectiveState),
_ => throw new InvalidOperationException("Invalid state")
};
// We can call GetDefaultResolution here as validation has already happened in IsDbNullValue.
// And we know it was called due to the writeState being filled.
Debug.Assert(typeInfo.PgTypeId is not null);
var converter = typeInfo is PgResolverTypeInfo resolverTypeInfo
? resolverTypeInfo.GetDefaultResolution(null).Converter
: typeInfo.GetResolution().Converter;
var writeRequirement = typeInfo.GetBufferRequirements(converter, DataFormat.Binary)!.Value.Write;
using var _ = await writer.BeginNestedWrite(async, writeRequirement, writer.Current.Size.Value, effectiveState, cancellationToken).ConfigureAwait(false);
await converter.WriteAsObject(async, writer, value, cancellationToken).ConfigureAwait(false);
}
PgTypeInfo GetTypeInfo(Type type)
=> options.GetTypeInfoInternal(type, pgTypeId)
?? throw new NotSupportedException($"Writing values of '{type.FullName}' having DataTypeName '{options.DatabaseInfo.GetPostgresType(pgTypeId).DisplayName}' is not supported.");
sealed class WriteState
{
public required PgTypeInfo TypeInfo { get; init; }
public required object EffectiveState { get; set; }
}
}