Skip to content

Commit 4117241

Browse files
committed
Move backend types to their own files/namespace
In preparation for exposing them publicly. Also renamed to PostgresType.
1 parent c6b2756 commit 4117241

60 files changed

Lines changed: 733 additions & 584 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Npgsql/FrontendMessages/ParseMessage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ internal ParseMessage Populate(NpgsqlStatement statement, TypeHandlerRegistry ty
7272
Statement = statement.PreparedStatementName ?? "";
7373
foreach (var inputParam in statement.InputParameters) {
7474
inputParam.ResolveHandler(typeHandlerRegistry);
75-
ParameterTypeOIDs.Add(inputParam.Handler.BackendType.OID);
75+
ParameterTypeOIDs.Add(inputParam.Handler.PostgresType.OID);
7676
}
7777
return this;
7878
}

src/Npgsql/GeneratedAsync.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Threading.Tasks;
2121
#pragma warning disable
2222
using System;
23+
using System.Collections.Concurrent;
2324
using System.ComponentModel;
2425
using System.Data;
2526
using System.Data.Common;
@@ -134,9 +135,9 @@
134135
using System.Collections.Concurrent;
135136
using System.Collections.Generic;
136137
using System.Diagnostics;
137-
using System.Linq;
138138
using System.Threading;
139139
using System.Threading.Tasks;
140+
using JetBrains.Annotations;
140141
using Npgsql.Logging;
141142
using System.Threading;
142143
using System.Threading.Tasks;
@@ -173,6 +174,7 @@
173174
using System.Reflection;
174175
using JetBrains.Annotations;
175176
using Npgsql.Logging;
177+
using Npgsql.PostgresTypes;
176178
using Npgsql.TypeHandlers;
177179
using NpgsqlTypes;
178180
using System.Threading;
@@ -1572,16 +1574,16 @@ internal static async Task SetupAsync(NpgsqlConnector connector, NpgsqlTimeout t
15721574
// Note that there's a chicken and egg problem here - LoadBackendTypes below needs a functional
15731575
// connector to load the types, hence the strange initialization code here
15741576
connector.TypeHandlerRegistry = new TypeHandlerRegistry(connector);
1575-
BackendTypes types;
1577+
AvailablePostgresTypes types;
15761578
if (!BackendTypeCache.TryGetValue(connector.ConnectionString, out types))
15771579
types = BackendTypeCache[connector.ConnectionString] = await (LoadBackendTypesAsync(connector, timeout, cancellationToken).ConfigureAwait(false));
1578-
connector.TypeHandlerRegistry._backendTypes = types;
1580+
connector.TypeHandlerRegistry._postgresTypes = types;
15791581
connector.TypeHandlerRegistry.ActivateGlobalMappings();
15801582
}
15811583

1582-
static async Task<BackendTypes> LoadBackendTypesAsync(NpgsqlConnector connector, NpgsqlTimeout timeout, CancellationToken cancellationToken)
1584+
static async Task<AvailablePostgresTypes> LoadBackendTypesAsync(NpgsqlConnector connector, NpgsqlTimeout timeout, CancellationToken cancellationToken)
15831585
{
1584-
var types = new BackendTypes();
1586+
var types = new AvailablePostgresTypes();
15851587
using (var command = new NpgsqlCommand(connector.SupportsRangeTypes ? TypesQueryWithRange : TypesQueryWithoutRange, connector.Connection))
15861588
{
15871589
command.CommandTimeout = timeout.IsSet ? (int)timeout.TimeLeft.TotalSeconds : 0;
@@ -2236,4 +2238,4 @@ public async Task<bool> HasBufferedReadDataAsync(bool checkNetworkStream, Cancel
22362238
return await (ReadInternalAsync(null, 0, 0, true, checkNetworkStream, cancellationToken).ConfigureAwait(false)) == 1;
22372239
}
22382240
}
2239-
}
2241+
}

