Skip to content

Commit 3b1814b

Browse files
authored
Support for PG14 multirange (#3938)
Closes #3868
1 parent 7b31356 commit 3b1814b

30 files changed

Lines changed: 582 additions & 208 deletions

Npgsql.sln.DotSettings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
<s:Boolean x:Key="/Default/UserDictionary/Words/=IANA/@EntryIndexedValue">True</s:Boolean>
8888
<s:Boolean x:Key="/Default/UserDictionary/Words/=MSDTC/@EntryIndexedValue">True</s:Boolean>
8989
<s:Boolean x:Key="/Default/UserDictionary/Words/=multiquery/@EntryIndexedValue">True</s:Boolean>
90+
<s:Boolean x:Key="/Default/UserDictionary/Words/=multirange/@EntryIndexedValue">True</s:Boolean>
91+
<s:Boolean x:Key="/Default/UserDictionary/Words/=multiranges/@EntryIndexedValue">True</s:Boolean>
9092
<s:Boolean x:Key="/Default/UserDictionary/Words/=Noda/@EntryIndexedValue">True</s:Boolean>
9193
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOEXPORT/@EntryIndexedValue">True</s:Boolean>
9294
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql/@EntryIndexedValue">True</s:Boolean>
@@ -100,6 +102,7 @@
100102
<s:Boolean x:Key="/Default/UserDictionary/Words/=P_0020keepaliv/@EntryIndexedValue">True</s:Boolean>
101103
<s:Boolean x:Key="/Default/UserDictionary/Words/=regtype/@EntryIndexedValue">True</s:Boolean>
102104
<s:Boolean x:Key="/Default/UserDictionary/Words/=resultset/@EntryIndexedValue">True</s:Boolean>
105+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subrange/@EntryIndexedValue">True</s:Boolean>
103106
<s:Boolean x:Key="/Default/UserDictionary/Words/=subtransaction/@EntryIndexedValue">True</s:Boolean>
104107
<s:Boolean x:Key="/Default/UserDictionary/Words/=timestamptz/@EntryIndexedValue">True</s:Boolean>
105108
<s:Boolean x:Key="/Default/UserDictionary/Words/=timetz/@EntryIndexedValue">True</s:Boolean>

src/Npgsql/Internal/NpgsqlDatabaseInfo.cs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,42 @@ internal static readonly ConcurrentDictionary<NpgsqlDatabaseInfoCacheKey, Npgsql
6262
/// Whether the backend supports range types.
6363
/// </summary>
6464
public virtual bool SupportsRangeTypes => Version.IsGreaterOrEqual(9, 2);
65+
66+
/// <summary>
67+
/// Whether the backend supports multirange types.
68+
/// </summary>
69+
public virtual bool SupportsMultirangeTypes => Version.IsGreaterOrEqual(14, 0);
70+
6571
/// <summary>
6672
/// Whether the backend supports enum types.
6773
/// </summary>
6874
public virtual bool SupportsEnumTypes => Version.IsGreaterOrEqual(8, 3);
75+
6976
/// <summary>
7077
/// Whether the backend supports the CLOSE ALL statement.
7178
/// </summary>
7279
public virtual bool SupportsCloseAll => Version.IsGreaterOrEqual(8, 3);
80+
7381
/// <summary>
7482
/// Whether the backend supports advisory locks.
7583
/// </summary>
7684
public virtual bool SupportsAdvisoryLocks => Version.IsGreaterOrEqual(8, 2);
85+
7786
/// <summary>
7887
/// Whether the backend supports the DISCARD SEQUENCES statement.
7988
/// </summary>
8089
public virtual bool SupportsDiscardSequences => Version.IsGreaterOrEqual(9, 4);
90+
8191
/// <summary>
8292
/// Whether the backend supports the UNLISTEN statement.
8393
/// </summary>
8494
public virtual bool SupportsUnlisten => Version.IsGreaterOrEqual(6, 4); // overridden by PostgresDatabase
95+
8596
/// <summary>
8697
/// Whether the backend supports the DISCARD TEMP statement.
8798
/// </summary>
8899
public virtual bool SupportsDiscardTemp => Version.IsGreaterOrEqual(8, 3);
100+
89101
/// <summary>
90102
/// Whether the backend supports the DISCARD statement.
91103
/// </summary>
@@ -105,19 +117,21 @@ internal static readonly ConcurrentDictionary<NpgsqlDatabaseInfoCacheKey, Npgsql
105117

106118
#region Types
107119

108-
readonly List<PostgresBaseType> _baseTypesMutable = new();
109-
readonly List<PostgresArrayType> _arrayTypesMutable = new();
110-
readonly List<PostgresRangeType> _rangeTypesMutable = new();
111-
readonly List<PostgresEnumType> _enumTypesMutable = new();
112-
readonly List<PostgresCompositeType> _compositeTypesMutable = new();
113-
readonly List<PostgresDomainType> _domainTypesMutable = new();
114-
115-
internal IReadOnlyList<PostgresBaseType> BaseTypes => _baseTypesMutable;
116-
internal IReadOnlyList<PostgresArrayType> ArrayTypes => _arrayTypesMutable;
117-
internal IReadOnlyList<PostgresRangeType> RangeTypes => _rangeTypesMutable;
118-
internal IReadOnlyList<PostgresEnumType> EnumTypes => _enumTypesMutable;
119-
internal IReadOnlyList<PostgresCompositeType> CompositeTypes => _compositeTypesMutable;
120-
internal IReadOnlyList<PostgresDomainType> DomainTypes => _domainTypesMutable;
120+
readonly List<PostgresBaseType> _baseTypesMutable = new();
121+
readonly List<PostgresArrayType> _arrayTypesMutable = new();
122+
readonly List<PostgresRangeType> _rangeTypesMutable = new();
123+
readonly List<PostgresMultirangeType> _multirangeTypesMutable = new();
124+
readonly List<PostgresEnumType> _enumTypesMutable = new();
125+
readonly List<PostgresCompositeType> _compositeTypesMutable = new();
126+
readonly List<PostgresDomainType> _domainTypesMutable = new();
127+
128+
internal IReadOnlyList<PostgresBaseType> BaseTypes => _baseTypesMutable;
129+
internal IReadOnlyList<PostgresArrayType> ArrayTypes => _arrayTypesMutable;
130+
internal IReadOnlyList<PostgresRangeType> RangeTypes => _rangeTypesMutable;
131+
internal IReadOnlyList<PostgresMultirangeType> MultirangeTypes => _multirangeTypesMutable;
132+
internal IReadOnlyList<PostgresEnumType> EnumTypes => _enumTypesMutable;
133+
internal IReadOnlyList<PostgresCompositeType> CompositeTypes => _compositeTypesMutable;
134+
internal IReadOnlyList<PostgresDomainType> DomainTypes => _domainTypesMutable;
121135

122136
/// <summary>
123137
/// Indexes backend types by their type OID.
@@ -216,6 +230,9 @@ internal void ProcessTypes()
216230
case PostgresRangeType rangeType:
217231
_rangeTypesMutable.Add(rangeType);
218232
continue;
233+
case PostgresMultirangeType multirangeType:
234+
_multirangeTypesMutable.Add(multirangeType);
235+
continue;
219236
case PostgresEnumType enumType:
220237
_enumTypesMutable.Add(enumType);
221238
continue;

src/Npgsql/Internal/TypeHandlers/ArrayHandler.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@ protected ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler element
4646
internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescription = null) => typeof(Array);
4747

