Skip to content

Commit 0f7267a

Browse files
authored
Move asObject to be a caller concern (#5290)
1 parent ffbf6fe commit 0f7267a

6 files changed

Lines changed: 113 additions & 107 deletions

File tree

src/Npgsql/BackendMessages/RowDescriptionMessage.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ internal PgConverterInfo ObjectOrDefaultInfo
337337
return _objectOrDefaultInfo;
338338

339339
ref var info = ref _objectOrDefaultInfo;
340-
GetInfo(null, ref _objectOrDefaultInfo);
340+
GetInfo(null, ref _objectOrDefaultInfo, out _);
341341
return info;
342342
}
343343
}
@@ -350,34 +350,39 @@ internal FieldDescription Clone()
350350
return field;
351351
}
352352

353-
internal void GetInfo(Type? type, ref PgConverterInfo lastConverterInfo)
353+
internal void GetInfo(Type? type, ref PgConverterInfo lastConverterInfo, out bool asObject)
354354
{
355355
Debug.Assert(lastConverterInfo.IsDefault || (
356356
ReferenceEquals(_serializerOptions, lastConverterInfo.TypeInfo.Options) &&
357357
lastConverterInfo.TypeInfo.PgTypeId == _serializerOptions.ToCanonicalTypeId(PostgresType)), "Cache is bleeding over");
358358

359359
if (!lastConverterInfo.IsDefault && lastConverterInfo.TypeToConvert == type)
360+
{
361+
asObject = lastConverterInfo.IsBoxingConverter;
360362
return;
363+
}
361364

362365
// Have to check for null as it's a sentinel value used by ObjectOrDefaultTypeInfo init itself.
363366
if (type is not null && ObjectOrDefaultInfo is var odfInfo)
364367
{
365368
if (typeof(object) == type)
366369
{
367-
lastConverterInfo = odfInfo with { AsObject = true };
370+
lastConverterInfo = odfInfo;
371+
asObject = true;
368372
return;
369373
}
370374
if (odfInfo.TypeToConvert == type)
371375
{
372376
lastConverterInfo = odfInfo;
377+
asObject = lastConverterInfo.IsBoxingConverter;
373378
return;
374379
}
375380
}
376381

377-
GetInfoSlow(out lastConverterInfo);
382+
GetInfoSlow(out lastConverterInfo, out asObject);
378383

379384
[MethodImpl(MethodImplOptions.NoInlining)]
380-
void GetInfoSlow(out PgConverterInfo lastConverterInfo)
385+
void GetInfoSlow(out PgConverterInfo lastConverterInfo, out bool asObject)
381386
{
382387
PgConverterInfo converterInfo;
383388
var typeInfo = AdoSerializerHelpers.GetTypeInfoForReading(type ?? typeof(object), PostgresType, _serializerOptions);
@@ -398,6 +403,7 @@ void GetInfoSlow(out PgConverterInfo lastConverterInfo)
398403
}
399404

400405
lastConverterInfo = converterInfo;
406+
asObject = converterInfo.IsBoxingConverter;
401407
}
402408
}
403409

src/Npgsql/Internal/PgTypeInfo.cs

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -77,45 +77,6 @@ internal void DisposeWriteState(object writeState)
7777
disposable.Dispose();
7878
}
7979

80-
internal bool TryBind(Field field, DataFormat format, out PgConverterInfo info)
81-
{
82-
switch (this)
83-
{
84-
case { IsResolverInfo: false }:
85-
// Type lies when IsBoxing is true.
86-
var typeToConvert = IsBoxing ? typeof(object) : Type;
87-
if (!CachedCanConvert(format, out var bufferRequirements))
88-
{
89-
info = default;
90-
return false;
91-
}
92-
info = CreateConverterInfo(bufferRequirements, isRead: true, Converter, typeToConvert);
93-
return true;
94-
case PgResolverTypeInfo resolverInfo:
95-
var resolution = resolverInfo.GetResolution(field);
96-
if (!HasCachedInfo(resolution.Converter)
97-
? !CachedCanConvert(format, out bufferRequirements)
98-
: !resolution.Converter.CanConvert(format, out bufferRequirements))
99-
{
100-
info = default;
101-
return false;
102-
}
103-
info = CreateConverterInfo(bufferRequirements, isRead: true, resolution.Converter, resolution.Converter.TypeToConvert);
104-
return true;
105-
default:
106-
throw new NotSupportedException("Should not happen, please file a bug.");
107-
}
108-
}
109-
110-
// Bind for reading.
111-
internal PgConverterInfo Bind(Field field, DataFormat format)
112-
{
113-
if (!TryBind(field, format, out var info))
114-
ThrowHelper.ThrowInvalidOperationException($"Resolved converter does not support {format} format.");
115-
116-
return info;
117-
}
118-
11980
public PgConverterResolution GetResolution<T>(T? value)
12081
{
12182
// Other cases, to keep binary bloat minimal.
@@ -163,15 +124,6 @@ static PgConverterResolution ThrowNotSupported()
163124
=> throw new NotSupportedException("Should not happen, please file a bug.");
164125
}
165126