src/Npgsql/Npgsql.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
<Compile Include="BackendMessages\ParseCompleteMessage.cs" />
7373
<Compile Include="BackendMessages\RowDescriptionMessage.cs" />
7474
<Compile Include="BackendMessages\DataRowSequentialMessage.cs" />
75+
<Compile Include="PostgresTypes\PostgresArrayType.cs" />
76+
<Compile Include="PostgresTypes\PostgresType.cs" />
7577
<Compile Include="FrontendMessages\CopyFailMessage.cs" />
7678
<Compile Include="FrontendMessages\SSLRequestMessage.cs" />
7779
<Compile Include="GlobalSuppressions.cs" />
@@ -115,6 +117,12 @@
115117
<Compile Include="NpgsqlTypes\NpgsqlTsVector.cs" />
116118
<Compile Include="NpgsqlTypes\NpgsqlUserTypes.cs" />
117119
<Compile Include="NpgsqlTypes\PostgisTypes.cs" />
120+
<Compile Include="PostgresTypes\PostgresBaseType.cs" />
121+
<Compile Include="PostgresTypes\PostgresCompositeType.cs" />
122+
<Compile Include="PostgresTypes\PostgresDomainType.cs" />
123+
<Compile Include="PostgresTypes\PostgresEnumType.cs" />
124+
<Compile Include="PostgresTypes\PostgresRangeType.cs" />
125+
<Compile Include="PostgresTypes\PostgresUnknownType.cs" />
118126
<Compile Include="ReadBuffer.cs" />
119127
<Compile Include="NameTranslation\NpgsqlSnakeCaseNameTranslator.cs" />
120128
<Compile Include="PoolManager.cs" />

src/Npgsql/NpgsqlCommandBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ private static void DoDeriveParameters(NpgsqlCommand command)
216216
var param = new NpgsqlParameter();
217217

