Skip to content

Commit aeb44f9

Browse files
authored
Cleanup fully qualified name lookup code (#5311)
1 parent c047eca commit aeb44f9

1 file changed

Lines changed: 61 additions & 34 deletions

File tree

src/Npgsql/Internal/TypeInfoMapping.cs

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -129,28 +129,39 @@ public TypeInfoMappingCollection(IEnumerable<TypeInfoMapping> items)
129129
var typeMatch = type is not null && looseTypeMatch;
130130
var dataTypeMatch = dataTypeName is not null && mapping.DataTypeNameEquals(dataTypeName.Value.Value);
131131

132-
switch (mapping.MatchRequirement)
132+
var matchRequirement = mapping.MatchRequirement;
133+
if (dataTypeMatch && typeMatch
134+
|| matchRequirement is not MatchRequirement.All && dataTypeMatch && looseTypeMatch
135+
|| matchRequirement is MatchRequirement.Single && dataTypeName is null && typeMatch)
133136
{
134-
case var _ when dataTypeMatch && typeMatch:
135-
case not MatchRequirement.All when dataTypeMatch && looseTypeMatch:
136-
case MatchRequirement.Single when dataTypeName is null && looseTypeMatch:
137-
var resolvedMapping = mapping with
138-
{
139-
Type = type ?? mapping.Type,
140-
// Make sure plugins (which match on unqualified names) and resolvers get the fully qualified name to canonicalize.
141-
DataTypeName = dataTypeName is not null ? dataTypeName.GetValueOrDefault().Value : mapping.DataTypeName
142-
};
143-
return resolvedMapping.Factory(options, resolvedMapping, dataTypeName is not null);
144-
// DataTypeName is explicitly requiring dataTypeName so it won't be used for a fallback, Single would have matched above already.
145-
case MatchRequirement.All when fallback is null && dataTypeName is null && typeMatch:
146-
fallback = mapping.TypeMatchPredicate is not null ? mapping with { Type = type! } : mapping;
147-
break;
148-
default:
149-
continue;
137+
var resolvedDataTypeName = ResolveFullyQualifiedDataTypeName(dataTypeName, mapping.DataTypeName, options);
138+
return mapping.Factory(options, mapping with { Type = type ?? mapping.Type, DataTypeName = resolvedDataTypeName }, dataTypeName is not null);
150139
}
140+
141+
// DataTypeName is explicitly requiring dataTypeName so it won't be used for a fallback, Single would have matched above already.
142+
if (matchRequirement is MatchRequirement.All && fallback is null && dataTypeName is null && typeMatch)
143+
fallback = mapping;
144+
}
145+
146+
if (fallback is { } fbMapping)
147+
{
148+
var resolvedDataTypeName = ResolveFullyQualifiedDataTypeName(dataTypeName, fbMapping.DataTypeName, options);
149+
return fbMapping.Factory(options, fbMapping with { Type = type!, DataTypeName = resolvedDataTypeName }, dataTypeName is not null);
151150
}
152151

153-
return fallback?.Factory(options, fallback.Value, dataTypeName is not null);
152+
return null;
153+
154+
static string ResolveFullyQualifiedDataTypeName(DataTypeName? dataTypeName, string mappingDataTypeName, PgSerializerOptions options)
155+
{
156+
// Make sure plugins (which match on unqualified names) and converter resolvers get the fully qualified name to canonicalize.
157+
if (dataTypeName is not null)
158+
return dataTypeName.GetValueOrDefault().Value;
159+
160+
if (TypeInfoMappingHelpers.TryResolveFullyQualifiedName(options, mappingDataTypeName, out var fqDataTypeName))
161+
return fqDataTypeName.Value;
162+
163+
throw new NotSupportedException($"Cannot resolve '{mappingDataTypeName}' to a fully qualified datatype name. The datatype was not found in the current database info.");
164+
}
154165
}
155166