166-
PgConverterInfo CreateConverterInfo(BufferRequirements bufferRequirements, bool isRead, PgConverter converter, Type typeToConvert)
167-
=> new()
168-
{
169-
TypeInfo = this,
170-
Converter = converter,
171-
AsObject = Type != typeToConvert,
172-
BufferRequirement = isRead ? bufferRequirements.Read : bufferRequirements.Write
173-
};
174-
175127
bool CachedCanConvert(DataFormat format, out BufferRequirements bufferRequirements)
176128
{
177129
if (format is DataFormat.Binary)
@@ -193,6 +145,44 @@ bool CachedCanConvert(DataFormat format, out BufferRequirements bufferRequiremen
193145
return success ? bufferRequirements : null;
194146
}
195147

148+
// TryBind for reading.
149+
internal bool TryBind(Field field, DataFormat format, out PgConverterInfo info)
150+
{
151+
switch (this)
152+
{
153+
case { IsResolverInfo: false }:
154+
if (!CachedCanConvert(format, out var bufferRequirements))
155+
{
156+
info = default;
157+
return false;
158+
}
159+
info = new(this, Converter, bufferRequirements.Read);
160+
return true;
161+
case PgResolverTypeInfo resolverInfo:
162+
var resolution = resolverInfo.GetResolution(field);
163+
if (!HasCachedInfo(resolution.Converter)
164+
? !CachedCanConvert(format, out bufferRequirements)
165+
: !resolution.Converter.CanConvert(format, out bufferRequirements))
166+
{
167+
info = default;
168+
return false;
169+
}
170+
info = new(this, resolution.Converter, bufferRequirements.Read);
171+
return true;
172+
default:
173+
throw new NotSupportedException("Should not happen, please file a bug.");
174+
}
175+
}
176+
177+
// Bind for reading.
178+
internal PgConverterInfo Bind(Field field, DataFormat format)
179+
{
180+
if (!TryBind(field, format, out var info))
181+
ThrowHelper.ThrowInvalidOperationException($"Resolved converter does not support {format} format.");
182+
183+
return info;
184+
}
185+
196186
// Bind for writing.
197187
/// When result is null, the value was interpreted to be a SQL NULL.
198188
internal PgConverterInfo? Bind<T>(PgConverter<T> converter, T? value, out Size size, out object? writeState, out DataFormat format, DataFormat? formatPreference = null)
@@ -215,13 +205,7 @@ bool CachedCanConvert(DataFormat format, out BufferRequirements bufferRequiremen
215205
if (size is { Kind: SizeKind.Unknown})
216206
ThrowHelper.ThrowNotSupportedException($"Returning {nameof(Size.Unknown)} from {nameof(PgConverter<object>.GetSize)} is not supported yet.");
217207

218-
return new()
219-
{
220-
TypeInfo = this,
221-
Converter = converter,
222-
AsObject = IsBoxing,
223-
BufferRequirement = bufferRequirements.Write,
224-
};
208+
return new(this, converter, bufferRequirements.Write);
225209
}
226210

227211
// Bind for writing.
@@ -249,13 +233,7 @@ bool CachedCanConvert(DataFormat format, out BufferRequirements bufferRequiremen
249233
if (size is { Kind: SizeKind.Unknown})
250234
ThrowHelper.ThrowNotSupportedException($"Returning {nameof(Size.Unknown)} from {nameof(PgConverter.GetSizeAsObject)} is not supported yet.");
251235

252-
return new()
253-
{
254-
TypeInfo = this,
255-
Converter = converter,
256-
AsObject = Type != typeof(object),
257-
BufferRequirement = bufferRequirements.Write,
258-
};
236+
return new(this, converter, bufferRequirements.Write);
259237
}
260238

261239
// If we don't have a converter stored we must ask the retrieved one.
@@ -337,6 +315,13 @@ public PgConverterResolution(PgConverter converter, PgTypeId pgTypeId)
337315

338316
readonly struct PgConverterInfo
339317
{
318+
public PgConverterInfo(PgTypeInfo pgTypeInfo, PgConverter converter, Size bufferRequirement)
319+
{
320+
TypeInfo = pgTypeInfo;
321+
Converter = converter;
322+
BufferRequirement = bufferRequirement;
323+
}
324+
340325
public bool IsDefault => TypeInfo is null;
341326

342327
public Type TypeToConvert
@@ -352,11 +337,12 @@ public Type TypeToConvert
352337
}
353338
}
354339