4848
/// <inheritdoc />
49-
public override ArrayHandler CreateArrayHandler(PostgresArrayType arrayBackendType, ArrayNullabilityMode arrayNullabilityMode)
49+
public override ArrayHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
5050
=> throw new NotSupportedException();
5151

5252
/// <inheritdoc />
53-
public override IRangeHandler CreateRangeHandler(PostgresType rangeBackendType)
53+
public override IRangeHandler CreateRangeHandler(PostgresType pgRangeType)
54+
=> throw new NotSupportedException();
55+
56+
/// <inheritdoc />
57+
public override IMultirangeHandler CreateMultirangeHandler(PostgresMultirangeType pgMultirangeType)
5458
=> throw new NotSupportedException();
5559

5660
#region Read
@@ -219,8 +223,6 @@ internal static class ArrayTypeInfo<TArrayOrList>
219223
public static readonly Func<ArrayHandler, NpgsqlReadBuffer, bool, ValueTask<TArrayOrList>> ReadListFunc = default!;
220224
// ReSharper restore StaticMemberInGenericType
221225

222-
public static bool IsArrayOrList => IsArray || IsList;
223-
224226
static ArrayTypeInfo()
225227
{
226228
var type = typeof(TArrayOrList);
@@ -289,7 +291,7 @@ public ArrayHandler(PostgresType arrayPostgresType, NpgsqlTypeHandler elementHan
289291
#region Read
290292

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

294296
#endregion
295297

@@ -317,8 +319,7 @@ public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCa
317319

318320
int ValidateAndGetLength(object value, ref NpgsqlLengthCache? lengthCache)
319321
{
320-
if (lengthCache == null)
321-
lengthCache = new NpgsqlLengthCache(1);
322+
lengthCache ??= new NpgsqlLengthCache(1);
322323
if (lengthCache.IsPopulated)
323324
return lengthCache.Get();
324325
if (value is ICollection<TElement> generic)
@@ -341,7 +342,7 @@ int ValidateAndGetLengthGeneric(ICollection<TElement> value, ref NpgsqlLengthCac
341342
4 * value.Count; // sum of element lengths
342343

343344
lengthCache.Set(0);
344-
NpgsqlLengthCache? elemLengthCache = lengthCache;
345+
var elemLengthCache = lengthCache;
345346

346347
foreach (var element in value)
347348
{

src/Npgsql/Internal/TypeHandlers/BitStringHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ internal override Type GetProviderSpecificFieldType(FieldDescription? fieldDescr
3535

3636
// BitString requires a special array handler which returns bool or BitArray
3737
/// <inheritdoc />
38-
public override ArrayHandler CreateArrayHandler(PostgresArrayType backendType, ArrayNullabilityMode arrayNullabilityMode)
39-
=> new BitStringArrayHandler(backendType, this, arrayNullabilityMode);
38+
public override ArrayHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
39+
=> new BitStringArrayHandler(pgArrayType, this, arrayNullabilityMode);
4040

4141
#region Read
4242

src/Npgsql/Internal/TypeHandlers/CompositeHandlers/CompositeHandler.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,7 @@ public override int ValidateAndGetLength(T value, ref NpgsqlLengthCache? lengthC
110110
{
111111
Initialize();
112112

113-
if (lengthCache == null)
114-
lengthCache = new NpgsqlLengthCache(1);
115-
113+
lengthCache ??= new NpgsqlLengthCache(1);
116114
if (lengthCache.IsPopulated)
117115
return lengthCache.Get();
118116

src/Npgsql/Internal/TypeHandlers/DateTimeHandlers/TimestampTzHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public TimestampTzHandler(PostgresType postgresType, bool convertInfinityDateTim
4646
: base(postgresType, convertInfinityDateTime) {}
4747

4848
/// <inheritdoc />
49-
public override IRangeHandler CreateRangeHandler(PostgresType rangeBackendType)
50-
=> new RangeHandler<DateTime, DateTimeOffset>(rangeBackendType, this);
49+
public override IRangeHandler CreateRangeHandler(PostgresType pgRangeType)
50+
=> new RangeHandler<DateTime, DateTimeOffset>(pgRangeType, this);
5151

5252
#region Read
5353

src/Npgsql/Internal/TypeHandlers/HstoreHandler.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ internal HstoreHandler(PostgresType postgresType, NpgsqlConnector connector)
6666
/// <inheritdoc />
6767
public int ValidateAndGetLength(IDictionary<string, string?> value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
6868
{
69-
if (lengthCache == null)
70-
lengthCache = new NpgsqlLengthCache(1);
69+
lengthCache ??= new NpgsqlLengthCache(1);
7170
if (lengthCache.IsPopulated)
7271
return lengthCache.Get();
7372

src/Npgsql/Internal/TypeHandlers/InternalTypeHandlers/Int2VectorHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Int2VectorHandler : ArrayHandler<short>
2525
public Int2VectorHandler(PostgresType arrayPostgresType, PostgresType postgresShortType)
2626
: base(arrayPostgresType, new Int16Handler { PostgresType = postgresShortType }, ArrayNullabilityMode.Never, 0) { }
2727

28-
public override ArrayHandler CreateArrayHandler(PostgresArrayType arrayBackendType, ArrayNullabilityMode arrayNullabilityMode)
29-
=> new ArrayHandler<ArrayHandler<short>>(arrayBackendType, this, arrayNullabilityMode);
28+
public override ArrayHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
29+
=> new ArrayHandler<ArrayHandler<short>>(pgArrayType, this, arrayNullabilityMode);
3030
}
3131
}

src/Npgsql/Internal/TypeHandlers/InternalTypeHandlers/OIDVectorHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class OIDVectorHandler : ArrayHandler<uint>
2525
public OIDVectorHandler(PostgresType oidvectorType, PostgresType oidType)
2626
: base(oidvectorType, new UInt32Handler { PostgresType = oidType }, ArrayNullabilityMode.Never, 0) { }
2727

28-
public override ArrayHandler CreateArrayHandler(PostgresArrayType arrayBackendType, ArrayNullabilityMode arrayNullabilityMode)
29-
=> new ArrayHandler<ArrayHandler<uint>>(arrayBackendType, this, arrayNullabilityMode);
28+
public override ArrayHandler CreateArrayHandler(PostgresArrayType pgArrayType, ArrayNullabilityMode arrayNullabilityMode)
29+
=> new ArrayHandler<ArrayHandler<uint>>(pgArrayType, this, arrayNullabilityMode);
3030
}
3131
}

src/Npgsql/Internal/TypeHandlers/JsonHandler.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ protected internal override int ValidateAndGetLengthCustom<TAny>([DisallowNull]
112112

113113
if (typeof(TAny) == typeof(JsonDocument))
114114
{
115-
if (lengthCache == null)
116-
lengthCache = new NpgsqlLengthCache(1);
115+
lengthCache ??= new NpgsqlLengthCache(1);
117116
if (lengthCache.IsPopulated)
118117
return lengthCache.Get();
119118

0 commit comments

Comments
 (0)