Skip to content

Commit 2f07854

Browse files
authored
Type handler redo (#3956)
Closes #3953 Closes #3962
1 parent c8b9c26 commit 2f07854

File tree

140 files changed

+2387
-4287
lines changed

Some content is hidden

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

140 files changed

+2387
-4287
lines changed

Npgsql.sln

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.NodaTime", "src\Npgs
1919
EndProject
2020
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.PluginTests", "test\Npgsql.PluginTests\Npgsql.PluginTests.csproj", "{9BD7FC3D-6956-42A8-A586-2558C499EBA2}"
2121
EndProject
22-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.LegacyPostgis", "src\Npgsql.LegacyPostgis\Npgsql.LegacyPostgis.csproj", "{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}"
23-
EndProject
2422
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.NetTopologySuite", "src\Npgsql.NetTopologySuite\Npgsql.NetTopologySuite.csproj", "{6CB12050-DC9B-4155-BADD-BFDD54CDD70F}"
2523
EndProject
2624
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.GeoJSON", "src\Npgsql.GeoJSON\Npgsql.GeoJSON.csproj", "{F7C53EBD-0075-474F-A083-419257D04080}"
@@ -95,14 +93,6 @@ Global
9593
{9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|Any CPU.Build.0 = Release|Any CPU
9694
{9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|x86.ActiveCfg = Release|Any CPU
9795
{9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|x86.Build.0 = Release|Any CPU
98-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
99-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU
100-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Debug|x86.ActiveCfg = Debug|Any CPU
101-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Debug|x86.Build.0 = Debug|Any CPU
102-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU
103-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Release|Any CPU.Build.0 = Release|Any CPU
104-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Release|x86.ActiveCfg = Release|Any CPU
105-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE}.Release|x86.Build.0 = Release|Any CPU
10696
{6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
10797
{6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
10898
{6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -154,7 +144,6 @@ Global
154144
{9CBE603F-6746-411D-A5FD-CB2C948CD7D0} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
155145
{D8DF12D6-FA70-4653-BD8F-C188944836DE} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
156146
{9BD7FC3D-6956-42A8-A586-2558C499EBA2} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
157-
{D96CC113-7D64-4B31-9DCC-13FDE92C1ECE} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
158147
{6CB12050-DC9B-4155-BADD-BFDD54CDD70F} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
159148
{F7C53EBD-0075-474F-A083-419257D04080} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
160149
{A77E5FAF-D775-4AB4-8846-8965C2104E60} = {ED612DB1-AB32-4603-95E7-891BACA71C39}

Npgsql.sln.DotSettings

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,15 @@
8282
<s:Boolean x:Key="/Default/UserDictionary/Words/=autoprepare/@EntryIndexedValue">True</s:Boolean>
8383
<s:Boolean x:Key="/Default/UserDictionary/Words/=autoprepared/@EntryIndexedValue">True</s:Boolean>
8484
<s:Boolean x:Key="/Default/UserDictionary/Words/=bytea/@EntryIndexedValue">True</s:Boolean>
85+
<s:Boolean x:Key="/Default/UserDictionary/Words/=citext/@EntryIndexedValue">True</s:Boolean>
8586
<s:Boolean x:Key="/Default/UserDictionary/Words/=Conformant/@EntryIndexedValue">True</s:Boolean>
8687
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDEX/@EntryIndexedValue">True</s:Boolean>
8788
<s:Boolean x:Key="/Default/UserDictionary/Words/=IANA/@EntryIndexedValue">True</s:Boolean>
89+
<s:Boolean x:Key="/Default/UserDictionary/Words/=lquery/@EntryIndexedValue">True</s:Boolean>
90+
<s:Boolean x:Key="/Default/UserDictionary/Words/=lseg/@EntryIndexedValue">True</s:Boolean>
91+
<s:Boolean x:Key="/Default/UserDictionary/Words/=ltree/@EntryIndexedValue">True</s:Boolean>
92+
<s:Boolean x:Key="/Default/UserDictionary/Words/=ltxtquery/@EntryIndexedValue">True</s:Boolean>
93+
<s:Boolean x:Key="/Default/UserDictionary/Words/=macaddr/@EntryIndexedValue">True</s:Boolean>
8894
<s:Boolean x:Key="/Default/UserDictionary/Words/=MSDTC/@EntryIndexedValue">True</s:Boolean>
8995
<s:Boolean x:Key="/Default/UserDictionary/Words/=multiquery/@EntryIndexedValue">True</s:Boolean>
9096
<s:Boolean x:Key="/Default/UserDictionary/Words/=multirange/@EntryIndexedValue">True</s:Boolean>
@@ -93,22 +99,29 @@
9399
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOEXPORT/@EntryIndexedValue">True</s:Boolean>
94100
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql/@EntryIndexedValue">True</s:Boolean>
95101
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql_0027s/@EntryIndexedValue">True</s:Boolean>
102+
<s:Boolean x:Key="/Default/UserDictionary/Words/=oidvector/@EntryIndexedValue">True</s:Boolean>
96103
<s:Boolean x:Key="/Default/UserDictionary/Words/=pgoutput/@EntryIndexedValue">True</s:Boolean>
97104
<s:Boolean x:Key="/Default/UserDictionary/Words/=pgpass/@EntryIndexedValue">True</s:Boolean>
98105
<s:Boolean x:Key="/Default/UserDictionary/Words/=PGTZ/@EntryIndexedValue">True</s:Boolean>
99106
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgis/@EntryIndexedValue">True</s:Boolean>
100107
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean>
101108
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pregenerated/@EntryIndexedValue">True</s:Boolean>
102109
<s:Boolean x:Key="/Default/UserDictionary/Words/=P_0020keepaliv/@EntryIndexedValue">True</s:Boolean>
110+
<s:Boolean x:Key="/Default/UserDictionary/Words/=refcursor/@EntryIndexedValue">True</s:Boolean>
111+
<s:Boolean x:Key="/Default/UserDictionary/Words/=regconfig/@EntryIndexedValue">True</s:Boolean>
103112
<s:Boolean x:Key="/Default/UserDictionary/Words/=regtype/@EntryIndexedValue">True</s:Boolean>
104113
<s:Boolean x:Key="/Default/UserDictionary/Words/=resultset/@EntryIndexedValue">True</s:Boolean>
105114
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subrange/@EntryIndexedValue">True</s:Boolean>
106115
<s:Boolean x:Key="/Default/UserDictionary/Words/=subtransaction/@EntryIndexedValue">True</s:Boolean>
107116
<s:Boolean x:Key="/Default/UserDictionary/Words/=timestamptz/@EntryIndexedValue">True</s:Boolean>
108117
<s:Boolean x:Key="/Default/UserDictionary/Words/=timetz/@EntryIndexedValue">True</s:Boolean>
118+
<s:Boolean x:Key="/Default/UserDictionary/Words/=tsquery/@EntryIndexedValue">True</s:Boolean>
119+
<s:Boolean x:Key="/Default/UserDictionary/Words/=tsvector/@EntryIndexedValue">True</s:Boolean>
120+
<s:Boolean x:Key="/Default/UserDictionary/Words/=typname/@EntryIndexedValue">True</s:Boolean>
109121
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNLISTEN/@EntryIndexedValue">True</s:Boolean>
110122
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unmap/@EntryIndexedValue">True</s:Boolean>
111123
<s:Boolean x:Key="/Default/UserDictionary/Words/=unpooled/@EntryIndexedValue">True</s:Boolean>
112124
<s:Boolean x:Key="/Default/UserDictionary/Words/=unprepare/@EntryIndexedValue">True</s:Boolean>
113125
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unprepares/@EntryIndexedValue">True</s:Boolean>
126+
<s:Boolean x:Key="/Default/UserDictionary/Words/=varbit/@EntryIndexedValue">True</s:Boolean>
114127
<s:Boolean x:Key="/Default/UserDictionary/Words/=_0020Unprepare/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

src/Npgsql.GeoJSON/Internal/GeoJSONHandler.cs

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,6 @@
1313

1414
namespace Npgsql.GeoJSON.Internal
1515
{
16-
public sealed class GeoJSONHandlerFactory : NpgsqlTypeHandlerFactory<GeoJSONObject>
17-
{
18-
readonly GeoJSONOptions _options;
19-
20-
public GeoJSONHandlerFactory(GeoJSONOptions options = GeoJSONOptions.None)
21-
=> _options = options;
22-
23-
static readonly ConcurrentDictionary<string, CrsMap> s_crsMaps = new();
24-
25-
public override NpgsqlTypeHandler<GeoJSONObject> Create(PostgresType postgresType, NpgsqlConnector conn)
26-
{
27-
var crsMap = (_options & (GeoJSONOptions.ShortCRS | GeoJSONOptions.LongCRS)) == GeoJSONOptions.None
28-
? default : s_crsMaps.GetOrAdd(conn.Settings.ConnectionString, _ =>
29-
{
30-
var builder = new CrsMapBuilder();
31-
using (var cmd = conn.CreateCommand(
32-
"SELECT min(srid), max(srid), auth_name " +
33-
"FROM(SELECT srid, auth_name, srid - rank() OVER(ORDER BY srid) AS range " +
34-
"FROM spatial_ref_sys) AS s GROUP BY range, auth_name ORDER BY 1;"))
35-
using (var reader = cmd.ExecuteReader())
36-
while (reader.Read())
37-
{
38-
builder.Add(new CrsMapEntry(
39-
reader.GetInt32(0),
40-
reader.GetInt32(1),
41-
reader.GetString(2)));
42-
}
43-
return builder.Build();
44-
});
45-
return new GeoJsonHandler(postgresType, _options, crsMap);
46-
}
47-
}
48-
4916
sealed partial class GeoJsonHandler : NpgsqlTypeHandler<GeoJSONObject>,
5017
INpgsqlTypeHandler<Point>, INpgsqlTypeHandler<MultiPoint>,
5118
INpgsqlTypeHandler<Polygon>, INpgsqlTypeHandler<MultiPolygon>,
@@ -60,8 +27,8 @@ sealed partial class GeoJsonHandler : NpgsqlTypeHandler<GeoJSONObject>,
6027
int _lastSrid;
6128

6229
internal GeoJsonHandler(PostgresType postgresType, GeoJSONOptions options, CrsMap crsMap)
30+
: base(postgresType)
6331
{
64-
PostgresType = postgresType;
6532
_options = options;
6633
_crsMap = crsMap;
6734
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Data;
5+
using GeoJSON.Net;
6+
using GeoJSON.Net.Geometry;
7+
using Newtonsoft.Json;
8+
using Npgsql.Internal;
9+
using Npgsql.Internal.TypeHandling;
10+
using Npgsql.PostgresTypes;
11+
using Npgsql.TypeMapping;
12+
using NpgsqlTypes;
13+
14+
namespace Npgsql.GeoJSON.Internal
15+
{
16+
public class GeoJSONTypeHandlerResolver : ITypeHandlerResolver
17+
{
18+
readonly NpgsqlDatabaseInfo _databaseInfo;
19+
readonly GeoJsonHandler _geometryHandler, _geographyHandler;
20+
readonly bool _geographyAsDefault;
21+
22+
static readonly ConcurrentDictionary<string, CrsMap> CRSMaps = new();
23+
24+
internal GeoJSONTypeHandlerResolver(NpgsqlConnector connector, GeoJSONOptions options, bool geographyAsDefault)
25+
{
26+
_databaseInfo = connector.DatabaseInfo;
27+
_geographyAsDefault = geographyAsDefault;
28+
29+
var crsMap = (options & (GeoJSONOptions.ShortCRS | GeoJSONOptions.LongCRS)) == GeoJSONOptions.None
30+
? default : CRSMaps.GetOrAdd(connector.Settings.ConnectionString, _ =>
31+
{
32+
var builder = new CrsMapBuilder();
33+
using (var cmd = connector.CreateCommand(
34+
"SELECT min(srid), max(srid), auth_name " +
35+
"FROM(SELECT srid, auth_name, srid - rank() OVER(ORDER BY srid) AS range " +
36+
"FROM spatial_ref_sys) AS s GROUP BY range, auth_name ORDER BY 1;"))
37+
using (var reader = cmd.ExecuteReader())
38+
while (reader.Read())
39+
{
40+
builder.Add(new CrsMapEntry(
41+
reader.GetInt32(0),
42+
reader.GetInt32(1),
43+
reader.GetString(2)));
44+
}
45+
return builder.Build();
46+
});
47+
48+
var (pgGeometryType, pgGeographyType) = (PgType("geometry"), PgType("geography"));
49+
50+
_geometryHandler = new GeoJsonHandler(pgGeometryType, options, crsMap);
51+
_geographyHandler = new GeoJsonHandler(pgGeographyType, options, crsMap);
52+
}
53+
54+
public NpgsqlTypeHandler? ResolveByDataTypeName(string typeName)
55+
=> typeName switch
56+
{
57+
"geometry" => _geometryHandler,
58+
"geography" => _geographyHandler,
59+
_ => null
60+
};
61+
62+
public NpgsqlTypeHandler? ResolveByClrType(Type type)
63+
=> ClrTypeToDataTypeName(type, _geographyAsDefault) is { } dataTypeName && ResolveByDataTypeName(dataTypeName) is { } handler
64+
? handler
65+
: null;
66+
67+
internal static string? ClrTypeToDataTypeName(Type type, bool geographyAsDefault)
68+
=> type.BaseType != typeof(GeoJSONObject)
69+
? null
70+
: geographyAsDefault
71+
? "geography"
72+
: "geometry";
73+
74+
public TypeMappingInfo? GetMappingByDataTypeName(string dataTypeName)
75+
=> DoGetMappingByDataTypeName(dataTypeName);
76+
77+
internal static TypeMappingInfo? DoGetMappingByDataTypeName(string dataTypeName)
78+
=> dataTypeName switch
79+
{
80+
"geometry" => new(NpgsqlDbType.Geometry, DbType.Object, "geometry"),
81+
"geography" => new(NpgsqlDbType.Geography, DbType.Object, "geography"),
82+
_ => null
83+
};
84+
85+
PostgresType PgType(string pgTypeName) => _databaseInfo.GetPostgresTypeByName(pgTypeName);
86+
}
87+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using Npgsql.Internal;
3+
using Npgsql.Internal.TypeHandling;
4+
using Npgsql.TypeMapping;
5+
6+
namespace Npgsql.GeoJSON.Internal
7+
{
8+
public class GeoJSONTypeHandlerResolverFactory : ITypeHandlerResolverFactory
9+
{
10+
readonly GeoJSONOptions _options;
11+
readonly bool _geographyAsDefault;
12+
13+
public GeoJSONTypeHandlerResolverFactory(GeoJSONOptions options, bool geographyAsDefault)
14+
=> (_options, _geographyAsDefault) = (options, geographyAsDefault);
15+
16+
public ITypeHandlerResolver Create(NpgsqlConnector connector)
17+
=> new GeoJSONTypeHandlerResolver(connector, _options, _geographyAsDefault);
18+
19+
public string? GetDataTypeNameByClrType(Type type)
20+
=> GeoJSONTypeHandlerResolver.ClrTypeToDataTypeName(type, _geographyAsDefault);
21+
22+
public TypeMappingInfo? GetMappingByDataTypeName(string dataTypeName)
23+
=> GeoJSONTypeHandlerResolver.DoGetMappingByDataTypeName(dataTypeName);
24+
}
25+
}

src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
</ItemGroup>
1414
<ItemGroup>
1515
<ProjectReference Include="../Npgsql/Npgsql.csproj" />
16-
<ProjectReference Include="../Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj"
17-
OutputItemType="Analyzer"
18-
ReferenceOutputAssembly="false" />
16+
<ProjectReference Include="../Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
1917
</ItemGroup>
2018
</Project>
Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
using System;
2-
using System.Data;
3-
using GeoJSON.Net;
4-
using GeoJSON.Net.Geometry;
5-
using Npgsql.GeoJSON.Internal;
1+
using Npgsql.GeoJSON.Internal;
62
using Npgsql.TypeMapping;
7-
using NpgsqlTypes;
8-
9-
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
103

114
// ReSharper disable once CheckNamespace
125
namespace Npgsql
@@ -16,14 +9,6 @@ namespace Npgsql
169
/// </summary>
1710
public static class NpgsqlGeoJSONExtensions
1811
{
19-
static readonly Type[] ClrTypes =
20-
{
21-
typeof(GeoJSONObject), typeof(IGeoJSONObject), typeof(IGeometryObject),
22-
typeof(Point), typeof(LineString), typeof(Polygon),
23-
typeof(MultiPoint), typeof(MultiLineString), typeof(MultiPolygon),
24-
typeof(GeometryCollection)
25-
};
26-
2712
/// <summary>
2813
/// Sets up GeoJSON mappings for the PostGIS types.
2914
/// </summary>
@@ -32,24 +17,8 @@ public static class NpgsqlGeoJSONExtensions
3217
/// <param name="geographyAsDefault">Specifies that the geography type is used for mapping by default.</param>
3318
public static INpgsqlTypeMapper UseGeoJson(this INpgsqlTypeMapper mapper, GeoJSONOptions options = GeoJSONOptions.None, bool geographyAsDefault = false)
3419
{
35-
var factory = new GeoJSONHandlerFactory(options);
36-
return mapper
37-
.AddMapping(new NpgsqlTypeMappingBuilder
38-
{
39-
PgTypeName = "geometry",
40-
NpgsqlDbType = NpgsqlDbType.Geometry,
41-
ClrTypes = geographyAsDefault ? Type.EmptyTypes : ClrTypes,
42-
InferredDbType = DbType.Object,
43-
TypeHandlerFactory = factory
44-
}.Build())
45-
.AddMapping(new NpgsqlTypeMappingBuilder
46-
{
47-
PgTypeName = "geography",
48-
NpgsqlDbType = NpgsqlDbType.Geography,
49-
ClrTypes = geographyAsDefault ? ClrTypes : Type.EmptyTypes,
50-
InferredDbType = DbType.Object,
51-
TypeHandlerFactory = factory
52-
}.Build());
20+
mapper.AddTypeResolverFactory(new GeoJSONTypeHandlerResolverFactory(options, geographyAsDefault));
21+
return mapper;
5322
}
5423
}
5524
}

src/Npgsql.Json.NET/Internal/JsonHandler.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,16 @@
55
using Newtonsoft.Json;
66
using Npgsql.BackendMessages;
77
using Npgsql.Internal;
8-
using Npgsql.Internal.TypeHandling;
98
using Npgsql.PostgresTypes;
109

1110
namespace Npgsql.Json.NET.Internal
1211
{
13-
public class JsonHandlerFactory : NpgsqlTypeHandlerFactory<string>
14-
{
15-
readonly JsonSerializerSettings _settings;
16-
17-
public JsonHandlerFactory(JsonSerializerSettings? settings = null)
18-
=> _settings = settings ?? new JsonSerializerSettings();
19-
20-
public override NpgsqlTypeHandler<string> Create(PostgresType postgresType, NpgsqlConnector conn)
21-
=> new JsonHandler(postgresType, conn, _settings);
22-
}
23-
2412
class JsonHandler : Npgsql.Internal.TypeHandlers.JsonHandler
2513
{
2614
readonly JsonSerializerSettings _settings;
2715

2816
public JsonHandler(PostgresType postgresType, NpgsqlConnector connector, JsonSerializerSettings settings)
29-
: base(postgresType, connector, isJsonb: false) => _settings = settings;
17+
: base(postgresType, connector.TextEncoding, isJsonb: false) => _settings = settings;
3018

3119
protected override async ValueTask<T> ReadCustom<T>(NpgsqlReadBuffer buf, int len, bool async, FieldDescription? fieldDescription = null)
3220
{

0 commit comments

Comments
 (0)