156167
bool TryGetMapping(Type type, string dataTypeName, out TypeInfoMapping value)
@@ -177,13 +188,17 @@ TypeInfoMapping GetMapping(Type type, string dataTypeName)
177188
static TypeInfoFactory CreateComposedFactory(Type mappingType, TypeInfoMapping innerMapping, Func<TypeInfoMapping, PgTypeInfo, PgConverter> mapper, bool copyPreferredFormat = false, bool supportsWriting = true)
178189
=> (options, mapping, dataTypeNameMatch) =>
179190
{
180-
var innerInfo = innerMapping.Factory(options, innerMapping, dataTypeNameMatch);
191+
var resolvedInnerMapping = innerMapping;
192+
if (!DataTypeName.IsFullyQualified(innerMapping.DataTypeName.AsSpan()))
193+
resolvedInnerMapping = innerMapping with { DataTypeName = new DataTypeName(mapping.DataTypeName).Schema + "." + innerMapping.DataTypeName };
194+
195+
var innerInfo = innerMapping.Factory(options, resolvedInnerMapping, dataTypeNameMatch);
181196
var converter = mapper(mapping, innerInfo);
182197
var preferredFormat = copyPreferredFormat ? innerInfo.PreferredFormat : null;
183198
var writingSupported = supportsWriting && innerInfo.SupportsWriting;
184199
var unboxedType = ComputeUnboxedType(defaultType: mappingType, converter.TypeToConvert, mapping.Type);
185200

186-
return new PgTypeInfo(options, converter, TypeInfoMappingHelpers.ResolveFullyQualifiedName(options, mapping.DataTypeName), unboxedType)
201+
return new PgTypeInfo(options, converter, options.GetCanonicalTypeId(new DataTypeName(mapping.DataTypeName)), unboxedType)
187202
{
188203
PreferredFormat = preferredFormat,
189204
SupportsWriting = writingSupported
@@ -194,15 +209,19 @@ static TypeInfoFactory CreateComposedFactory(Type mappingType, TypeInfoMapping i
194209
static TypeInfoFactory CreateComposedFactory(Type mappingType, TypeInfoMapping innerMapping, Func<TypeInfoMapping, PgResolverTypeInfo, PgConverterResolver> mapper, bool copyPreferredFormat = false, bool supportsWriting = true)
195210
=> (options, mapping, dataTypeNameMatch) =>
196211
{
197-
var innerInfo = (PgResolverTypeInfo)innerMapping.Factory(options, innerMapping, dataTypeNameMatch);
212+
var resolvedInnerMapping = innerMapping;
213+
if (!DataTypeName.IsFullyQualified(innerMapping.DataTypeName.AsSpan()))
214+
resolvedInnerMapping = innerMapping with { DataTypeName = new DataTypeName(mapping.DataTypeName).Schema + "." + innerMapping.DataTypeName };
215+
216+
var innerInfo = (PgResolverTypeInfo)innerMapping.Factory(options, resolvedInnerMapping, dataTypeNameMatch);
198217
var resolver = mapper(mapping, innerInfo);
199218
var preferredFormat = copyPreferredFormat ? innerInfo.PreferredFormat : null;
200219
var writingSupported = supportsWriting && innerInfo.SupportsWriting;
201220
var unboxedType = ComputeUnboxedType(defaultType: mappingType, resolver.TypeToConvert, mapping.Type);
202221
// We include the data type name if the inner info did so as well.
203222
// This way we can rely on its logic around resolvedDataTypeName, including when it ignores that flag.
204223
PgTypeId? pgTypeId = innerInfo.PgTypeId is not null
205-
? TypeInfoMappingHelpers.ResolveFullyQualifiedName(options, mapping.DataTypeName)
224+
? options.GetCanonicalTypeId(new DataTypeName(mapping.DataTypeName))
206225
: null;
207226
return new PgResolverTypeInfo(options, resolver, pgTypeId, unboxedType)
208227
{
@@ -689,30 +708,38 @@ static void ThrowBoxingNotSupported(bool resolver)
689708

690709
public static class TypeInfoMappingHelpers
691710
{
692-
internal static PgTypeId ResolveFullyQualifiedName(PgSerializerOptions options, string dataTypeName)
693-
=> !DataTypeName.IsFullyQualified(dataTypeName.AsSpan())
694-
? options.ToCanonicalTypeId(options.DatabaseInfo.GetPostgresType(dataTypeName))
695-
: new(new DataTypeName(dataTypeName));
711+
internal static bool TryResolveFullyQualifiedName(PgSerializerOptions options, string dataTypeName, out DataTypeName fqDataTypeName)
712+
{
713+
if (DataTypeName.IsFullyQualified(dataTypeName.AsSpan()))
714+
{
715+
fqDataTypeName = new DataTypeName(dataTypeName);
716+
return true;
717+
}
718+
719+
if (options.DatabaseInfo.TryGetPostgresTypeByName(dataTypeName, out var pgType))
720+
{
721+
fqDataTypeName = pgType.DataTypeName;
722+
return true;
723+
}
724+
725+
fqDataTypeName = default;
726+
return false;
727+
}
696728

697729
internal static PostgresType GetPgType(this TypeInfoMapping mapping, PgSerializerOptions options)
698-
=> !DataTypeName.IsFullyQualified(mapping.DataTypeName.AsSpan())
699-
? options.DatabaseInfo.GetPostgresType(mapping.DataTypeName)
700-
: options.DatabaseInfo.GetPostgresType(new DataTypeName(mapping.DataTypeName));
730+
=> options.DatabaseInfo.GetPostgresType(new DataTypeName(mapping.DataTypeName));
701731

702732
public static PgTypeInfo CreateInfo(this TypeInfoMapping mapping, PgSerializerOptions options, PgConverter converter, DataFormat? preferredFormat = null, bool supportsWriting = true)
703-
=> new(options, converter, ResolveFullyQualifiedName(options, mapping.DataTypeName))
733+
=> new(options, converter, new DataTypeName(mapping.DataTypeName))
704734
{
705735
PreferredFormat = preferredFormat,
706736
SupportsWriting = supportsWriting
707737
};
708738

709739
public static PgResolverTypeInfo CreateInfo(this TypeInfoMapping mapping, PgSerializerOptions options, PgConverterResolver resolver, bool includeDataTypeName = true, DataFormat? preferredFormat = null, bool supportsWriting = true)
710-
{
711-
PgTypeId? pgTypeId = includeDataTypeName ? ResolveFullyQualifiedName(options, mapping.DataTypeName) : null;
712-
return new(options, resolver, pgTypeId)
740+
=> new(options, resolver, includeDataTypeName ? new DataTypeName(mapping.DataTypeName) : null)
713741
{
714742
PreferredFormat = preferredFormat,
715743
SupportsWriting = supportsWriting
716744
};
717-
}
718745
}

0 commit comments

Comments
 (0)