218218
// TODO: Fix enums, composite types
219-
var npgsqlDbType = c.Connection.Connector.TypeHandlerRegistry[types[i]].BackendType.NpgsqlDbType;
219+
var npgsqlDbType = c.Connection.Connector.TypeHandlerRegistry[types[i]].PostgresType.NpgsqlDbType;
220220
if (!npgsqlDbType.HasValue)
221221
throw new InvalidOperationException($"Invalid parameter type: {types[i]}");
222222
param.NpgsqlDbType = npgsqlDbType.Value;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Npgsql.TypeHandlers;
7+
8+
namespace Npgsql.PostgresTypes
9+
{
10+
class PostgresArrayType : PostgresType
11+
{
12+
readonly PostgresType _element;
13+
14+
internal PostgresArrayType(string ns, string name, uint oid, PostgresType elementPostgresType)
15+
: base(ns, name, oid)
16+
{
17+
_element = elementPostgresType;
18+
if (elementPostgresType.NpgsqlDbType.HasValue)
19+
NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Array | elementPostgresType.NpgsqlDbType;
20+
_element.Array = this;
21+
}
22+
23+
internal override TypeHandler Activate(TypeHandlerRegistry registry)
24+
{
25+
TypeHandler elementHandler;
26+
if (!registry.TryGetByOID(_element.OID, out elementHandler))
27+
{
28+
// Element type hasn't been set up yet, do it now
29+
elementHandler = _element.Activate(registry);
30+
}
31+
32+
var arrayHandler = elementHandler.CreateArrayHandler(this);
33+
registry.ByOID[OID] = arrayHandler;
34+
35+
var asEnumHandler = elementHandler as IEnumHandler;
36+
if (asEnumHandler != null)
37+
{
38+
if (registry.ArrayHandlerByType == null)
39+
registry.ArrayHandlerByType = new Dictionary<Type, TypeHandler>();
40+
registry.ArrayHandlerByType[asEnumHandler.EnumType] = arrayHandler;
41+
return arrayHandler;
42+
}
43+
44+
var asCompositeHandler = elementHandler as ICompositeHandler;
45+
if (asCompositeHandler != null)
46+
{
47+
if (registry.ArrayHandlerByType == null)
48+
registry.ArrayHandlerByType = new Dictionary<Type, TypeHandler>();
49+
registry.ArrayHandlerByType[asCompositeHandler.CompositeType] = arrayHandler;
50+
return arrayHandler;
51+
}
52+
53+
if (NpgsqlDbType.HasValue)
54+
registry.ByNpgsqlDbType[NpgsqlDbType.Value] = arrayHandler;
55+
56+
// Note that array handlers aren't registered in _byType, because they handle all dimension types and not just one CLR type
57+
// (e.g. int[], int[,], int[,,]). So the by-type lookup is special, see this[Type type]
58+
// TODO: register single-dimensional in _byType as a specific optimization? But do PSV as well...
59+
60+
return arrayHandler;
61+
}
62+
}
63+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Diagnostics;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using JetBrains.Annotations;
10+
11+
namespace Npgsql.PostgresTypes
12+
{
13+
class PostgresBaseType : PostgresType
14+
{
15+
[CanBeNull]
16+
readonly DbType[] _dbTypes;
17+
[CanBeNull]
18+
readonly Type[] _clrTypes;
19+
20+
[CanBeNull]
21+
ConstructorInfo _ctorWithRegistry;
22+
[CanBeNull]
23+
ConstructorInfo _ctorWithoutRegistry;
24+
25+
/// <summary>
26+
/// Constructs an unsupported base type (no handler exists in Npgsql for this type)
27+
/// </summary>
28+
internal PostgresBaseType(string ns, string name, uint oid) : base(ns, name, oid)
29+
{
30+
_dbTypes = new DbType[0];
31+
_clrTypes = new Type[0];
32+
}
33+
34+
internal PostgresBaseType(string ns, string name, uint oid, Type handlerType, TypeMappingAttribute mapping)
35+
: base(ns, name, oid)
36+
{
37+
HandlerType = handlerType;
38+
NpgsqlDbType = mapping.NpgsqlDbType;
39+
_dbTypes = mapping.DbTypes;
40+
_clrTypes = mapping.ClrTypes;
41+
}
42+
43+
internal override void AddTo(TypeHandlerRegistry.AvailablePostgresTypes types)
44+
{
45+
base.AddTo(types);
46+
47+
if (_dbTypes != null)
48+
foreach (var dbType in _dbTypes)
49+
types.ByDbType[dbType] = this;
50+
if (_clrTypes != null)
51+
foreach (var type in _clrTypes)
52+
types.ByClrType[type] = this;
53+
}
54+
55+
internal override TypeHandler Activate(TypeHandlerRegistry registry)
56+
{
57+
if (HandlerType == null)
58+
{
59+
registry.ByOID[OID] = registry.UnrecognizedTypeHandler;
60+
return registry.UnrecognizedTypeHandler;
61+
}
62+
63+
var handler = InstantiateHandler(registry);
64+
65+
registry.ByOID[OID] = handler;
66+
67+
if (NpgsqlDbType.HasValue)
68+
{
69+
var value = NpgsqlDbType.Value;
70+
if (registry.ByNpgsqlDbType.ContainsKey(value))
71+
throw new Exception($"Two type handlers registered on same NpgsqlDbType {NpgsqlDbType}: {registry.ByNpgsqlDbType[value].GetType().Name} and {HandlerType.Name}");
72+
registry.ByNpgsqlDbType[NpgsqlDbType.Value] = handler;
73+
}
74+
75+
if (_dbTypes != null)
76+
{
77+
foreach (var dbType in _dbTypes)
78+
{
79+
if (registry.ByDbType.ContainsKey(dbType))
80+
throw new Exception($"Two type handlers registered on same DbType {dbType}: {registry.ByDbType[dbType].GetType().Name} and {HandlerType.Name}");
81+
registry.ByDbType[dbType] = handler;
82+
}
83+
}
84+
85+
if (_clrTypes != null)
86+
{
87+
foreach (var type in _clrTypes)
88+
{
89+
if (registry.ByType.ContainsKey(type))
90+
throw new Exception($"Two type handlers registered on same .NET type {type}: {registry.ByType[type].GetType().Name} and {HandlerType.Name}");
91+
registry.ByType[type] = handler;
92+
}
93+
}
94+
95+
return handler;
96+
}
97+
98+
/// <summary>
99+
/// Instantiate the type handler. If it has a constructor that accepts a TypeHandlerRegistry, use that to allow
100+
/// the handler to make connector-specific adjustments. Otherwise (the normal case), use the default constructor.
101+
/// </summary>
102+
/// <param name="registry"></param>
103+
/// <returns></returns>
104+
TypeHandler InstantiateHandler(TypeHandlerRegistry registry)
105+
{
106+
Debug.Assert(HandlerType != null);
107+
108+
if (_ctorWithRegistry == null && _ctorWithoutRegistry == null)
109+
{
110+
var ctors = HandlerType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
111+
_ctorWithRegistry = (
112+
from c in ctors
113+
let p = c.GetParameters()
114+
where p.Length == 2 && p[0].ParameterType == typeof(PostgresType) && p[1].ParameterType == typeof(TypeHandlerRegistry)
115+
select c
116+
).FirstOrDefault();
117+
118+
if (_ctorWithRegistry == null)
119+
{
120+
_ctorWithoutRegistry = (
121+
from c in ctors
122+
let p = c.GetParameters()
123+
where p.Length == 1 && p[0].ParameterType == typeof(PostgresType)
124+
select c
125+
).FirstOrDefault();
126+
if (_ctorWithoutRegistry == null)
127+
throw new Exception($"Type handler type {HandlerType.Name} does not have an appropriate constructor");
128+
}
129+
}
130+
131+
if (_ctorWithRegistry != null)
132+
return (TypeHandler)_ctorWithRegistry.Invoke(new object[] { this, registry });
133+
Debug.Assert(_ctorWithoutRegistry != null);
134+
return (TypeHandler)_ctorWithoutRegistry.Invoke(new object[] { this });
135+
}
136+
}
137+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Npgsql.TypeHandlers;
7+
8+
namespace Npgsql.PostgresTypes
9+
{
10+
class PostgresCompositeType : PostgresType
11+
{
12+
/// <summary>
13+
/// Holds the name and OID for all fields.
14+
/// Populated on the first activation of the composite.
15+
/// </summary>
16+
internal List<RawCompositeField> RawFields { get; }
17+
18+
internal PostgresCompositeType(string ns, string name, uint oid, List<RawCompositeField> rawFields)
19+
: base(ns, name, oid)
20+
{
21+
NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Composite;
22+
RawFields = rawFields;
23+
}
24+
25+
internal override void AddTo(TypeHandlerRegistry.AvailablePostgresTypes types)
26+
{
27+
base.AddTo(types);
28+
types.ByFullName[FullName] = this;
29+
types.ByName[Name] = types.ByName.ContainsKey(Name)
30+
? null
31+
: this;
32+
}
33+
34+
internal override TypeHandler Activate(TypeHandlerRegistry registry)
35+
{
36+
// Composites need to be mapped by the user with an explicit mapping call (MapComposite or MapCompositeGlobally).
37+
// If we're here the enum hasn't been mapped to a CLR type and we should activate it as text.
38+
throw new Exception($"Composite PostgreSQL type {Name} must be mapped before use");
39+
}
40+
41+
internal void Activate(TypeHandlerRegistry registry, ICompositeHandlerFactory factory)
42+
=> Activate(registry, factory.Create(this, RawFields, registry));
43+
44+
internal void Activate(TypeHandlerRegistry registry, ICompositeHandler compositeHandler)
45+
{
46+
var handler = (TypeHandler)compositeHandler;
47+
48+
registry.ByOID[OID] = handler;
49+
registry.ByType[compositeHandler.CompositeType] = handler;
50+
Array?.Activate(registry);
51+
}
52+
}
53+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Npgsql.PostgresTypes
8+
{
9+
/// <summary>
10+
/// Represents a PostgreSQL domain type.
11+
/// </summary>
12+
/// <remarks>
13+
/// When PostgreSQL returns a RowDescription for a domain type, the type OID is the base type's
14+
/// (so fetching a domain type over text returns a RowDescription for text).
15+
/// However, when a composite type is returned, the type OID there is that of the domain,
16+
/// so we provide "clean" support for domain types.
17+
/// </remarks>
18+
class PostgresDomainType : PostgresType
19+
{
20+
readonly PostgresType _basePostgresType;
21+
22+
public PostgresDomainType(string ns, string name, uint oid, PostgresType basePostgresType)
23+
: base(ns, name, oid)
24+
{
25+
_basePostgresType = basePostgresType;
26+
}
27+
28+
internal override TypeHandler Activate(TypeHandlerRegistry registry)
29+
{
30+
TypeHandler baseTypeHandler;
31+
if (!registry.TryGetByOID(_basePostgresType.OID, out baseTypeHandler))
32+
{
33+
// Base type hasn't been set up yet, do it now
34+
baseTypeHandler = _basePostgresType.Activate(registry);
35+
}
36+
37+
// Make the domain type OID point to the base type's type handler, the wire encoding
38+
// is the same
39+
registry.ByOID[OID] = baseTypeHandler;
40+
41+
return baseTypeHandler;
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)