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
4 changes: 4 additions & 0 deletions Npgsql.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=bytea/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=citext/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Conformant/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=daterange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDEX/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=IANA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lquery/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -99,6 +100,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOEXPORT/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=numrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=oidvector/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pgoutput/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pgpass/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -116,6 +118,8 @@
<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/=tsquery/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tsrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tstzrange/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tsvector/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=typname/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNLISTEN/@EntryIndexedValue">True</s:Boolean>
Expand Down
42 changes: 42 additions & 0 deletions src/Npgsql.NodaTime/Internal/DateRangeHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using NodaTime;
using Npgsql.BackendMessages;
using Npgsql.Internal;
using Npgsql.Internal.TypeHandlers;
using Npgsql.Internal.TypeHandling;
using Npgsql.PostgresTypes;
using NpgsqlTypes;

namespace Npgsql.NodaTime.Internal
{
public partial class DateRangeHandler : RangeHandler<LocalDate>, INpgsqlTypeHandler<DateInterval>
{
public DateRangeHandler(PostgresType rangePostgresType, NpgsqlTypeHandler subtypeHandler)
: base(rangePostgresType, subtypeHandler)
{
}

async ValueTask<DateInterval> INpgsqlTypeHandler<DateInterval>.Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
{
var range = await Read(buf, len, async, fieldDescription);
return new(range.LowerBound, range.UpperBound - Period.FromDays(1));
}

public override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(DateInterval);
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(DateInterval);

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

public int ValidateAndGetLength(DateInterval value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(new NpgsqlRange<LocalDate>(value.Start, value.End), ref lengthCache, parameter);

public Task Write(
DateInterval value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async,
CancellationToken cancellationToken = default)
=> Write(new NpgsqlRange<LocalDate>(value.Start, value.End), buf, lengthCache, parameter, async, cancellationToken);
}
}
9 changes: 9 additions & 0 deletions src/Npgsql.NodaTime/Internal/NodaTimeTypeHandlerResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class NodaTimeTypeHandlerResolver : TypeHandlerResolver
readonly TimeHandler _timeHandler;
readonly TimeTzHandler _timeTzHandler;
readonly IntervalHandler _intervalHandler;
readonly DateRangeHandler _dateRangeHandler;

internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
{
Expand All @@ -35,6 +36,7 @@ internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
_timeHandler = new TimeHandler(PgType("time without time zone"));
_timeTzHandler = new TimeTzHandler(PgType("time with time zone"));
_intervalHandler = new IntervalHandler(PgType("interval"));
_dateRangeHandler = new DateRangeHandler(PgType("daterange"), _dateHandler);
}

public override NpgsqlTypeHandler? ResolveByDataTypeName(string typeName)
Expand All @@ -46,6 +48,7 @@ internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
"time without time zone" => _timeHandler,
"time with time zone" => _timeTzHandler,
"interval" => _intervalHandler,
"daterange" => _dateRangeHandler,

_ => null
};
Expand Down Expand Up @@ -79,6 +82,9 @@ internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
return _intervalHandler;
if (typeof(T) == typeof(Duration))
return _intervalHandler;
if (typeof(T) == typeof(NpgsqlRange<LocalDate>))
return _dateRangeHandler;


return null;
}
Expand All @@ -100,6 +106,8 @@ internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
return "time with time zone";
if (type == typeof(Period) || type == typeof(Duration))
return "interval";
if (type == typeof(DateInterval) || type == typeof(NpgsqlRange<LocalDate>))
return "daterange";

return null;
}
Expand All @@ -116,6 +124,7 @@ internal NodaTimeTypeHandlerResolver(NpgsqlConnector connector)
"time without time zone" => new(NpgsqlDbType.Time, DbType.Time, "time without time zone"),
"time with time zone" => new(NpgsqlDbType.TimeTz, DbType.Object, "time with time zone"),
"interval" => new(NpgsqlDbType.Interval, DbType.Object, "interval"),
"daterange" => new(NpgsqlDbType.DateRange, DbType.Object, "daterange"),

_ => null
};
Expand Down
6 changes: 3 additions & 3 deletions src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ protected ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler element
ArrayNullabilityMode = arrayNullabilityMode;
}

internal override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(Array);
internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(Array);
public override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(Array);
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(Array);

/// <inheritdoc />
public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
Expand Down Expand Up @@ -290,7 +290,7 @@ public ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler elementHan

#region Read

internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
public override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
=> await ReadArray<TElement>(buf, async, readAsObject: true);