355-
public required PgTypeInfo TypeInfo { get; init; }
356-
public required PgConverter Converter { get; init; }
357-
public required Size BufferRequirement { get; init; }
358-
// Whether Converter.TypeToConvert matches the PgTypeInfo.Type, if it doesn't object apis and a downcast should be used.
359-
public required bool AsObject { get; init; }
340+
public PgTypeInfo TypeInfo { get; }
341+
public PgConverter Converter { get; }
342+
public Size BufferRequirement { get; }
343+
344+
/// Whether Converter.TypeToConvert matches PgTypeInfo.Type, if it doesn't object apis should be used.
345+
public bool IsBoxingConverter => TypeInfo.IsBoxing;
360346

361347
public PgConverter<T> GetConverter<T>() => (PgConverter<T>)Converter;
362348
}

src/Npgsql/NpgsqlBinaryExporter.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,11 @@ async ValueTask<T> Read<T>(bool async, NpgsqlDbType? type, CancellationToken can
290290

291291
// We must commit the current column before reading the next one unless it was an IsNull call.
292292
PgConverterInfo info;
293+
bool asObject;
293294
if (!PgReader.Resumable || PgReader.CurrentRemaining != PgReader.FieldSize)
294295
{
295296
await Commit(async, resumableOp: false).ConfigureAwait(false);
296-
info = GetInfo();
297+
info = GetInfo(out asObject);
297298

298299
// We need to get info after potential I/O as we don't know beforehand at what column we're at.
299300
var columnLen = await ReadColumnLenIfNeeded(async, resumableOp: false).ConfigureAwait(false);
@@ -305,32 +306,34 @@ async ValueTask<T> Read<T>(bool async, NpgsqlDbType? type, CancellationToken can
305306

306307
}
307308
else
308-
info = GetInfo();
309+
info = GetInfo(out asObject);
309310

310311
T result;
311312
if (async)
312313
{
313314
await PgReader.StartReadAsync(info.BufferRequirement, cancellationToken).ConfigureAwait(false);
314-
result = info.AsObject
315+
result = asObject
315316
? (T)await info.Converter.ReadAsObjectAsync(PgReader, cancellationToken).ConfigureAwait(false)
316317
: await info.GetConverter<T>().ReadAsync(PgReader, cancellationToken).ConfigureAwait(false);
317318
await PgReader.EndReadAsync().ConfigureAwait(false);
318319
}
319320
else
320321
{
321322
PgReader.StartRead(info.BufferRequirement);
322-
result = info.AsObject
323+
result = asObject
323324
? (T)info.Converter.ReadAsObject(PgReader)
324325
: info.GetConverter<T>().Read(PgReader);
325326
PgReader.EndRead();
326327
}
327328

328329
return result;
329330

330-
PgConverterInfo GetInfo()
331+
PgConverterInfo GetInfo(out bool asObject)
331332
{
332333
ref var cachedInfo = ref _columnInfoCache[_column];
333-
return cachedInfo.IsDefault ? cachedInfo = CreateConverterInfo(typeof(T), type) : cachedInfo;
334+
var converterInfo = cachedInfo.IsDefault ? cachedInfo = CreateConverterInfo(typeof(T), type) : cachedInfo;
335+
asObject = converterInfo.IsBoxingConverter;
336+
return converterInfo;
334337
}
335338

336339
T DbNullOrThrow()

src/Npgsql/NpgsqlDataReader.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,17 +2121,17 @@ FieldDescription GetInfo(int ordinal, Type? type, out PgConverter converter, out
21212121

21222122
if (type is null)
21232123
{
2124-
converter = field.ObjectOrDefaultInfo.Converter;
2125-
bufferRequirement = field.ObjectOrDefaultInfo.BufferRequirement;
2126-
asObject = field.ObjectOrDefaultInfo.AsObject;
2124+
var odfInfo = field.ObjectOrDefaultInfo;
2125+
converter = odfInfo.Converter;
2126+
bufferRequirement = odfInfo.BufferRequirement;
2127+
asObject = odfInfo.IsBoxingConverter;
21272128
return field;
21282129
}
21292130

21302131
ref var info = ref ColumnInfoCache![ordinal];
2131-
field.GetInfo(type, ref info);
2132+
field.GetInfo(type, ref info, out asObject);
21322133
converter = info.Converter;
21332134
bufferRequirement = info.BufferRequirement;
2134-
asObject = info.AsObject;
21352135
return field;
21362136
}
21372137

0 commit comments

Comments
 (0)