├── Npgsql.snk ├── global.json ├── src ├── EFCore.KingbaseES │ ├── Lib │ │ └── Kdbndp.dll │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── KdbndpStrings.Designer.tt │ ├── Storage │ │ ├── ValueConversion │ │ │ ├── IKdbndpArrayConverter.cs │ │ │ └── KdbndpValueConverterSelector.cs │ │ └── Internal │ │ │ ├── Mapping │ │ │ ├── IKdbndpTypeMapping.cs │ │ │ ├── KdbndpUintTypeMapping.cs │ │ │ ├── KdbndpBoolTypeMapping.cs │ │ │ ├── KdbndpMoneyTypeMapping.cs │ │ │ ├── KdbndpTsRankingNormalizationTypeMapping.cs │ │ │ ├── KdbndpRegconfigTypeMapping.cs │ │ │ ├── KdbndpRegdictionaryTypeMapping.cs │ │ │ ├── KdbndpEStringTypeMapping.cs │ │ │ ├── KdbndpBigIntegerTypeMapping.cs │ │ │ ├── KdbndpFloatTypeMapping.cs │ │ │ ├── KdbndpDoubleTypeMapping.cs │ │ │ ├── KdbndpByteArrayTypeMapping.cs │ │ │ ├── KdbndpTsVectorTypeMapping.cs │ │ │ ├── KdbndpIntervalTypeMapping.cs │ │ │ ├── KdbndpTidTypeMapping.cs │ │ │ ├── KdbndpVarbitTypeMapping.cs │ │ │ ├── KdbndpDecimalTypeMapping.cs │ │ │ ├── KdbndpCharacterCharTypeMapping.cs │ │ │ ├── KdbndpStringTypeMapping.cs │ │ │ ├── KdbndpBitTypeMapping.cs │ │ │ ├── KdbndpTimestampTypeMapping.cs │ │ │ ├── KdbndpTimeTzTypeMapping.cs │ │ │ ├── KdbndpDateTypeMapping.cs │ │ │ ├── KdbndpEnumTypeMapping.cs │ │ │ ├── KdbndpTimeTypeMapping.cs │ │ │ ├── KdbndpCharacterStringTypeMapping.cs │ │ │ ├── KdbndpJsonTypeMapping.cs │ │ │ ├── KdbndpTimestampTzTypeMapping.cs │ │ │ └── KdbndpTypeMapping.cs │ │ │ ├── IKdbndpRelationalConnection.cs │ │ │ ├── KdbndpTransientExceptionDetector.cs │ │ │ ├── KdbndpExecutionStrategyFactory.cs │ │ │ ├── KdbndpExecutionStrategy.cs │ │ │ └── KdbndpSqlGenerationHelper.cs │ ├── Migrations │ │ ├── Operations │ │ │ ├── KdbndpDropDatabaseOperation.cs │ │ │ └── KdbndpCreateDatabaseOperation.cs │ │ └── Internal │ │ │ └── KdbndpHistoryRepository.cs │ ├── Metadata │ │ ├── Internal │ │ │ ├── CockroachDbAnnotationNames.cs │ │ │ └── KdbndpAnnotationNames.cs │ │ ├── SortOrder.cs │ │ ├── NullSortOrder.cs │ │ ├── Conventions │ │ │ └── KdbndpSharedTableConvention.cs │ │ └── KdbndpValueGenerationStrategy.cs │ ├── Extensions │ │ ├── MethodInfoExtensions.cs │ │ ├── RelationalTypeMappingExtensions.cs │ │ ├── PropertyInfoExtensions.cs │ │ ├── VersionExtensions.cs │ │ ├── KdbndpDatabaseModelExtensions.cs │ │ ├── KdbndpDatabaseFacadeExtensions.cs │ │ ├── MemberInfoExtensions.cs │ │ ├── KdbndpMigrationBuilderExtensions.cs │ │ ├── TypeExtensions.cs │ │ ├── KdbndpAlterDatabaseOperationExtensions.cs │ │ ├── DbFunctionsExtensions │ │ │ └── KdbndpDbFunctionsExtensions.cs │ │ └── MetadataExtensions │ │ │ └── KdbndpEntityTypeExtensions.cs │ ├── Query │ │ ├── Internal │ │ │ ├── KdbndpQueryCompilationContext.cs │ │ │ ├── KdbndpParameterBasedSqlProcessorFactory.cs │ │ │ ├── KdbndpSqlTranslatingExpressionVisitorFactory.cs │ │ │ ├── KdbndpQuerySqlGeneratorFactory.cs │ │ │ ├── KdbndpQueryCompilationContextFactory.cs │ │ │ ├── KdbndpParameterBasedSqlProcessor.cs │ │ │ ├── KdbndpCompiledQueryCacheKeyGenerator.cs │ │ │ └── KdbndpEvaluatableExpressionFilter.cs │ │ ├── Expressions │ │ │ ├── PostgresExpressionType.cs │ │ │ └── Internal │ │ │ │ ├── PostgresRegexMatchExpression.cs │ │ │ │ ├── PostgresArrayIndexExpression.cs │ │ │ │ ├── PostgresILikeExpression.cs │ │ │ │ └── PostgresUnknownBinaryExpression.cs │ │ └── ExpressionTranslators │ │ │ └── Internal │ │ │ ├── KdbndpStringMemberTranslator.cs │ │ │ ├── KdbndpRandomTranslator.cs │ │ │ ├── KdbndpNewGuidTranslator.cs │ │ │ ├── KdbndpBigIntegerMemberTranslator.cs │ │ │ ├── KdbndpMemberTranslatorProvider.cs │ │ │ ├── KdbndpConvertTranslator.cs │ │ │ ├── KdbndpRegexIsMatchTranslator.cs │ │ │ ├── KdbndpMethodCallTranslatorProvider.cs │ │ │ ├── KdbndpTimeSpanMemberTranslator.cs │ │ │ ├── KdbndpObjectToStringTranslator.cs │ │ │ ├── KdbndpFuzzyStringMatchMethodTranslator.cs │ │ │ └── KdbndpByteArrayMethodTranslator.cs │ ├── Utilities │ │ ├── Util.cs │ │ ├── StringBuilderExtensions.cs │ │ └── SortOrderHelper.cs │ ├── ValueGeneration │ │ └── Internal │ │ │ ├── IKdbndpValueGeneratorCache.cs │ │ │ ├── IKdbndpSequenceValueGeneratorFactory.cs │ │ │ ├── KdbndpSequenceValueGeneratorState.cs │ │ │ ├── KdbndpValueGeneratorCache.cs │ │ │ ├── KdbndpValueGeneratorSelector.cs │ │ │ └── KdbndpSequenceValueGeneratorFactory.cs │ ├── Design │ │ └── Internal │ │ │ └── KdbndpDesignTimeServices.cs │ ├── Internal │ │ ├── IReadOnlyListExtensions.cs │ │ └── KdbndpSingletonOptions.cs │ ├── Scaffolding │ │ └── Internal │ │ │ ├── DbDataReaderExtension.cs │ │ │ └── KdbndpCodeGenerator.cs │ ├── Diagnostics │ │ └── Internal │ │ │ └── KdbndpLoggingDefinitions.cs │ ├── Update │ │ └── Internal │ │ │ └── KdbndpModificationCommandBatchFactory.cs │ ├── Infrastructure │ │ └── Internal │ │ │ └── IKdbndpSingletonOptions.cs │ ├── README.md │ ├── Types │ │ └── KdbndpTsRankingNormalization.cs │ └── EFCore.KingbaseES.csproj ├── Directory.Build.props └── .editorconfig ├── NuGet.config ├── test ├── Directory.Build.props └── KingbaseES.BasicTest │ ├── Blog.cs │ ├── KingbaseES.BasicTest.csproj │ ├── BlogContextFactory.cs │ ├── BlogContext.cs │ ├── Migrations │ ├── 20231013152623_initial.cs │ ├── BlogContextModelSnapshot.cs │ └── 20231013152623_initial.Designer.cs │ └── Program.cs ├── LICENSE ├── Directory.Build.props ├── Directory.Packages.props ├── README.md └── EFCore.KingbaseES.sln /Npgsql.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnetcore/EntityFrameworkCore.KingbaseES/HEAD/Npgsql.snk -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.100", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": "false" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Lib/Kdbndp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnetcore/EntityFrameworkCore.KingbaseES/HEAD/src/EFCore.KingbaseES/Lib/Kdbndp.dll -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | 4 | [assembly: DesignTimeProviderServices("Kdbndp.EntityFrameworkCore.KingbaseES.Design.Internal.KdbndpDesignTimeServices")] 5 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Properties/KdbndpStrings.Designer.tt: -------------------------------------------------------------------------------- 1 | <# 2 | Session["ResourceFile"] = "KdbndpStrings.resx"; 3 | Session["LoggingDefinitionsClass"] = "Diagnostics.Internal.KdbndpLoggingDefinitions"; 4 | #> 5 | <#@ include file="..\..\..\tools\Resources.tt" #> 6 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/ValueConversion/IKdbndpArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.ValueConversion; 4 | 5 | public interface IKdbndpArrayConverter 6 | { 7 | ValueConverter ElementConverter { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Migrations/Operations/KdbndpDropDatabaseOperation.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations.Operations; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Migrations.Operations; 4 | 5 | public class KdbndpDropDatabaseOperation : MigrationOperation 6 | { 7 | public virtual string Name { get; set; } = null!; 8 | } 9 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/Internal/CockroachDbAnnotationNames.cs: -------------------------------------------------------------------------------- 1 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata.Internal; 2 | 3 | public static class CockroachDbAnnotationNames 4 | { 5 | public const string Prefix = KdbndpAnnotationNames.Prefix + "CockroachDB:"; 6 | 7 | public const string InterleaveInParent = Prefix + "InterleaveInParent"; 8 | } 9 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/IKdbndpTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using KdbndpTypes; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 4 | 5 | public interface IKdbndpTypeMapping 6 | { 7 | /// 8 | /// The database type used by Kdbndp. 9 | /// 10 | KdbndpDbType KdbndpDbType { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/IKdbndpRelationalConnection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 4 | 5 | public interface IKdbndpRelationalConnection : IRelationalConnection 6 | { 7 | IKdbndpRelationalConnection CreateMasterConnection(); 8 | 9 | KdbndpRelationalConnection CloneWith(string connectionString); 10 | } 11 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/Blog.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace KingbaseES.BasicTest 5 | { 6 | [Table("blogs")] 7 | public class Blog 8 | { 9 | [Key] 10 | [Column("id")] 11 | public int Id { get; set; } 12 | [Column("name")] 13 | public string Name { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Migrations/Operations/KdbndpCreateDatabaseOperation.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations.Operations; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Migrations.Operations; 4 | 5 | public class KdbndpCreateDatabaseOperation : DatabaseOperation 6 | { 7 | public virtual string Name { get; set; } = null!; 8 | public virtual string? Template { get; set; } 9 | public virtual string? Tablespace { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 2 | 3 | /// 4 | /// Options for modifying sort ordering of index values. 5 | /// 6 | public enum SortOrder 7 | { 8 | /// 9 | /// Specifies ascending sort order, which is the default. 10 | /// 11 | Ascending = 0, 12 | 13 | /// 14 | /// Specifies descending sort order. 15 | /// 16 | Descending = 1, 17 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/KdbndpTransientExceptionDetector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 4 | 5 | /// 6 | /// Detects the exceptions caused by KingbaseES or network transient failures. 7 | /// 8 | public class KdbndpTransientExceptionDetector 9 | { 10 | public static bool ShouldRetryOn(Exception? ex) 11 | => (ex as KdbndpException)?.IsTransient == true || ex is TimeoutException; 12 | } 13 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | snupkg 7 | true 8 | enable 9 | nullablePublicOnly 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/MethodInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace System.Reflection; 5 | 6 | [DebuggerStepThrough] 7 | internal static class MethodInfoExtensions 8 | { 9 | internal static bool IsClosedFormOf( 10 | this MethodInfo methodInfo, MethodInfo genericMethod) 11 | => methodInfo.IsGenericMethod 12 | && Equals( 13 | methodInfo.GetGenericMethodDefinition(), 14 | genericMethod); 15 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/RelationalTypeMappingExtensions.cs: -------------------------------------------------------------------------------- 1 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace Microsoft.EntityFrameworkCore.Storage; 5 | 6 | internal static class RelationalTypeMappingExtensions 7 | { 8 | internal static string GenerateEmbeddedSqlLiteral(this RelationalTypeMapping mapping, object? value) 9 | => mapping is KdbndpTypeMapping KdbndpTypeMapping 10 | ? KdbndpTypeMapping.GenerateEmbeddedSqlLiteral(value) 11 | : mapping.GenerateSqlLiteral(value); 12 | } -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/KingbaseES.BasicTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/KdbndpExecutionStrategyFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 4 | 5 | public class KdbndpExecutionStrategyFactory : RelationalExecutionStrategyFactory 6 | { 7 | public KdbndpExecutionStrategyFactory( 8 | ExecutionStrategyDependencies dependencies) 9 | : base(dependencies) 10 | { 11 | } 12 | 13 | protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyDependencies dependencies) 14 | => new KdbndpExecutionStrategy(dependencies); 15 | } 16 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/BlogContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | 4 | namespace KingbaseES.BasicTest 5 | { 6 | public class BlogContextFactory : IDesignTimeDbContextFactory 7 | { 8 | public BlogContext CreateDbContext(string[] args) 9 | { 10 | var optionsBuilder = new DbContextOptionsBuilder(); 11 | 12 | optionsBuilder.UseKdbndp("host=localhost;port=54321;database=test;username=system;password=123456;"); 13 | 14 | return new BlogContext(optionsBuilder.Options); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/PropertyInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace System.Reflection; 5 | 6 | [DebuggerStepThrough] 7 | internal static class PropertyInfoExtensions 8 | { 9 | public static bool IsStatic(this PropertyInfo property) 10 | => (property.GetMethod ?? property.SetMethod)!.IsStatic; 11 | 12 | public static bool IsIndexerProperty(this PropertyInfo propertyInfo) 13 | { 14 | var indexParams = propertyInfo.GetIndexParameters(); 15 | return indexParams.Length == 1 16 | && indexParams[0].ParameterType == typeof(string); 17 | } 18 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/VersionExtensions.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace System; 3 | 4 | internal static class VersionExtensions 5 | { 6 | // Note: a null version is interpreted as the latest version and will always return true. 7 | internal static bool AtLeast(this Version? version, int major, int minor = 0) 8 | => version is null || version >= new Version(major, minor); 9 | 10 | // Note: a null version is interpreted as the latest version and will always return false. 11 | internal static bool IsUnder(this Version? version, int major, int minor = 0) 12 | => version is not null && version < new Version(major, minor); 13 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpQueryCompilationContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 4 | 5 | public class KdbndpQueryCompilationContext : RelationalQueryCompilationContext 6 | { 7 | public KdbndpQueryCompilationContext( 8 | QueryCompilationContextDependencies dependencies, 9 | RelationalQueryCompilationContextDependencies relationalDependencies, bool async) 10 | : base(dependencies, relationalDependencies, async) 11 | { 12 | } 13 | 14 | public override bool IsBuffering 15 | => base.IsBuffering || 16 | QuerySplittingBehavior == Microsoft.EntityFrameworkCore.QuerySplittingBehavior.SplitQuery; 17 | } 18 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpParameterBasedSqlProcessorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 4 | 5 | public class KdbndpParameterBasedSqlProcessorFactory : IRelationalParameterBasedSqlProcessorFactory 6 | { 7 | private readonly RelationalParameterBasedSqlProcessorDependencies _dependencies; 8 | 9 | public KdbndpParameterBasedSqlProcessorFactory( 10 | RelationalParameterBasedSqlProcessorDependencies dependencies) 11 | => _dependencies = dependencies; 12 | 13 | public virtual RelationalParameterBasedSqlProcessor Create(bool useRelationalNulls) 14 | => new KdbndpParameterBasedSqlProcessor(_dependencies, useRelationalNulls); 15 | } 16 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpUintTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using KdbndpTypes; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 5 | 6 | public class KdbndpUintTypeMapping : KdbndpTypeMapping 7 | { 8 | public KdbndpUintTypeMapping(string storeType, KdbndpDbType KdbndpDbType) 9 | : base(storeType, typeof(uint), KdbndpDbType) {} 10 | 11 | protected KdbndpUintTypeMapping(RelationalTypeMappingParameters parameters, KdbndpDbType KdbndpDbType) 12 | : base(parameters, KdbndpDbType) {} 13 | 14 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 15 | => new KdbndpUintTypeMapping(parameters, KdbndpDbType); 16 | } 17 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpBoolTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 4 | 5 | public class KdbndpBoolTypeMapping : RelationalTypeMapping 6 | { 7 | public KdbndpBoolTypeMapping() : base("boolean", typeof(bool), System.Data.DbType.Boolean) {} 8 | 9 | protected KdbndpBoolTypeMapping(RelationalTypeMappingParameters parameters) 10 | : base(parameters) {} 11 | 12 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 13 | => new KdbndpBoolTypeMapping(parameters); 14 | 15 | protected override string GenerateNonNullSqlLiteral(object value) 16 | => (bool)value ? "TRUE" : "FALSE"; 17 | } 18 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/BlogContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace KingbaseES.BasicTest 4 | { 5 | public class BlogContext : DbContext 6 | { 7 | public BlogContext(DbContextOptions options):base(options) 8 | { 9 | 10 | } 11 | public DbSet Blogs { get; set; } 12 | 13 | protected override void OnModelCreating(ModelBuilder modelBuilder) 14 | { 15 | modelBuilder.HasSequence("tests_id_seq") 16 | .HasMin(1).IncrementsBy(1).HasMax(long.MaxValue).StartsAt(1); 17 | 18 | modelBuilder.Entity(b => 19 | { 20 | b.Property(p => p.Id).HasDefaultValueSql("nextval('tests_id_seq'::regclass)"); 21 | }); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Utilities/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Utilities; 4 | 5 | internal static class Statics 6 | { 7 | internal static readonly bool[][] TrueArrays = 8 | { 9 | Array.Empty(), 10 | new[] { true }, 11 | new[] { true, true }, 12 | new[] { true, true, true }, 13 | new[] { true, true, true, true }, 14 | new[] { true, true, true, true, true }, 15 | new[] { true, true, true, true, true, true }, 16 | new[] { true, true, true, true, true, true, true }, 17 | new[] { true, true, true, true, true, true, true, true } 18 | }; 19 | 20 | internal static readonly bool[][] FalseArrays = 21 | { 22 | Array.Empty(), 23 | new[] { false }, 24 | new[] { false, false } 25 | }; 26 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpMoneyTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpMoneyTypeMapping : DecimalTypeMapping 9 | { 10 | public KdbndpMoneyTypeMapping() : base("money", System.Data.DbType.Currency) {} 11 | 12 | protected KdbndpMoneyTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters) 14 | { 15 | } 16 | 17 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 18 | => new KdbndpMoneyTypeMapping(parameters); 19 | 20 | protected override string GenerateNonNullSqlLiteral(object value) 21 | => base.GenerateNonNullSqlLiteral(value) + "::money"; 22 | } 23 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/NullSortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 2 | 3 | /// 4 | /// Options for modifying sort ordering of NULL-values in indexes. 5 | /// 6 | public enum NullSortOrder 7 | { 8 | /// 9 | /// Represents an unspecified sort order. The database default will be used. 10 | /// 11 | Unspecified = 0, 12 | 13 | /// 14 | /// Specifies that nulls sort before non-nulls. 15 | /// 16 | /// 17 | /// This is the default when is specified. 18 | /// 19 | NullsFirst = 1, 20 | 21 | /// 22 | /// Specifies that nulls sort after non-nulls. 23 | /// 24 | /// 25 | /// This is the default when is not specified. 26 | /// 27 | NullsLast = 2, 28 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002-2021, Npgsql 2 | 3 | Permission to use, copy, modify, and distribute this software and its 4 | documentation for any purpose, without fee, and without a written agreement 5 | is hereby granted, provided that the above copyright notice and this 6 | paragraph and the following two paragraphs appear in all copies. 7 | 8 | IN NO EVENT SHALL NPGSQL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 9 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, 10 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 11 | Npgsql HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | 13 | NPGSQL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED 14 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 | PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND Npgsql 16 | HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, 17 | OR MODIFICATIONS. 18 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTsRankingNormalizationTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | public class KdbndpTsRankingNormalizationTypeMapping : IntTypeMapping 8 | { 9 | public KdbndpTsRankingNormalizationTypeMapping() : base(new RelationalTypeMappingParameters( 10 | new CoreTypeMappingParameters(typeof(KdbndpTsRankingNormalization), new EnumToNumberConverter()), 11 | "integer")) {} 12 | 13 | protected KdbndpTsRankingNormalizationTypeMapping(RelationalTypeMappingParameters parameters) 14 | : base(parameters) {} 15 | 16 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 17 | => new KdbndpTsRankingNormalizationTypeMapping(parameters); 18 | } 19 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/IKdbndpValueGeneratorCache.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using Microsoft.EntityFrameworkCore.ValueGeneration; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 6 | 7 | /// 8 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 9 | /// directly from your code. This API may change or be removed in future releases. 10 | /// 11 | public interface IKdbndpValueGeneratorCache : IValueGeneratorCache 12 | { 13 | /// 14 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 15 | /// directly from your code. This API may change or be removed in future releases. 16 | /// 17 | KdbndpSequenceValueGeneratorState GetOrAddSequenceState( 18 | IProperty property, 19 | IRelationalConnection connection); 20 | } 21 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/KdbndpDatabaseModelExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.EntityFrameworkCore.Scaffolding.Metadata; 4 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace Microsoft.EntityFrameworkCore; 8 | 9 | public static class KdbndpDatabaseModelExtensions 10 | { 11 | public static PostgresExtension GetOrAddPostgresExtension( 12 | this DatabaseModel model, 13 | string? schema, 14 | string name, 15 | string? version) 16 | => PostgresExtension.GetOrAddPostgresExtension(model, schema, name, version); 17 | 18 | public static IReadOnlyList GetPostgresExtensions(this DatabaseModel model) 19 | => PostgresExtension.GetPostgresExtensions(model).ToArray(); 20 | 21 | public static IReadOnlyList GetPostgresEnums(this DatabaseModel model) 22 | => PostgresEnum.GetPostgresEnums(model).ToArray(); 23 | } 24 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpSqlTranslatingExpressionVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 4 | 5 | public class KdbndpSqlTranslatingExpressionVisitorFactory : IRelationalSqlTranslatingExpressionVisitorFactory 6 | { 7 | private readonly RelationalSqlTranslatingExpressionVisitorDependencies _dependencies; 8 | 9 | public KdbndpSqlTranslatingExpressionVisitorFactory( 10 | RelationalSqlTranslatingExpressionVisitorDependencies dependencies) 11 | => _dependencies = dependencies; 12 | 13 | public virtual RelationalSqlTranslatingExpressionVisitor Create( 14 | QueryCompilationContext queryCompilationContext, 15 | QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) 16 | => new KdbndpSqlTranslatingExpressionVisitor( 17 | _dependencies, 18 | queryCompilationContext, 19 | queryableMethodTranslatingExpressionVisitor); 20 | } 21 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpRegconfigTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | using KdbndpTypes; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | public class KdbndpRegconfigTypeMapping : KdbndpTypeMapping 8 | { 9 | public KdbndpRegconfigTypeMapping() : base("regconfig", typeof(uint), KdbndpDbType.Regconfig) { } 10 | 11 | protected KdbndpRegconfigTypeMapping(RelationalTypeMappingParameters parameters) 12 | : base(parameters, KdbndpDbType.Regconfig) {} 13 | 14 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 15 | => new KdbndpRegconfigTypeMapping(parameters); 16 | 17 | protected override string GenerateNonNullSqlLiteral(object value) 18 | => $"'{EscapeSqlLiteral((string)value)}'"; 19 | 20 | private string EscapeSqlLiteral(string literal) 21 | => Check.NotNull(literal, nameof(literal)).Replace("'", "''"); 22 | } 23 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpRegdictionaryTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | using KdbndpTypes; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | public class KdbndpRegdictionaryTypeMapping : KdbndpTypeMapping 8 | { 9 | public KdbndpRegdictionaryTypeMapping() : base("regdictionary", typeof(uint), KdbndpDbType.Oid) { } 10 | 11 | protected KdbndpRegdictionaryTypeMapping(RelationalTypeMappingParameters parameters) 12 | : base(parameters, KdbndpDbType.Oid) { } 13 | 14 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 15 | => new KdbndpRegdictionaryTypeMapping(parameters); 16 | 17 | protected override string GenerateNonNullSqlLiteral(object value) 18 | => $"'{EscapeSqlLiteral((string)value)}'"; 19 | 20 | private string EscapeSqlLiteral(string literal) 21 | => Check.NotNull(literal, nameof(literal)).Replace("'", "''"); 22 | } 23 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpEStringTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using Kdbndp.EntityFrameworkCore.KingbaseES.Utilities; 3 | using KdbndpTypes; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | /// 8 | /// Represents a so-called KingbaseES E-string literal string, which allows C-style escape sequences. 9 | /// This is a "virtual" type mapping which is never returned by . 10 | /// It is only used internally by some method translators to produce literal strings. 11 | /// 12 | /// 13 | /// See https://www.KingbaseES.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS 14 | /// 15 | public class KdbndpEStringTypeMapping : StringTypeMapping 16 | { 17 | public KdbndpEStringTypeMapping() : base("does_not_exist", System.Data.DbType.String) {} 18 | 19 | protected override string GenerateNonNullSqlLiteral(object value) 20 | => $"E'{EscapeSqlLiteral((string)value)}'"; 21 | } 22 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Utilities/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace System.Text; 5 | 6 | internal static class StringBuilderExtensions 7 | { 8 | public static StringBuilder AppendJoin( 9 | this StringBuilder stringBuilder, IEnumerable values, string separator = ", ") 10 | => stringBuilder.AppendJoin(values, (sb, value) => sb.Append(value), separator); 11 | 12 | public static StringBuilder AppendJoin( 13 | this StringBuilder stringBuilder, IEnumerable values, Action joinAction, 14 | string separator) 15 | { 16 | var appended = false; 17 | 18 | foreach (var value in values) 19 | { 20 | joinAction(stringBuilder, value); 21 | stringBuilder.Append(separator); 22 | appended = true; 23 | } 24 | 25 | if (appended) 26 | { 27 | stringBuilder.Length -= separator.Length; 28 | } 29 | 30 | return stringBuilder; 31 | } 32 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Design/Internal/KdbndpDesignTimeServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Design; 2 | using Microsoft.EntityFrameworkCore.Scaffolding; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Kdbndp.EntityFrameworkCore.KingbaseES.Scaffolding.Internal; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Design.Internal; 8 | 9 | public class KdbndpDesignTimeServices : IDesignTimeServices 10 | { 11 | public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection) 12 | { 13 | Check.NotNull(serviceCollection, nameof(serviceCollection)); 14 | 15 | serviceCollection.AddEntityFrameworkKdbndp(); 16 | new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection) 17 | .TryAdd() 18 | .TryAdd() 19 | .TryAdd() 20 | .TryAddCoreServices(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Internal/IReadOnlyListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Internal; 6 | 7 | internal static class IReadOnlyListExtensions 8 | { 9 | public static IReadOnlyList Slice(this IReadOnlyList list, int start) 10 | => new IReadOnlyListSlice(list, start); 11 | 12 | private sealed class IReadOnlyListSlice : IReadOnlyList 13 | { 14 | private IReadOnlyList _underlying; 15 | private int _start; 16 | 17 | internal IReadOnlyListSlice(IReadOnlyList underlying, int start) 18 | { 19 | _underlying = underlying; 20 | _start = start; 21 | } 22 | 23 | public IEnumerator GetEnumerator() => _underlying.Skip(_start).GetEnumerator(); 24 | 25 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 26 | 27 | public int Count => _underlying.Count - _start; 28 | 29 | public T this[int index] => _underlying[_start + index]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Scaffolding/Internal/DbDataReaderExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.Diagnostics; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Scaffolding.Internal; 6 | 7 | internal static class DbDataReaderExtension 8 | { 9 | [DebuggerStepThrough] 10 | [return: MaybeNull] 11 | internal static T GetValueOrDefault(this DbDataReader reader, string name) 12 | { 13 | var idx = reader.GetOrdinal(name); 14 | return reader.IsDBNull(idx) 15 | ? default 16 | : reader.GetFieldValue(idx); 17 | } 18 | 19 | [DebuggerStepThrough] 20 | [return: MaybeNull] 21 | internal static T GetValueOrDefault(this DbDataRecord record, string name) 22 | { 23 | var idx = record.GetOrdinal(name); 24 | return record.IsDBNull(idx) 25 | ? default 26 | : (T)record.GetValue(idx); 27 | } 28 | 29 | [DebuggerStepThrough] 30 | internal static T GetFieldValue(this DbDataRecord record, string name) 31 | => (T)record.GetValue(record.GetOrdinal(name)); 32 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpQuerySqlGeneratorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 5 | 6 | /// 7 | /// The default factory for Kdbndp-specific query SQL generators. 8 | /// 9 | public class KdbndpQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory 10 | { 11 | private readonly QuerySqlGeneratorDependencies _dependencies; 12 | private readonly IKdbndpSingletonOptions _KdbndpSingletonOptions; 13 | 14 | public KdbndpQuerySqlGeneratorFactory( 15 | QuerySqlGeneratorDependencies dependencies, 16 | IKdbndpSingletonOptions KdbndpSingletonOptions) 17 | { 18 | _dependencies = dependencies; 19 | _KdbndpSingletonOptions = KdbndpSingletonOptions; 20 | } 21 | 22 | public virtual QuerySqlGenerator Create() 23 | => new KdbndpQuerySqlGenerator( 24 | _dependencies, 25 | _KdbndpSingletonOptions.ReverseNullOrderingEnabled, 26 | _KdbndpSingletonOptions.PostgresVersion); 27 | } 28 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpBigIntegerTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using KdbndpTypes; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | public class KdbndpBigIntegerTypeMapping : KdbndpTypeMapping 8 | { 9 | public KdbndpBigIntegerTypeMapping() : base("numeric", typeof(BigInteger), KdbndpDbType.Numeric) {} 10 | 11 | protected KdbndpBigIntegerTypeMapping(RelationalTypeMappingParameters parameters) 12 | : base(parameters, KdbndpDbType.Numeric) 13 | { 14 | } 15 | 16 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 17 | => new KdbndpBigIntegerTypeMapping(parameters); 18 | 19 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 20 | => parameters.Precision is null 21 | ? storeType 22 | : parameters.Scale is null 23 | ? $"numeric({parameters.Precision})" 24 | : $"numeric({parameters.Precision},{parameters.Scale})"; 25 | } 26 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpQueryCompilationContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 5 | 6 | public class KdbndpQueryCompilationContextFactory : IQueryCompilationContextFactory 7 | { 8 | private readonly QueryCompilationContextDependencies _dependencies; 9 | private readonly RelationalQueryCompilationContextDependencies _relationalDependencies; 10 | 11 | public KdbndpQueryCompilationContextFactory( 12 | QueryCompilationContextDependencies dependencies, 13 | RelationalQueryCompilationContextDependencies relationalDependencies) 14 | { 15 | Check.NotNull(dependencies, nameof(dependencies)); 16 | Check.NotNull(relationalDependencies, nameof(relationalDependencies)); 17 | 18 | _dependencies = dependencies; 19 | _relationalDependencies = relationalDependencies; 20 | } 21 | 22 | public virtual QueryCompilationContext Create(bool async) 23 | => new KdbndpQueryCompilationContext(_dependencies, _relationalDependencies, async); 24 | } 25 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6.0.22 4 | net6.0 5 | latest 6 | true 7 | latest 8 | $(NoWarn);NU5105 9 | 10 | 11 | $(NoWarn);CS1572;CS1573;CS1574;CS1591 12 | true 13 | $(MSBuildThisFileDirectory)Npgsql.snk 14 | true 15 | true 16 | true 17 | true 18 | PostgreSQL 19 | https://github.com/dotnetcore/EntityFrameworkCore.KingbaseES 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpParameterBasedSqlProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 7 | 8 | public class KdbndpParameterBasedSqlProcessor : RelationalParameterBasedSqlProcessor 9 | { 10 | public KdbndpParameterBasedSqlProcessor( 11 | RelationalParameterBasedSqlProcessorDependencies dependencies, 12 | bool useRelationalNulls) 13 | : base(dependencies, useRelationalNulls) 14 | { 15 | } 16 | 17 | /// 18 | protected override SelectExpression ProcessSqlNullability( 19 | SelectExpression selectExpression, IReadOnlyDictionary parametersValues, out bool canCache) 20 | { 21 | Check.NotNull(selectExpression, nameof(selectExpression)); 22 | Check.NotNull(parametersValues, nameof(parametersValues)); 23 | 24 | return new KdbndpSqlNullabilityProcessor(Dependencies, UseRelationalNulls).Process(selectExpression, parametersValues, out canCache); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpFloatTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 5 | 6 | public class KdbndpFloatTypeMapping : FloatTypeMapping 7 | { 8 | public KdbndpFloatTypeMapping() : base("real", System.Data.DbType.Single) {} 9 | 10 | protected KdbndpFloatTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) {} 11 | 12 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 13 | => new KdbndpFloatTypeMapping(parameters); 14 | 15 | protected override string GenerateNonNullSqlLiteral(object value) 16 | { 17 | var singleValue = Convert.ToSingle(value); 18 | if (double.IsNaN(singleValue)) 19 | { 20 | return "'NaN'"; 21 | } 22 | 23 | if (double.IsPositiveInfinity(singleValue)) 24 | { 25 | return "'Infinity'"; 26 | } 27 | 28 | if (double.IsNegativeInfinity(singleValue)) 29 | { 30 | return "'-Infinity'"; 31 | } 32 | 33 | return base.GenerateNonNullSqlLiteral(singleValue); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpDoubleTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 5 | 6 | public class KdbndpDoubleTypeMapping : DoubleTypeMapping 7 | { 8 | public KdbndpDoubleTypeMapping() : base("double precision", System.Data.DbType.Double) {} 9 | 10 | protected KdbndpDoubleTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) {} 11 | 12 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 13 | => new KdbndpDoubleTypeMapping(parameters); 14 | 15 | protected override string GenerateNonNullSqlLiteral(object value) 16 | { 17 | var doubleValue = Convert.ToDouble(value); 18 | if (double.IsNaN(doubleValue)) 19 | { 20 | return "'NaN'"; 21 | } 22 | 23 | if (double.IsPositiveInfinity(doubleValue)) 24 | { 25 | return "'Infinity'"; 26 | } 27 | 28 | if (double.IsNegativeInfinity(doubleValue)) 29 | { 30 | return "'-Infinity'"; 31 | } 32 | 33 | return base.GenerateNonNullSqlLiteral(doubleValue); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6.0.22 4 | 6.0.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Diagnostics/Internal/KdbndpLoggingDefinitions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Diagnostics; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Diagnostics.Internal; 4 | 5 | public class KdbndpLoggingDefinitions : RelationalLoggingDefinitions 6 | { 7 | public EventDefinitionBase? LogFoundDefaultSchema; 8 | public EventDefinitionBase? LogFoundColumn; 9 | public EventDefinitionBase? LogFoundForeignKey; 10 | public EventDefinitionBase? LogFoundCollation; 11 | public EventDefinitionBase? LogPrincipalTableNotInSelectionSet; 12 | public EventDefinitionBase? LogMissingSchema; 13 | public EventDefinitionBase? LogMissingTable; 14 | public EventDefinitionBase? LogFoundSequence; 15 | public EventDefinitionBase? LogFoundTable; 16 | public EventDefinitionBase? LogFoundIndex; 17 | public EventDefinitionBase? LogFoundPrimaryKey; 18 | public EventDefinitionBase? LogFoundUniqueConstraint; 19 | public EventDefinitionBase? LogPrincipalColumnNotFound; 20 | public EventDefinitionBase? LogEnumColumnSkipped; 21 | public EventDefinitionBase? LogExpressionIndexSkipped; 22 | public EventDefinitionBase? LogUnsupportedColumnConstraintSkipped; 23 | public EventDefinitionBase? LogUnsupportedColumnIndexSkipped; 24 | } 25 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Update/Internal/KdbndpModificationCommandBatchFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Microsoft.EntityFrameworkCore.Update; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Update.Internal; 8 | 9 | public class KdbndpModificationCommandBatchFactory : IModificationCommandBatchFactory 10 | { 11 | private readonly ModificationCommandBatchFactoryDependencies _dependencies; 12 | private readonly IDbContextOptions _options; 13 | 14 | public KdbndpModificationCommandBatchFactory( 15 | ModificationCommandBatchFactoryDependencies dependencies, 16 | IDbContextOptions options) 17 | { 18 | Check.NotNull(dependencies, nameof(dependencies)); 19 | Check.NotNull(options, nameof(options)); 20 | 21 | _dependencies = dependencies; 22 | _options = options; 23 | } 24 | 25 | public virtual ModificationCommandBatch Create() 26 | { 27 | var optionsExtension = _options.Extensions.OfType().FirstOrDefault(); 28 | 29 | return new KdbndpModificationCommandBatch(_dependencies, optionsExtension?.MaxBatchSize); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Infrastructure/Internal/IKdbndpSingletonOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 6 | 7 | /// 8 | /// Represents options for Kdbndp that can only be set at the singleton level. 9 | /// 10 | public interface IKdbndpSingletonOptions : ISingletonOptions 11 | { 12 | /// 13 | /// The backend version to target. 14 | /// 15 | Version PostgresVersion { get; } 16 | 17 | /// 18 | /// The backend version to target, but returns unless the user explicitly specified a version. 19 | /// 20 | Version? PostgresVersionWithoutDefault { get; } 21 | 22 | /// 23 | /// Whether to target Redshift. 24 | /// 25 | bool UseRedshift { get; } 26 | 27 | /// 28 | /// True if reverse null ordering is enabled; otherwise, false. 29 | /// 30 | bool ReverseNullOrderingEnabled { get; } 31 | 32 | /// 33 | /// The collection of range mappings. 34 | /// 35 | IReadOnlyList UserRangeDefinitions { get; } 36 | } 37 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/IKdbndpSequenceValueGeneratorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Diagnostics; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using Microsoft.EntityFrameworkCore.Storage; 5 | using Microsoft.EntityFrameworkCore.ValueGeneration; 6 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 9 | 10 | /// 11 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 12 | /// directly from your code. This API may change or be removed in future releases. 13 | /// 14 | public interface IKdbndpSequenceValueGeneratorFactory 15 | { 16 | /// 17 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 18 | /// directly from your code. This API may change or be removed in future releases. 19 | /// 20 | ValueGenerator Create( 21 | IProperty property, 22 | KdbndpSequenceValueGeneratorState generatorState, 23 | IKdbndpRelationalConnection connection, 24 | IRawSqlCommandBuilder rawSqlCommandBuilder, 25 | IRelationalCommandDiagnosticsLogger commandLogger); 26 | } 27 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpByteArrayTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpByteArrayTypeMapping : RelationalTypeMapping 9 | { 10 | public KdbndpByteArrayTypeMapping() : base("bytea", typeof(byte[]), System.Data.DbType.Binary) {} 11 | 12 | protected KdbndpByteArrayTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpByteArrayTypeMapping(parameters); 17 | 18 | protected override string GenerateNonNullSqlLiteral(object value) 19 | { 20 | Check.NotNull(value, nameof(value)); 21 | var bytea = (byte[])value; 22 | 23 | var builder = new StringBuilder(bytea.Length * 2 + 6); 24 | 25 | builder.Append("BYTEA E'\\\\x"); 26 | foreach (var b in bytea) 27 | { 28 | builder.Append(b.ToString("X2", CultureInfo.InvariantCulture)); 29 | } 30 | 31 | builder.Append('\''); 32 | 33 | return builder.ToString(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTsVectorTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpTsVectorTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpTsVectorTypeMapping() : base("tsvector", typeof(KdbndpTsVector), KdbndpDbType.TsVector) { } 11 | 12 | protected KdbndpTsVectorTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpDbType.TsVector) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpTsVectorTypeMapping(parameters); 17 | 18 | protected override string GenerateNonNullSqlLiteral(object value) 19 | { 20 | Check.NotNull(value, nameof(value)); 21 | var vector = (KdbndpTsVector)value; 22 | var builder = new StringBuilder(); 23 | builder.Append("TSVECTOR "); 24 | var indexOfFirstQuote = builder.Length - 1; 25 | builder.Append(vector); 26 | builder.Replace("'", "''"); 27 | builder[indexOfFirstQuote] = '\''; 28 | builder.Append("'"); 29 | return builder.ToString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/README.md: -------------------------------------------------------------------------------- 1 | # Entity Framework Core provider for KingbaseES 2 | 3 | DotNetCore.EntityFrameworkCore.KingbaseES is the open source EF Core provider for KingbaseES. It allows you to interact with KingbaseES via the most widely-used .NET O/RM from Microsoft, and use familiar LINQ syntax to express queries. It's built on top of [KingbaseES](https://github.com/dotnetcore/EntityFrameworkCore.KingbaseES). 4 | 5 | The provider looks and feels just like any other Entity Framework Core provider. Here's a quick sample to get you started: 6 | 7 | ```csharp 8 | await using var ctx = new BlogContext(); 9 | await ctx.Database.EnsureDeletedAsync(); 10 | await ctx.Database.EnsureCreatedAsync(); 11 | 12 | // Insert a Blog 13 | ctx.Blogs.Add(new() { Name = "FooBlog" }); 14 | await ctx.SaveChangesAsync(); 15 | 16 | // Query all blogs who's name starts with F 17 | var fBlogs = await ctx.Blogs.Where(b => b.Name.StartsWith("F")).ToListAsync(); 18 | 19 | public class BlogContext : DbContext 20 | { 21 | public DbSet Blogs { get; set; } 22 | 23 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 24 | => optionsBuilder.UseKdbndp(@"Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase"); 25 | } 26 | 27 | public class Blog 28 | { 29 | public int Id { get; set; } 30 | public string Name { get; set; } 31 | } 32 | ``` -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/KdbndpSequenceValueGeneratorState.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | using Microsoft.EntityFrameworkCore.ValueGeneration; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 6 | 7 | /// 8 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 9 | /// directly from your code. This API may change or be removed in future releases. 10 | /// 11 | public class KdbndpSequenceValueGeneratorState : HiLoValueGeneratorState 12 | { 13 | /// 14 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 15 | /// directly from your code. This API may change or be removed in future releases. 16 | /// 17 | public KdbndpSequenceValueGeneratorState(ISequence sequence) 18 | : base(Check.NotNull(sequence, nameof(sequence)).IncrementBy) 19 | { 20 | Sequence = sequence; 21 | } 22 | 23 | /// 24 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 25 | /// directly from your code. This API may change or be removed in future releases. 26 | /// 27 | public virtual ISequence Sequence { get; } 28 | } 29 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/Migrations/20231013152623_initial.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KingbaseES.BasicTest.Migrations 6 | { 7 | public partial class Initial : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.CreateSequence( 12 | name: "tests_id_seq", 13 | minValue: 1L, 14 | maxValue: 9223372036854775807L); 15 | 16 | migrationBuilder.CreateTable( 17 | name: "blogs", 18 | columns: table => new 19 | { 20 | id = table.Column(type: "integer", nullable: false, defaultValueSql: "nextval('tests_id_seq'::regclass)"), 21 | name = table.Column(type: "text", nullable: true) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_blogs", x => x.id); 26 | }); 27 | } 28 | 29 | protected override void Down(MigrationBuilder migrationBuilder) 30 | { 31 | migrationBuilder.DropTable( 32 | name: "blogs"); 33 | 34 | migrationBuilder.DropSequence( 35 | name: "tests_id_seq"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/KdbndpDatabaseFacadeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace Microsoft.EntityFrameworkCore; 7 | 8 | /// 9 | /// Kdbndp specific extension methods for . 10 | /// 11 | public static class KdbndpDatabaseFacadeExtensions 12 | { 13 | /// 14 | /// 15 | /// Returns true if the database provider currently in use is the Kdbndp provider. 16 | /// 17 | /// 18 | /// This method can only be used after the has been configured because 19 | /// it is only then that the provider is known. This means that this method cannot be used 20 | /// in because this is where application code sets the 21 | /// provider to use as part of configuring the context. 22 | /// 23 | /// 24 | /// The facade from . 25 | /// True if Kdbndp is being used; false otherwise. 26 | public static bool IsKdbndp(this DatabaseFacade database) 27 | => database.ProviderName == typeof(KdbndpOptionsExtension).GetTypeInfo().Assembly.GetName().Name; 28 | } 29 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Expressions/PostgresExpressionType.cs: -------------------------------------------------------------------------------- 1 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions; 2 | 3 | /// 4 | /// KingbaseES-specific expression node types. 5 | /// 6 | public enum PostgresExpressionType 7 | { 8 | Contains, // >> (inet/cidr), @> 9 | ContainedBy, // << (inet/cidr), <@ 10 | Overlaps, // && 11 | 12 | AtTimeZone, // AT TIME ZONE 13 | 14 | NetworkContainedByOrEqual, // <<= 15 | NetworkContainsOrEqual, // >>= 16 | NetworkContainsOrContainedBy, // && 17 | 18 | RangeIsStrictlyLeftOf, // << 19 | RangeIsStrictlyRightOf, // >> 20 | RangeDoesNotExtendRightOf, // &< 21 | RangeDoesNotExtendLeftOf, // &> 22 | RangeIsAdjacentTo, // -|- 23 | RangeUnion, // + 24 | RangeIntersect, // * 25 | RangeExcept, // - 26 | 27 | TextSearchMatch, // @@ 28 | TextSearchAnd, // && 29 | TextSearchOr, // || 30 | 31 | JsonExists, // ? 32 | JsonExistsAny, // ?@> 33 | JsonExistsAll, // ?<@ 34 | 35 | LTreeMatches, // ~ or @ 36 | LTreeMatchesAny, // ? 37 | LTreeFirstAncestor, // ?@> 38 | LTreeFirstDescendent, // ?<@ 39 | LTreeFirstMatches, // ?~ or ?@ 40 | 41 | PostgisDistanceKnn // <-> 42 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpStringMemberTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Diagnostics; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 7 | using static Kdbndp.EntityFrameworkCore.KingbaseES.Utilities.Statics; 8 | 9 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 10 | 11 | /// 12 | /// Translates to 'length(text)'. 13 | /// 14 | public class KdbndpStringMemberTranslator : IMemberTranslator 15 | { 16 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 17 | 18 | public KdbndpStringMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) 19 | => _sqlExpressionFactory = sqlExpressionFactory; 20 | 21 | public virtual SqlExpression? Translate(SqlExpression? instance, 22 | MemberInfo member, 23 | Type returnType, 24 | IDiagnosticsLogger logger) 25 | => member.Name == nameof(string.Length) && instance?.Type == typeof(string) 26 | ? _sqlExpressionFactory.Convert( 27 | _sqlExpressionFactory.Function( 28 | "length", 29 | new[] { instance }, 30 | nullable: true, 31 | argumentsPropagateNullability: TrueArrays[1], 32 | typeof(long)), 33 | returnType) 34 | : null; 35 | } 36 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpIntervalTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpIntervalTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpIntervalTypeMapping() : base("interval", typeof(TimeSpan), KdbndpDbType.Interval) {} 11 | 12 | protected KdbndpIntervalTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpDbType.Interval) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpIntervalTypeMapping(parameters); 17 | 18 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 19 | => parameters.Precision is null ? storeType : $"interval({parameters.Precision})"; 20 | 21 | protected override string GenerateNonNullSqlLiteral(object value) 22 | => $"INTERVAL '{FormatTimeSpanAsInterval((TimeSpan)value)}'"; 23 | 24 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 25 | => $@"""{FormatTimeSpanAsInterval((TimeSpan)value)}"""; 26 | 27 | public static string FormatTimeSpanAsInterval(TimeSpan ts) 28 | => ts.ToString( 29 | $@"{(ts < TimeSpan.Zero ? "\\-" : "")}{(ts.Days == 0 ? "" : "d\\ ")}hh\:mm\:ss{(ts.Ticks % 10000000 == 0 ? "" : "\\.FFFFFF")}", 30 | CultureInfo.InvariantCulture); 31 | } 32 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTidTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using System.Reflection; 3 | using System.Text; 4 | using Microsoft.EntityFrameworkCore.Storage; 5 | using KdbndpTypes; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 8 | 9 | public class KdbndpTidTypeMapping : KdbndpTypeMapping 10 | { 11 | public KdbndpTidTypeMapping() 12 | : base("tid", typeof(KdbndpTid), KdbndpDbType.Tid) {} 13 | 14 | protected KdbndpTidTypeMapping(RelationalTypeMappingParameters parameters) 15 | : base(parameters, KdbndpDbType.Tid) {} 16 | 17 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 18 | => new KdbndpTidTypeMapping(parameters); 19 | 20 | protected override string GenerateNonNullSqlLiteral(object value) 21 | { 22 | var tid = (KdbndpTid)value; 23 | var builder = new StringBuilder("TID '("); 24 | builder.Append(tid.BlockNumber); 25 | builder.Append(','); 26 | builder.Append(tid.OffsetNumber); 27 | builder.Append(")'"); 28 | return builder.ToString(); 29 | } 30 | 31 | public override Expression GenerateCodeLiteral(object value) 32 | { 33 | var tid = (KdbndpTid)value; 34 | return Expression.New(Constructor, Expression.Constant(tid.BlockNumber), Expression.Constant(tid.OffsetNumber)); 35 | } 36 | 37 | private static readonly ConstructorInfo Constructor = 38 | typeof(KdbndpTid).GetConstructor(new[] { typeof(uint), typeof(ushort) })!; 39 | } 40 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Scaffolding/Internal/KdbndpCodeGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Design; 5 | using Microsoft.EntityFrameworkCore.Scaffolding; 6 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Scaffolding.Internal; 9 | 10 | /// 11 | /// The default code generator for Kdbndp. 12 | /// 13 | public class KdbndpCodeGenerator : ProviderCodeGenerator 14 | { 15 | private static readonly MethodInfo _useKdbndpMethodInfo 16 | = typeof(KdbndpDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod( 17 | nameof(KdbndpDbContextOptionsBuilderExtensions.UseKdbndp), 18 | typeof(DbContextOptionsBuilder), 19 | typeof(string), 20 | typeof(Action)); 21 | 22 | /// 23 | /// Constructs an instance of the class. 24 | /// 25 | /// The dependencies. 26 | public KdbndpCodeGenerator(ProviderCodeGeneratorDependencies dependencies) 27 | : base(dependencies) {} 28 | 29 | public override MethodCallCodeFragment GenerateUseProvider( 30 | string connectionString, 31 | MethodCallCodeFragment? providerOptions) 32 | => new( 33 | _useKdbndpMethodInfo, 34 | providerOptions is null 35 | ? new object[] { connectionString } 36 | : new object[] { connectionString, new NestedClosureCodeFragment("x", providerOptions) }); 37 | } 38 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/Conventions/KdbndpSharedTableConvention.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions; 3 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata.Conventions; 7 | 8 | /// 9 | /// A convention that manipulates names of database objects for entity types that share a table to avoid clashes. 10 | /// 11 | public class KdbndpSharedTableConvention : SharedTableConvention 12 | { 13 | /// 14 | /// Creates a new instance of . 15 | /// 16 | /// Parameter object containing dependencies for this convention. 17 | /// Parameter object containing relational dependencies for this convention. 18 | public KdbndpSharedTableConvention( 19 | ProviderConventionSetBuilderDependencies dependencies, 20 | RelationalConventionSetBuilderDependencies relationalDependencies) 21 | : base(dependencies, relationalDependencies) 22 | { 23 | } 24 | 25 | /// 26 | protected override bool AreCompatible(IReadOnlyIndex index, IReadOnlyIndex duplicateIndex, in StoreObjectIdentifier storeObject) 27 | => base.AreCompatible(index, duplicateIndex, storeObject) 28 | && index.AreCompatibleForKdbndp(duplicateIndex, storeObject, shouldThrow: false); 29 | 30 | /// 31 | protected override bool CheckConstraintsUniqueAcrossTables 32 | => false; 33 | } 34 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Utilities/SortOrderHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Utilities; 6 | 7 | internal static class SortOrderHelper 8 | { 9 | public static bool IsDefaultSortOrder(IReadOnlyList? sortOrders) 10 | => sortOrders?.All(sortOrder => sortOrder == SortOrder.Ascending) ?? true; 11 | 12 | public static bool IsDefaultNullSortOrder( 13 | IReadOnlyList? nullSortOrders, 14 | IReadOnlyList? sortOrders) 15 | { 16 | if (nullSortOrders is null) 17 | { 18 | return true; 19 | } 20 | 21 | for (var i = 0; i < nullSortOrders.Count; i++) 22 | { 23 | var nullSortOrder = nullSortOrders[i]; 24 | 25 | // We need to consider the ASC/DESC sort order to determine the default NULLS FIRST/LAST sort order. 26 | var sortOrder = i < sortOrders?.Count ? sortOrders[i] : SortOrder.Ascending; 27 | 28 | if (sortOrder == SortOrder.Descending) 29 | { 30 | // NULLS FIRST is the default when DESC is specified. 31 | if (nullSortOrder != NullSortOrder.NullsFirst) 32 | { 33 | return false; 34 | } 35 | } 36 | else 37 | { 38 | // NULLS LAST is the default when DESC is NOT specified. 39 | if (nullSortOrder != NullSortOrder.NullsLast) 40 | { 41 | return false; 42 | } 43 | } 44 | } 45 | 46 | return true; 47 | } 48 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpRandomTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Query; 7 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 8 | using Microsoft.EntityFrameworkCore.Utilities; 9 | 10 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 11 | 12 | public class KdbndpRandomTranslator : IMethodCallTranslator 13 | { 14 | private static readonly MethodInfo _methodInfo 15 | = typeof(DbFunctionsExtensions).GetRuntimeMethod(nameof(DbFunctionsExtensions.Random), new[] { typeof(DbFunctions) })!; 16 | 17 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 18 | 19 | public KdbndpRandomTranslator(ISqlExpressionFactory sqlExpressionFactory) 20 | => _sqlExpressionFactory = sqlExpressionFactory; 21 | 22 | public virtual SqlExpression? Translate( 23 | SqlExpression? instance, 24 | MethodInfo method, 25 | IReadOnlyList arguments, 26 | IDiagnosticsLogger logger) 27 | { 28 | Check.NotNull(method, nameof(method)); 29 | Check.NotNull(arguments, nameof(arguments)); 30 | Check.NotNull(logger, nameof(logger)); 31 | 32 | return _methodInfo.Equals(method) 33 | ? _sqlExpressionFactory.Function( 34 | "random", 35 | Array.Empty(), 36 | nullable: false, 37 | argumentsPropagateNullability: Array.Empty(), 38 | method.ReturnType) 39 | : null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/Migrations/BlogContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 3 | using KingbaseES.BasicTest; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | #nullable disable 9 | 10 | namespace KingbaseES.BasicTest.Migrations 11 | { 12 | [DbContext(typeof(BlogContext))] 13 | partial class BlogContextModelSnapshot : ModelSnapshot 14 | { 15 | protected override void BuildModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "6.0.22") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 63); 21 | 22 | KdbndpModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); 23 | 24 | modelBuilder.HasSequence("tests_id_seq") 25 | .HasMin(1L) 26 | .HasMax(9223372036854775807L); 27 | 28 | modelBuilder.Entity("KingbaseES.BasicTest.Blog", b => 29 | { 30 | b.Property("Id") 31 | .ValueGeneratedOnAdd() 32 | .HasColumnType("integer") 33 | .HasColumnName("id") 34 | .HasDefaultValueSql("nextval('tests_id_seq'::regclass)"); 35 | 36 | b.Property("Name") 37 | .HasColumnType("text") 38 | .HasColumnName("name"); 39 | 40 | b.HasKey("Id"); 41 | 42 | b.ToTable("blogs"); 43 | }); 44 | #pragma warning restore 612, 618 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpVarbitTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using System.Text; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using KdbndpTypes; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 9 | 10 | public class KdbndpVarbitTypeMapping : KdbndpTypeMapping 11 | { 12 | public KdbndpVarbitTypeMapping() : base("bit varying", typeof(BitArray), KdbndpDbType.Varbit) {} 13 | 14 | protected KdbndpVarbitTypeMapping(RelationalTypeMappingParameters parameters) 15 | : base(parameters, KdbndpDbType.Varbit) {} 16 | 17 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 18 | => new KdbndpVarbitTypeMapping(parameters); 19 | 20 | protected override string GenerateNonNullSqlLiteral(object value) 21 | { 22 | var bits = (BitArray)value; 23 | var sb = new StringBuilder(); 24 | sb.Append("B'"); 25 | for (var i = 0; i < bits.Count; i++) 26 | { 27 | sb.Append(bits[i] ? '1' : '0'); 28 | } 29 | 30 | sb.Append('\''); 31 | return sb.ToString(); 32 | } 33 | 34 | public override Expression GenerateCodeLiteral(object value) 35 | { 36 | var bits = (BitArray)value; 37 | var exprs = new Expression[bits.Count]; 38 | for (var i = 0; i < bits.Count; i++) 39 | { 40 | exprs[i] = Expression.Constant(bits[i]); 41 | } 42 | 43 | return Expression.New(Constructor, 44 | Expression.NewArrayInit(typeof(bool), exprs)); 45 | } 46 | 47 | private static readonly ConstructorInfo Constructor = 48 | typeof(BitArray).GetConstructor(new[] { typeof(bool[]) })!; 49 | } 50 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpDecimalTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | 4 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 5 | 6 | public class KdbndpDecimalTypeMapping : KdbndpTypeMapping 7 | { 8 | private const string DecimalFormatConst = "{0:0.0###########################}"; 9 | 10 | public KdbndpDecimalTypeMapping(Type? clrType = null) : base("numeric", clrType ?? typeof(decimal), KdbndpTypes.KdbndpDbType.Numeric) {} 11 | 12 | protected KdbndpDecimalTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpTypes.KdbndpDbType.Numeric) 14 | { 15 | } 16 | 17 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 18 | => new KdbndpDecimalTypeMapping(parameters); 19 | 20 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 21 | => parameters.Precision is null 22 | ? storeType 23 | : parameters.Scale is null 24 | ? $"numeric({parameters.Precision})" 25 | : $"numeric({parameters.Precision},{parameters.Scale})"; 26 | 27 | /// 28 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to 29 | /// the same compatibility standards as public APIs. It may be changed or removed without notice in 30 | /// any release. You should only use it directly in your code with extreme caution and knowing that 31 | /// doing so can result in application failures when updating to a new Entity Framework Core release. 32 | /// 33 | protected override string SqlLiteralFormatString 34 | => DecimalFormatConst; 35 | } 36 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Types/KdbndpTsRankingNormalization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace Microsoft.EntityFrameworkCore; 6 | 7 | /// 8 | /// Specifies whether and how a document's length should impact its rank. 9 | /// This is used with the ranking functions in . 10 | /// 11 | /// See http://www.KingbaseES.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-RANKING 12 | /// for more information about the behaviors that are controlled by this value. 13 | /// 14 | [Flags] 15 | [SuppressMessage("ReSharper", "UnusedMember.Global")] 16 | public enum KdbndpTsRankingNormalization 17 | { 18 | /// 19 | /// Ignores the document length. 20 | /// 21 | Default = 0, 22 | 23 | /// 24 | /// Divides the rank by 1 + the logarithm of the document length. 25 | /// 26 | DivideBy1PlusLogLength = 1, 27 | 28 | /// 29 | /// Divides the rank by the document length. 30 | /// 31 | DivideByLength = 2, 32 | 33 | /// 34 | /// Divides the rank by the mean harmonic distance between extents (this is implemented only by ts_rank_cd). 35 | /// 36 | DivideByMeanHarmonicDistanceBetweenExtents = 4, 37 | 38 | /// 39 | /// Divides the rank by the number of unique words in document. 40 | /// 41 | DivideByUniqueWordCount = 8, 42 | 43 | /// 44 | /// Divides the rank by 1 + the logarithm of the number of unique words in document. 45 | /// 46 | DividesBy1PlusLogUniqueWordCount = 16, 47 | 48 | /// 49 | /// Divides the rank by itself + 1. 50 | /// 51 | DivideByItselfPlusOne = 32 52 | } 53 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace System.Reflection; 7 | 8 | internal static class EntityFrameworkMemberInfoExtensions 9 | { 10 | public static Type GetMemberType(this MemberInfo memberInfo) 11 | => (memberInfo as PropertyInfo)?.PropertyType ?? ((FieldInfo)memberInfo).FieldType; 12 | 13 | public static bool IsSameAs(this MemberInfo? propertyInfo, MemberInfo? otherPropertyInfo) 14 | => propertyInfo is null 15 | ? otherPropertyInfo is null 16 | : (otherPropertyInfo is not null 17 | && (Equals(propertyInfo, otherPropertyInfo) 18 | || (propertyInfo.Name == otherPropertyInfo.Name 19 | && propertyInfo.DeclaringType is not null 20 | && otherPropertyInfo.DeclaringType is not null 21 | && (propertyInfo.DeclaringType == otherPropertyInfo.DeclaringType 22 | || propertyInfo.DeclaringType.GetTypeInfo().IsSubclassOf(otherPropertyInfo.DeclaringType) 23 | || otherPropertyInfo.DeclaringType.GetTypeInfo().IsSubclassOf(propertyInfo.DeclaringType) 24 | || propertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces.Contains(otherPropertyInfo.DeclaringType) 25 | || otherPropertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces 26 | .Contains(propertyInfo.DeclaringType))))); 27 | 28 | public static string GetSimpleMemberName(this MemberInfo member) 29 | { 30 | var name = member.Name; 31 | var index = name.LastIndexOf('.'); 32 | return index >= 0 ? name.Substring(index + 1) : name; 33 | } 34 | } -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/Migrations/20231013152623_initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 3 | using KingbaseES.BasicTest; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace KingbaseES.BasicTest.Migrations 12 | { 13 | [DbContext(typeof(BlogContext))] 14 | [Migration("20231013152623_initial")] 15 | partial class Initial 16 | { 17 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 18 | { 19 | #pragma warning disable 612, 618 20 | modelBuilder 21 | .HasAnnotation("ProductVersion", "6.0.22") 22 | .HasAnnotation("Relational:MaxIdentifierLength", 63); 23 | 24 | KdbndpModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); 25 | 26 | modelBuilder.HasSequence("tests_id_seq") 27 | .HasMin(1L) 28 | .HasMax(9223372036854775807L); 29 | 30 | modelBuilder.Entity("KingbaseES.BasicTest.Blog", b => 31 | { 32 | b.Property("Id") 33 | .ValueGeneratedOnAdd() 34 | .HasColumnType("integer") 35 | .HasColumnName("id") 36 | .HasDefaultValueSql("nextval('tests_id_seq'::regclass)"); 37 | 38 | b.Property("Name") 39 | .HasColumnType("text") 40 | .HasColumnName("name"); 41 | 42 | b.HasKey("Id"); 43 | 44 | b.ToTable("blogs"); 45 | }); 46 | #pragma warning restore 612, 618 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpCharacterCharTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using Microsoft.EntityFrameworkCore.ChangeTracking; 4 | using Microsoft.EntityFrameworkCore.Storage; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using KdbndpTypes; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 9 | 10 | /// 11 | /// Type mapping for the KingbaseES 'character' data type. Handles both CLR strings and chars. 12 | /// 13 | /// 14 | /// See: https://www.KingbaseES.org/docs/current/static/datatype-character.html 15 | /// 16 | public class KdbndpCharacterCharTypeMapping : CharTypeMapping, IKdbndpTypeMapping 17 | { 18 | /// 19 | public virtual KdbndpDbType KdbndpDbType 20 | => KdbndpDbType.Char; 21 | 22 | public KdbndpCharacterCharTypeMapping(string storeType) 23 | : this(new RelationalTypeMappingParameters( 24 | new CoreTypeMappingParameters(typeof(char)), 25 | storeType, 26 | StoreTypePostfix.Size, 27 | System.Data.DbType.StringFixedLength, 28 | unicode: false, 29 | fixedLength: true)) {} 30 | 31 | protected KdbndpCharacterCharTypeMapping(RelationalTypeMappingParameters parameters) 32 | : base(parameters) 33 | { 34 | } 35 | 36 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 37 | => new KdbndpCharacterCharTypeMapping(parameters); 38 | 39 | protected override void ConfigureParameter(DbParameter parameter) 40 | { 41 | if (parameter is not KdbndpParameter KdbndpParameter) 42 | { 43 | throw new InvalidOperationException($"Kdbndp-specific type mapping {GetType().Name} being used with non-Kdbndp parameter type {parameter.GetType().Name}"); 44 | } 45 | 46 | base.ConfigureParameter(parameter); 47 | KdbndpParameter.KdbndpDbType = KdbndpDbType; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Migrations/Internal/KdbndpHistoryRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | using Microsoft.EntityFrameworkCore.Storage; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Migrations.Internal; 7 | 8 | public class KdbndpHistoryRepository : HistoryRepository 9 | { 10 | public KdbndpHistoryRepository(HistoryRepositoryDependencies dependencies) 11 | : base(dependencies) 12 | { 13 | } 14 | 15 | protected override string ExistsSql 16 | { 17 | get 18 | { 19 | var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string)); 20 | 21 | return $@" 22 | SELECT EXISTS ( 23 | SELECT 1 FROM sys_catalog.sys_class c 24 | JOIN sys_catalog.sys_namespace n ON n.oid=c.relnamespace 25 | WHERE n.nspname={stringTypeMapping.GenerateSqlLiteral(TableSchema ?? "public")} AND 26 | c.relname={stringTypeMapping.GenerateSqlLiteral(TableName)} 27 | )"; 28 | } 29 | } 30 | 31 | protected override bool InterpretExistsResult(object? value) => (bool?)value == true; 32 | 33 | public override string GetCreateIfNotExistsScript() 34 | { 35 | var script = GetCreateScript(); 36 | return script.Insert(script.IndexOf("CREATE TABLE", StringComparison.Ordinal) + 12, " IF NOT EXISTS"); 37 | } 38 | 39 | public override string GetBeginIfNotExistsScript(string migrationId) => $@" 40 | DO $EF$ 41 | BEGIN 42 | IF NOT EXISTS(SELECT 1 FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)} WHERE ""{MigrationIdColumnName}"" = '{migrationId}') THEN"; 43 | 44 | public override string GetBeginIfExistsScript(string migrationId) => $@" 45 | DO $EF$ 46 | BEGIN 47 | IF EXISTS(SELECT 1 FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)} WHERE ""{MigrationIdColumnName}"" = '{migrationId}') THEN"; 48 | 49 | public override string GetEndIfScript() => 50 | @" END IF; 51 | END $EF$;"; 52 | } 53 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpStringTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | /// 9 | /// The base class for mapping Kdbndp-specific string types. It configures parameters with the 10 | /// provider-specific type enum. 11 | /// 12 | public class KdbndpStringTypeMapping : StringTypeMapping, IKdbndpTypeMapping 13 | { 14 | /// 15 | public virtual KdbndpDbType KdbndpDbType { get; } 16 | 17 | // ReSharper disable once PublicConstructorInAbstractClass 18 | /// 19 | /// Constructs an instance of the class. 20 | /// 21 | /// The database type to map. 22 | /// The database type used by Kdbndp. 23 | public KdbndpStringTypeMapping(string storeType, KdbndpDbType KdbndpDbType) 24 | : base(storeType, System.Data.DbType.String) 25 | => this.KdbndpDbType = KdbndpDbType; 26 | 27 | protected KdbndpStringTypeMapping( 28 | RelationalTypeMappingParameters parameters, 29 | KdbndpDbType KdbndpDbType) 30 | : base(parameters) 31 | => this.KdbndpDbType = KdbndpDbType; 32 | 33 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 34 | => new KdbndpStringTypeMapping(parameters, KdbndpDbType); 35 | 36 | protected override void ConfigureParameter(DbParameter parameter) 37 | { 38 | if (parameter is not KdbndpParameter KdbndpParameter) 39 | { 40 | throw new InvalidOperationException($"Kdbndp-specific type mapping {GetType().Name} being used with non-Kdbndp parameter type {parameter.GetType().Name}"); 41 | } 42 | 43 | base.ConfigureParameter(parameter); 44 | KdbndpParameter.KdbndpDbType = KdbndpDbType; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpBitTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using System.Text; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using KdbndpTypes; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 9 | 10 | /// 11 | /// The type mapping for the KingbaseES bit string type. 12 | /// 13 | /// 14 | /// See: https://www.KingbaseES.org/docs/current/static/datatype-bit.html 15 | /// 16 | public class KdbndpBitTypeMapping : KdbndpTypeMapping 17 | { 18 | /// 19 | /// Constructs an instance of the class. 20 | /// 21 | public KdbndpBitTypeMapping() : base("bit", typeof(BitArray), KdbndpDbType.Bit) {} 22 | 23 | protected KdbndpBitTypeMapping(RelationalTypeMappingParameters parameters) 24 | : base(parameters, KdbndpDbType.Bit) {} 25 | 26 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 27 | => new KdbndpBitTypeMapping(parameters); 28 | 29 | protected override string GenerateNonNullSqlLiteral(object value) 30 | { 31 | var bits = (BitArray)value; 32 | var sb = new StringBuilder(); 33 | sb.Append("B'"); 34 | for (var i = 0; i < bits.Count; i++) 35 | { 36 | sb.Append(bits[i] ? '1' : '0'); 37 | } 38 | 39 | sb.Append('\''); 40 | return sb.ToString(); 41 | } 42 | 43 | public override Expression GenerateCodeLiteral(object value) 44 | { 45 | var bits = (BitArray)value; 46 | var exprs = new Expression[bits.Count]; 47 | for (var i = 0; i < bits.Count; i++) 48 | { 49 | exprs[i] = Expression.Constant(bits[i]); 50 | } 51 | 52 | return Expression.New(Constructor, Expression.NewArrayInit(typeof(bool), exprs)); 53 | } 54 | 55 | private static readonly ConstructorInfo Constructor = 56 | typeof(BitArray).GetConstructor(new[] { typeof(bool[]) })!; 57 | } 58 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpNewGuidTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Query; 7 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 8 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 9 | using static Kdbndp.EntityFrameworkCore.KingbaseES.Utilities.Statics; 10 | 11 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 12 | 13 | /// 14 | /// Provides translation services for KingbaseES UUID functions. 15 | /// 16 | /// 17 | /// See: https://www.KingbaseES.org/docs/current/datatype-uuid.html 18 | /// 19 | public class KdbndpNewGuidTranslator : IMethodCallTranslator 20 | { 21 | private static readonly MethodInfo MethodInfo = typeof(Guid).GetRuntimeMethod(nameof(Guid.NewGuid), Array.Empty())!; 22 | 23 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 24 | private readonly string _uuidGenerationFunction; 25 | 26 | public KdbndpNewGuidTranslator( 27 | ISqlExpressionFactory sqlExpressionFactory, 28 | IKdbndpSingletonOptions KdbndpSingletonOptions) 29 | { 30 | _sqlExpressionFactory = sqlExpressionFactory; 31 | _uuidGenerationFunction = KdbndpSingletonOptions.PostgresVersion.AtLeast(13) ? "gen_random_uuid" : "uuid_generate_v4"; 32 | } 33 | 34 | public virtual SqlExpression? Translate( 35 | SqlExpression? instance, 36 | MethodInfo method, 37 | IReadOnlyList arguments, 38 | IDiagnosticsLogger logger) 39 | => MethodInfo.Equals(method) 40 | ? _sqlExpressionFactory.Function( 41 | _uuidGenerationFunction, 42 | Array.Empty(), 43 | nullable: false, 44 | argumentsPropagateNullability: FalseArrays[0], 45 | method.ReturnType) 46 | : null; 47 | } 48 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/KdbndpExecutionStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 8 | 9 | public class KdbndpExecutionStrategy : IExecutionStrategy 10 | { 11 | private ExecutionStrategyDependencies Dependencies { get; } 12 | 13 | public KdbndpExecutionStrategy(ExecutionStrategyDependencies dependencies) 14 | => Dependencies = dependencies; 15 | 16 | public virtual bool RetriesOnFailure => false; 17 | 18 | public virtual TResult Execute( 19 | TState state, 20 | Func operation, 21 | Func>? verifySucceeded) 22 | { 23 | try 24 | { 25 | return operation(Dependencies.CurrentContext.Context, state); 26 | } 27 | catch (Exception ex) when (ExecutionStrategy.CallOnWrappedException(ex, KdbndpTransientExceptionDetector.ShouldRetryOn)) 28 | { 29 | throw new InvalidOperationException("An exception has been raised that is likely due to a transient failure.", ex); 30 | } 31 | } 32 | 33 | public virtual async Task ExecuteAsync( 34 | TState state, 35 | Func> operation, 36 | Func>>? verifySucceeded, 37 | CancellationToken cancellationToken) 38 | { 39 | try 40 | { 41 | return await operation(Dependencies.CurrentContext.Context, state, cancellationToken).ConfigureAwait(false); 42 | } 43 | catch (Exception ex) when (ExecutionStrategy.CallOnWrappedException(ex, KdbndpTransientExceptionDetector.ShouldRetryOn)) 44 | { 45 | throw new InvalidOperationException("An exception has been raised that is likely due to a transient failure.", ex); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpCompiledQueryCacheKeyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 7 | 8 | public class KdbndpCompiledQueryCacheKeyGenerator : RelationalCompiledQueryCacheKeyGenerator 9 | { 10 | public KdbndpCompiledQueryCacheKeyGenerator( 11 | CompiledQueryCacheKeyGeneratorDependencies dependencies, 12 | RelationalCompiledQueryCacheKeyGeneratorDependencies relationalDependencies) 13 | : base(dependencies, relationalDependencies) 14 | { 15 | } 16 | 17 | public override object GenerateCacheKey(Expression query, bool async) 18 | => new KdbndpCompiledQueryCacheKey( 19 | GenerateCacheKeyCore(query, async), 20 | RelationalDependencies.ContextOptions.FindExtension()?.ReverseNullOrdering ?? false); 21 | 22 | private struct KdbndpCompiledQueryCacheKey 23 | { 24 | private readonly RelationalCompiledQueryCacheKey _relationalCompiledQueryCacheKey; 25 | private readonly bool _reverseNullOrdering; 26 | 27 | public KdbndpCompiledQueryCacheKey( 28 | RelationalCompiledQueryCacheKey relationalCompiledQueryCacheKey, bool reverseNullOrdering) 29 | { 30 | _relationalCompiledQueryCacheKey = relationalCompiledQueryCacheKey; 31 | _reverseNullOrdering = reverseNullOrdering; 32 | } 33 | 34 | public override bool Equals(object? obj) 35 | => !(obj is null) 36 | && obj is KdbndpCompiledQueryCacheKey key 37 | && Equals(key); 38 | 39 | private bool Equals(KdbndpCompiledQueryCacheKey other) 40 | => _relationalCompiledQueryCacheKey.Equals(other._relationalCompiledQueryCacheKey) 41 | && _reverseNullOrdering == other._reverseNullOrdering; 42 | 43 | public override int GetHashCode() => HashCode.Combine(_relationalCompiledQueryCacheKey, _reverseNullOrdering); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTimestampTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpTimestampTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpTimestampTypeMapping() : base("timestamp without time zone", typeof(DateTime), KdbndpDbType.Timestamp) {} 11 | 12 | protected KdbndpTimestampTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpDbType.Timestamp) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpTimestampTypeMapping(parameters); 17 | 18 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 19 | => parameters.Precision is null ? storeType : $"timestamp({parameters.Precision}) without time zone"; 20 | 21 | protected override string GenerateNonNullSqlLiteral(object value) 22 | => $"TIMESTAMP '{GenerateLiteralCore(value)}'"; 23 | 24 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 25 | => $@"""{GenerateLiteralCore(value)}"""; 26 | 27 | private string GenerateLiteralCore(object value) 28 | { 29 | var dateTime = (DateTime)value; 30 | 31 | if (!KdbndpTypeMappingSource.DisableDateTimeInfinityConversions) 32 | { 33 | if (dateTime == DateTime.MinValue) 34 | { 35 | return "-infinity"; 36 | } 37 | 38 | if (dateTime == DateTime.MaxValue) 39 | { 40 | return "infinity"; 41 | } 42 | } 43 | 44 | return KdbndpTypeMappingSource.LegacyTimestampBehavior || dateTime.Kind != DateTimeKind.Utc 45 | ? dateTime.ToString("yyyy-MM-dd HH:mm:ss.FFFFFF", CultureInfo.InvariantCulture) 46 | : throw new InvalidCastException("'timestamp without time zone' literal cannot be generated for a UTC DateTime"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpBigIntegerMemberTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Query; 7 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 8 | 9 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 10 | 11 | public class KdbndpBigIntegerMemberTranslator : IMemberTranslator 12 | { 13 | private readonly KdbndpSqlExpressionFactory _sqlExpressionFactory; 14 | 15 | private static readonly MemberInfo IsZero = typeof(BigInteger).GetProperty(nameof(BigInteger.IsZero))!; 16 | private static readonly MemberInfo IsOne = typeof(BigInteger).GetProperty(nameof(BigInteger.IsOne))!; 17 | private static readonly MemberInfo IsEven = typeof(BigInteger).GetProperty(nameof(BigInteger.IsEven))!; 18 | 19 | public KdbndpBigIntegerMemberTranslator(KdbndpSqlExpressionFactory sqlExpressionFactory) 20 | => _sqlExpressionFactory = sqlExpressionFactory; 21 | 22 | /// 23 | public virtual SqlExpression? Translate(SqlExpression? instance, MemberInfo member, Type returnType, IDiagnosticsLogger logger) 24 | { 25 | if (member.DeclaringType == typeof(BigInteger)) 26 | { 27 | if (member == IsZero) 28 | { 29 | return _sqlExpressionFactory.Equal(instance!, _sqlExpressionFactory.Constant(BigInteger.Zero)); 30 | } 31 | 32 | if (member == IsOne) 33 | { 34 | return _sqlExpressionFactory.Equal(instance!, _sqlExpressionFactory.Constant(BigInteger.One)); 35 | } 36 | 37 | if (member == IsEven) 38 | { 39 | return _sqlExpressionFactory.Equal( 40 | _sqlExpressionFactory.Modulo(instance!, _sqlExpressionFactory.Constant(new BigInteger(2))), 41 | _sqlExpressionFactory.Constant(BigInteger.Zero)); 42 | } 43 | } 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpMemberTranslatorProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 7 | 8 | /// 9 | /// A composite member translator that dispatches to multiple specialized member translators specific to Kdbndp. 10 | /// 11 | public class KdbndpMemberTranslatorProvider : RelationalMemberTranslatorProvider 12 | { 13 | public virtual KdbndpJsonPocoTranslator JsonPocoTranslator { get; } 14 | 15 | public KdbndpMemberTranslatorProvider( 16 | RelationalMemberTranslatorProviderDependencies dependencies, 17 | IModel model, 18 | IRelationalTypeMappingSource typeMappingSource, 19 | IKdbndpSingletonOptions KdbndpSingletonOptions) 20 | : base(dependencies) 21 | { 22 | var sqlExpressionFactory = (KdbndpSqlExpressionFactory)dependencies.SqlExpressionFactory; 23 | JsonPocoTranslator = new KdbndpJsonPocoTranslator(typeMappingSource, sqlExpressionFactory, model); 24 | 25 | AddTranslators( 26 | new IMemberTranslator[] { 27 | new KdbndpArrayTranslator(sqlExpressionFactory, JsonPocoTranslator, KdbndpSingletonOptions.UseRedshift), 28 | new KdbndpBigIntegerMemberTranslator(sqlExpressionFactory), 29 | new KdbndpDateTimeMemberTranslator(typeMappingSource, sqlExpressionFactory), 30 | new KdbndpJsonDomTranslator(typeMappingSource, sqlExpressionFactory, model), 31 | new KdbndpLTreeTranslator(typeMappingSource, sqlExpressionFactory, model), 32 | JsonPocoTranslator, 33 | new KdbndpRangeTranslator(typeMappingSource, sqlExpressionFactory, model, KdbndpSingletonOptions), 34 | new KdbndpStringMemberTranslator(sqlExpressionFactory), 35 | new KdbndpTimeSpanMemberTranslator(sqlExpressionFactory), 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/KdbndpMigrationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | using Microsoft.EntityFrameworkCore.Migrations.Operations; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | // ReSharper disable once CheckNamespace 8 | namespace Microsoft.EntityFrameworkCore; 9 | 10 | public static class KdbndpMigrationBuilderExtensions 11 | { 12 | /// 13 | /// Returns true if the active provider in a migration is the Kdbndp provider. 14 | /// 15 | /// The migrationBuilder from the parameters on or 16 | /// . 17 | /// True if Kdbndp is being used; false otherwise. 18 | public static bool IsKdbndp(this MigrationBuilder builder) 19 | => builder.ActiveProvider == typeof(KdbndpMigrationBuilderExtensions).GetTypeInfo().Assembly.GetName().Name; 20 | 21 | public static MigrationBuilder EnsurePostgresExtension( 22 | this MigrationBuilder builder, 23 | string name, 24 | string? schema = null, 25 | string? version = null) 26 | { 27 | Check.NotEmpty(name, nameof(name)); 28 | Check.NullButNotEmpty(schema, nameof(schema)); 29 | Check.NullButNotEmpty(version, nameof(schema)); 30 | 31 | var op = new AlterDatabaseOperation(); 32 | op.GetOrAddPostgresExtension(schema, name, version); 33 | builder.Operations.Add(op); 34 | 35 | return builder; 36 | } 37 | 38 | [Obsolete("Use EnsurePostgresExtension instead")] 39 | public static MigrationBuilder CreatePostgresExtension( 40 | this MigrationBuilder builder, 41 | string name, 42 | string? schema = null, 43 | string? version = null) 44 | => EnsurePostgresExtension(builder, name, schema, version); 45 | 46 | [Obsolete("This no longer does anything and should be removed.")] 47 | public static MigrationBuilder DropPostgresExtension( 48 | this MigrationBuilder builder, 49 | string name) 50 | => builder; 51 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/KdbndpValueGenerationStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 2 | 3 | public enum KdbndpValueGenerationStrategy 4 | { 5 | /// 6 | /// No Kdbndp-specific strategy. 7 | /// 8 | None, 9 | 10 | /// 11 | /// 12 | /// A sequence-based hi-lo pattern where blocks of IDs are allocated from the server and 13 | /// used client-side for generating keys. 14 | /// 15 | /// 16 | /// This is an advanced pattern--only use this strategy if you are certain it is what you need. 17 | /// 18 | /// 19 | SequenceHiLo, 20 | 21 | /// 22 | /// 23 | /// Selects the serial column strategy, which is a regular column backed by an auto-created index. 24 | /// 25 | /// 26 | /// If you are creating a new project on KingbaseES 10 or above, consider using instead. 27 | /// 28 | /// 29 | SerialColumn, 30 | 31 | /// 32 | /// Selects the always-identity column strategy (a value cannot be provided). 33 | /// Available only starting KingbaseES 10. 34 | /// 35 | IdentityAlwaysColumn, 36 | 37 | /// 38 | /// Selects the by-default-identity column strategy (a value can be provided to override the identity mechanism). 39 | /// Available only starting KingbaseES 10. 40 | /// 41 | IdentityByDefaultColumn, 42 | } 43 | 44 | public static class KdbndpValueGenerationStrategyExtensions 45 | { 46 | public static bool IsIdentity(this KdbndpValueGenerationStrategy strategy) 47 | => strategy == KdbndpValueGenerationStrategy.IdentityByDefaultColumn || 48 | strategy == KdbndpValueGenerationStrategy.IdentityAlwaysColumn; 49 | 50 | public static bool IsIdentity(this KdbndpValueGenerationStrategy? strategy) 51 | => strategy == KdbndpValueGenerationStrategy.IdentityByDefaultColumn || 52 | strategy == KdbndpValueGenerationStrategy.IdentityAlwaysColumn; 53 | } 54 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Linq; 4 | using KdbndpTypes; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace System.Reflection; 8 | 9 | internal static class TypeExtensions 10 | { 11 | internal static bool IsGenericList(this Type? type) 12 | => type is not null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); 13 | 14 | internal static bool IsArrayOrGenericList(this Type type) 15 | => type.IsArray || type.IsGenericList(); 16 | 17 | internal static bool TryGetElementType(this Type type, [NotNullWhen(true)] out Type? elementType) 18 | { 19 | elementType = type.IsArray 20 | ? type.GetElementType() 21 | : type.IsGenericList() 22 | ? type.GetGenericArguments()[0] 23 | : null; 24 | return elementType is not null; 25 | } 26 | 27 | internal static bool TryGetRangeSubtype(this Type type, [NotNullWhen(true)] out Type? subtypeType) 28 | { 29 | if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KdbndpRange<>)) 30 | { 31 | subtypeType = type.GetGenericArguments()[0]; 32 | return true; 33 | } 34 | 35 | subtypeType = null; 36 | return false; 37 | } 38 | 39 | internal static bool TryGetMultirangeSubtype(this Type type, [NotNullWhen(true)] out Type? subtypeType) 40 | { 41 | if (type.TryGetElementType(out var elementType) && elementType.TryGetRangeSubtype(out subtypeType)) 42 | { 43 | return true; 44 | } 45 | 46 | subtypeType = null; 47 | return false; 48 | } 49 | 50 | public static PropertyInfo? FindIndexerProperty(this Type type) 51 | { 52 | var defaultPropertyAttribute = type.GetCustomAttributes().FirstOrDefault(); 53 | 54 | return defaultPropertyAttribute is null 55 | ? null 56 | : type.GetRuntimeProperties() 57 | .FirstOrDefault(pi => pi.Name == defaultPropertyAttribute.MemberName && pi.GetIndexParameters().Length == 1); 58 | } 59 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTimeTzTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Storage; 3 | using KdbndpTypes; 4 | 5 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 6 | 7 | public class KdbndpTimeTzTypeMapping : KdbndpTypeMapping 8 | { 9 | public KdbndpTimeTzTypeMapping() : base("time with time zone", typeof(DateTimeOffset), KdbndpDbType.TimeTz) {} 10 | 11 | protected KdbndpTimeTzTypeMapping(RelationalTypeMappingParameters parameters) 12 | : base(parameters, KdbndpDbType.TimeTz) {} 13 | 14 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 15 | => new KdbndpTimeTzTypeMapping(parameters); 16 | 17 | /// 18 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to 19 | /// the same compatibility standards as public APIs. It may be changed or removed without notice in 20 | /// any release. You should only use it directly in your code with extreme caution and knowing that 21 | /// doing so can result in application failures when updating to a new Entity Framework Core release. 22 | /// 23 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 24 | => parameters.Precision is null ? storeType : $"time({parameters.Precision}) with time zone"; 25 | 26 | /// 27 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to 28 | /// the same compatibility standards as public APIs. It may be changed or removed without notice in 29 | /// any release. You should only use it directly in your code with extreme caution and knowing that 30 | /// doing so can result in application failures when updating to a new Entity Framework Core release. 31 | /// 32 | protected override string GenerateNonNullSqlLiteral(object value) 33 | => FormattableString.Invariant($"TIMETZ '{(DateTimeOffset)value:HH:mm:ss.FFFFFFz}'"); 34 | 35 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 36 | => FormattableString.Invariant(@$"""{(DateTimeOffset)value:HH:mm:ss.FFFFFFz}"""); 37 | } 38 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpDateTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpDateTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpDateTypeMapping(Type clrType) : base("date", clrType, KdbndpDbType.Date) {} 11 | 12 | protected KdbndpDateTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpDbType.Date) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpDateTypeMapping(parameters); 17 | 18 | protected override string GenerateNonNullSqlLiteral(object value) 19 | => $"DATE '{GenerateEmbeddedNonNullSqlLiteral(value)}'"; 20 | 21 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 22 | { 23 | switch (value) 24 | { 25 | case DateTime dateTime: 26 | if (!KdbndpTypeMappingSource.DisableDateTimeInfinityConversions) 27 | { 28 | if (dateTime == DateTime.MinValue) 29 | { 30 | return "-infinity"; 31 | } 32 | 33 | if (dateTime == DateTime.MaxValue) 34 | { 35 | return "infinity"; 36 | } 37 | } 38 | 39 | return dateTime.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); 40 | 41 | case DateOnly dateOnly: 42 | if (!KdbndpTypeMappingSource.DisableDateTimeInfinityConversions) 43 | { 44 | if (dateOnly == DateOnly.MinValue) 45 | { 46 | return "-infinity"; 47 | } 48 | 49 | if (dateOnly == DateOnly.MaxValue) 50 | { 51 | return "infinity"; 52 | } 53 | } 54 | 55 | return dateOnly.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); 56 | 57 | default: 58 | throw new InvalidCastException($"Can't generate a date SQL literal for CLR type {value.GetType()}"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | # Code analysis rules and configuration 2 | 3 | root = false 4 | 5 | # Code files 6 | [*.cs] 7 | 8 | # Design rules 9 | 10 | # CA1001: Types that own disposable fields should be disposable 11 | dotnet_diagnostic.CA1001.severity = error 12 | 13 | # Globalization rules 14 | 15 | # CA1303: Do not pass literals as localized parameters 16 | dotnet_diagnostic.CA1303.severity = none 17 | 18 | # CA1304: Specify CultureInfo 19 | dotnet_diagnostic.CA1304.severity = error 20 | 21 | # CA1305: Specify IFormatProvider 22 | dotnet_diagnostic.CA1305.severity = none 23 | 24 | # CA1307: Specify StringComparison for clarity 25 | dotnet_diagnostic.CA1307.severity = none 26 | 27 | # CA1308: Normalize strings to uppercase 28 | dotnet_diagnostic.CA1308.severity = none 29 | 30 | # CA1309: Use ordinal stringcomparison 31 | dotnet_diagnostic.CA1309.severity = error 32 | 33 | # CA1310: Specify StringComparison for correctness 34 | dotnet_diagnostic.CA1310.severity = error 35 | 36 | # CA2101: Specify marshaling for P/Invoke string arguments 37 | dotnet_diagnostic.CA2101.severity = error 38 | 39 | # Reliability Rules 40 | 41 | # CA2000: Dispose objects before losing scope 42 | # Not reliable enough - false positives 43 | dotnet_diagnostic.CA2000.severity = suggestion 44 | 45 | # CA2002: Do not lock on objects with weak identity 46 | dotnet_diagnostic.CA2002.severity = error 47 | 48 | # CA2007: Consider calling ConfigureAwait on the awaited task 49 | dotnet_diagnostic.CA2007.severity = error 50 | 51 | # CA2008: Do not create tasks without passing a TaskScheduler 52 | dotnet_diagnostic.CA2008.severity = error 53 | 54 | # CA2009: Do not call ToImmutableCollection on an ImmutableCollection value 55 | dotnet_diagnostic.CA2009.severity = error 56 | 57 | # CA2011: Avoid infinite recursion 58 | dotnet_diagnostic.CA2011.severity = error 59 | 60 | # CA2012: Use ValueTasks correctly 61 | dotnet_diagnostic.CA2012.severity = error 62 | 63 | # CA2013: Do not use ReferenceEquals with value types 64 | dotnet_diagnostic.CA2013.severity = error 65 | 66 | # CA2014: Do not use stackalloc in loops 67 | dotnet_diagnostic.CA2014.severity = error 68 | 69 | # CA2015: Do not define finalizers for types derived from MemoryManager 70 | dotnet_diagnostic.CA2015.severity = error 71 | 72 | # CA2016: Forward the 'CancellationToken' parameter to methods that take one 73 | dotnet_diagnostic.CA2016.severity = error 74 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/KdbndpAlterDatabaseOperationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.EntityFrameworkCore.Migrations.Operations; 4 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace Microsoft.EntityFrameworkCore; 8 | 9 | /// 10 | /// Extension methods for for Kdbndp-specific metadata. 11 | /// 12 | public static class KdbndpAlterDatabaseOperationExtensions 13 | { 14 | public static IReadOnlyList GetPostgresCollations(this AlterDatabaseOperation operation) 15 | => PostgresCollation.GetCollations(operation).ToArray(); 16 | 17 | public static IReadOnlyList GetOldPostgresCollations(this AlterDatabaseOperation operation) 18 | => PostgresCollation.GetCollations(operation.OldDatabase).ToArray(); 19 | 20 | public static IReadOnlyList GetPostgresExtensions(this AlterDatabaseOperation operation) 21 | => PostgresExtension.GetPostgresExtensions(operation).ToArray(); 22 | 23 | public static IReadOnlyList GetOldPostgresExtensions(this AlterDatabaseOperation operation) 24 | => PostgresExtension.GetPostgresExtensions(operation.OldDatabase).ToArray(); 25 | 26 | public static IReadOnlyList GetPostgresEnums(this AlterDatabaseOperation operation) 27 | => PostgresEnum.GetPostgresEnums(operation).ToArray(); 28 | 29 | public static IReadOnlyList GetOldPostgresEnums(this AlterDatabaseOperation operation) 30 | => PostgresEnum.GetPostgresEnums(operation.OldDatabase).ToArray(); 31 | 32 | public static IReadOnlyList GetPostgresRanges(this AlterDatabaseOperation operation) 33 | => PostgresRange.GetPostgresRanges(operation).ToArray(); 34 | 35 | public static IReadOnlyList GetOldPostgresRanges(this AlterDatabaseOperation operation) 36 | => PostgresRange.GetPostgresRanges(operation.OldDatabase).ToArray(); 37 | 38 | public static PostgresExtension GetOrAddPostgresExtension( 39 | this AlterDatabaseOperation operation, 40 | string? schema, 41 | string name, 42 | string? version) 43 | => PostgresExtension.GetOrAddPostgresExtension(operation, schema, name, version); 44 | } 45 | -------------------------------------------------------------------------------- /test/KingbaseES.BasicTest/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace KingbaseES.BasicTest 5 | { 6 | internal static class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | var services = new ServiceCollection(); 11 | 12 | services.AddDbContext(options => 13 | { 14 | options.UseKdbndp(@"host=localhost;port=54321;database=test;user id=system;password=123456;"); 15 | }); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var context = serviceProvider.GetRequiredService(); 20 | 21 | context.Database.EnsureCreated(); 22 | 23 | // get list 24 | var blogs = context.Blogs.ToList(); 25 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "当前 Blogs 表中条目数:" + blogs.Count); 26 | if (blogs.Any()) 27 | { 28 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "当前 Blogs 表中条目数大于1,进行清理..."); 29 | context.Blogs.RemoveRange(blogs); 30 | context.SaveChanges(); 31 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "清理完成!"); 32 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "当前 Blogs 表中条目数:" + blogs.Count); 33 | } 34 | //add 35 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "新增 Blog 实体 值为I love EFCore!"); 36 | context.Add(new Blog() { Name = "I love EFCore!" }); 37 | var result = context.SaveChanges(); 38 | 39 | //update 40 | var first = context.Blogs.FirstOrDefault(); 41 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "查询结果:" + first.Name); 42 | 43 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "变更 Blog 实体 值为I love EFCore too!"); 44 | first.Name = "I love EFCore too!"; 45 | context.SaveChanges(); 46 | 47 | // get 48 | var testselect = context.Blogs.FirstOrDefault(); 49 | Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "查询结果:" + testselect.Name); 50 | Console.ReadKey(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/KdbndpValueGeneratorCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Diagnostics; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using Microsoft.EntityFrameworkCore.ValueGeneration; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 9 | 10 | /// 11 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 12 | /// directly from your code. This API may change or be removed in future releases. 13 | /// 14 | public class KdbndpValueGeneratorCache : ValueGeneratorCache, IKdbndpValueGeneratorCache 15 | { 16 | private readonly ConcurrentDictionary _sequenceGeneratorCache 17 | = new(); 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// Parameter object containing dependencies for this service. 23 | public KdbndpValueGeneratorCache(ValueGeneratorCacheDependencies dependencies) 24 | : base(dependencies) 25 | { 26 | } 27 | 28 | /// 29 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 30 | /// directly from your code. This API may change or be removed in future releases. 31 | /// 32 | public virtual KdbndpSequenceValueGeneratorState GetOrAddSequenceState( 33 | IProperty property, 34 | IRelationalConnection connection) 35 | { 36 | var sequence = property.FindHiLoSequence(); 37 | 38 | Debug.Assert(sequence is not null); 39 | 40 | return _sequenceGeneratorCache.GetOrAdd( 41 | GetSequenceName(sequence, connection), 42 | sequenceName => new KdbndpSequenceValueGeneratorState(sequence)); 43 | } 44 | 45 | private static string GetSequenceName(ISequence sequence, IRelationalConnection connection) 46 | { 47 | var dbConnection = connection.DbConnection; 48 | 49 | return dbConnection.Database.ToUpperInvariant() 50 | + "::" 51 | + dbConnection.DataSource?.ToUpperInvariant() 52 | + "::" 53 | + (sequence.Schema is null ? "" : sequence.Schema + ".") + sequence.Name; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/DbFunctionsExtensions/KdbndpDbFunctionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Diagnostics; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace Microsoft.EntityFrameworkCore; 6 | 7 | /// 8 | /// Provides Kdbndp-specific extension methods on . 9 | /// 10 | public static class KdbndpDbFunctionsExtensions 11 | { 12 | // ReSharper disable once InconsistentNaming 13 | /// 14 | /// An implementation of the KingbaseES ILIKE operation, which is an insensitive LIKE. 15 | /// 16 | /// The instance. 17 | /// The string that is to be matched. 18 | /// The pattern which may involve wildcards %,_,[,],^. 19 | /// if there is a match. 20 | public static bool ILike(this DbFunctions _, string matchExpression, string pattern) 21 | => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ILike))); 22 | 23 | // ReSharper disable once InconsistentNaming 24 | /// 25 | /// An implementation of the KingbaseES ILIKE operation, which is an insensitive LIKE. 26 | /// 27 | /// The instance. 28 | /// The string that is to be matched. 29 | /// The pattern which may involve wildcards %,_,[,],^. 30 | /// 31 | /// The escape character (as a single character string) to use in front of %,_,[,],^ 32 | /// if they are not used as wildcards. 33 | /// 34 | /// if there is a match. 35 | public static bool ILike(this DbFunctions _, string matchExpression, string pattern, string escapeCharacter) 36 | => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(ILike))); 37 | 38 | /// 39 | /// Reverses a string by calling KingbaseES reverse(). 40 | /// 41 | /// The instance. 42 | /// The string that is to be reversed. 43 | /// The reversed string. 44 | public static string Reverse(this DbFunctions _, string value) 45 | => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Reverse))); 46 | } 47 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpConvertTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.Diagnostics; 7 | using Microsoft.EntityFrameworkCore.Query; 8 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 9 | 10 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 11 | 12 | /// 13 | /// Translates methods defined on into KingbaseES CAST expressions. 14 | /// 15 | public class KdbndpConvertTranslator : IMethodCallTranslator 16 | { 17 | private static readonly Dictionary TypeMapping = new() 18 | { 19 | [nameof(Convert.ToBoolean)] = "bool", 20 | [nameof(Convert.ToByte)] = "smallint", 21 | [nameof(Convert.ToDecimal)] = "numeric", 22 | [nameof(Convert.ToDouble)] = "double precision", 23 | [nameof(Convert.ToInt16)] = "smallint", 24 | [nameof(Convert.ToInt32)] = "int", 25 | [nameof(Convert.ToInt64)] = "bigint", 26 | [nameof(Convert.ToString)] = "text" 27 | }; 28 | 29 | private static readonly List SupportedTypes = new() 30 | { 31 | typeof(bool), 32 | typeof(byte), 33 | typeof(decimal), 34 | typeof(double), 35 | typeof(float), 36 | typeof(int), 37 | typeof(long), 38 | typeof(short), 39 | typeof(string) 40 | }; 41 | 42 | private static readonly List SupportedMethods 43 | = TypeMapping.Keys 44 | .SelectMany( 45 | t => typeof(Convert).GetTypeInfo().GetDeclaredMethods(t) 46 | .Where( 47 | m => m.GetParameters().Length == 1 48 | && SupportedTypes.Contains(m.GetParameters().First().ParameterType))) 49 | .ToList(); 50 | 51 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 52 | 53 | public KdbndpConvertTranslator(ISqlExpressionFactory sqlExpressionFactory) 54 | => _sqlExpressionFactory = sqlExpressionFactory; 55 | 56 | public virtual SqlExpression? Translate( 57 | SqlExpression? instance, 58 | MethodInfo method, 59 | IReadOnlyList arguments, 60 | IDiagnosticsLogger logger) 61 | => SupportedMethods.Contains(method) 62 | ? _sqlExpressionFactory.Convert(arguments[0], method.ReturnType) 63 | : null; 64 | } 65 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpEnumTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using KdbndpTypes; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 9 | 10 | public class KdbndpEnumTypeMapping : RelationalTypeMapping 11 | { 12 | private readonly ISqlGenerationHelper _sqlGenerationHelper; 13 | private readonly IKdbndpNameTranslator _nameTranslator; 14 | 15 | /// 16 | /// Translates the CLR member value to the KingbaseES value label. 17 | /// 18 | private readonly Dictionary _members; 19 | 20 | public KdbndpEnumTypeMapping( 21 | string storeType, 22 | string? storeTypeSchema, 23 | Type enumType, 24 | ISqlGenerationHelper sqlGenerationHelper, 25 | IKdbndpNameTranslator? nameTranslator = null) 26 | : base(sqlGenerationHelper.DelimitIdentifier(storeType, storeTypeSchema), enumType) 27 | { 28 | if (!enumType.IsEnum || !enumType.IsValueType) 29 | { 30 | throw new ArgumentException($"Enum type mappings require a CLR enum. {enumType.FullName} is not an enum."); 31 | } 32 | 33 | nameTranslator ??= KdbndpConnection.GlobalTypeMapper.DefaultNameTranslator; 34 | 35 | _nameTranslator = nameTranslator; 36 | _sqlGenerationHelper = sqlGenerationHelper; 37 | _members = CreateValueMapping(enumType, nameTranslator); 38 | } 39 | 40 | protected KdbndpEnumTypeMapping( 41 | RelationalTypeMappingParameters parameters, 42 | ISqlGenerationHelper sqlGenerationHelper, 43 | IKdbndpNameTranslator nameTranslator) 44 | : base(parameters) 45 | { 46 | _nameTranslator = nameTranslator; 47 | _sqlGenerationHelper = sqlGenerationHelper; 48 | _members = CreateValueMapping(parameters.CoreParameters.ClrType, nameTranslator); 49 | } 50 | 51 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 52 | => new KdbndpEnumTypeMapping(parameters, _sqlGenerationHelper, _nameTranslator); 53 | 54 | protected override string GenerateNonNullSqlLiteral(object value) => $"'{_members[value]}'::{StoreType}"; 55 | 56 | private static Dictionary CreateValueMapping(Type enumType, IKdbndpNameTranslator nameTranslator) 57 | => enumType.GetFields(BindingFlags.Static | BindingFlags.Public) 58 | .ToDictionary( 59 | x => x.GetValue(null)!, 60 | x => x.GetCustomAttribute()?.KbName ?? nameTranslator.TranslateMemberName(x.Name)); 61 | } 62 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/KdbndpSqlGenerationHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 8 | 9 | public class KdbndpSqlGenerationHelper : RelationalSqlGenerationHelper 10 | { 11 | private static readonly HashSet ReservedWords; 12 | 13 | static KdbndpSqlGenerationHelper() 14 | { 15 | // https://www.KingbaseES.org/docs/current/static/sql-keywords-appendix.html 16 | using (var conn = new KdbndpConnection()) 17 | { 18 | ReservedWords = new HashSet(conn.GetSchema("ReservedWords").Rows.Cast().Select(r => (string)r["ReservedWord"])); 19 | } 20 | } 21 | 22 | public KdbndpSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies) 23 | : base(dependencies) {} 24 | 25 | public override string DelimitIdentifier(string identifier) 26 | => RequiresQuoting(identifier) ? base.DelimitIdentifier(identifier) : identifier; 27 | 28 | public override void DelimitIdentifier(StringBuilder builder, string identifier) 29 | { 30 | if (RequiresQuoting(identifier)) 31 | { 32 | base.DelimitIdentifier(builder, identifier); 33 | } 34 | else 35 | { 36 | builder.Append(identifier); 37 | } 38 | } 39 | 40 | /// 41 | /// Returns whether the given string can be used as an unquoted identifier in KingbaseES, without quotes. 42 | /// 43 | private static bool RequiresQuoting(string identifier) 44 | { 45 | var first = identifier[0]; 46 | if (!char.IsLower(first) && first != '_') 47 | { 48 | return true; 49 | } 50 | 51 | for (var i = 1; i < identifier.Length; i++) 52 | { 53 | var c = identifier[i]; 54 | 55 | if (char.IsLower(c)) 56 | { 57 | continue; 58 | } 59 | 60 | switch (c) 61 | { 62 | case '0': 63 | case '1': 64 | case '2': 65 | case '3': 66 | case '4': 67 | case '5': 68 | case '6': 69 | case '7': 70 | case '8': 71 | case '9': 72 | case '_': 73 | case '$': // yes it's true 74 | continue; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | if (ReservedWords.Contains(identifier.ToUpperInvariant())) 81 | { 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTimeTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpTimeTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpTimeTypeMapping(Type clrType) : base("time without time zone", clrType, KdbndpDbType.Time) {} 11 | 12 | protected KdbndpTimeTypeMapping(RelationalTypeMappingParameters parameters) 13 | : base(parameters, KdbndpDbType.Time) {} 14 | 15 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 16 | => new KdbndpTimeTypeMapping(parameters); 17 | 18 | /// 19 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to 20 | /// the same compatibility standards as public APIs. It may be changed or removed without notice in 21 | /// any release. You should only use it directly in your code with extreme caution and knowing that 22 | /// doing so can result in application failures when updating to a new Entity Framework Core release. 23 | /// 24 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 25 | => parameters.Precision is null ? storeType : $"time({parameters.Precision}) without time zone"; 26 | 27 | /// 28 | /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to 29 | /// the same compatibility standards as public APIs. It may be changed or removed without notice in 30 | /// any release. You should only use it directly in your code with extreme caution and knowing that 31 | /// doing so can result in application failures when updating to a new Entity Framework Core release. 32 | /// 33 | protected override string GenerateNonNullSqlLiteral(object value) 34 | => $"TIME '{GenerateEmbeddedNonNullSqlLiteral(value)}'"; 35 | 36 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 37 | => value switch 38 | { 39 | TimeSpan ts => ts.Ticks % 10000000 == 0 40 | ? ts.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture) 41 | : ts.ToString(@"hh\:mm\:ss\.FFFFFF", CultureInfo.InvariantCulture), 42 | TimeOnly t => t.Ticks % 10000000 == 0 43 | ? t.ToString(@"HH\:mm\:ss", CultureInfo.InvariantCulture) 44 | : t.ToString(@"HH\:mm\:ss\.FFFFFF", CultureInfo.InvariantCulture), 45 | _ => throw new InvalidCastException($"Can't generate a time SQL literal for CLR type {value.GetType()}") 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpRegexIsMatchTranslator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using System.Text.RegularExpressions; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Query; 7 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 8 | 9 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 10 | 11 | /// 12 | /// Translates Regex.IsMatch calls into KingbaseES regex expressions for database-side processing. 13 | /// 14 | /// 15 | /// http://www.KingbaseES.org/docs/current/static/functions-matching.html 16 | /// 17 | public class KdbndpRegexIsMatchTranslator : IMethodCallTranslator 18 | { 19 | private static readonly MethodInfo IsMatch = 20 | typeof(Regex).GetRuntimeMethod(nameof(Regex.IsMatch), new[] { typeof(string), typeof(string) })!; 21 | 22 | private static readonly MethodInfo IsMatchWithRegexOptions = 23 | typeof(Regex).GetRuntimeMethod(nameof(Regex.IsMatch), new[] { typeof(string), typeof(string), typeof(RegexOptions) })!; 24 | 25 | private const RegexOptions UnsupportedRegexOptions = RegexOptions.RightToLeft | RegexOptions.ECMAScript; 26 | 27 | private readonly KdbndpSqlExpressionFactory _sqlExpressionFactory; 28 | 29 | public KdbndpRegexIsMatchTranslator(KdbndpSqlExpressionFactory sqlExpressionFactory) 30 | => _sqlExpressionFactory = sqlExpressionFactory; 31 | 32 | /// 33 | public virtual SqlExpression? Translate( 34 | SqlExpression? instance, 35 | MethodInfo method, 36 | IReadOnlyList arguments, 37 | IDiagnosticsLogger logger) 38 | { 39 | if (method != IsMatch && method != IsMatchWithRegexOptions) 40 | { 41 | return null; 42 | } 43 | 44 | var (input, pattern) = (arguments[0], arguments[1]); 45 | var typeMapping = ExpressionExtensions.InferTypeMapping(input, pattern); 46 | 47 | RegexOptions options; 48 | 49 | if (method == IsMatch) 50 | { 51 | options = RegexOptions.None; 52 | } 53 | else if (arguments[2] is SqlConstantExpression { Value: RegexOptions regexOptions }) 54 | { 55 | options = regexOptions; 56 | } 57 | else 58 | { 59 | return null; // We don't support non-constant regex options 60 | } 61 | 62 | return (options & UnsupportedRegexOptions) == 0 63 | ? _sqlExpressionFactory.RegexMatch( 64 | _sqlExpressionFactory.ApplyTypeMapping(input, typeMapping), 65 | _sqlExpressionFactory.ApplyTypeMapping(pattern, typeMapping), 66 | options) 67 | : null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/ValueConversion/KdbndpValueConverterSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.ValueConversion; 9 | 10 | public class KdbndpValueConverterSelector : ValueConverterSelector 11 | { 12 | private readonly ConcurrentDictionary<(Type ModelElementClrType, Type ProviderElementClrType), ValueConverterInfo> _arrayConverters 13 | = new(); 14 | 15 | public KdbndpValueConverterSelector(ValueConverterSelectorDependencies dependencies) 16 | : base(dependencies) {} 17 | 18 | /// 19 | public override IEnumerable Select(Type modelClrType, Type? providerClrType = null) 20 | { 21 | var providerElementType = default(Type); 22 | 23 | if (modelClrType.TryGetElementType(out var modelElementType) && 24 | (providerClrType is null || providerClrType.TryGetElementType(out providerElementType))) 25 | { 26 | // For each ValueConverterInfo selected by the superclass for the element type, 27 | // return a ValueConverterInfo for its array type 28 | 29 | // Note that the value converter system operates with the assumption that nullable CLR types have already been 30 | // unwrapped. This means that when looking for element type converters, we need to unwrap. 31 | var arrayConverters = base 32 | .Select(modelElementType.UnwrapNullableType(), providerElementType) 33 | .Select(elementConverterInfo => new 34 | { 35 | ModelArrayType = modelClrType, 36 | ProviderArrayType = providerClrType ?? (modelElementType.IsNullableType() 37 | ? elementConverterInfo.ProviderClrType.MakeNullable().MakeArrayType() 38 | : elementConverterInfo.ProviderClrType.MakeArrayType()), 39 | ElementConverterInfo = elementConverterInfo 40 | }) 41 | .Select(x => _arrayConverters.GetOrAdd( 42 | (x.ModelArrayType, x.ProviderArrayType), 43 | new ValueConverterInfo( 44 | x.ModelArrayType, 45 | x.ProviderArrayType, 46 | _ => (ValueConverter)Activator.CreateInstance( 47 | typeof(KdbndpArrayConverter<,>).MakeGenericType( 48 | modelClrType, 49 | x.ProviderArrayType), 50 | x.ElementConverterInfo.Create())!))); 51 | 52 | return arrayConverters.Concat(base.Select(modelClrType, providerClrType)); 53 | } 54 | 55 | return base.Select(modelClrType, providerClrType); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/EFCore.KingbaseES.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DotNetCore.EntityFrameworkCore.KingbaseES 5 | DotNetCore.EntityFrameworkCore.KingbaseES 6 | NCC;Jeffcky 7 | git 8 | https://github.com/dotnetcore/EntityFrameworkCore.KingbaseES 9 | KingbaseES provider for Entity Framework Core. 10 | KingbaseES;kdbndp;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql 11 | README.md 12 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesAssemblyToPackage 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | TextTemplatingFileGenerator 31 | KdbndpStrings.Designer.cs 32 | 33 | 34 | 35 | TextTemplatingFileGenerator 36 | 37 | 38 | 39 | Kdbndp.EntityFrameworkCore.KingbaseES.Internal 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | lib\$(TargetFramework) 51 | 52 | 53 | 54 | 55 | 56 | 57 | Lib\Kdbndp.dll 58 | all 59 | 60 | 61 | 62 | 63 | 64 | True 65 | True 66 | KdbndpStrings.Designer.tt 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpMethodCallTranslatorProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 4 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 7 | 8 | public class KdbndpMethodCallTranslatorProvider : RelationalMethodCallTranslatorProvider 9 | { 10 | public virtual KdbndpLTreeTranslator LTreeTranslator { get; } 11 | 12 | public KdbndpMethodCallTranslatorProvider( 13 | RelationalMethodCallTranslatorProviderDependencies dependencies, 14 | IModel model, 15 | IKdbndpSingletonOptions KdbndpSingletonOptions) 16 | : base(dependencies) 17 | { 18 | var sqlExpressionFactory = (KdbndpSqlExpressionFactory)dependencies.SqlExpressionFactory; 19 | var typeMappingSource = (KdbndpTypeMappingSource)dependencies.RelationalTypeMappingSource; 20 | var jsonTranslator = new KdbndpJsonPocoTranslator(typeMappingSource, sqlExpressionFactory, model); 21 | LTreeTranslator = new KdbndpLTreeTranslator(typeMappingSource, sqlExpressionFactory, model); 22 | 23 | AddTranslators(new IMethodCallTranslator[] 24 | { 25 | new KdbndpArrayTranslator(sqlExpressionFactory, jsonTranslator, KdbndpSingletonOptions.UseRedshift), 26 | new KdbndpByteArrayMethodTranslator(sqlExpressionFactory), 27 | new KdbndpConvertTranslator(sqlExpressionFactory), 28 | new KdbndpDateTimeMethodTranslator(typeMappingSource, sqlExpressionFactory), 29 | new KdbndpFullTextSearchMethodTranslator(typeMappingSource, sqlExpressionFactory, model), 30 | new KdbndpFuzzyStringMatchMethodTranslator(sqlExpressionFactory), 31 | new KdbndpJsonDomTranslator(typeMappingSource, sqlExpressionFactory, model), 32 | new KdbndpJsonDbFunctionsTranslator(typeMappingSource, sqlExpressionFactory, model), 33 | new KdbndpLikeTranslator(sqlExpressionFactory), 34 | LTreeTranslator, 35 | new KdbndpMathTranslator(typeMappingSource, sqlExpressionFactory, model), 36 | new KdbndpNetworkTranslator(typeMappingSource, sqlExpressionFactory, model), 37 | new KdbndpNewGuidTranslator(sqlExpressionFactory, KdbndpSingletonOptions), 38 | new KdbndpObjectToStringTranslator(typeMappingSource, sqlExpressionFactory), 39 | new KdbndpRandomTranslator(sqlExpressionFactory), 40 | new KdbndpRangeTranslator(typeMappingSource, sqlExpressionFactory, model, KdbndpSingletonOptions), 41 | new KdbndpRegexIsMatchTranslator(sqlExpressionFactory), 42 | new KdbndpStringMethodTranslator(typeMappingSource, sqlExpressionFactory, model), 43 | new KdbndpTrigramsMethodTranslator(typeMappingSource, sqlExpressionFactory, model), 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Internal/KdbndpEvaluatableExpressionFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using KdbndpTypes; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 9 | 10 | public class KdbndpEvaluatableExpressionFilter : RelationalEvaluatableExpressionFilter 11 | { 12 | private static readonly MethodInfo TsQueryParse = 13 | typeof(KdbndpTsQuery).GetRuntimeMethod(nameof(KdbndpTsQuery.Parse), new[] { typeof(string) })!; 14 | 15 | private static readonly MethodInfo TsVectorParse = 16 | typeof(KdbndpTsVector).GetRuntimeMethod(nameof(KdbndpTsVector.Parse), new[] { typeof(string) })!; 17 | 18 | public KdbndpEvaluatableExpressionFilter( 19 | EvaluatableExpressionFilterDependencies dependencies, 20 | RelationalEvaluatableExpressionFilterDependencies relationalDependencies) 21 | : base(dependencies, relationalDependencies) 22 | { 23 | } 24 | 25 | public override bool IsEvaluatableExpression(Expression expression, IModel model) 26 | { 27 | switch (expression) 28 | { 29 | case MethodCallExpression methodCallExpression: 30 | var declaringType = methodCallExpression.Method.DeclaringType; 31 | 32 | if (methodCallExpression.Method == TsQueryParse 33 | || methodCallExpression.Method == TsVectorParse 34 | || declaringType == typeof(KdbndpDbFunctionsExtensions) 35 | || declaringType == typeof(KdbndpFullTextSearchDbFunctionsExtensions) 36 | || declaringType == typeof(KdbndpFullTextSearchLinqExtensions) 37 | || declaringType == typeof(KdbndpNetworkDbFunctionsExtensions) 38 | || declaringType == typeof(KdbndpJsonDbFunctionsExtensions) 39 | || declaringType == typeof(KdbndpRangeDbFunctionsExtensions)) 40 | { 41 | return false; 42 | } 43 | 44 | break; 45 | 46 | case MemberExpression memberExpression: 47 | // We support translating certain NodaTime patterns which accept a time zone as a parameter, 48 | // e.g. Instant.InZone(timezone), as long as the timezone is expressed as an access on DateTimeZoneProviders.Tzdb. 49 | // Prevent this from being evaluated locally and so parameterized, so we can access the access tree on 50 | // DateTimeZoneProviders and extract the constant (see KdbndpNodaTimeMethodCallTranslatorPlugin) 51 | if (memberExpression.Member.DeclaringType?.FullName == "NodaTime.DateTimeZoneProviders") 52 | { 53 | return false; 54 | } 55 | 56 | break; 57 | } 58 | 59 | return base.IsEvaluatableExpression(expression, model); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Metadata/Internal/KdbndpAnnotationNames.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Metadata.Internal; 4 | 5 | public static class KdbndpAnnotationNames 6 | { 7 | public const string Prefix = "Kdbndp:"; 8 | 9 | public const string CompressionMethod = Prefix + "Compression:"; 10 | public const string CreatedConcurrently = Prefix + "CreatedConcurrently"; 11 | public const string DatabaseTemplate = Prefix + "DatabaseTemplate"; 12 | public const string DefaultColumnCollation = Prefix + "DefaultColumnCollation"; 13 | public const string HiLoSequenceName = Prefix + "HiLoSequenceName"; 14 | public const string HiLoSequenceSchema = Prefix + "HiLoSequenceSchema"; 15 | public const string IdentityOptions = Prefix + "IdentitySequenceOptions"; 16 | public const string IndexMethod = Prefix + "IndexMethod"; 17 | public const string IndexOperators = Prefix + "IndexOperators"; 18 | public const string IndexSortOrder = Prefix + "IndexSortOrder"; 19 | public const string IndexNullSortOrder = Prefix + "IndexNullSortOrder"; 20 | public const string IndexInclude = Prefix + "IndexInclude"; 21 | public const string Tablespace = Prefix + "Tablespace"; 22 | public const string TsVectorConfig = Prefix + "TsVectorConfig"; 23 | public const string TsVectorProperties = Prefix + "TsVectorProperties"; 24 | public const string UnloggedTable = Prefix + "UnloggedTable"; 25 | public const string ValueGenerationStrategy = Prefix + "ValueGenerationStrategy"; 26 | 27 | public const string CollationDefinitionPrefix = Prefix + "CollationDefinition:"; 28 | public const string EnumPrefix = Prefix + "Enum:"; 29 | public const string PostgresExtensionPrefix = Prefix + "PostgresExtension:"; 30 | public const string RangePrefix = Prefix + "Range:"; 31 | public const string StorageParameterPrefix = Prefix + "StorageParameter:"; 32 | 33 | // Database model annotations 34 | 35 | /// 36 | /// Identifies the type of the KingbaseES type of this column (e.g. array, range, base). 37 | /// 38 | public const string PostgresTypeType = Prefix + "PostgresTypeType"; 39 | 40 | /// 41 | /// If this column's data type is an array, contains the data type of its elements. 42 | /// Otherwise null. 43 | /// 44 | public const string ElementDataType = Prefix + "ElementDataType"; 45 | 46 | /// 47 | /// If the index contains an expression (rather than simple column references), the expression is contained here. 48 | /// This is currently unsupported and will be ignored. 49 | /// 50 | public const string IndexExpression = Prefix + "IndexExpression"; 51 | 52 | [Obsolete("Replaced by ValueGenerationStrategy.SerialColumn")] 53 | public const string ValueGeneratedOnAdd = Prefix + "ValueGeneratedOnAdd"; 54 | 55 | [Obsolete("Replaced by built-in EF Core support, use HasComment on entities or properties.")] 56 | public const string Comment = Prefix + "Comment"; 57 | 58 | [Obsolete("Replaced by RelationalAnnotationNames.Collation")] 59 | public const string IndexCollation = Prefix + "IndexCollation"; 60 | } 61 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Expressions/Internal/PostgresRegexMatchExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Text.RegularExpressions; 4 | using Microsoft.EntityFrameworkCore.Query; 5 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 6 | using Microsoft.EntityFrameworkCore.Storage; 7 | using Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 8 | 9 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions.Internal; 10 | 11 | public class PostgresRegexMatchExpression : SqlExpression, IEquatable 12 | { 13 | /// 14 | public override Type Type => typeof(bool); 15 | 16 | /// 17 | /// The match expression. 18 | /// 19 | public virtual SqlExpression Match { get; } 20 | 21 | /// 22 | /// The pattern to match. 23 | /// 24 | public virtual SqlExpression Pattern { get; } 25 | 26 | /// 27 | /// The options for regular expression evaluation. 28 | /// 29 | public virtual RegexOptions Options { get; } 30 | 31 | /// 32 | /// Constructs a . 33 | /// 34 | /// The expression to match. 35 | /// The pattern to match. 36 | /// The options for regular expression evaluation. 37 | /// The type mapping for the expression. 38 | public PostgresRegexMatchExpression( 39 | SqlExpression match, 40 | SqlExpression pattern, 41 | RegexOptions options, 42 | RelationalTypeMapping? typeMapping) 43 | : base(typeof(bool), typeMapping) 44 | { 45 | Match = match; 46 | Pattern = pattern; 47 | Options = options; 48 | } 49 | 50 | /// 51 | protected override Expression VisitChildren(ExpressionVisitor visitor) 52 | => Update((SqlExpression)visitor.Visit(Match), (SqlExpression)visitor.Visit(Pattern)); 53 | 54 | public virtual PostgresRegexMatchExpression Update(SqlExpression match, SqlExpression pattern) 55 | => match != Match || pattern != Pattern 56 | ? new PostgresRegexMatchExpression(match, pattern, Options, TypeMapping) 57 | : this; 58 | 59 | public virtual bool Equals(PostgresRegexMatchExpression? other) 60 | => ReferenceEquals(this, other) || 61 | other is object && 62 | base.Equals(other) && 63 | Match.Equals(other.Match) && 64 | Pattern.Equals(other.Pattern) && 65 | Options.Equals(other.Options); 66 | 67 | public override bool Equals(object? other) 68 | => other is PostgresRegexMatchExpression otherRegexMatch && Equals(otherRegexMatch); 69 | 70 | public override int GetHashCode() 71 | => HashCode.Combine(base.GetHashCode(), Match, Pattern, Options); 72 | 73 | /// 74 | protected override void Print(ExpressionPrinter expressionPrinter) 75 | { 76 | expressionPrinter.Visit(Match); 77 | expressionPrinter.Append(" ~ "); 78 | expressionPrinter.Visit(Pattern); 79 | } 80 | 81 | /// 82 | public override string ToString() => $"{Match} ~ {Pattern}"; 83 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DotNetCore Entity Framework Core provider for KingbaseES for R6 V008R006C008B0014 2 | 3 | [![Member project of .NET Core Community](https://img.shields.io/badge/member%20project%20of-NCC-9e20c9.svg)](https://github.com/dotnetcore) 4 | [![nuget](https://img.shields.io/nuget/v/DotNetCore.EntityFrameworkCore.KingbaseES.svg?style=flat-square)](https://www.nuget.org/packages/DotNetCore.EntityFrameworkCore.KingbaseES) 5 | [![stats](https://img.shields.io/nuget/dt/DotNetCore.EntityFrameworkCore.KingbaseES.svg?style=flat-square)](https://www.nuget.org/stats/packages/DotNetCore.EntityFrameworkCore.KingbaseES?groupby=Version) 6 | 7 | `DotNetCore.EntityFrameworkCore.KingbaseES` is an open source Entity Framework Core provider for KingbaseES. It supports you to interact with KingbaseES via EFCore, the most widely-used .NET O/RM from Microsoft, up to its latest version, and use familiar LINQ syntax to express queries. 8 | 9 | ## Getting Started 10 | 11 | ### 1. Project Configuration 12 | 13 | Ensure that your `.csproj` file contains the following reference: 14 | 15 | ```xml 16 | 17 | ``` 18 | 19 | ### 2. Services Configuration 20 | 21 | Add `DotNetCore.EntityFrameworkCore.KingbaseES` to the services configuration in your the `Startup.cs` file of your ASP.NET Core project: 22 | 23 | ```csharp 24 | public class Startup 25 | { 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | // Replace with your connection string. 29 | var connectionString = "host={host};port={port};database={database};username={username};password={password};"; 30 | 31 | // Replace 'YourDbContext' with the name of your own DbContext derived class. 32 | services.AddDbContext( 33 | dbContextOptions => dbContextOptions 34 | .UseKdbndp(connectionString) 35 | ); 36 | } 37 | } 38 | ``` 39 | 40 | ### 3. Sample Application 41 | 42 | Check out our [Basic Test](https://github.com/dotnetcore/EntityFrameworkCore.KingbaseES/tree/main/test/KingbaseES.BasicTest) for an example repository that includes an ASP.NET Core MVC Application. 43 | 44 | There are also many complete and concise console application samples posted in the issue section (some of them can be found by searching for `Program.cs`). 45 | 46 | ### 4. Read the EF Core Documentation 47 | 48 | Refer to Microsoft's [EF Core Documentation](https://docs.microsoft.com/en-us/ef/core/) for detailed instructions and examples on using EF Core. 49 | 50 | ## Scaffolding / Reverse Engineering 51 | 52 | Use the [EF Core tools](https://docs.microsoft.com/en-us/ef/core/cli/dotnet) to execute scaffolding commands: 53 | 54 | ``` 55 | dotnet ef dbcontext scaffold "Server=localhost;User=root;Password=1234;Database=ef" "DotNetCore.EntityFrameworkCore.KingbaseES" 56 | ``` 57 | 58 | ## Contribute 59 | 60 | One of the easiest ways to contribute is to report issues and participate in discussions. You can also contribute by submitting pull requests with code changes and supporting tests. 61 | 62 | We are always looking for additional core contributors. If you got a couple of hours a week and know your way around EF Core and KingbaseES, give us a nudge. 63 | 64 | ## License 65 | 66 | [PostgreSQL license](https://github.com/dotnetcore/EntityFrameworkCore.KingbaseES/blob/main/LICENSE) 67 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/KdbndpValueGeneratorSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using Microsoft.EntityFrameworkCore.ValueGeneration; 8 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 9 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 10 | 11 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 12 | 13 | public class KdbndpValueGeneratorSelector : RelationalValueGeneratorSelector 14 | { 15 | private readonly IKdbndpSequenceValueGeneratorFactory _sequenceFactory; 16 | private readonly IKdbndpRelationalConnection _connection; 17 | private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder; 18 | private readonly IRelationalCommandDiagnosticsLogger _commandLogger; 19 | 20 | public KdbndpValueGeneratorSelector( 21 | ValueGeneratorSelectorDependencies dependencies, 22 | IKdbndpSequenceValueGeneratorFactory sequenceFactory, 23 | IKdbndpRelationalConnection connection, 24 | IRawSqlCommandBuilder rawSqlCommandBuilder, 25 | IRelationalCommandDiagnosticsLogger commandLogger) 26 | : base(dependencies) 27 | { 28 | _sequenceFactory = sequenceFactory; 29 | _connection = connection; 30 | _rawSqlCommandBuilder = rawSqlCommandBuilder; 31 | _commandLogger = commandLogger; 32 | } 33 | 34 | /// 35 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 36 | /// directly from your code. This API may change or be removed in future releases. 37 | /// 38 | public new virtual IKdbndpValueGeneratorCache Cache => (IKdbndpValueGeneratorCache)base.Cache; 39 | 40 | public override ValueGenerator Select(IProperty property, IEntityType entityType) 41 | { 42 | Check.NotNull(property, nameof(property)); 43 | Check.NotNull(entityType, nameof(entityType)); 44 | 45 | return property.GetValueGeneratorFactory() is null 46 | && property.GetValueGenerationStrategy() == KdbndpValueGenerationStrategy.SequenceHiLo 47 | ? _sequenceFactory.Create( 48 | property, 49 | Cache.GetOrAddSequenceState(property, _connection), 50 | _connection, 51 | _rawSqlCommandBuilder, 52 | _commandLogger) 53 | : base.Select(property, entityType); 54 | } 55 | 56 | public override ValueGenerator Create(IProperty property, IEntityType entityType) 57 | { 58 | Check.NotNull(property, nameof(property)); 59 | Check.NotNull(entityType, nameof(entityType)); 60 | 61 | // Generate temporary values if the user specified a default value (to allow 62 | // generating server-side with uuid-ossp or whatever) 63 | return property.ClrType.UnwrapNullableType() == typeof(Guid) 64 | ? property.ValueGenerated == ValueGenerated.Never 65 | || property.GetDefaultValueSql() is not null 66 | ? new TemporaryGuidValueGenerator() 67 | : new GuidValueGenerator() 68 | : base.Create(property, entityType); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Expressions/Internal/PostgresArrayIndexExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore.Query; 5 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 6 | using Microsoft.EntityFrameworkCore.Storage; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | 9 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions.Internal; 10 | 11 | /// 12 | /// An SQL expression that represents an indexing into a KingbaseES array. 13 | /// 14 | /// 15 | /// specifically disallows having an 16 | /// of value as arrays are a KingbaseES-only feature. 17 | /// 18 | public class PostgresArrayIndexExpression : SqlExpression, IEquatable 19 | { 20 | /// 21 | /// The array being indexed. 22 | /// 23 | public virtual SqlExpression Array { get; } 24 | 25 | /// 26 | /// The index in the array. 27 | /// 28 | public virtual SqlExpression Index { get; } 29 | 30 | public PostgresArrayIndexExpression( 31 | SqlExpression array, 32 | SqlExpression index, 33 | Type type, 34 | RelationalTypeMapping? typeMapping) 35 | : base(type.UnwrapNullableType(), typeMapping) 36 | { 37 | Check.NotNull(array, nameof(array)); 38 | Check.NotNull(index, nameof(index)); 39 | 40 | if (!array.Type.TryGetElementType(out var elementType)) 41 | { 42 | throw new ArgumentException("Array expression must of an array type", nameof(array)); 43 | } 44 | 45 | if (type.UnwrapNullableType() != elementType.UnwrapNullableType()) 46 | { 47 | throw new ArgumentException($"Mismatch between array type ({array.Type.Name}) and expression type ({type})"); 48 | } 49 | 50 | if (index.Type != typeof(int)) 51 | { 52 | throw new ArgumentException("Index expression must of type int", nameof(index)); 53 | } 54 | 55 | Array = array; 56 | Index = index; 57 | } 58 | 59 | public virtual PostgresArrayIndexExpression Update(SqlExpression array, SqlExpression index) 60 | => array == Array && index == Index 61 | ? this 62 | : new PostgresArrayIndexExpression(array, index, Type, TypeMapping); 63 | 64 | /// 65 | protected override Expression VisitChildren(ExpressionVisitor visitor) 66 | => Update((SqlExpression)visitor.Visit(Array), (SqlExpression)visitor.Visit(Index)); 67 | 68 | public virtual bool Equals(PostgresArrayIndexExpression? other) 69 | => ReferenceEquals(this, other) || 70 | other is object && 71 | base.Equals(other) && 72 | Array.Equals(other.Array) && 73 | Index.Equals(other.Index); 74 | 75 | public override bool Equals(object? obj) => obj is PostgresArrayIndexExpression e && Equals(e); 76 | 77 | public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Array, Index); 78 | 79 | protected override void Print(ExpressionPrinter expressionPrinter) 80 | { 81 | expressionPrinter.Visit(Array); 82 | expressionPrinter.Append("["); 83 | expressionPrinter.Visit(Index); 84 | expressionPrinter.Append("]"); 85 | } 86 | 87 | public override string ToString() => $"{Array}[{Index}]"; 88 | } 89 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpTimeSpanMemberTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Diagnostics; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | using static Kdbndp.EntityFrameworkCore.KingbaseES.Utilities.Statics; 9 | 10 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 11 | 12 | public class KdbndpTimeSpanMemberTranslator : IMemberTranslator 13 | { 14 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 15 | 16 | public KdbndpTimeSpanMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) 17 | => _sqlExpressionFactory = sqlExpressionFactory; 18 | 19 | private static readonly bool[] FalseTrueArray = { false, true }; 20 | 21 | public virtual SqlExpression? Translate(SqlExpression? instance, 22 | MemberInfo member, 23 | Type returnType, 24 | IDiagnosticsLogger logger) 25 | { 26 | Check.NotNull(member, nameof(member)); 27 | Check.NotNull(returnType, nameof(returnType)); 28 | 29 | if (member.DeclaringType == typeof(TimeSpan) && instance is not null) 30 | { 31 | return member.Name switch 32 | { 33 | nameof(TimeSpan.Days) => Floor(DatePart("day", instance)), 34 | nameof(TimeSpan.Hours) => Floor(DatePart("hour", instance)), 35 | nameof(TimeSpan.Minutes) => Floor(DatePart("minute", instance)), 36 | nameof(TimeSpan.Seconds) => Floor(DatePart("second", instance)), 37 | nameof(TimeSpan.Milliseconds) => _sqlExpressionFactory.Modulo( 38 | Floor(DatePart("millisecond", instance!)), 39 | _sqlExpressionFactory.Constant(1000)), 40 | 41 | nameof(TimeSpan.TotalDays) => TranslateDurationTotalMember(instance, 86400), 42 | nameof(TimeSpan.TotalHours) => TranslateDurationTotalMember(instance, 3600), 43 | nameof(TimeSpan.TotalMinutes) => TranslateDurationTotalMember(instance, 60), 44 | nameof(TimeSpan.TotalSeconds) => DatePart("epoch", instance), 45 | nameof(TimeSpan.TotalMilliseconds) => TranslateDurationTotalMember(instance, 0.001), 46 | 47 | _ => null 48 | }; 49 | } 50 | 51 | return null; 52 | 53 | SqlExpression Floor(SqlExpression value) 54 | => _sqlExpressionFactory.Convert( 55 | _sqlExpressionFactory.Function( 56 | "floor", 57 | new[] { value }, 58 | nullable: true, 59 | argumentsPropagateNullability: TrueArrays[1], 60 | typeof(double)), 61 | typeof(int)); 62 | 63 | SqlFunctionExpression DatePart(string part, SqlExpression value) 64 | => _sqlExpressionFactory.Function("date_part", new[] 65 | { 66 | _sqlExpressionFactory.Constant(part), 67 | value 68 | }, 69 | nullable: true, 70 | argumentsPropagateNullability: FalseTrueArray, 71 | returnType); 72 | 73 | SqlBinaryExpression TranslateDurationTotalMember(SqlExpression instance, double divisor) 74 | => _sqlExpressionFactory.Divide(DatePart("epoch", instance), _sqlExpressionFactory.Constant(divisor)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpCharacterStringTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using Microsoft.EntityFrameworkCore.ChangeTracking; 4 | using Microsoft.EntityFrameworkCore.Storage; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 8 | 9 | /// 10 | /// Type mapping for the KingbaseES 'character' data type. Handles both CLR strings and chars. 11 | /// 12 | /// 13 | /// See: https://www.KingbaseES.org/docs/current/static/datatype-character.html 14 | /// 15 | /// 16 | public class KdbndpCharacterStringTypeMapping : KdbndpStringTypeMapping 17 | { 18 | /// 19 | /// Static for fixed-width character types. 20 | /// 21 | /// 22 | ///

