未验证 提交 3880d96e 编写于 作者: S Shay Rojansky 提交者: GitHub

Don't map to multiranges when PG version is explicitly set to < 14 (#2353)

Fixes #2351
上级 36376107
......@@ -113,13 +113,13 @@ public static IServiceCollection AddEntityFrameworkNpgsql(this IServiceCollectio
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, NpgsqlSqlTranslatingExpressionVisitorFactory>()
.TryAdd<IRelationalParameterBasedSqlProcessorFactory, NpgsqlParameterBasedSqlProcessorFactory>()
.TryAdd<ISqlExpressionFactory, NpgsqlSqlExpressionFactory>()
.TryAdd<ISingletonOptions, INpgsqlOptions>(p => p.GetRequiredService<INpgsqlOptions>())
.TryAdd<ISingletonOptions, INpgsqlSingletonOptions>(p => p.GetRequiredService<INpgsqlSingletonOptions>())
.TryAdd<IValueConverterSelector, NpgsqlValueConverterSelector>()
.TryAdd<IQueryCompilationContextFactory, NpgsqlQueryCompilationContextFactory>()
.TryAddProviderSpecificServices(
b => b
.TryAddSingleton<INpgsqlValueGeneratorCache, NpgsqlValueGeneratorCache>()
.TryAddSingleton<INpgsqlOptions, NpgsqlOptions>()
.TryAddSingleton<INpgsqlSingletonOptions, NpgsqlSingletonOptions>()
.TryAddSingleton<INpgsqlSequenceValueGeneratorFactory, NpgsqlSequenceValueGeneratorFactory>()
.TryAddScoped<INpgsqlRelationalConnection, NpgsqlRelationalConnection>())
.TryAddCoreServices();
......
......@@ -3,13 +3,18 @@
/// <summary>
/// Represents options for Npgsql that can only be set at the <see cref="IServiceProvider"/> singleton level.
/// </summary>
public interface INpgsqlOptions : ISingletonOptions
public interface INpgsqlSingletonOptions : ISingletonOptions
{
/// <summary>
/// The backend version to target.
/// </summary>
Version PostgresVersion { get; }
/// <summary>
/// The backend version to target, but returns <see langword="null" /> unless the user explicitly specified a version.
/// </summary>
Version? PostgresVersionWithoutDefault { get; }
/// <summary>
/// Whether to target Redshift.
/// </summary>
......
......@@ -9,8 +9,6 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
/// </summary>
public class NpgsqlOptionsExtension : RelationalOptionsExtension
{
public static readonly Version DefaultPostgresVersion = new(12, 0);
private DbContextOptionsExtensionInfo? _info;
private readonly List<UserRangeDefinition> _userRangeDefinitions;
......@@ -22,7 +20,7 @@ public class NpgsqlOptionsExtension : RelationalOptionsExtension
/// <summary>
/// The backend version to target.
/// </summary>
public virtual Version PostgresVersion { get; private set; } = DefaultPostgresVersion;
public virtual Version? PostgresVersion { get; private set; }
/// <summary>
/// Whether to target Redshift.
......@@ -133,7 +131,7 @@ public virtual NpgsqlOptionsExtension WithPostgresVersion(Version? postgresVersi
{
var clone = (NpgsqlOptionsExtension)Clone();
clone.PostgresVersion = postgresVersion ?? DefaultPostgresVersion;
clone.PostgresVersion = postgresVersion;
return clone;
}
......@@ -172,7 +170,7 @@ public override void Validate(IDbContextOptions options)
{
base.Validate(options);
if (UseRedshift && !PostgresVersion.Equals(DefaultPostgresVersion))
if (UseRedshift && PostgresVersion is not null)
{
throw new InvalidOperationException($"{nameof(UseRedshift)} and {nameof(PostgresVersion)} cannot both be set");
}
......@@ -264,7 +262,7 @@ public override string LogFragment
builder.Append(nameof(Extension.AdminDatabase)).Append("=").Append(Extension.AdminDatabase).Append(' ');
}
if (!Extension.PostgresVersion.Equals(DefaultPostgresVersion))
if (Extension.PostgresVersion is not null)
{
builder.Append(nameof(Extension.PostgresVersion)).Append("=").Append(Extension.PostgresVersion).Append(' ');
}
......@@ -356,7 +354,7 @@ public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
= (Extension.AdminDatabase?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture);
debugInfo["Npgsql.EntityFrameworkCore.PostgreSQL:" + nameof(NpgsqlDbContextOptionsBuilder.SetPostgresVersion)]
= Extension.PostgresVersion.GetHashCode().ToString(CultureInfo.InvariantCulture);
= (Extension.PostgresVersion?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture);
debugInfo["Npgsql.EntityFrameworkCore.PostgreSQL:" + nameof(NpgsqlDbContextOptionsBuilder.UseRedshift)]
= Extension.UseRedshift.GetHashCode().ToString(CultureInfo.InvariantCulture);
......
......@@ -18,9 +18,9 @@ public class NpgsqlModelValidator : RelationalModelValidator
public NpgsqlModelValidator(
ModelValidatorDependencies dependencies,
RelationalModelValidatorDependencies relationalDependencies,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies, relationalDependencies)
=> _postgresVersion = Check.NotNull(npgsqlOptions, nameof(npgsqlOptions)).PostgresVersion;
=> _postgresVersion = npgsqlSingletonOptions.PostgresVersion;
public override void Validate(IModel model, IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
......@@ -43,8 +43,7 @@ protected virtual void ValidateIdentityVersionCompatibility(IModel model)
var strategy = model.GetValueGenerationStrategy();
if (strategy == NpgsqlValueGenerationStrategy.IdentityAlwaysColumn ||
strategy == NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
if (strategy is NpgsqlValueGenerationStrategy.IdentityAlwaysColumn or NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
{
throw new InvalidOperationException(
$"'{strategy}' requires PostgreSQL 10.0 or later. " +
......@@ -57,8 +56,8 @@ protected virtual void ValidateIdentityVersionCompatibility(IModel model)
{
var propertyStrategy = property.GetValueGenerationStrategy();
if (propertyStrategy == NpgsqlValueGenerationStrategy.IdentityAlwaysColumn ||
propertyStrategy == NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
if (propertyStrategy is NpgsqlValueGenerationStrategy.IdentityAlwaysColumn
or NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
{
throw new InvalidOperationException(
$"{property.DeclaringEntityType}.{property.Name}: '{propertyStrategy}' requires PostgreSQL 10.0 or later.");
......
......@@ -4,11 +4,16 @@
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Internal;
/// <inheritdoc />
public class NpgsqlOptions : INpgsqlOptions
public class NpgsqlSingletonOptions : INpgsqlSingletonOptions
{
public static readonly Version DefaultPostgresVersion = new(12, 0);
/// <inheritdoc />
public virtual Version PostgresVersion { get; private set; } = null!;
/// <inheritdoc />
public virtual Version? PostgresVersionWithoutDefault { get; private set; }
/// <inheritdoc />
public virtual bool UseRedshift { get; private set; }
......@@ -18,7 +23,7 @@ public class NpgsqlOptions : INpgsqlOptions
/// <inheritdoc />
public virtual IReadOnlyList<UserRangeDefinition> UserRangeDefinitions { get; private set; }
public NpgsqlOptions()
public NpgsqlSingletonOptions()
=> UserRangeDefinitions = Array.Empty<UserRangeDefinition>();
/// <inheritdoc />
......@@ -26,7 +31,8 @@ public virtual void Initialize(IDbContextOptions options)
{
var npgsqlOptions = options.FindExtension<NpgsqlOptionsExtension>() ?? new NpgsqlOptionsExtension();
PostgresVersion = npgsqlOptions.PostgresVersion;
PostgresVersionWithoutDefault = npgsqlOptions.PostgresVersion;
PostgresVersion = npgsqlOptions.PostgresVersion ?? DefaultPostgresVersion;
UseRedshift = npgsqlOptions.UseRedshift;
ReverseNullOrderingEnabled = npgsqlOptions.ReverseNullOrdering;
UserRangeDefinitions = npgsqlOptions.UserRangeDefinitions;
......@@ -37,7 +43,7 @@ public virtual void Validate(IDbContextOptions options)
{
var npgsqlOptions = options.FindExtension<NpgsqlOptionsExtension>() ?? new NpgsqlOptionsExtension();
if (!PostgresVersion.Equals(npgsqlOptions.PostgresVersion))
if (PostgresVersionWithoutDefault != npgsqlOptions.PostgresVersion)
{
throw new InvalidOperationException(
CoreStrings.SingletonOptionChanged(
......
......@@ -13,11 +13,11 @@ public class NpgsqlConventionSetBuilder : RelationalConventionSetBuilder
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies,
IRelationalTypeMappingSource typeMappingSource,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies, relationalDependencies)
{
_typeMappingSource = typeMappingSource;
_postgresVersion = npgsqlOptions.PostgresVersion;
_postgresVersion = npgsqlSingletonOptions.PostgresVersion;
}
[EntityFrameworkInternal]
......
......@@ -20,10 +20,10 @@ public class NpgsqlMigrationsSqlGenerator : MigrationsSqlGenerator
public NpgsqlMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies)
{
_postgresVersion = npgsqlOptions.PostgresVersion;
_postgresVersion = npgsqlSingletonOptions.PostgresVersion;
_stringTypeMapping = dependencies.TypeMappingSource.GetMapping(typeof(string))
?? throw new InvalidOperationException("No string type mapping found");
}
......
......@@ -13,7 +13,7 @@ public class NpgsqlMemberTranslatorProvider : RelationalMemberTranslatorProvider
RelationalMemberTranslatorProviderDependencies dependencies,
IModel model,
IRelationalTypeMappingSource typeMappingSource,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies)
{
var sqlExpressionFactory = (NpgsqlSqlExpressionFactory)dependencies.SqlExpressionFactory;
......@@ -21,13 +21,13 @@ public class NpgsqlMemberTranslatorProvider : RelationalMemberTranslatorProvider
AddTranslators(
new IMemberTranslator[] {
new NpgsqlArrayTranslator(sqlExpressionFactory, JsonPocoTranslator, npgsqlOptions.UseRedshift),
new NpgsqlArrayTranslator(sqlExpressionFactory, JsonPocoTranslator, npgsqlSingletonOptions.UseRedshift),
new NpgsqlBigIntegerMemberTranslator(sqlExpressionFactory),
new NpgsqlDateTimeMemberTranslator(typeMappingSource, sqlExpressionFactory),
new NpgsqlJsonDomTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlLTreeTranslator(typeMappingSource, sqlExpressionFactory, model),
JsonPocoTranslator,
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model, npgsqlSingletonOptions),
new NpgsqlStringMemberTranslator(sqlExpressionFactory),
new NpgsqlTimeSpanMemberTranslator(sqlExpressionFactory),
});
......
......@@ -10,7 +10,7 @@ public class NpgsqlMethodCallTranslatorProvider : RelationalMethodCallTranslator
public NpgsqlMethodCallTranslatorProvider(
RelationalMethodCallTranslatorProviderDependencies dependencies,
IModel model,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies)
{
var sqlExpressionFactory = (NpgsqlSqlExpressionFactory)dependencies.SqlExpressionFactory;
......@@ -20,7 +20,7 @@ public class NpgsqlMethodCallTranslatorProvider : RelationalMethodCallTranslator
AddTranslators(new IMethodCallTranslator[]
{
new NpgsqlArrayTranslator(sqlExpressionFactory, jsonTranslator, npgsqlOptions.UseRedshift),
new NpgsqlArrayTranslator(sqlExpressionFactory, jsonTranslator, npgsqlSingletonOptions.UseRedshift),
new NpgsqlByteArrayMethodTranslator(sqlExpressionFactory),
new NpgsqlConvertTranslator(sqlExpressionFactory),
new NpgsqlDateTimeMethodTranslator(typeMappingSource, sqlExpressionFactory),
......@@ -32,10 +32,10 @@ public class NpgsqlMethodCallTranslatorProvider : RelationalMethodCallTranslator
LTreeTranslator,
new NpgsqlMathTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlNetworkTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlNewGuidTranslator(sqlExpressionFactory, npgsqlOptions.PostgresVersion),
new NpgsqlNewGuidTranslator(sqlExpressionFactory, npgsqlSingletonOptions),
new NpgsqlObjectToStringTranslator(typeMappingSource, sqlExpressionFactory),
new NpgsqlRandomTranslator(sqlExpressionFactory),
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model, npgsqlSingletonOptions),
new NpgsqlRegexIsMatchTranslator(sqlExpressionFactory),
new NpgsqlRowValueTranslator(sqlExpressionFactory),
new NpgsqlStringMethodTranslator(typeMappingSource, sqlExpressionFactory, model),
......
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
......@@ -17,10 +18,10 @@ public class NpgsqlNewGuidTranslator : IMethodCallTranslator
public NpgsqlNewGuidTranslator(
ISqlExpressionFactory sqlExpressionFactory,
Version? postgresVersion)
INpgsqlSingletonOptions npgsqlSingletonOptions)
{
_sqlExpressionFactory = sqlExpressionFactory;
_uuidGenerationFunction = postgresVersion?.AtLeast(13) == true ? "gen_random_uuid" : "uuid_generate_v4";
_uuidGenerationFunction = npgsqlSingletonOptions.PostgresVersion.AtLeast(13) ? "gen_random_uuid" : "uuid_generate_v4";
}
public virtual SqlExpression? Translate(
......
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics;
......@@ -10,6 +11,7 @@ public class NpgsqlRangeTranslator : IMethodCallTranslator, IMemberTranslator
private readonly IRelationalTypeMappingSource _typeMappingSource;
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
private readonly IModel _model;
private readonly bool _supportsMultiranges;
private static readonly MethodInfo EnumerableAnyWithoutPredicate =
typeof(Enumerable).GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
......@@ -18,11 +20,14 @@ public class NpgsqlRangeTranslator : IMethodCallTranslator, IMemberTranslator
public NpgsqlRangeTranslator(
IRelationalTypeMappingSource typeMappingSource,
NpgsqlSqlExpressionFactory npgsqlSqlExpressionFactory,
IModel model)
IModel model,
INpgsqlSingletonOptions npgsqlSingletonOptions)
{
_typeMappingSource = typeMappingSource;
_sqlExpressionFactory = npgsqlSqlExpressionFactory;
_model = model;
_supportsMultiranges = npgsqlSingletonOptions.PostgresVersionWithoutDefault is null
|| npgsqlSingletonOptions.PostgresVersionWithoutDefault.AtLeast(14);
}
/// <inheritdoc />
......@@ -33,7 +38,8 @@ public class NpgsqlRangeTranslator : IMethodCallTranslator, IMemberTranslator
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
// Any() over multirange -> NOT isempty(). NpgsqlRange<T> has IsEmpty which is translated below.
if (method.IsGenericMethod
if (_supportsMultiranges
&& method.IsGenericMethod
&& method.GetGenericMethodDefinition() == EnumerableAnyWithoutPredicate
&& arguments[0].Type.TryGetMultirangeSubtype(out _))
{
......@@ -47,7 +53,7 @@ public class NpgsqlRangeTranslator : IMethodCallTranslator, IMemberTranslator
}
if (method.DeclaringType != typeof(NpgsqlRangeDbFunctionsExtensions)
&& method.DeclaringType != typeof(NpgsqlMultirangeDbFunctionsExtensions))
&& (method.DeclaringType != typeof(NpgsqlMultirangeDbFunctionsExtensions) || !_supportsMultiranges))
{
return null;
}
......
......@@ -8,19 +8,19 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory
{
private readonly QuerySqlGeneratorDependencies _dependencies;
private readonly INpgsqlOptions _npgsqlOptions;
private readonly INpgsqlSingletonOptions _npgsqlSingletonOptions;
public NpgsqlQuerySqlGeneratorFactory(
QuerySqlGeneratorDependencies dependencies,
INpgsqlOptions npgsqlOptions)
INpgsqlSingletonOptions npgsqlSingletonOptions)
{
_dependencies = dependencies;
_npgsqlOptions = npgsqlOptions;
_npgsqlSingletonOptions = npgsqlSingletonOptions;
}
public virtual QuerySqlGenerator Create()
=> new NpgsqlQuerySqlGenerator(
_dependencies,
_npgsqlOptions.ReverseNullOrderingEnabled,
_npgsqlOptions.PostgresVersion);
_npgsqlSingletonOptions.ReverseNullOrderingEnabled,
_npgsqlSingletonOptions.PostgresVersion);
}
\ No newline at end of file
......@@ -7,10 +7,8 @@
using System.Numerics;
using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
using Npgsql.EntityFrameworkCore.PostgreSQL.Utilities;
using Npgsql.Internal.TypeMapping;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
......@@ -52,6 +50,8 @@ static NpgsqlTypeMappingSource()
private static MethodInfo? _adoUserTypeMappingsGetMethodInfo;
private readonly bool _supportsMultiranges;
#region Mappings
// Numeric types
......@@ -177,10 +177,12 @@ static NpgsqlTypeMappingSource()
public NpgsqlTypeMappingSource(TypeMappingSourceDependencies dependencies,
RelationalTypeMappingSourceDependencies relationalDependencies,
ISqlGenerationHelper sqlGenerationHelper,
INpgsqlOptions? npgsqlOptions = null)
INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies, relationalDependencies)
{
_sqlGenerationHelper = Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper));
_supportsMultiranges = npgsqlSingletonOptions.PostgresVersionWithoutDefault is null
|| npgsqlSingletonOptions.PostgresVersionWithoutDefault.AtLeast(14);
// Initialize some mappings which depend on other mappings
_int4range = new NpgsqlRangeTypeMapping("int4range", typeof(NpgsqlRange<int>), _int4, sqlGenerationHelper);
......@@ -306,13 +308,6 @@ static NpgsqlTypeMappingSource()
{ "tstzrange", new[] { _tstzrange } },
{ "daterange", new[] { _dateOnlyDaterange, _dateTimeDaterange } },
{ "int4multirange", new[] { _int4multirangeArray, _int4multirangeList } },
{ "int8multirange", new[] { _int8multirangeArray, _int8multirangeList } },
{ "nummultirange", new[] { _nummultirangeArray, _nummultirangeList } },
{ "tsmultirange", new[] { _tsmultirangeArray, _tsmultirangeList } },
{ "tstzmultirange", new[] { _tstzmultirangeArray, _tstzmultirangeList } },
{ "datemultirange", new[] { _dateOnlyDatemultirangeArray, _dateOnlyDatemultirangeList, _dateTimeDatemultirangeArray, _dateTimeMultirangeList } },
{ "tsquery", new[] { _tsquery } },
{ "tsvector", new[] { _tsvector } },
{ "regconfig", new[] { _regconfig } },
......@@ -382,18 +377,6 @@ static NpgsqlTypeMappingSource()
{ typeof(NpgsqlRange<DateTimeOffset>), _tstzrange },
{ typeof(NpgsqlRange<DateOnly>), _dateOnlyDaterange },
{ typeof(NpgsqlRange<int>[]), _int4multirangeArray },
{ typeof(NpgsqlRange<long>[]), _int8multirangeArray },
{ typeof(NpgsqlRange<decimal>[]), _nummultirangeArray },
{ typeof(NpgsqlRange<DateTime>[]), LegacyTimestampBehavior ? _tsmultirangeArray : _tstzmultirangeArray },
{ typeof(NpgsqlRange<DateOnly>[]), _dateOnlyDatemultirangeArray },
{ typeof(List<NpgsqlRange<int>>), _int4multirangeList },
{ typeof(List<NpgsqlRange<long>>), _int8multirangeList },
{ typeof(List<NpgsqlRange<decimal>>), _nummultirangeList },
{ typeof(List<NpgsqlRange<DateTime>>), LegacyTimestampBehavior ? _tsmultirangeList : _tstzmultirangeList },
{ typeof(List<NpgsqlRange<DateOnly>>), _dateOnlyDatemultirangeList },
{ typeof(NpgsqlTsQuery), _tsquery },
{ typeof(NpgsqlTsVector), _tsvector },
{ typeof(NpgsqlTsRankingNormalization), _rankingNormalization },
......@@ -401,12 +384,34 @@ static NpgsqlTypeMappingSource()
{ typeof(LTree), _ltree }
};
if (_supportsMultiranges)
{
storeTypeMappings["int4multirange"] = new[] { _int4multirangeArray, _int4multirangeList };
storeTypeMappings["int8multirange"] = new[] { _int8multirangeArray, _int8multirangeList };
storeTypeMappings["nummultirange"] = new[] { _nummultirangeArray, _nummultirangeList };
storeTypeMappings["tsmultirange"] = new[] { _tsmultirangeArray, _tsmultirangeList };
storeTypeMappings["tstzmultirange"] = new[] { _tstzmultirangeArray, _tstzmultirangeList };
storeTypeMappings["datemultirange"] = new[] { _dateOnlyDatemultirangeArray, _dateOnlyDatemultirangeList, _dateTimeDatemultirangeArray, _dateTimeMultirangeList };
clrTypeMappings[typeof(NpgsqlRange<int>[])] = _int4multirangeArray;
clrTypeMappings[typeof(NpgsqlRange<long>[])] = _int8multirangeArray;
clrTypeMappings[typeof(NpgsqlRange<decimal>[])] = _nummultirangeArray;
clrTypeMappings[typeof(NpgsqlRange<DateTime>[])] = LegacyTimestampBehavior ? _tsmultirangeArray : _tstzmultirangeArray;
clrTypeMappings[typeof(NpgsqlRange<DateOnly>[])] = _dateOnlyDatemultirangeArray;
clrTypeMappings[typeof(List<NpgsqlRange<int>>)] = _int4multirangeList;
clrTypeMappings[typeof(List<NpgsqlRange<long>>)] = _int8multirangeList;
clrTypeMappings[typeof(List<NpgsqlRange<decimal>>)] = _nummultirangeList;
clrTypeMappings[typeof(List<NpgsqlRange<DateTime>>)] = LegacyTimestampBehavior ? _tsmultirangeList : _tstzmultirangeList;
clrTypeMappings[typeof(List<NpgsqlRange<DateOnly>>)] = _dateOnlyDatemultirangeList;
}
StoreTypeMappings = new ConcurrentDictionary<string, RelationalTypeMapping[]>(storeTypeMappings, StringComparer.OrdinalIgnoreCase);
ClrTypeMappings = new ConcurrentDictionary<Type, RelationalTypeMapping>(clrTypeMappings);
LoadUserDefinedTypeMappings(sqlGenerationHelper);
_userRangeDefinitions = npgsqlOptions?.UserRangeDefinitions ?? Array.Empty<UserRangeDefinition>();
_userRangeDefinitions = npgsqlSingletonOptions?.UserRangeDefinitions ?? Array.Empty<UserRangeDefinition>();
}
/// <summary>
......@@ -699,9 +704,9 @@ protected virtual void SetupEnumMappings(ISqlGenerationHelper sqlGenerationHelpe
}
// We now have a user-defined range definition from the context options. Use it to get the subtype's mapping
var subtypeMapping = (RelationalTypeMapping?)(rangeDefinition.SubtypeName is null
var subtypeMapping = rangeDefinition.SubtypeName is null
? FindMapping(rangeDefinition.SubtypeClrType)
: FindMapping(rangeDefinition.SubtypeName));
: FindMapping(rangeDefinition.SubtypeName);
if (subtypeMapping is null)
{
......@@ -712,7 +717,7 @@ protected virtual void SetupEnumMappings(ISqlGenerationHelper sqlGenerationHelpe
}
/// <summary>
/// Finds the mapping for a container given its CLR type and its containee's type mapping'; this is currently used to infer type
/// Finds the mapping for a container given its CLR type and its containee's type mapping; this is currently used to infer type
/// mappings for ranges and multiranges from their values.
/// </summary>
public virtual RelationalTypeMapping? FindContainerMapping(Type containerClrType, RelationalTypeMapping containeeTypeMapping)
......@@ -724,7 +729,7 @@ protected virtual void SetupEnumMappings(ISqlGenerationHelper sqlGenerationHelpe
: null;
}
if (containerClrType.TryGetMultirangeSubtype(out subtypeType))
if (_supportsMultiranges && containerClrType.TryGetMultirangeSubtype(out subtypeType))
{
return _multirangeTypeMappings.TryGetValue(subtypeType, out var candidateMappings)
? candidateMappings.FirstOrDefault(m => m.SubtypeMapping.StoreType == containeeTypeMapping.StoreType)
......
......@@ -1867,7 +1867,7 @@ public void Bug453()
[Fact]
public void Column_default_type_names_are_scaffolded()
{
var options = new NpgsqlOptions();
var options = new NpgsqlSingletonOptions();
options.Initialize(new DbContextOptionsBuilder().Options);
Test(@"
......
......@@ -67,7 +67,7 @@ public void GenerateSqlLiteral_returns_instant_range_in_legacy_mode()
new NpgsqlNodaTimeTypeMappingSourcePlugin(new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()))
}),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions()
new NpgsqlSingletonOptions()
);
private static RelationalTypeMapping GetMapping(string storeType) => Mapper.FindMapping(storeType);
......
......@@ -597,7 +597,7 @@ public void GenerateCodeLiteral_returns_Duration_literal()
new NpgsqlNodaTimeTypeMappingSourcePlugin(new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()))
}),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions()
new NpgsqlSingletonOptions()
);
private static RelationalTypeMapping GetMapping(string storeType) => Mapper.FindMapping(storeType);
......
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Npgsql.EntityFrameworkCore.PostgreSQL.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
......@@ -396,5 +397,6 @@ private NpgsqlAnnotationCodeGenerator CreateGenerator()
Array.Empty<ITypeMappingSourcePlugin>()
),
new RelationalTypeMappingSourceDependencies(Array.Empty<IRelationalTypeMappingSourcePlugin>()),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()))));
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlSingletonOptions())));
}
......@@ -75,7 +75,7 @@ public static RelationalConnectionDependencies CreateDependencies(DbContextOptio
TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
TestServiceFactory.Instance.Create<RelationalTypeMappingSourceDependencies>(),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions()),
new NpgsqlSingletonOptions()),
new ExceptionDetector())));
}
......
......@@ -47,7 +47,7 @@ public void GenerateSqlLiteral_returns_timestamptz_datetime_literal()
),
new RelationalTypeMappingSourceDependencies(Array.Empty<IRelationalTypeMappingSourcePlugin>()),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions()
new NpgsqlSingletonOptions()
);
private static RelationalTypeMapping GetMapping(string storeType) => Mapper.FindMapping(storeType);
......
......@@ -28,6 +28,7 @@ public class NpgsqlTypeMappingSourceTest
[InlineData("int4range", typeof(NpgsqlRange<int>), null, false)]
[InlineData("floatrange", typeof(NpgsqlRange<float>), null, false)]
[InlineData("dummyrange", typeof(NpgsqlRange<DummyType>), null, false)]
[InlineData("int4multirange", typeof(NpgsqlRange<int>[]), null, false)]
[InlineData("geometry", typeof(Geometry), null, false)]
[InlineData("geometry(Polygon)", typeof(Polygon), null, false)]
[InlineData("geography(Point, 4326)", typeof(Point), null, false)]
......@@ -36,7 +37,7 @@ public class NpgsqlTypeMappingSourceTest
[InlineData("geometry(POLYGONM)", typeof(Polygon), null, false)]
public void By_StoreType(string typeName, Type type, int? size, bool fixedLength)
{
var mapping = Source.FindMapping(typeName);
var mapping = CreateTypeMappingSource().FindMapping(typeName);
Assert.Same(type, mapping.ClrType);
Assert.Equal(size, mapping.Size);
......@@ -48,7 +49,7 @@ public void By_StoreType(string typeName, Type type, int? size, bool fixedLength
[Fact]
public void Varchar32()
{
var mapping = Source.FindMapping("varchar(32)");
var mapping = CreateTypeMappingSource().FindMapping("varchar(32)");
Assert.Same(typeof(string), mapping.ClrType);
Assert.Equal("varchar(32)", mapping.StoreType);
Assert.Equal(32, mapping.Size);
......@@ -57,7 +58,7 @@ public void Varchar32()
[Fact]
public void Varchar32_Array()
{
var mapping = Source.FindMapping("varchar(32)[]");
var mapping = CreateTypeMappingSource().FindMapping("varchar(32)[]");
var arrayMapping = Assert.IsType<NpgsqlArrayArrayTypeMapping>(mapping);
Assert.Same(typeof(string[]), arrayMapping.ClrType);
......@@ -73,7 +74,7 @@ public void Varchar32_Array()
[Fact]
public void Timestamp_without_time_zone_5()
{
var mapping = Source.FindMapping("timestamp(5) without time zone");
var mapping = CreateTypeMappingSource().FindMapping("timestamp(5) without time zone");
Assert.Same(typeof(DateTime), mapping.ClrType);
Assert.Equal("timestamp(5) without time zone", mapping.StoreType);
// Precision/Scale not actually exposed on RelationalTypeMapping...
......@@ -82,7 +83,7 @@ public void Timestamp_without_time_zone_5()
[Fact]
public void Timestamp_without_time_zone_Array_5()
{
var arrayMapping = Assert.IsType<NpgsqlArrayArrayTypeMapping>(Source.FindMapping("timestamp(5) without time zone[]"));
var arrayMapping = Assert.IsType<NpgsqlArrayArrayTypeMapping>(CreateTypeMappingSource().FindMapping("timestamp(5) without time zone[]"));
Assert.Same(typeof(DateTime[]), arrayMapping.ClrType);
Assert.Equal("timestamp(5) without time zone[]", arrayMapping.StoreType);
......@@ -99,11 +100,13 @@ public void Timestamp_without_time_zone_Array_5()
[InlineData(typeof(NpgsqlRange<int>), "int4range")]
[InlineData(typeof(NpgsqlRange<float>), "floatrange")]
[InlineData(typeof(NpgsqlRange<DummyType>), "dummyrange")]
[InlineData(typeof(NpgsqlRange<int>[]), "int4multirange")]
[InlineData(typeof(List<NpgsqlRange<int>>), "int4multirange")]
[InlineData(typeof(Geometry), "geometry")]
[InlineData(typeof(Point), "geometry")]
public void By_ClrType(Type clrType, string expectedStoreType)
{
var mapping = (RelationalTypeMapping)Source.FindMapping(clrType);
var mapping = CreateTypeMappingSource().FindMapping(clrType);
Assert.Equal(expectedStoreType, mapping.StoreType);
Assert.Same(clrType, mapping.ClrType);
}
......@@ -119,7 +122,7 @@ public void By_ClrType(Type clrType, string expectedStoreType)
[InlineData(typeof(int[]), "integer[]")]
public void By_ClrType_and_precision(Type clrType, string expectedStoreType)
{
var mapping = Source.FindMapping(clrType, null, precision: 5);
var mapping = CreateTypeMappingSource().FindMapping(clrType, null, precision: 5);
Assert.Equal(expectedStoreType, mapping.StoreType);
Assert.Same(clrType, mapping.ClrType);
}
......@@ -137,7 +140,7 @@ public void By_ClrType_and_precision(Type clrType, string expectedStoreType)
[InlineData("geometry(Point, 4326)", typeof(Geometry))]
public void By_StoreType_with_ClrType(string storeType, Type clrType)
{
var mapping = Source.FindMapping(clrType, storeType);
var mapping = CreateTypeMappingSource().FindMapping(clrType, storeType);
Assert.Equal(storeType, mapping.StoreType);
Assert.Same(clrType, mapping.ClrType);
}
......@@ -151,17 +154,17 @@ public void By_StoreType_with_ClrType(string storeType, Type clrType)
[InlineData("dummyrange", typeof(UnknownType))]
[InlineData("geometry", typeof(UnknownType))]
public void By_StoreType_with_wrong_ClrType(string storeType, Type wrongClrType)
=> Assert.Null(Source.FindMapping(wrongClrType, storeType));
=> Assert.Null(CreateTypeMappingSource().FindMapping(wrongClrType, storeType));
// Happens when using domain/aliases: we don't know about the domain but continue with the mapping based on the ClrType
[Fact]
public void Unknown_StoreType_with_known_ClrType()
=> Assert.Equal("some_domain", Source.FindMapping(typeof(int), "some_domain").StoreType);
=> Assert.Equal("some_domain", CreateTypeMappingSource().FindMapping(typeof(int), "some_domain").StoreType);
[Fact]
public void Varchar_mapping_sets_NpgsqlDbType()
{
var mapping = Source.FindMapping("character varying");
var mapping = CreateTypeMappingSource().FindMapping("character varying");
var parameter = (NpgsqlParameter)mapping.CreateParameter(new NpgsqlCommand(), "p", "foo");
Assert.Equal(NpgsqlDbType.Varchar, parameter.NpgsqlDbType);
}
......@@ -169,7 +172,7 @@ public void Varchar_mapping_sets_NpgsqlDbType()
[Fact]
public void Single_char_mapping_sets_NpgsqlDbType()
{
var mapping = (RelationalTypeMapping)Source.FindMapping(typeof(char));
var mapping = CreateTypeMappingSource().FindMapping(typeof(char));
var parameter = (NpgsqlParameter)mapping.CreateParameter(new NpgsqlCommand(), "p", "foo");
Assert.Equal(NpgsqlDbType.Char, parameter.NpgsqlDbType);
}
......@@ -177,22 +180,22 @@ public void Single_char_mapping_sets_NpgsqlDbType()
[Fact]
public void String_as_single_char_mapping_sets_NpgsqlDbType()
{
var mapping = Source.FindMapping(typeof(string), "char(1)");
var mapping = CreateTypeMappingSource().FindMapping(typeof(string), "char(1)");
var parameter = (NpgsqlParameter)mapping.CreateParameter(new NpgsqlCommand(), "p", "foo");
Assert.Equal(NpgsqlDbType.Char, parameter.NpgsqlDbType);
}
[Fact]
public void Array_over_type_mapping_with_value_converter_by_clr_type_array()
=> Array_over_type_mapping_with_value_converter(Source.FindMapping(typeof(LTree[])), typeof(LTree[]));
=> Array_over_type_mapping_with_value_converter(CreateTypeMappingSource().FindMapping(typeof(LTree[])), typeof(LTree[]));
[Fact]
public void Array_over_type_mapping_with_value_converter_by_clr_type_list()
=> Array_over_type_mapping_with_value_converter(Source.FindMapping(typeof(List<LTree>)), typeof(List<LTree>));
=> Array_over_type_mapping_with_value_converter(CreateTypeMappingSource().FindMapping(typeof(List<LTree>)), typeof(List<LTree>));
[Fact]
public void Array_over_type_mapping_with_value_converter_by_store_type()
=> Array_over_type_mapping_with_value_converter(Source.FindMapping("ltree[]"), typeof(LTree[]));
=> Array_over_type_mapping_with_value_converter(CreateTypeMappingSource().FindMapping("ltree[]"), typeof(LTree[]));
private void Array_over_type_mapping_with_value_converter(CoreTypeMapping mapping, Type expectedType)
{
......@@ -218,17 +221,57 @@ private void Array_over_type_mapping_with_value_converter(CoreTypeMapping mappin
s => Assert.Equal("bar", s));
}
[Fact]
public void Multirange_by_clr_type_across_pg_versions()
{
var mapping14 = CreateTypeMappingSource(postgresVersion: new(14, 0)).FindMapping(typeof(NpgsqlRange<int>[]))!;
var mapping13 = CreateTypeMappingSource(postgresVersion: new(13, 0)).FindMapping(typeof(NpgsqlRange<int>[]))!;
var mappingDefault = CreateTypeMappingSource().FindMapping(typeof(NpgsqlRange<int>[]))!;
Assert.Equal("int4multirange", mapping14.StoreType);
Assert.Equal("int4range[]", mapping13.StoreType);
// See #2351 - we didn't put multiranges behind a version opt-in in 6.0, although the default PG version is still 12; this causes
// anyone with arrays of ranges to fail if they upgrade to 6.0 with pre-14 PG.
// Changing this in a patch would break people already using 6.0 with PG14, so multiranges are on by default unless users explicitly
// specify < 14.
// Once 14 is made the default version, this stuff can be removed.
Assert.Equal("int4multirange", mappingDefault.StoreType);
}
[Fact]
public void Multirange_by_store_type_across_pg_versions()
{
var mapping14 = CreateTypeMappingSource(postgresVersion: new(14, 0)).FindMapping("int4multirange")!;
var mapping13 = CreateTypeMappingSource(postgresVersion: new(13, 0)).FindMapping("int4multirange");
var mappingDefault = CreateTypeMappingSource().FindMapping("int4multirange")!;
Assert.Same(typeof(NpgsqlRange<int>[]), mapping14.ClrType);
Assert.Null(mapping13);
// See #2351 - we didn't put multiranges behind a version opt-in in 6.0, although the default PG version is still 12; this causes
// anyone with arrays of ranges to fail if they upgrade to 6.0 with pre-14 PG.
// Changing this in a patch would break people already using 6.0 with PG14, so multiranges are on by default unless users explicitly
// specify < 14.
// Once 14 is made the default version, this stuff can be removed.
Assert.Same(typeof(NpgsqlRange<int>[]), mappingDefault.ClrType);
}
#region Support
public NpgsqlTypeMappingSourceTest()
private NpgsqlTypeMappingSource CreateTypeMappingSource(Version postgresVersion = null)
{
var builder = new DbContextOptionsBuilder();
new NpgsqlDbContextOptionsBuilder(builder).MapRange<float>("floatrange");
new NpgsqlDbContextOptionsBuilder(builder).MapRange<DummyType>("dummyrange", subtypeName: "dummy");
var options = new NpgsqlOptions();
var npgsqlBuilder = new NpgsqlDbContextOptionsBuilder(builder);
npgsqlBuilder.MapRange<float>("floatrange");
npgsqlBuilder.MapRange<DummyType>("dummyrange", subtypeName: "dummy");
npgsqlBuilder.SetPostgresVersion(postgresVersion);
var options = new NpgsqlSingletonOptions();
options.Initialize(builder.Options);
Source = new NpgsqlTypeMappingSource(
return new NpgsqlTypeMappingSource(
new TypeMappingSourceDependencies(
new ValueConverterSelector(new ValueConverterSelectorDependencies()),
Array.Empty<ITypeMappingSourcePlugin>()),
......@@ -241,8 +284,6 @@ public NpgsqlTypeMappingSourceTest()
options);
}
private NpgsqlTypeMappingSource Source { get; }
private class DummyTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
{
public RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
......@@ -276,4 +317,4 @@ private class DummyType {}
private class UnknownType {}
#endregion Support
}
\ No newline at end of file
}
......@@ -890,7 +890,7 @@ public class Order
),
new RelationalTypeMappingSourceDependencies(Array.Empty<IRelationalTypeMappingSourcePlugin>()),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions()
new NpgsqlSingletonOptions()
);
private static RelationalTypeMapping GetMapping(string storeType)
......
......@@ -20,7 +20,7 @@ public void Uses_MaxBatchSize_specified_in_NpgsqlOptionsExtension()
TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
TestServiceFactory.Instance.Create<RelationalTypeMappingSourceDependencies>(),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions());
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
......@@ -60,7 +60,7 @@ public void MaxBatchSize_is_optional()
TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
TestServiceFactory.Instance.Create<RelationalTypeMappingSourceDependencies>(),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions());
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
......
......@@ -18,7 +18,7 @@ public void AddCommand_returns_false_when_max_batch_size_is_reached()
TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
TestServiceFactory.Instance.Create<RelationalTypeMappingSourceDependencies>(),
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlOptions());
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册