Skip to content
Permalink
Browse files

Make most type handlers public

And add public documentation, plus a remark about the API being unstable.

Closes #2624
  • Loading branch information...
roji committed Sep 22, 2019
1 parent 995b02d commit d4132d0d546594629bcef658bcb1418b4a8624cc
Showing with 797 additions and 241 deletions.
  1. +2 −0 Npgsql.sln.DotSettings
  2. +39 −13 src/Npgsql/TypeHandlers/ArrayHandler.cs
  3. +29 −6 src/Npgsql/TypeHandlers/BitStringHandler.cs
  4. +13 −2 src/Npgsql/TypeHandlers/BoolHandler.cs
  5. +8 −1 src/Npgsql/TypeHandlers/ByteaHandler.cs
  6. +29 −13 src/Npgsql/TypeHandlers/DateTimeHandlers/DateHandler.cs
  7. +30 −9 src/Npgsql/TypeHandlers/DateTimeHandlers/IntervalHandler.cs
  8. +25 −5 src/Npgsql/TypeHandlers/DateTimeHandlers/TimeHandler.cs
  9. +32 −13 src/Npgsql/TypeHandlers/DateTimeHandlers/TimeTzHandler.cs
  10. +33 −11 src/Npgsql/TypeHandlers/DateTimeHandlers/TimestampHandler.cs
  11. +29 −7 src/Npgsql/TypeHandlers/DateTimeHandlers/TimestampTzHandler.cs
  12. +25 −2 src/Npgsql/TypeHandlers/FullTextSearchHandlers/TsQueryHandler.cs
  13. +13 −2 src/Npgsql/TypeHandlers/FullTextSearchHandlers/TsVectorHandler.cs
  14. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/BoxHandler.cs
  15. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/CircleHandler.cs
  16. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/LineHandler.cs
  17. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/LineSegmentHandler.cs
  18. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/PathHandler.cs
  19. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/PointHandler.cs
  20. +11 −3 src/Npgsql/TypeHandlers/GeometricHandlers/PolygonHandler.cs
  21. +30 −2 src/Npgsql/TypeHandlers/HstoreHandler.cs
  22. +19 −3 src/Npgsql/TypeHandlers/InternalCharHandler.cs
  23. +46 −2 src/Npgsql/TypeHandlers/JsonHandler.cs
  24. +16 −2 src/Npgsql/TypeHandlers/NetworkHandlers/CidrHandler.cs
  25. +21 −2 src/Npgsql/TypeHandlers/NetworkHandlers/InetHandler.cs
  26. +8 −3 src/Npgsql/TypeHandlers/NetworkHandlers/MacaddrHandler.cs
  27. +13 −2 src/Npgsql/TypeHandlers/NumericHandlers/DoubleHandler.cs
  28. +35 −18 src/Npgsql/TypeHandlers/NumericHandlers/Int16Handler.cs
  29. +32 −16 src/Npgsql/TypeHandlers/NumericHandlers/Int32Handler.cs
  30. +32 −16 src/Npgsql/TypeHandlers/NumericHandlers/Int64Handler.cs
  31. +14 −5 src/Npgsql/TypeHandlers/NumericHandlers/MoneyHandler.cs
  32. +40 −39 src/Npgsql/TypeHandlers/NumericHandlers/NumericHandler.cs
  33. +19 −12 src/Npgsql/TypeHandlers/NumericHandlers/SingleHandler.cs
  34. +13 −2 src/Npgsql/TypeHandlers/NumericHandlers/UInt32Handler.cs
  35. +26 −5 src/Npgsql/TypeHandlers/RangeHandler.cs
  36. +35 −4 src/Npgsql/TypeHandlers/TextHandler.cs
  37. +13 −2 src/Npgsql/TypeHandlers/UuidHandler.cs
  38. +1 −1 src/Npgsql/TypeHandling/NpgsqlTypeHandlerFactory.cs