23 | /// Comparisons of 'character' data as defined in the SQL standard differ dramatically from CLR string 24 | /// comparisons. This value comparer adjusts for this by only comparing strings after truncating trailing 25 | /// whitespace. 26 | ///

27 | ///

28 | /// Note that if a value converter is used and the CLR type isn't a string at all, we just use the default 29 | /// value converter instead. 30 | ///

31 | ///
32 | private static readonly ValueComparer CharacterValueComparer = 33 | new( 34 | (x, y) => EqualsWithoutTrailingWhitespace(x, y), 35 | x => GetHashCodeWithoutTrailingWhitespace(x)); 36 | 37 | public override ValueComparer Comparer => ClrType == typeof(string) ? CharacterValueComparer : base.Comparer; 38 | 39 | public override ValueComparer KeyComparer => Comparer; 40 | 41 | public KdbndpCharacterStringTypeMapping(string storeType, int size = 1) 42 | : this(new RelationalTypeMappingParameters( 43 | new CoreTypeMappingParameters(typeof(string)), 44 | storeType, 45 | StoreTypePostfix.Size, 46 | System.Data.DbType.StringFixedLength, 47 | unicode: false, 48 | size, 49 | fixedLength: true)) {} 50 | 51 | protected KdbndpCharacterStringTypeMapping(RelationalTypeMappingParameters parameters) 52 | : base(parameters, KdbndpTypes.KdbndpDbType.Char) 53 | { 54 | } 55 | 56 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 57 | => new KdbndpCharacterStringTypeMapping(new RelationalTypeMappingParameters( 58 | parameters.CoreParameters, 59 | parameters.StoreType, 60 | StoreTypePostfix.Size, 61 | parameters.DbType, 62 | parameters.Unicode, 63 | parameters.Size, 64 | parameters.FixedLength, 65 | parameters.Precision, 66 | parameters.Scale)); 67 | 68 | protected override void ConfigureParameter(DbParameter parameter) 69 | { 70 | if (parameter.Value is string value) 71 | { 72 | parameter.Value = value.TrimEnd(); 73 | } 74 | 75 | base.ConfigureParameter(parameter); 76 | } 77 | 78 | private static bool EqualsWithoutTrailingWhitespace(string? a, string? b) 79 | => (a, b) switch 80 | { 81 | (null, null) => true, 82 | (_, null) => false, 83 | (null, _) => false, 84 | _ => a.AsSpan().TrimEnd().SequenceEqual(b.AsSpan().TrimEnd()) 85 | }; 86 | 87 | private static int GetHashCodeWithoutTrailingWhitespace(string a) 88 | => a.TrimEnd().GetHashCode(); 89 | } 90 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpJsonTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Text.Json; 7 | using Microsoft.EntityFrameworkCore.Storage; 8 | using Microsoft.EntityFrameworkCore.Utilities; 9 | using KdbndpTypes; 10 | 11 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 12 | 13 | /// 14 | /// A mapping for an arbitrary user POCO to KingbaseES jsonb or json. 15 | /// For mapping to .NET string, see . 16 | /// 17 | public class KdbndpJsonTypeMapping : KdbndpTypeMapping 18 | { 19 | public KdbndpJsonTypeMapping(string storeType, Type clrType) 20 | : base(storeType, clrType, storeType == "jsonb" ? KdbndpDbType.Jsonb : KdbndpDbType.Json) 21 | { 22 | if (storeType != "json" && storeType != "jsonb") 23 | { 24 | throw new ArgumentException($"{nameof(storeType)} must be 'json' or 'jsonb'", nameof(storeType)); 25 | } 26 | } 27 | 28 | protected KdbndpJsonTypeMapping(RelationalTypeMappingParameters parameters, KdbndpDbType KdbndpDbType) 29 | : base(parameters, KdbndpDbType) 30 | { 31 | } 32 | 33 | public virtual bool IsJsonb => StoreType == "jsonb"; 34 | 35 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 36 | => new KdbndpJsonTypeMapping(parameters, KdbndpDbType); 37 | 38 | protected virtual string EscapeSqlLiteral(string literal) 39 | => Check.NotNull(literal, nameof(literal)).Replace("'", "''"); 40 | 41 | protected override string GenerateNonNullSqlLiteral(object value) 42 | { 43 | switch (value) 44 | { 45 | case JsonDocument _: 46 | case JsonElement _: 47 | { 48 | using var stream = new MemoryStream(); 49 | using var writer = new Utf8JsonWriter(stream); 50 | if (value is JsonDocument doc) 51 | { 52 | doc.WriteTo(writer); 53 | } 54 | else 55 | { 56 | ((JsonElement)value).WriteTo(writer); 57 | } 58 | 59 | writer.Flush(); 60 | return $"'{EscapeSqlLiteral(Encoding.UTF8.GetString(stream.ToArray()))}'"; 61 | } 62 | case string s: 63 | return $"'{EscapeSqlLiteral(s)}'"; 64 | default: // User POCO 65 | return $"'{EscapeSqlLiteral(JsonSerializer.Serialize(value))}'"; 66 | } 67 | } 68 | 69 | public override Expression GenerateCodeLiteral(object value) 70 | => value switch 71 | { 72 | JsonDocument document => Expression.Call(ParseMethod, Expression.Constant(document.RootElement.ToString()), DefaultJsonDocumentOptions), 73 | JsonElement element => Expression.Property( 74 | Expression.Call(ParseMethod, Expression.Constant(element.ToString()), DefaultJsonDocumentOptions), 75 | nameof(JsonDocument.RootElement)), 76 | string s => Expression.Constant(s), 77 | _ => throw new NotSupportedException("Cannot generate code literals for JSON POCOs") 78 | }; 79 | 80 | private static readonly Expression DefaultJsonDocumentOptions = Expression.New(typeof(JsonDocumentOptions)); 81 | 82 | private static readonly MethodInfo ParseMethod = 83 | typeof(JsonDocument).GetMethod(nameof(JsonDocument.Parse), new[] { typeof(string), typeof(JsonDocumentOptions) })!; 84 | } 85 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpObjectToStringTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Query; 7 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 8 | using Microsoft.EntityFrameworkCore.Storage; 9 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 10 | 11 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 12 | 13 | public class KdbndpObjectToStringTranslator : IMethodCallTranslator 14 | { 15 | private static readonly HashSet _typeMapping = new() 16 | { 17 | typeof(int), 18 | typeof(long), 19 | typeof(DateTime), 20 | typeof(Guid), 21 | typeof(bool), 22 | typeof(byte), 23 | //typeof(byte[]) 24 | typeof(double), 25 | typeof(DateTimeOffset), 26 | typeof(char), 27 | typeof(short), 28 | typeof(float), 29 | typeof(decimal), 30 | typeof(TimeSpan), 31 | typeof(uint), 32 | typeof(ushort), 33 | typeof(ulong), 34 | typeof(sbyte), 35 | }; 36 | 37 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 38 | private readonly RelationalTypeMapping _textTypeMapping; 39 | 40 | public KdbndpObjectToStringTranslator(IRelationalTypeMappingSource typeMappingSource, ISqlExpressionFactory sqlExpressionFactory) 41 | { 42 | _sqlExpressionFactory = sqlExpressionFactory; 43 | 44 | _textTypeMapping = typeMappingSource.FindMapping("text")!; 45 | } 46 | 47 | public virtual SqlExpression? Translate( 48 | SqlExpression? instance, 49 | MethodInfo method, 50 | IReadOnlyList arguments, 51 | IDiagnosticsLogger logger) 52 | { 53 | if (instance is null || method.Name != nameof(ToString) || arguments.Count != 0) 54 | { 55 | return null; 56 | } 57 | 58 | if (instance.Type == typeof(bool)) 59 | { 60 | return instance is ColumnExpression columnExpression && columnExpression.IsNullable 61 | ? _sqlExpressionFactory.Case( 62 | new[] 63 | { 64 | new CaseWhenClause( 65 | _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)), 66 | _sqlExpressionFactory.Constant(false.ToString())), 67 | new CaseWhenClause( 68 | _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(true)), 69 | _sqlExpressionFactory.Constant(true.ToString())) 70 | }, 71 | _sqlExpressionFactory.Constant(null)) 72 | : _sqlExpressionFactory.Case( 73 | new[] 74 | { 75 | new CaseWhenClause( 76 | _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)), 77 | _sqlExpressionFactory.Constant(false.ToString())) 78 | }, 79 | _sqlExpressionFactory.Constant(true.ToString())); 80 | } 81 | 82 | return _typeMapping.Contains(instance.Type) 83 | || instance.Type.UnwrapNullableType().IsEnum && instance.TypeMapping is KdbndpEnumTypeMapping 84 | ? _sqlExpressionFactory.Convert(instance, typeof(string), _textTypeMapping) 85 | : null; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Expressions/Internal/PostgresILikeExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using Kdbndp.EntityFrameworkCore.KingbaseES.Query.Internal; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions.Internal; 9 | 10 | /// 11 | /// Represents a KingbaseES ILIKE expression. 12 | /// 13 | // ReSharper disable once InconsistentNaming 14 | public class PostgresILikeExpression : SqlExpression, IEquatable 15 | { 16 | /// 17 | /// The match expression. 18 | /// 19 | public virtual SqlExpression Match { get; } 20 | 21 | /// 22 | /// The pattern to match. 23 | /// 24 | public virtual SqlExpression Pattern { get; } 25 | 26 | /// 27 | /// The escape character to use in . 28 | /// 29 | public virtual SqlExpression? EscapeChar { get; } 30 | 31 | /// 32 | /// Constructs a . 33 | /// 34 | /// The expression to match. 35 | /// The pattern to match. 36 | /// The escape character to use in . 37 | /// 38 | public PostgresILikeExpression( 39 | SqlExpression match, 40 | SqlExpression pattern, 41 | SqlExpression? escapeChar, 42 | RelationalTypeMapping? typeMapping) 43 | : base(typeof(bool), typeMapping) 44 | { 45 | Match = match; 46 | Pattern = pattern; 47 | EscapeChar = escapeChar; 48 | } 49 | 50 | /// 51 | protected override Expression VisitChildren(ExpressionVisitor visitor) 52 | => Update( 53 | (SqlExpression)visitor.Visit(Match), 54 | (SqlExpression)visitor.Visit(Pattern), 55 | EscapeChar is null ? null : (SqlExpression)visitor.Visit(EscapeChar)); 56 | 57 | public virtual PostgresILikeExpression Update( 58 | SqlExpression match, 59 | SqlExpression pattern, 60 | SqlExpression? escapeChar) 61 | => match == Match && pattern == Pattern && escapeChar == EscapeChar 62 | ? this 63 | : new PostgresILikeExpression(match, pattern, escapeChar, TypeMapping); 64 | 65 | /// 66 | public override bool Equals(object? obj) => obj is PostgresILikeExpression other && Equals(other); 67 | 68 | /// 69 | public virtual bool Equals(PostgresILikeExpression? other) 70 | => ReferenceEquals(this, other) || 71 | other is object && 72 | base.Equals(other) && 73 | Equals(Match, other.Match) && 74 | Equals(Pattern, other.Pattern) && 75 | Equals(EscapeChar, other.EscapeChar); 76 | 77 | /// 78 | public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Match, Pattern, EscapeChar); 79 | 80 | protected override void Print(ExpressionPrinter expressionPrinter) 81 | { 82 | expressionPrinter.Visit(Match); 83 | expressionPrinter.Append(" ILIKE "); 84 | expressionPrinter.Visit(Pattern); 85 | 86 | if (EscapeChar is not null) 87 | { 88 | expressionPrinter.Append(" ESCAPE "); 89 | expressionPrinter.Visit(EscapeChar); 90 | } 91 | } 92 | 93 | /// 94 | public override string ToString() => $"{Match} ILIKE {Pattern}{(EscapeChar is null ? "" : $" ESCAPE {EscapeChar}")}"; 95 | } -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Internal/KdbndpSingletonOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Diagnostics; 6 | using Microsoft.EntityFrameworkCore.Infrastructure; 7 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure; 8 | using Kdbndp.EntityFrameworkCore.KingbaseES.Infrastructure.Internal; 9 | 10 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Internal; 11 | 12 | /// 13 | public class KdbndpSingletonOptions : IKdbndpSingletonOptions 14 | { 15 | public static readonly Version DefaultPostgresVersion = new(12, 0); 16 | 17 | /// 18 | public virtual Version PostgresVersion { get; private set; } = null!; 19 | 20 | /// 21 | public virtual Version? PostgresVersionWithoutDefault { get; private set; } 22 | 23 | /// 24 | public virtual bool UseRedshift { get; private set; } 25 | 26 | /// 27 | public virtual bool ReverseNullOrderingEnabled { get; private set; } 28 | 29 | /// 30 | public virtual IReadOnlyList UserRangeDefinitions { get; private set; } 31 | 32 | public KdbndpSingletonOptions() 33 | => UserRangeDefinitions = new UserRangeDefinition[0]; 34 | 35 | /// 36 | public virtual void Initialize(IDbContextOptions options) 37 | { 38 | var KdbndpOptions = options.FindExtension() ?? new KdbndpOptionsExtension(); 39 | 40 | PostgresVersionWithoutDefault = KdbndpOptions.PostgresVersion; 41 | PostgresVersion = KdbndpOptions.PostgresVersion ?? DefaultPostgresVersion; 42 | UseRedshift = KdbndpOptions.UseRedshift; 43 | ReverseNullOrderingEnabled = KdbndpOptions.ReverseNullOrdering; 44 | UserRangeDefinitions = KdbndpOptions.UserRangeDefinitions; 45 | } 46 | 47 | /// 48 | public virtual void Validate(IDbContextOptions options) 49 | { 50 | var KdbndpOptions = options.FindExtension() ?? new KdbndpOptionsExtension(); 51 | 52 | if (PostgresVersionWithoutDefault != KdbndpOptions.PostgresVersion) 53 | { 54 | throw new InvalidOperationException( 55 | CoreStrings.SingletonOptionChanged( 56 | nameof(KdbndpDbContextOptionsBuilder.SetPostgresVersion), 57 | nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); 58 | } 59 | 60 | if (UseRedshift != KdbndpOptions.UseRedshift) 61 | { 62 | throw new InvalidOperationException( 63 | CoreStrings.SingletonOptionChanged( 64 | nameof(KdbndpDbContextOptionsBuilder.UseRedshift), 65 | nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); 66 | } 67 | 68 | if (ReverseNullOrderingEnabled != KdbndpOptions.ReverseNullOrdering) 69 | { 70 | throw new InvalidOperationException( 71 | CoreStrings.SingletonOptionChanged( 72 | nameof(KdbndpDbContextOptionsBuilder.ReverseNullOrdering), 73 | nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); 74 | } 75 | 76 | if (UserRangeDefinitions.Count != KdbndpOptions.UserRangeDefinitions.Count 77 | || UserRangeDefinitions.Zip(KdbndpOptions.UserRangeDefinitions).Any(t => t.First != t.Second)) 78 | { 79 | throw new InvalidOperationException( 80 | CoreStrings.SingletonOptionChanged( 81 | nameof(KdbndpDbContextOptionsBuilder.MapRange), 82 | nameof(DbContextOptionsBuilder.UseInternalServiceProvider))); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/Expressions/Internal/PostgresUnknownBinaryExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | 8 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions.Internal; 9 | 10 | /// 11 | /// A binary expression only to be used by plugins, since new expressions can only be added (and handled) 12 | /// within the provider itself. Allows defining the operator as a string within the expression, and has 13 | /// default (i.e. propagating) nullability semantics. 14 | /// All type mappings must be applied to the operands before the expression is constructed, since there's 15 | /// no inference logic for it in . 16 | /// 17 | public class PostgresUnknownBinaryExpression : SqlExpression, IEquatable 18 | { 19 | /// 20 | /// The left-hand expression. 21 | /// 22 | public virtual SqlExpression Left { get; } 23 | 24 | /// 25 | /// The right-hand expression. 26 | /// 27 | public virtual SqlExpression Right { get; } 28 | 29 | /// 30 | /// The operator. 31 | /// 32 | public virtual string Operator { get; } 33 | 34 | /// 35 | /// Constructs a . 36 | /// 37 | /// The left-hand expression. 38 | /// The right-hand expression. 39 | /// The operator symbol acting on the expression. 40 | /// The result type. 41 | /// The type mapping for the expression. 42 | /// 43 | public PostgresUnknownBinaryExpression( 44 | SqlExpression left, 45 | SqlExpression right, 46 | string binaryOperator, 47 | Type type, 48 | RelationalTypeMapping? typeMapping = null) 49 | : base(type, typeMapping) 50 | { 51 | Left = Check.NotNull(left, nameof(left)); 52 | Right = Check.NotNull(right, nameof(right)); 53 | Operator = Check.NotEmpty(binaryOperator, nameof(binaryOperator)); 54 | } 55 | 56 | /// 57 | protected override Expression VisitChildren(ExpressionVisitor visitor) 58 | => Update((SqlExpression)visitor.Visit(Left), (SqlExpression)visitor.Visit(Right)); 59 | 60 | public virtual PostgresUnknownBinaryExpression Update(SqlExpression left, SqlExpression right) 61 | => left == Left && right == Right 62 | ? this 63 | : new PostgresUnknownBinaryExpression(left, right, Operator, Type, TypeMapping); 64 | 65 | public virtual bool Equals(PostgresUnknownBinaryExpression? other) 66 | => ReferenceEquals(this, other) || 67 | other is object && 68 | Left.Equals(other.Left) && 69 | Right.Equals(other.Right) && 70 | Operator == other.Operator; 71 | 72 | public override bool Equals(object? obj) => obj is PostgresUnknownBinaryExpression e && Equals(e); 73 | 74 | public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Left, Right, Operator); 75 | 76 | protected override void Print(ExpressionPrinter expressionPrinter) 77 | { 78 | expressionPrinter.Visit(Left); 79 | expressionPrinter.Append(Operator); 80 | expressionPrinter.Visit(Right); 81 | } 82 | 83 | /// 84 | public override string ToString() => $"{Left} {Operator} {Right}"; 85 | } -------------------------------------------------------------------------------- /EFCore.KingbaseES.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4A5A60DD-41B6-40BF-B677-227A921ECCC8}" 7 | ProjectSection(SolutionItems) = preProject 8 | Directory.Build.props = Directory.Build.props 9 | Directory.Packages.props = Directory.Packages.props 10 | Npgsql.snk = Npgsql.snk 11 | NuGet.config = NuGet.config 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{ED612DB1-AB32-4603-95E7-891BACA71C39}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.KingbaseES", "src\EFCore.KingbaseES\EFCore.KingbaseES.csproj", "{FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KingbaseES.BasicTest", "test\KingbaseES.BasicTest\KingbaseES.BasicTest.csproj", "{25141572-8051-4D49-98ED-7AE12F7EF9FC}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Debug|x64 = Debug|x64 27 | Debug|x86 = Debug|x86 28 | Release|Any CPU = Release|Any CPU 29 | Release|x64 = Release|x64 30 | Release|x86 = Release|x86 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Debug|x64.ActiveCfg = Debug|Any CPU 36 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Release|x64.ActiveCfg = Release|Any CPU 40 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A}.Release|x86.ActiveCfg = Release|Any CPU 41 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|x64.ActiveCfg = Debug|Any CPU 44 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|x64.Build.0 = Debug|Any CPU 45 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|x86.ActiveCfg = Debug|Any CPU 46 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Debug|x86.Build.0 = Debug|Any CPU 47 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|x64.ActiveCfg = Release|Any CPU 50 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|x64.Build.0 = Release|Any CPU 51 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|x86.ActiveCfg = Release|Any CPU 52 | {25141572-8051-4D49-98ED-7AE12F7EF9FC}.Release|x86.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(NestedProjects) = preSolution 58 | {FADDA2D1-03B4-4DEF-8D24-DD1CA4E81F4A} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050} 59 | {25141572-8051-4D49-98ED-7AE12F7EF9FC} = {ED612DB1-AB32-4603-95E7-891BACA71C39} 60 | EndGlobalSection 61 | GlobalSection(ExtensibilityGlobals) = postSolution 62 | SolutionGuid = {F4EAAE6D-758C-4184-9D8C-7113384B61A8} 63 | EndGlobalSection 64 | GlobalSection(MonoDevelopProperties) = preSolution 65 | StartupItem = Npgsql.csproj 66 | EndGlobalSection 67 | EndGlobal 68 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpFuzzyStringMatchMethodTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.Diagnostics; 7 | using Microsoft.EntityFrameworkCore.Query; 8 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 9 | 10 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 11 | 12 | public class KdbndpFuzzyStringMatchMethodTranslator : IMethodCallTranslator 13 | { 14 | private static readonly Dictionary Functions = new() 15 | { 16 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchSoundex), new[] { typeof(DbFunctions), typeof(string) })] = "soundex", 17 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDifference), new[] { typeof(DbFunctions), typeof(string), typeof(string) })] = "difference", 18 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshtein), new[] { typeof(DbFunctions), typeof(string), typeof(string) })] = "levenshtein", 19 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshtein), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int), typeof(int), typeof(int) })] = "levenshtein", 20 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshteinLessEqual), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int) })] = "levenshtein_less_equal", 21 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshteinLessEqual), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) })] = "levenshtein_less_equal", 22 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchMetaphone), new[] { typeof(DbFunctions), typeof(string), typeof(int) })] = "metaphone", 23 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDoubleMetaphone), new[] { typeof(DbFunctions), typeof(string) })] = "dmetaphone", 24 | [GetRuntimeMethod(nameof(KdbndpFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDoubleMetaphoneAlt), new[] { typeof(DbFunctions), typeof(string) })] = "dmetaphone_alt" 25 | }; 26 | 27 | private static MethodInfo GetRuntimeMethod(string name, params Type[] parameters) 28 | => typeof(KdbndpFuzzyStringMatchDbFunctionsExtensions).GetRuntimeMethod(name, parameters)!; 29 | 30 | private readonly KdbndpSqlExpressionFactory _sqlExpressionFactory; 31 | 32 | private static readonly bool[][] TrueArrays = 33 | { 34 | Array.Empty(), 35 | new[] { true }, 36 | new[] { true, true }, 37 | new[] { true, true, true }, 38 | new[] { true, true, true, true }, 39 | new[] { true, true, true, true, true }, 40 | new[] { true, true, true, true, true, true } 41 | }; 42 | 43 | public KdbndpFuzzyStringMatchMethodTranslator(KdbndpSqlExpressionFactory sqlExpressionFactory) 44 | => _sqlExpressionFactory = sqlExpressionFactory; 45 | 46 | /// 47 | public virtual SqlExpression? Translate( 48 | SqlExpression? instance, 49 | MethodInfo method, 50 | IReadOnlyList arguments, 51 | IDiagnosticsLogger logger) 52 | => Functions.TryGetValue(method, out var function) 53 | ? _sqlExpressionFactory.Function( 54 | function, 55 | arguments.Skip(1), 56 | nullable: true, 57 | argumentsPropagateNullability: TrueArrays[arguments.Count - 1], 58 | method.ReturnType) 59 | : null; 60 | } 61 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Extensions/MetadataExtensions/KdbndpEntityTypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata; 7 | using Kdbndp.EntityFrameworkCore.KingbaseES.Metadata.Internal; 8 | 9 | // ReSharper disable once CheckNamespace 10 | namespace Microsoft.EntityFrameworkCore; 11 | 12 | /// 13 | /// Extension methods for for Kdbndp-specific metadata. 14 | /// 15 | public static class KdbndpEntityTypeExtensions 16 | { 17 | #region Storage parameters 18 | 19 | public static Dictionary GetStorageParameters(this IReadOnlyEntityType entityType) 20 | => entityType.GetAnnotations() 21 | .Where(a => a.Name.StartsWith(KdbndpAnnotationNames.StorageParameterPrefix, StringComparison.Ordinal)) 22 | .ToDictionary( 23 | a => a.Name.Substring(KdbndpAnnotationNames.StorageParameterPrefix.Length), 24 | a => a.Value 25 | ); 26 | 27 | public static string? GetStorageParameter(this IEntityType entityType, string parameterName) 28 | { 29 | Check.NotEmpty(parameterName, nameof(parameterName)); 30 | 31 | return (string?)entityType[KdbndpAnnotationNames.StorageParameterPrefix + parameterName]; 32 | } 33 | 34 | public static void SetStorageParameter( 35 | this IMutableEntityType entityType, 36 | string parameterName, 37 | object? parameterValue) 38 | { 39 | Check.NotEmpty(parameterName, nameof(parameterName)); 40 | 41 | entityType.SetOrRemoveAnnotation(KdbndpAnnotationNames.StorageParameterPrefix + parameterName, parameterValue); 42 | } 43 | 44 | public static object SetStorageParameter( 45 | this IConventionEntityType entityType, 46 | string parameterName, 47 | object? parameterValue, 48 | bool fromDataAnnotation = false) 49 | { 50 | Check.NotEmpty(parameterName, nameof(parameterName)); 51 | 52 | entityType.SetOrRemoveAnnotation(KdbndpAnnotationNames.StorageParameterPrefix + parameterName, parameterValue, fromDataAnnotation); 53 | 54 | return parameterName; 55 | } 56 | 57 | public static ConfigurationSource? GetStorageParameterConfigurationSource( 58 | this IConventionEntityType index, 59 | string parameterName) 60 | { 61 | Check.NotEmpty(parameterName, nameof(parameterName)); 62 | 63 | return index.FindAnnotation(KdbndpAnnotationNames.StorageParameterPrefix + parameterName)?.GetConfigurationSource(); 64 | } 65 | 66 | #endregion Storage parameters 67 | 68 | #region Unlogged 69 | 70 | public static bool GetIsUnlogged(this IReadOnlyEntityType entityType) 71 | => entityType[KdbndpAnnotationNames.UnloggedTable] as bool? ?? false; 72 | 73 | public static void SetIsUnlogged(this IMutableEntityType entityType, bool unlogged) 74 | => entityType.SetOrRemoveAnnotation(KdbndpAnnotationNames.UnloggedTable, unlogged); 75 | 76 | public static bool SetIsUnlogged( 77 | this IConventionEntityType entityType, 78 | bool unlogged, 79 | bool fromDataAnnotation = false) 80 | { 81 | entityType.SetOrRemoveAnnotation(KdbndpAnnotationNames.UnloggedTable, unlogged, fromDataAnnotation); 82 | 83 | return unlogged; 84 | } 85 | 86 | public static ConfigurationSource? GetIsUnloggedConfigurationSource(this IConventionEntityType index) 87 | => index.FindAnnotation(KdbndpAnnotationNames.UnloggedTable)?.GetConfigurationSource(); 88 | 89 | #endregion Unlogged 90 | 91 | #region CockroachDb interleave in parent 92 | 93 | public static CockroachDbInterleaveInParent GetCockroachDbInterleaveInParent(this IReadOnlyEntityType entityType) 94 | => new(entityType); 95 | 96 | #endregion CockroachDb interleave in parent 97 | } 98 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTimestampTzTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | public class KdbndpTimestampTzTypeMapping : KdbndpTypeMapping 9 | { 10 | public KdbndpTimestampTzTypeMapping(Type clrType) 11 | : base("timestamp with time zone", clrType, KdbndpDbType.TimestampTz) {} 12 | 13 | protected KdbndpTimestampTzTypeMapping(RelationalTypeMappingParameters parameters) 14 | : base(parameters, KdbndpDbType.TimestampTz) {} 15 | 16 | protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) 17 | => new KdbndpTimestampTzTypeMapping(parameters); 18 | 19 | protected override string ProcessStoreType(RelationalTypeMappingParameters parameters, string storeType, string _) 20 | => parameters.Precision is null ? storeType : $"timestamp({parameters.Precision}) with time zone"; 21 | 22 | protected override string GenerateNonNullSqlLiteral(object value) 23 | => $"TIMESTAMPTZ '{GenerateLiteralCore(value)}'"; 24 | 25 | protected override string GenerateEmbeddedNonNullSqlLiteral(object value) 26 | => @$"""{GenerateLiteralCore(value)}"""; 27 | 28 | private string GenerateLiteralCore(object value) 29 | { 30 | switch (value) 31 | { 32 | case DateTime dateTime: 33 | if (!KdbndpTypeMappingSource.DisableDateTimeInfinityConversions) 34 | { 35 | if (dateTime == DateTime.MinValue) 36 | { 37 | return "-infinity"; 38 | } 39 | 40 | if (dateTime == DateTime.MaxValue) 41 | { 42 | return "infinity"; 43 | } 44 | } 45 | 46 | return dateTime.Kind switch 47 | { 48 | DateTimeKind.Utc => dateTime.ToString("yyyy-MM-dd HH:mm:ss.FFFFFF", CultureInfo.InvariantCulture) + 'Z', 49 | 50 | DateTimeKind.Unspecified => KdbndpTypeMappingSource.LegacyTimestampBehavior || dateTime == default 51 | ? dateTime.ToString("yyyy-MM-dd HH:mm:ss.FFFFFF", CultureInfo.InvariantCulture) + 'Z' 52 | : throw new InvalidCastException( 53 | $"'timestamp with time zone' literal cannot be generated for {dateTime.Kind} DateTime: a UTC DateTime is required"), 54 | 55 | DateTimeKind.Local => KdbndpTypeMappingSource.LegacyTimestampBehavior 56 | ? dateTime.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFzzz", CultureInfo.InvariantCulture) 57 | : throw new InvalidCastException( 58 | $"'timestamp with time zone' literal cannot be generated for {dateTime.Kind} DateTime: a UTC DateTime is required"), 59 | 60 | _ => throw new ArgumentOutOfRangeException() 61 | }; 62 | 63 | case DateTimeOffset dateTimeOffset: 64 | if (!KdbndpTypeMappingSource.DisableDateTimeInfinityConversions) 65 | { 66 | if (dateTimeOffset == DateTimeOffset.MinValue) 67 | { 68 | return "-infinity"; 69 | } 70 | 71 | if (dateTimeOffset == DateTimeOffset.MaxValue) 72 | { 73 | return "infinity"; 74 | } 75 | } 76 | 77 | return dateTimeOffset.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFzzz", CultureInfo.InvariantCulture); 78 | 79 | default: 80 | throw new InvalidCastException( 81 | $"Attempted to generate timestamptz literal for type {value.GetType()}, only DateTime and DateTimeOffset are supported"); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Storage/Internal/Mapping/KdbndpTypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using KdbndpTypes; 5 | 6 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 7 | 8 | /// 9 | /// The base class for mapping Kdbndp-specific types. It configures parameters with the 10 | /// provider-specific type enum. 11 | /// 12 | public abstract class KdbndpTypeMapping : RelationalTypeMapping, IKdbndpTypeMapping 13 | { 14 | /// 15 | public virtual KdbndpDbType KdbndpDbType { get; } 16 | 17 | // ReSharper disable once PublicConstructorInAbstractClass 18 | /// 19 | /// Constructs an instance of the class. 20 | /// 21 | /// The database type to map. 22 | /// The CLR type to map. 23 | /// The database type used by Kdbndp. 24 | public KdbndpTypeMapping(string storeType, Type clrType, KdbndpDbType KdbndpDbType) 25 | : base(storeType, clrType) 26 | => this.KdbndpDbType = KdbndpDbType; 27 | 28 | /// 29 | /// Constructs an instance of the class. 30 | /// 31 | /// The parameters for this mapping. 32 | /// The database type of the range subtype. 33 | protected KdbndpTypeMapping(RelationalTypeMappingParameters parameters, KdbndpDbType KdbndpDbType) 34 | : base(parameters) 35 | => this.KdbndpDbType = KdbndpDbType; 36 | 37 | protected override void ConfigureParameter(DbParameter parameter) 38 | { 39 | if (parameter is not KdbndpParameter KdbndpParameter) 40 | { 41 | throw new InvalidOperationException($"Kdbndp-specific type mapping {GetType().Name} being used with non-Kdbndp parameter type {parameter.GetType().Name}"); 42 | } 43 | 44 | base.ConfigureParameter(parameter); 45 | KdbndpParameter.KdbndpDbType = KdbndpDbType; 46 | } 47 | 48 | /// 49 | /// Generates the SQL representation of a literal value meant to be embedded in another literal value, e.g. in a range. 50 | /// 51 | /// The literal value. 52 | /// 53 | /// The generated string. 54 | /// 55 | public virtual string GenerateEmbeddedSqlLiteral(object? value) 56 | { 57 | value = ConvertUnderlyingEnumValueToEnum(value); 58 | 59 | if (Converter != null) 60 | { 61 | value = Converter.ConvertToProvider(value); 62 | } 63 | 64 | return GenerateEmbeddedProviderValueSqlLiteral(value); 65 | } 66 | 67 | /// 68 | /// Generates the SQL representation of a literal value without conversion, meant to be embedded in another literal value, 69 | /// e.g. in a range. 70 | /// 71 | /// The literal value. 72 | /// 73 | /// The generated string. 74 | /// 75 | public virtual string GenerateEmbeddedProviderValueSqlLiteral(object? value) 76 | => value == null 77 | ? "NULL" 78 | : GenerateEmbeddedNonNullSqlLiteral(value); 79 | 80 | /// 81 | /// Generates the SQL representation of a non-null literal value, meant to be embedded in another literal value, e.g. in a range. 82 | /// 83 | /// The literal value. 84 | /// 85 | /// The generated string. 86 | /// 87 | protected virtual string GenerateEmbeddedNonNullSqlLiteral(object value) 88 | => GenerateNonNullSqlLiteral(value); 89 | 90 | // Copied from RelationalTypeMapping 91 | private object? ConvertUnderlyingEnumValueToEnum(object? value) 92 | => value?.GetType().IsInteger() == true && ClrType.UnwrapNullableType().IsEnum 93 | ? Enum.ToObject(ClrType.UnwrapNullableType(), value) 94 | : value; 95 | } 96 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/Query/ExpressionTranslators/Internal/KdbndpByteArrayMethodTranslator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Diagnostics; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using Microsoft.EntityFrameworkCore.Query.SqlExpressions; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | using Kdbndp.EntityFrameworkCore.KingbaseES.Internal; 9 | using Kdbndp.EntityFrameworkCore.KingbaseES.Query.Expressions.Internal; 10 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal.Mapping; 11 | using static Kdbndp.EntityFrameworkCore.KingbaseES.Utilities.Statics; 12 | 13 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.Query.ExpressionTranslators.Internal; 14 | 15 | public class KdbndpByteArrayMethodTranslator : IMethodCallTranslator 16 | { 17 | private readonly ISqlExpressionFactory _sqlExpressionFactory; 18 | 19 | public KdbndpByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) 20 | => _sqlExpressionFactory = sqlExpressionFactory; 21 | 22 | public virtual SqlExpression? Translate( 23 | SqlExpression? instance, 24 | MethodInfo method, 25 | IReadOnlyList arguments, 26 | IDiagnosticsLogger logger) 27 | { 28 | Check.NotNull(method, nameof(method)); 29 | Check.NotNull(arguments, nameof(arguments)); 30 | 31 | if (method.IsGenericMethod && arguments[0].TypeMapping is KdbndpByteArrayTypeMapping typeMapping) 32 | { 33 | // Note: we only translate if the array argument is a column mapped to bytea. There are various other 34 | // cases (e.g. Where(b => new byte[] { 1, 2, 3 }.Contains(b.SomeByte))) where we prefer to translate via 35 | // regular KingbaseES array logic. 36 | if (method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains)) 37 | { 38 | var source = arguments[0]; 39 | 40 | // We have a byte value, but we need a bytea for KingbaseES POSITION. 41 | var value = arguments[1] is SqlConstantExpression constantValue 42 | ? (SqlExpression)_sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, typeMapping) 43 | // Create bytea from non-constant byte: SELECT set_byte('\x00', 0, 8::smallint); 44 | : _sqlExpressionFactory.Function( 45 | "set_byte", 46 | new[] 47 | { 48 | _sqlExpressionFactory.Constant(new[] { (byte)0 }, typeMapping), 49 | _sqlExpressionFactory.Constant(0), 50 | arguments[1] 51 | }, 52 | nullable: true, 53 | argumentsPropagateNullability: TrueArrays[3], 54 | typeof(byte[]), 55 | typeMapping); 56 | 57 | return _sqlExpressionFactory.GreaterThan( 58 | PostgresFunctionExpression.CreateWithArgumentSeparators( 59 | "position", 60 | new[] { value, source }, 61 | new[] { "IN" }, // POSITION(x IN y) 62 | nullable: true, 63 | argumentsPropagateNullability: TrueArrays[2], 64 | builtIn: true, 65 | typeof(int), 66 | null), 67 | _sqlExpressionFactory.Constant(0)); 68 | } 69 | 70 | if (method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)) 71 | { 72 | return _sqlExpressionFactory.Convert( 73 | _sqlExpressionFactory.Function( 74 | "get_byte", 75 | new[] { arguments[0], _sqlExpressionFactory.Constant(0) }, 76 | nullable: true, 77 | argumentsPropagateNullability: TrueArrays[2], 78 | typeof(byte)), 79 | method.ReturnType); 80 | } 81 | } 82 | 83 | return null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EFCore.KingbaseES/ValueGeneration/Internal/KdbndpSequenceValueGeneratorFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Storage; 6 | using Microsoft.EntityFrameworkCore.Update; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | using Microsoft.EntityFrameworkCore.ValueGeneration; 9 | using Kdbndp.EntityFrameworkCore.KingbaseES.Storage.Internal; 10 | 11 | namespace Kdbndp.EntityFrameworkCore.KingbaseES.ValueGeneration.Internal; 12 | 13 | /// 14 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 15 | /// directly from your code. This API may change or be removed in future releases. 16 | /// 17 | public class KdbndpSequenceValueGeneratorFactory : IKdbndpSequenceValueGeneratorFactory 18 | { 19 | private readonly IUpdateSqlGenerator _sqlGenerator; 20 | 21 | /// 22 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 23 | /// directly from your code. This API may change or be removed in future releases. 24 | /// 25 | public KdbndpSequenceValueGeneratorFactory( 26 | IUpdateSqlGenerator sqlGenerator) 27 | { 28 | Check.NotNull(sqlGenerator, nameof(sqlGenerator)); 29 | 30 | _sqlGenerator = sqlGenerator; 31 | } 32 | 33 | /// 34 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 35 | /// directly from your code. This API may change or be removed in future releases. 36 | /// 37 | public virtual ValueGenerator Create( 38 | IProperty property, 39 | KdbndpSequenceValueGeneratorState generatorState, 40 | IKdbndpRelationalConnection connection, 41 | IRawSqlCommandBuilder rawSqlCommandBuilder, 42 | IRelationalCommandDiagnosticsLogger commandLogger) 43 | { 44 | var type = property.ClrType.UnwrapNullableType().UnwrapEnumType(); 45 | 46 | if (type == typeof(long)) 47 | { 48 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 49 | } 50 | 51 | if (type == typeof(int)) 52 | { 53 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 54 | } 55 | 56 | if (type == typeof(short)) 57 | { 58 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 59 | } 60 | 61 | if (type == typeof(byte)) 62 | { 63 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 64 | } 65 | 66 | if (type == typeof(char)) 67 | { 68 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 69 | } 70 | 71 | if (type == typeof(ulong)) 72 | { 73 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 74 | } 75 | 76 | if (type == typeof(uint)) 77 | { 78 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 79 | } 80 | 81 | if (type == typeof(ushort)) 82 | { 83 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 84 | } 85 | 86 | if (type == typeof(sbyte)) 87 | { 88 | return new KdbndpSequenceHiLoValueGenerator(rawSqlCommandBuilder, _sqlGenerator, generatorState, connection, commandLogger); 89 | } 90 | 91 | throw new ArgumentException(CoreStrings.InvalidValueGeneratorFactoryProperty( 92 | nameof(KdbndpSequenceValueGeneratorFactory), property.Name, property.DeclaringEntityType.DisplayName())); 93 | } 94 | } 95 | --------------------------------------------------------------------------------