#endregion
Expand Down
8 changes: 4 additions & 4 deletions src/Npgsql/Internal/TypeHandlers/BitStringHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public partial class BitStringHandler : NpgsqlTypeHandler<BitArray>,
{
public BitStringHandler(PostgresType pgType) : base(pgType) {}

internal override Type GetFieldType(FieldDescription? fieldDescription = null)
public override Type GetFieldType(FieldDescription? fieldDescription = null)
=> fieldDescription != null && fieldDescription.TypeModifier == 1 ? typeof(bool) : typeof(BitArray);

internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null)
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null)
=> GetFieldType(fieldDescription);

// BitString requires a special array handler which returns bool or BitArray
Expand Down Expand Up @@ -118,7 +118,7 @@ async ValueTask<bool> INpgsqlTypeHandler<bool>.Read(NpgsqlReadBuffer buf, int le
ValueTask<string> INpgsqlTypeHandler<string>.Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription)
=> throw new NotSupportedException("Only writing string to PostgreSQL bitstring is supported, no reading.");

internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
public override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
=> fieldDescription?.TypeModifier == 1
? await Read<bool>(buf, len, async, fieldDescription)
: await Read<BitArray>(buf, len, async, fieldDescription);
Expand Down Expand Up @@ -290,7 +290,7 @@ protected internal override async ValueTask<TRequestedArray> ReadCustom<TRequest
return await base.ReadCustom<TRequestedArray>(buf, len, async, fieldDescription);
}

internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
public override async 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);
Expand Down
5 changes: 3 additions & 2 deletions src/Npgsql/Internal/TypeHandlers/RangeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using Npgsql.BackendMessages;
using Npgsql.Internal.TypeHandling;
using Npgsql.PostgresTypes;
Expand Down Expand Up @@ -36,8 +37,8 @@ public RangeHandler(PostgresType rangePostgresType, NpgsqlTypeHandler subtypeHan
public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
=> new ArrayHandler<NpgsqlRange<TElement>>(pgArrayType, this, arrayNullabilityMode);

internal override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(NpgsqlRange<TElement>);
internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(NpgsqlRange<TElement>);
public override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(NpgsqlRange<TElement>);
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(NpgsqlRange<TElement>);

/// <inheritdoc />
public override NpgsqlTypeHandler CreateRangeHandler(PostgresType pgRangeType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal override async ValueTask<object> ReadPsvAsObject(NpgsqlReadBuffer buf,

#region Misc

internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null)
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null)
=> typeof(TPsv);

/// <inheeritdoc />
Expand Down
6 changes: 3 additions & 3 deletions src/Npgsql/Internal/TypeHandling/NpgsqlTypeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ protected internal virtual ValueTask<TAny> ReadCustom<TAny>(NpgsqlReadBuffer buf
/// Reads a column as the type handler's default read type. If it is not already entirely in
/// memory, sync or async I/O will be performed as specified by <paramref name="async"/>.
/// </summary>
internal abstract ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null);
public abstract ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null);

/// <summary>
/// Version of <see cref="ReadAsObject(NpgsqlReadBuffer,int,bool,FieldDescription?)"/> that's called when we know the entire value
Expand Down Expand Up @@ -253,8 +253,8 @@ protected virtual Task WriteWithLengthCustom<TAny>(

#region Misc

internal abstract Type GetFieldType(FieldDescription? fieldDescription = null);
internal abstract Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null);
public abstract Type GetFieldType(FieldDescription? fieldDescription = null);
public abstract Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null);

internal virtual bool PreferTextWrite => false;