@@ -96,6 +96,8 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pregenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=P_0020keepaliv/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=resultset/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=timestamptz/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=timetz/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNLISTEN/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unmap/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unprepare/@EntryIndexedValue">True</s:Boolean>
@@ -8,11 +8,22 @@
using Npgsql.PostgresTypes;
using Npgsql.TypeHandling;

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Npgsql.TypeHandlers
{
/// <summary>
/// Non-generic base class for all type handlers which handle PostgreSQL arrays.
/// Extend from <see cref="ArrayHandler{TElement}"/> instead.
/// </summary>
/// <remarks>
/// http://www.postgresql.org/docs/current/static/arrays.html.
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
public abstract class ArrayHandler : NpgsqlTypeHandler
{
/// <inheritdoc />
protected ArrayHandler(PostgresType arrayPostgresType) : base(arrayPostgresType) {}

internal static class IsArrayOf<TArray, TElement>
@@ -33,13 +44,18 @@ public override RangeHandler CreateRangeHandler(PostgresRangeType rangeBackendTy
/// Base class for all type handlers which handle PostgreSQL arrays.
/// </summary>
/// <remarks>
/// http://www.postgresql.org/docs/current/static/arrays.html
/// http://www.postgresql.org/docs/current/static/arrays.html.
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// 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 ArrayHandler<TElement> : ArrayHandler
{
readonly int _lowerBound; // The lower bound value sent to the backend when writing arrays. Normally 1 (the PG default) but is 0 for OIDVector.
readonly NpgsqlTypeHandler _elementHandler;

/// <inheritdoc />
public ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler elementHandler, int lowerBound = 1)
: base(arrayPostgresType)
{
@@ -55,6 +71,7 @@ public ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler elementHan
internal override TAny Read<TAny>(NpgsqlReadBuffer buf, int len, FieldDescription? fieldDescription = null)
=> Read<TAny>(buf, len, false, fieldDescription).Result;

/// <inheritdoc />
protected internal override async ValueTask<TAny> Read<TAny>(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
{
if (IsArrayOf<TAny, TElement>.Value)
@@ -76,7 +93,10 @@ internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int
internal override object ReadAsObject(NpgsqlReadBuffer buf, int len, FieldDescription? fieldDescription = null)
=> ReadArray<TElement>(buf, false).Result;

protected async ValueTask<Array> ReadArray<T>(NpgsqlReadBuffer buf, bool async)
/// <summary>
/// Reads an array of element type <typeparamref name="TAnyElement"/> from the given buffer <paramref name="buf"/>.
/// </summary>
protected async ValueTask<Array> ReadArray<TAnyElement>(NpgsqlReadBuffer buf, bool async)
{
await buf.Ensure(12, async);
var dimensions = buf.ReadInt32();
@@ -93,23 +113,23 @@ protected async ValueTask<Array> ReadArray<T>(NpgsqlReadBuffer buf, bool async)
}

if (dimensions == 0)
return new T[0]; // TODO: static instance
return new TAnyElement[0]; // TODO: static instance

var result = Array.CreateInstance(typeof(T), dimLengths);
var result = Array.CreateInstance(typeof(TAnyElement), dimLengths);

if (dimensions == 1)
{
var oneDimensional = (T[])result;
var oneDimensional = (TAnyElement[])result;
for (var i = 0; i < oneDimensional.Length; i++)
oneDimensional[i] = await _elementHandler.ReadWithLength<T>(buf, async);
oneDimensional[i] = await _elementHandler.ReadWithLength<TAnyElement>(buf, async);
return oneDimensional;
}

// Multidimensional
var indices = new int[dimensions];
while (true)
{
var element = await _elementHandler.ReadWithLength<T>(buf, async);
var element = await _elementHandler.ReadWithLength<TAnyElement>(buf, async);
result.SetValue(element, indices);

// TODO: Overly complicated/inefficient...
@@ -129,15 +149,18 @@ protected async ValueTask<Array> ReadArray<T>(NpgsqlReadBuffer buf, bool async)
}
}

protected async ValueTask<List<T>> ReadList<T>(NpgsqlReadBuffer buf, bool async)
/// <summary>
/// Reads an array of element type <typeparamref name="TAnyElement"/> from the given buffer <paramref name="buf"/>.
/// </summary>
protected async ValueTask<List<TAnyElement>> ReadList<TAnyElement>(NpgsqlReadBuffer buf, bool async)
{
await buf.Ensure(12, async);
var dimensions = buf.ReadInt32();

if (dimensions == 0)
return new List<T>();
return new List<TAnyElement>();
if (dimensions > 1)
throw new NotSupportedException($"Can't read multidimensional array as List<{typeof(T).Name}>");
throw new NotSupportedException($"Can't read multidimensional array as List<{typeof(TAnyElement).Name}>");

buf.ReadInt32(); // Has nulls. Not populated by PG?
buf.ReadUInt32(); // Element OID. Ignored.
@@ -146,9 +169,9 @@ protected async ValueTask<List<T>> ReadList<T>(NpgsqlReadBuffer buf, bool async)
var length = buf.ReadInt32();
buf.ReadInt32(); // We don't care about the lower bounds

var list = new List<T>(length);
var list = new List<TAnyElement>(length);
for (var i = 0; i < length; i++)
list.Add(await _elementHandler.ReadWithLength<T>(buf, async));
list.Add(await _elementHandler.ReadWithLength<TAnyElement>(buf, async));
return list;
}

@@ -166,9 +189,11 @@ static Exception CantWriteTypeException(Type type)
// Since TAny isn't constrained to class? or struct (C# doesn't have a non-nullable constraint that doesn't limit us to either struct or class),
// we must use the bang operator here to tell the compiler that a null value will never be returned.

/// <inheritdoc />
protected internal override int ValidateAndGetLength<TAny>(TAny value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value!, ref lengthCache);

/// <inheritdoc />
protected internal override int ValidateObjectAndGetLength(object value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value!, ref lengthCache);

@@ -283,6 +308,7 @@ Task WriteWithLength()

// The default WriteObjectWithLength casts the type handler to INpgsqlTypeHandler<T>, but that's not sufficient for
// us (need to handle many types of T, e.g. int[], int[,]...)
/// <inheritdoc />
protected internal override Task WriteObjectWithLength(object value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
=> value is DBNull
? WriteWithLengthInternal(DBNull.Value, buf, lengthCache, parameter, async)
@@ -13,18 +13,24 @@
namespace Npgsql.TypeHandlers
{
/// <summary>
/// Handler for the PostgreSQL bit string type.
/// Note that for BIT(1), this handler will return a bool by default, to align with SQLClient
/// (see discussion https://github.com/npgsql/npgsql/pull/362#issuecomment-59622101).
/// A type handler for the PostgreSQL bit string data type.
/// </summary>
/// <remarks>
/// http://www.postgresql.org/docs/current/static/datatype-bit.html
/// See http://www.postgresql.org/docs/current/static/datatype-bit.html.
///
/// Note that for BIT(1), this handler will return a bool by default, to align with SQLClient
/// (see discussion https://github.com/npgsql/npgsql/pull/362#issuecomment-59622101).
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
[TypeMapping("bit varying", NpgsqlDbType.Varbit, new[] { typeof(BitArray), typeof(BitVector32) })]
[TypeMapping("bit", NpgsqlDbType.Bit)]
class BitStringHandler : NpgsqlTypeHandler<BitArray>,
public class BitStringHandler : NpgsqlTypeHandler<BitArray>,
INpgsqlTypeHandler<BitVector32>, INpgsqlTypeHandler<bool>, INpgsqlTypeHandler<string>
{
/// <inheritdoc />
public BitStringHandler(PostgresType postgresType) : base(postgresType) {}

internal override Type GetFieldType(FieldDescription? fieldDescription = null)
@@ -34,11 +40,13 @@ internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescr
=> GetFieldType(fieldDescription);

// BitString requires a special array handler which returns bool or BitArray
/// <inheritdoc />
public override ArrayHandler CreateArrayHandler(PostgresArrayType backendType)
=> new BitStringArrayHandler(backendType, this);

#region Read

/// <inheritdoc />
public override async ValueTask<BitArray> Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
{
await buf.Ensure(4, async);
@@ -130,22 +138,27 @@ internal override object ReadAsObject(NpgsqlReadBuffer buf, int len, FieldDescri

#region Write

/// <inheritdoc />
public override int ValidateAndGetLength(BitArray value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> 4 + (value.Length + 7) / 8;

/// <inheritdoc />
public int ValidateAndGetLength(BitVector32 value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> value.Data == 0 ? 4 : 8;

/// <inheritdoc />
public int ValidateAndGetLength(bool value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> 5;

/// <inheritdoc />
public int ValidateAndGetLength(string value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
{
if (value.Any(c => c != '0' && c != '1'))
throw new FormatException("Cannot interpret as ASCII BitString: " + value);
return 4 + (value.Length + 7) / 8;
}

/// <inheritdoc />
public override async Task Write(BitArray value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
{
// Initial bitlength byte
@@ -173,6 +186,7 @@ public override async Task Write(BitArray value, NpgsqlWriteBuffer buf, NpgsqlLe
}
}

/// <inheritdoc />
public async Task Write(BitVector32 value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
{
if (buf.WriteSpaceLeft < 8)
@@ -187,6 +201,7 @@ public async Task Write(BitVector32 value, NpgsqlWriteBuffer buf, NpgsqlLengthCa
}
}

/// <inheritdoc />
public async Task Write(bool value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
{
if (buf.WriteSpaceLeft < 5)
@@ -195,6 +210,7 @@ public async Task Write(bool value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? le
buf.WriteByte(value ? (byte)0x80 : (byte)0);
}

/// <inheritdoc />
public async Task Write(string value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async)
{
// Initial bitlength byte
@@ -250,11 +266,18 @@ public async Task Write(string value, NpgsqlWriteBuffer buf, NpgsqlLengthCache?
/// Differs from the standard array handlers in that it returns arrays of bool for BIT(1) and arrays
/// of BitArray otherwise (just like the scalar BitStringHandler does).
/// </summary>
class BitStringArrayHandler : ArrayHandler<BitArray>
/// <remarks>
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// 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>
{
/// <inheritdoc />
public BitStringArrayHandler(PostgresType postgresType, BitStringHandler elementHandler)
: base(postgresType, elementHandler) {}

/// <inheritdoc />
protected internal override async ValueTask<TAny> Read<TAny>(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
{
if (IsArrayOf<TAny, BitArray>.Value)
@@ -7,20 +7,31 @@

namespace Npgsql.TypeHandlers
{
/// <summary>
/// A type handler for the PostgreSQL bool data type.
/// </summary>
/// <remarks>
/// http://www.postgresql.org/docs/current/static/datatype-boolean.html
/// See http://www.postgresql.org/docs/current/static/datatype-boolean.html.
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
[TypeMapping("boolean", NpgsqlDbType.Boolean, DbType.Boolean, typeof(bool))]
class BoolHandler : NpgsqlSimpleTypeHandler<bool>
public class BoolHandler : NpgsqlSimpleTypeHandler<bool>
{
/// <inheritdoc />
public BoolHandler(PostgresType postgresType) : base(postgresType) {}

/// <inheritdoc />
public override bool Read(NpgsqlReadBuffer buf, int len, FieldDescription? fieldDescription = null)
=> buf.ReadByte() != 0;

/// <inheritdoc />
public override int ValidateAndGetLength(bool value, NpgsqlParameter? parameter)
=> 1;

/// <inheritdoc />
public override void Write(bool value, NpgsqlWriteBuffer buf, NpgsqlParameter? parameter)
=> buf.WriteByte(value ? (byte)1 : (byte)0);
}
@@ -9,8 +9,15 @@

namespace Npgsql.TypeHandlers
{
/// <summary>
/// A type handler for the PostgreSQL bytea data type.
/// </summary>
/// <remarks>
/// http://www.postgresql.org/docs/current/static/datatype-binary.html
/// See http://www.postgresql.org/docs/current/static/datatype-binary.html.
///
/// The type handler API allows customizing Npgsql's behavior in powerful ways. However, although it is public, it
/// should be considered somewhat unstable, and may change in breaking ways, including in non-major releases.
/// Use it at your own risk.
/// </remarks>
[TypeMapping(
"bytea",

0 comments on commit d4132d0

Please sign in to comment.
You can’t perform that action at this time.