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
6 changes: 3 additions & 3 deletions src/Npgsql.NodaTime/Internal/NodaTimeTypeHandlerResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ NpgsqlTypeHandler DateMultirange()

NpgsqlTypeHandler TsTzRangeArray()
=> _timestampTzRangeArray ??=
new ArrayHandler<Interval>((PostgresArrayType)PgType("tstzrange[]"), TsTzRange(), _arrayNullabilityMode);
new ArrayHandler((PostgresArrayType)PgType("tstzrange[]"), TsTzRange(), _arrayNullabilityMode);

NpgsqlTypeHandler DateRangeArray()
=> _dateRangeArray ??=
new ArrayHandler<DateInterval>((PostgresArrayType)PgType("daterange[]"), DateRange(), _arrayNullabilityMode);
}
new ArrayHandler((PostgresArrayType)PgType("daterange[]"), DateRange(), _arrayNullabilityMode);
}
632 changes: 372 additions & 260 deletions src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions src/Npgsql/Internal/TypeHandlers/BitStringHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,15 @@ public async Task Write(string value, NpgsqlWriteBuffer buf, NpgsqlLengthCache?
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
public class BitStringArrayHandler : ArrayHandler<BitArray>
public class BitStringArrayHandler : ArrayHandler
{
/// <inheritdoc />
public BitStringArrayHandler(PostgresType postgresType, BitStringHandler elementHandler, ArrayNullabilityMode arrayNullabilityMode)
: base(postgresType, elementHandler, arrayNullabilityMode) {}
: base(postgresType, elementHandler, arrayNullabilityMode)
{ }

public override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
public override ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
=> fieldDescription?.TypeModifier == 1
? await ReadArray<bool>(buf, async)
: await ReadArray<BitArray>(buf, async);
? base.ReadAsObject(typeof(bool), buf, len, async, fieldDescription)
: base.ReadAsObject(buf, len, async, fieldDescription);
}
16 changes: 8 additions & 8 deletions src/Npgsql/Internal/TypeHandlers/HstoreHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ public override int ValidateAndGetLength(Dictionary<string, string?> value, ref
public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> value switch
{
ImmutableDictionary<string, string?> converted => ((INpgsqlTypeHandler<ImmutableDictionary<string, string?>>)this).ValidateAndGetLength(converted, ref lengthCache, parameter),
Dictionary<string, string?> converted => ((INpgsqlTypeHandler<Dictionary<string, string?>>)this).ValidateAndGetLength(converted, ref lengthCache, parameter),
IDictionary<string, string?> converted => ((INpgsqlTypeHandler<IDictionary<string, string?>>)this).ValidateAndGetLength(converted, ref lengthCache, parameter),
ImmutableDictionary<string, string?> converted => ValidateAndGetLength(converted, ref lengthCache, parameter),
Dictionary<string, string?> converted => ValidateAndGetLength(converted, ref lengthCache, parameter),
IDictionary<string, string?> converted => ValidateAndGetLength(converted, ref lengthCache, parameter),

DBNull => 0,
null => 0,
Expand All @@ -94,9 +94,9 @@ public override Task WriteObjectWithLength(
CancellationToken cancellationToken = default)
=> value switch
{
ImmutableDictionary<string, string?> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
Dictionary<string, string?> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
IDictionary<string, string?> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
ImmutableDictionary<string, string?> converted => ((INpgsqlTypeHandler<ImmutableDictionary<string, string?>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
Dictionary<string, string?> converted => ((INpgsqlTypeHandler<Dictionary<string, string?>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
IDictionary<string, string?> converted => ((INpgsqlTypeHandler<IDictionary<string, string?>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),

DBNull => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
null => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
Expand All @@ -114,8 +114,8 @@ public async Task Write(IDictionary<string, string?> value, NpgsqlWriteBuffer bu

foreach (var kv in value)
{
await _textHandler.WriteWithLength(kv.Key, buf, lengthCache, parameter, async, cancellationToken);
await _textHandler.WriteWithLength(kv.Value, buf, lengthCache, parameter, async, cancellationToken);
await ((INpgsqlTypeHandler<string>)_textHandler).WriteWithLength(kv.Key, buf, lengthCache, parameter, async, cancellationToken);
await ((INpgsqlTypeHandler<string>)_textHandler).WriteWithLength(kv.Value, buf, lengthCache, parameter, async, cancellationToken);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
using System;
using Npgsql.Internal.TypeHandlers.NumericHandlers;
using Npgsql.Internal.TypeHandlers.NumericHandlers;
using Npgsql.Internal.TypeHandling;
using Npgsql.PostgresTypes;
using Npgsql.TypeMapping;
using NpgsqlTypes;

namespace Npgsql.Internal.TypeHandlers.InternalTypeHandlers;

/// <summary>
/// An int2vector is simply a regular array of shorts, with the sole exception that its lower bound must
/// be 0 (we send 1 for regular arrays).
/// </summary>
sealed class Int2VectorHandler : ArrayHandler<short>
sealed class Int2VectorHandler : ArrayHandler
{
public Int2VectorHandler(PostgresType arrayPostgresType, PostgresType postgresShortType)
: base(arrayPostgresType, new Int16Handler(postgresShortType), ArrayNullabilityMode.Never, 0) { }

public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
=> new ArrayHandler<ArrayHandler<short>>(pgArrayType, this, arrayNullabilityMode);
}
=> new ArrayHandler(pgArrayType, this, arrayNullabilityMode);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
using System;
using Npgsql.Internal.TypeHandlers.NumericHandlers;
using Npgsql.Internal.TypeHandlers.NumericHandlers;
using Npgsql.Internal.TypeHandling;
using Npgsql.PostgresTypes;
using Npgsql.TypeMapping;
using NpgsqlTypes;

namespace Npgsql.Internal.TypeHandlers.InternalTypeHandlers;

/// <summary>
/// An OIDVector is simply a regular array of uints, with the sole exception that its lower bound must
/// be 0 (we send 1 for regular arrays).
/// </summary>
sealed class OIDVectorHandler : ArrayHandler<uint>
sealed class OIDVectorHandler : ArrayHandler
{
public OIDVectorHandler(PostgresType oidvectorType, PostgresType oidType)
: base(oidvectorType, new UInt32Handler(oidType), ArrayNullabilityMode.Never, 0) { }

public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
=> new ArrayHandler<ArrayHandler<uint>>(pgArrayType, this, arrayNullabilityMode);
}
=> new ArrayHandler(pgArrayType, this, arrayNullabilityMode);
}
14 changes: 4 additions & 10 deletions src/Npgsql/Internal/TypeHandlers/JsonTextHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,8 @@ public override int ValidateObjectAndGetLength(object value, ref NpgsqlLengthCac
};

/// <inheritdoc />
public override async Task WriteObjectWithLength(object? value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
{
// We call into WriteWithLength<T> below, which assumes it as at least enough write space for the length
if (buf.WriteSpaceLeft < 4)
await buf.Flush(async, cancellationToken);

await (value switch
public override Task WriteObjectWithLength(object? value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
=> value switch
{
null => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
DBNull => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
Expand All @@ -173,8 +168,7 @@ public override async Task WriteObjectWithLength(object? value, NpgsqlWriteBuffe
_ => throw new InvalidCastException(
$"Can't write CLR type {value.GetType()}. " +
"You may need to use the System.Text.Json or Json.NET plugins, see the docs for more information.")
});
}
};

/// <inheritdoc />
protected internal override async ValueTask<T> ReadCustom<T>(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
Expand Down Expand Up @@ -212,4 +206,4 @@ public TextReader GetTextReader(Stream stream, NpgsqlReadBuffer buffer)

return TextHandler.GetTextReader(stream, buffer);
}
}
}
12 changes: 6 additions & 6 deletions src/Npgsql/Internal/TypeHandlers/MultirangeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public async Task WriteMultirange<TAnySubtype>(
buf.WriteInt32(value.Count);

for (var i = 0; i < value.Count; i++)
await RangeHandler.WriteWithLength(value[i], buf, lengthCache, parameter: null, async, cancellationToken);
await ((INpgsqlTypeHandler<NpgsqlRange<TAnySubtype>>)RangeHandler).WriteWithLength(value[i], buf, lengthCache, parameter: null, async, cancellationToken);
}

public override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(NpgsqlRange<TSubtype>[]);
Expand Down Expand Up @@ -200,13 +200,13 @@ public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCa
public override Task WriteObjectWithLength(object? value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
=> value switch
{
NpgsqlRange<TSubtype1>[] converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype2>[] converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
List<NpgsqlRange<TSubtype1>> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
List<NpgsqlRange<TSubtype2>> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype1>[] converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype1>[]>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype2>[] converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype2>[]>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
List<NpgsqlRange<TSubtype1>> converted => ((INpgsqlTypeHandler<List<NpgsqlRange<TSubtype1>>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
List<NpgsqlRange<TSubtype2>> converted => ((INpgsqlTypeHandler<List<NpgsqlRange<TSubtype2>>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),

DBNull => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
null => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
_ => throw new InvalidCastException($"Can't write CLR type {value.GetType()} with handler type RangeHandler<TElement>")
};
}
}
12 changes: 6 additions & 6 deletions src/Npgsql/Internal/TypeHandlers/RangeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public RangeHandler(PostgresType rangePostgresType, NpgsqlTypeHandler subtypeHan

/// <inheritdoc />
public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
=> new ArrayHandler<NpgsqlRange<TSubtype>>(pgArrayType, this, arrayNullabilityMode);
=> new ArrayHandler(pgArrayType, this, arrayNullabilityMode);

/// <inheritdoc />
public override NpgsqlTypeHandler CreateRangeHandler(PostgresType pgRangeType)
Expand Down Expand Up @@ -167,8 +167,8 @@ public Task Write(NpgsqlRange<TSubtype2> value, NpgsqlWriteBuffer buf, NpgsqlLen
public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> value switch
{
NpgsqlRange<TSubtype1> converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype1>>)this).ValidateAndGetLength(converted, ref lengthCache, parameter),
NpgsqlRange<TSubtype2> converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype2>>)this).ValidateAndGetLength(converted, ref lengthCache, parameter),
NpgsqlRange<TSubtype1> converted => ValidateAndGetLength(converted, ref lengthCache, parameter),
NpgsqlRange<TSubtype2> converted => ValidateAndGetLength(converted, ref lengthCache, parameter),

DBNull => 0,
null => 0,
Expand All @@ -178,11 +178,11 @@ public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCa
public override Task WriteObjectWithLength(object? value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
=> value switch
{
NpgsqlRange<TSubtype1> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype2> converted => WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype1> converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype1>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),
NpgsqlRange<TSubtype2> converted => ((INpgsqlTypeHandler<NpgsqlRange<TSubtype2>>)this).WriteWithLength(converted, buf, lengthCache, parameter, async, cancellationToken),

DBNull => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
null => WriteWithLength(DBNull.Value, buf, lengthCache, parameter, async, cancellationToken),
_ => throw new InvalidCastException($"Can't write CLR type {value.GetType()} with handler type RangeHandler<TElement>")
};
}
}
25 changes: 22 additions & 3 deletions src/Npgsql/Internal/TypeHandling/INpgsqlTypeHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Npgsql.BackendMessages;
Expand Down Expand Up @@ -34,7 +35,7 @@ public interface INpgsqlTypeHandler<T>
/// information relevant to the write process (e.g. <see cref="NpgsqlParameter.Size"/>).
/// </param>
/// <returns>The number of bytes required to write the value.</returns>
int ValidateAndGetLength([DisallowNull] T value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter);
int ValidateAndGetLength([DisallowNull] T value, [NotNullIfNotNull(nameof(lengthCache))]ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter);

/// <summary>
/// Writes a value to the provided buffer.
Expand All @@ -53,4 +54,22 @@ public interface INpgsqlTypeHandler<T>
/// An optional token to cancel the asynchronous operation. The default value is <see cref="CancellationToken.None"/>.
/// </param>
Task Write([DisallowNull] T value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default);
}
}

static class INpgsqlTypeHandlerExtensions
{
public static async Task WriteWithLength<T>(this INpgsqlTypeHandler<T> handler, T? value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
{
if (buf.WriteSpaceLeft < 4)
await buf.Flush(async, cancellationToken);

if (value is null or DBNull)
{
buf.WriteInt32(-1);
return;
}

buf.WriteInt32(handler.ValidateAndGetLength(value, ref lengthCache, parameter));
await handler.Write(value, buf, lengthCache, parameter, async, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ protected NpgsqlSimpleTypeHandler(PostgresType postgresType) : base(postgresType
public sealed override ValueTask<TDefault> Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
=> throw new NotSupportedException();

public override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
{
await buf.Ensure(len, async);
return Read(buf, len, fieldDescription)!;
}

#region Write

/// <summary>
Expand Down Expand Up @@ -75,4 +81,4 @@ public sealed override int ValidateAndGetLength(TDefault value, ref NpgsqlLength
=> throw new NotSupportedException();

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,5 @@ internal override async ValueTask<object> ReadPsvAsObject(NpgsqlReadBuffer buf,
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null)
=> typeof(TPsv);

/// <inheeritdoc />
public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
=> new ArrayHandlerWithPsv<TDefault, TPsv>(pgArrayType, this, arrayNullabilityMode);

#endregion Misc
}
}
11 changes: 7 additions & 4 deletions src/Npgsql/Internal/TypeHandling/NpgsqlTypeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ internal async ValueTask<TAny> ReadWithLength<TAny>(NpgsqlReadBuffer buf, bool a
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected internal int ValidateAndGetLength<TAny>(
[DisallowNull] TAny value, [NotNullIfNotNull("lengthCache")] ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
[DisallowNull] TAny value, [NotNullIfNotNull(nameof(lengthCache))] ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
{
Debug.Assert(value is not DBNull);

Expand All @@ -148,7 +148,10 @@ protected internal int ValidateAndGetLength<TAny>(
}

protected internal virtual int ValidateAndGetLengthCustom<TAny>(
[DisallowNull] TAny value, [NotNullIfNotNull("lengthCache")] ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
[DisallowNull] TAny value, [NotNullIfNotNull(nameof(lengthCache))] ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter) =>
ValidateAndGetLengthCustomCore(parameter, typeof(TAny), PgDisplayName);

static int ValidateAndGetLengthCustomCore(NpgsqlParameter? parameter, Type type, string displayName)
{
var parameterName = parameter is null
? null
Expand All @@ -159,8 +162,8 @@ protected internal virtual int ValidateAndGetLengthCustom<TAny>(
: parameter.TrimmedName;

throw new InvalidCastException(parameterName is null
? $"Cannot write a value of CLR type '{typeof(TAny)}' as database type '{PgDisplayName}'."
: $"Cannot write a value of CLR type '{typeof(TAny)}' as database type '{PgDisplayName}' for parameter '{parameterName}'.");
? $"Cannot write a value of CLR type '{type}' as database type '{displayName}'."
: $"Cannot write a value of CLR type '{type}' as database type '{displayName}' for parameter '{parameterName}'.");
}

/// <summary>
Expand Down
Loading