Expand Down
6 changes: 3 additions & 3 deletions src/Npgsql/Internal/TypeHandling/NpgsqlTypeHandler`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected NpgsqlTypeHandler(PostgresType postgresType) : base(postgresType) {}

// 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 returned.
internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
public override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
=> (await Read<TDefault>(buf, len, async, fieldDescription))!;

#endregion Read
Expand All @@ -60,8 +60,8 @@ internal override async ValueTask<object> ReadAsObject(NpgsqlReadBuffer buf, int

#region Misc

internal override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(TDefault);
internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(TDefault);
public override Type GetFieldType(FieldDescription? fieldDescription = null) => typeof(TDefault);
public override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(TDefault);

/// <inheritdoc />
public override NpgsqlTypeHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
Expand Down
40 changes: 40 additions & 0 deletions src/Npgsql/NpgsqlTypes/NpgsqlDbType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,46 @@ public enum NpgsqlDbType

#endregion

#region Range types

/// <summary>
/// Corresponds to the PostgreSQL "int4range" type.
/// </summary>
[BuiltInPostgresType("int4range", PostgresTypeOIDs.Int4Range)]
IntegerRange = Range | Integer,

/// <summary>
/// Corresponds to the PostgreSQL "numrange" type.
/// </summary>
[BuiltInPostgresType("numrange", PostgresTypeOIDs.NumRange)]
NumericRange = Range | Numeric,

/// <summary>
/// Corresponds to the PostgreSQL "tsrange" type.
/// </summary>
[BuiltInPostgresType("tsrange", PostgresTypeOIDs.TsRange)]
TimestampRange = Range | Timestamp,

/// <summary>
/// Corresponds to the PostgreSQL "tstzrange" type.
/// </summary>
[BuiltInPostgresType("tstzrange", PostgresTypeOIDs.TsTzRange)]
TimestampTzRange = Range | TimestampTz,

/// <summary>
/// Corresponds to the PostgreSQL "daterange" type.
/// </summary>
[BuiltInPostgresType("daterange", PostgresTypeOIDs.DateRange)]
DateRange = Range | Date,

/// <summary>
/// Corresponds to the PostgreSQL "int8range" type.
/// </summary>
[BuiltInPostgresType("int8range", PostgresTypeOIDs.Int8Range)]
BigIntRange = Range | Bigint,

#endregion Range types

#region Composables

/// <summary>
Expand Down
8 changes: 7 additions & 1 deletion src/Npgsql/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,14 @@ static NpgsqlTypes.NpgsqlDate.ToDateOnly(NpgsqlTypes.NpgsqlDate date) -> System.
static NpgsqlTypes.NpgsqlDate.ToNpgsqlDate(System.DateOnly date) -> NpgsqlTypes.NpgsqlDate
static NpgsqlTypes.NpgsqlDate.explicit operator NpgsqlTypes.NpgsqlDate(System.DateOnly date) -> NpgsqlTypes.NpgsqlDate
static NpgsqlTypes.NpgsqlDate.explicit operator System.DateOnly(NpgsqlTypes.NpgsqlDate date) -> System.DateOnly
NpgsqlTypes.NpgsqlDbType.Xid8 = 64 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.BigIntRange = 1073741825 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.DateRange = 1073741831 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.IntegerRange = 1073741833 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.Multirange = 536870912 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.NumericRange = 1073741837 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.TimestampRange = 1073741845 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.TimestampTzRange = 1073741850 -> NpgsqlTypes.NpgsqlDbType
NpgsqlTypes.NpgsqlDbType.Xid8 = 64 -> NpgsqlTypes.NpgsqlDbType
*REMOVED*Npgsql.NpgsqlCommand.Statements.get -> System.Collections.Generic.IReadOnlyList<Npgsql.NpgsqlStatement!>!
*REMOVED*Npgsql.NpgsqlDataReader.Statements.get -> System.Collections.Generic.IReadOnlyList<Npgsql.NpgsqlStatement!>!
*REMOVED*Npgsql.NpgsqlStatement
Expand Down
8 changes: 8 additions & 0 deletions src/Npgsql/TypeMapping/GlobalTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ bool TryResolveMappingByClrType(Type clrType, [NotNullWhen(true)] out TypeMappin
NpgsqlDbType.Geometry => "geometry",
NpgsqlDbType.Geography => "geography",

// Built-in range types
NpgsqlDbType.IntegerRange => "int4range",
NpgsqlDbType.NumericRange => "numrange",
NpgsqlDbType.TimestampRange => "tsrange",
NpgsqlDbType.TimestampTzRange => "tstzrange",
NpgsqlDbType.DateRange => "daterange",
NpgsqlDbType.BigIntRange => "int8range",

// Internal types
NpgsqlDbType.Int2Vector => "int2vector",
NpgsqlDbType.Oidvector => "oidvector",
Expand Down
8 changes: 8 additions & 0 deletions src/Npgsql/TypeMapping/PostgresTypeOIDs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,13 @@ public static class PostgresTypeOIDs
public const uint Record = 2249;
public const uint Void = 2278;
public const uint Unknown = 705;

// Range types
public const uint Int4Range = 3904;
public const uint NumRange = 3906;
public const uint TsRange = 3908;
public const uint TsTzRange = 3910;
public const uint DateRange = 3912;
public const uint Int8Range = 3926;
}
}
Loading