diff --git a/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlEntityTypeBuilderExtensions.cs b/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlEntityTypeBuilderExtensions.cs index d4d4dbfa4..4927937cc 100644 --- a/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlEntityTypeBuilderExtensions.cs +++ b/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlEntityTypeBuilderExtensions.cs @@ -328,6 +328,134 @@ public static EntityTypeBuilder UseCockroachDbInterleaveInParent PostgresXlDistributeBy( + this EntityTypeBuilder entityTypeBuilder, + PostgresXlDistributeByStrategy distributeByStrategy) + where TEntity : class + => (EntityTypeBuilder)PostgresXlDistributeBy((EntityTypeBuilder)entityTypeBuilder, distributeByStrategy); + + public static EntityTypeBuilder PostgresXlDistributeBy( + this EntityTypeBuilder entityTypeBuilder, + string propertyName, + PostgresXlDistributeByColumnFunction distributeByColumnFunction = PostgresXlDistributeByColumnFunction.None) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotEmpty(propertyName, nameof(propertyName)); + + var distribute = entityTypeBuilder.Metadata.GetPostgresXlDistributeBy(); + + distribute.DistributionStrategy = PostgresXlDistributeByStrategy.None; + distribute.DistributeByColumnFunction = distributeByColumnFunction; + distribute.DistributeByPropertyName = propertyName; + + return entityTypeBuilder; + } + + public static EntityTypeBuilder PostgresXlDistributeBy( + this EntityTypeBuilder entityTypeBuilder, + Expression> propertyExpression, + PostgresXlDistributeByColumnFunction distributeByColumnFunction = PostgresXlDistributeByColumnFunction.None) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotNull(propertyExpression, nameof(propertyExpression)); + + entityTypeBuilder.PostgresXlDistributeBy(propertyExpression.GetPropertyAccess().Name, distributeByColumnFunction); + + return entityTypeBuilder; + } + + public static EntityTypeBuilder PostgresXlDistributeBy( + this EntityTypeBuilder entityTypeBuilder, + string propertyName, + PostgresXlDistributeByColumnFunction distributeByColumnFunction = PostgresXlDistributeByColumnFunction.None) + where TEntity : class + => (EntityTypeBuilder)PostgresXlDistributeBy( + (EntityTypeBuilder)entityTypeBuilder, propertyName, distributeByColumnFunction); + + public static EntityTypeBuilder PostgresXlDistributionStyle( + this EntityTypeBuilder entityTypeBuilder, + PostgresXlDistributionStyle distributionStyle) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + + var distribute = entityTypeBuilder.Metadata.GetPostgresXlDistributeBy(); + + switch (distributionStyle) + { + case EntityFrameworkCore.PostgresXlDistributionStyle.Even: + case EntityFrameworkCore.PostgresXlDistributionStyle.All: + distribute.DistributionStyle = distributionStyle; + return entityTypeBuilder; + case EntityFrameworkCore.PostgresXlDistributionStyle.Key: + throw new ArgumentException( + $"Distribution style {EntityFrameworkCore.PostgresXlDistributionStyle.Key} was provided with no key. To use DISTSTYLE KEY, use {nameof(PostgresXlDistributionStyleKey)} instead."); + default: + throw new ArgumentOutOfRangeException(nameof(distributionStyle), distributionStyle, $@"Invalid {nameof(EntityFrameworkCore)} provided."); + } + } + + public static EntityTypeBuilder PostgresXlDistributionStyle( + this EntityTypeBuilder entityTypeBuilder, + PostgresXlDistributionStyle distributionStyle + ) + where TEntity : class + => (EntityTypeBuilder)PostgresXlDistributionStyle((EntityTypeBuilder)entityTypeBuilder, distributionStyle); + + + public static EntityTypeBuilder PostgresXlDistributionStyleKey( + this EntityTypeBuilder entityTypeBuilder, + string distributionKey) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotEmpty(distributionKey, nameof(distributionKey)); + + var distribute = entityTypeBuilder.Metadata.GetPostgresXlDistributeBy(); + + distribute.DistributionStyle = EntityFrameworkCore.PostgresXlDistributionStyle.Key; + distribute.DistributeByPropertyName = distributionKey; + return entityTypeBuilder; + } + + public static EntityTypeBuilder PostgresXlDistributionStyleKey( + this EntityTypeBuilder entityTypeBuilder, + Expression> distributionKeyExpression) + where TEntity : class + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotNull(distributionKeyExpression, nameof(distributionKeyExpression)); + + entityTypeBuilder.PostgresXlDistributionStyleKey(distributionKeyExpression.GetPropertyAccess().Name); + + return entityTypeBuilder; + } + + public static EntityTypeBuilder PostgresXlDistributionStyleKey( + this EntityTypeBuilder entityTypeBuilder, + string distributionKey) + where TEntity : class + => (EntityTypeBuilder)PostgresXlDistributionStyleKey((EntityTypeBuilder)entityTypeBuilder, distributionKey); + + #endregion Postgres-xl Distribute By + #region Obsolete /// diff --git a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlEntityTypeExtensions.cs b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlEntityTypeExtensions.cs index e3f68ad13..d6ca48ceb 100644 --- a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlEntityTypeExtensions.cs +++ b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlEntityTypeExtensions.cs @@ -94,5 +94,12 @@ public static CockroachDbInterleaveInParent GetCockroachDbInterleaveInParent(thi => new(entityType); #endregion CockroachDb interleave in parent + + #region Postgres-xl Distribute By + + public static PostgresXlDistributeBy GetPostgresXlDistributeBy(this IReadOnlyEntityType entityType) + => new(entityType); + + #endregion Postgres-xl Distribute By } } diff --git a/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeBy.cs b/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeBy.cs new file mode 100644 index 000000000..ddeb5ee51 --- /dev/null +++ b/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeBy.cs @@ -0,0 +1,157 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable +using System; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal +{ + public class PostgresXlDistributeBy + { + private const string AnnotationName = PostgresXlDistributeByAnnotationNames.DistributeBy; + + private readonly IReadOnlyAnnotatable _annotatable; + + public virtual Annotatable Annotatable + => (Annotatable)_annotatable; + + public PostgresXlDistributeBy(IReadOnlyAnnotatable annotatable) + => _annotatable = annotatable; + + public virtual PostgresXlDistributeByStrategy DistributionStrategy + { + get => GetData().DistributionStrategy; + set + { + var (_, distributeByColumnFunction, distributionStyle, columnName) = GetData(); + SetData(value, distributeByColumnFunction, distributionStyle, columnName); + } + } + + public virtual PostgresXlDistributeByColumnFunction DistributeByColumnFunction + { + get => GetData().DistributeByColumnFunction; + set + { + var (distributionStrategy, _, distributionStyle, columnName) = GetData(); + SetData(distributionStrategy, value, distributionStyle, columnName); + } + } + + public virtual PostgresXlDistributionStyle DistributionStyle + { + get => GetData().DistributionStyle; + set + { + var (distributionStrategy, distributeByColumnFunction, _, columnName) = GetData(); + SetData(distributionStrategy, distributeByColumnFunction, value, columnName); + } + } + + public virtual string? DistributeByPropertyName + { + get => GetData().ColumnName; + set + { + var (distributionStrategy, distributeByColumnFunction, distributionStyle, _) = GetData(); + SetData(distributionStrategy, distributeByColumnFunction, distributionStyle, value); + } + } + + public void Deconstruct(out PostgresXlDistributeByStrategy distributionStrategy, + out PostgresXlDistributeByColumnFunction distributeByColumnFunction, + out PostgresXlDistributionStyle distributionStyle, + out string? distributeByColumnName) + { + distributionStrategy = DistributionStrategy; + distributeByColumnFunction = DistributeByColumnFunction; + distributionStyle = DistributionStyle; + distributeByColumnName = DistributeByPropertyName; + } + + private (PostgresXlDistributeByStrategy DistributionStrategy, PostgresXlDistributeByColumnFunction DistributeByColumnFunction, PostgresXlDistributionStyle DistributionStyle, string? ColumnName) GetData() + { + var str = Annotatable[AnnotationName] as string; + return str == null + ? (0, 0, 0, null) + : Deserialize(str); + } + + private void SetData( + PostgresXlDistributeByStrategy distributionStrategy, + PostgresXlDistributeByColumnFunction distributeByColumnFunction, + PostgresXlDistributionStyle postgresXlDistributionStyle, + string? distributeByColumnName) + { + Annotatable[AnnotationName] = Serialize(distributionStrategy, distributeByColumnFunction, postgresXlDistributionStyle, distributeByColumnName); + } + + private string Serialize( + PostgresXlDistributeByStrategy distributionStrategy, + PostgresXlDistributeByColumnFunction distributeByColumnFunction, + PostgresXlDistributionStyle postgresXlDistributionStyle, + string? distributeByColumnName) + { + var stringBuilder = new StringBuilder(); + + EscapeAndQuote(stringBuilder, distributionStrategy); + stringBuilder.Append(","); + EscapeAndQuote(stringBuilder, distributeByColumnFunction); + stringBuilder.Append(","); + EscapeAndQuote(stringBuilder, postgresXlDistributionStyle); + stringBuilder.Append(","); + EscapeAndQuote(stringBuilder, distributeByColumnName); + + return stringBuilder.ToString(); + } + + private (PostgresXlDistributeByStrategy DistributionStrategy, + PostgresXlDistributeByColumnFunction DistributeByColumnFunction, + PostgresXlDistributionStyle DistributionStyle, + string? ColumnName) + Deserialize(string str) + { + var position = 0; + var distributionStrategy = Enum.Parse(ExtractValue(str, ref position)!); + var distributeByColumnFunction = Enum.Parse(ExtractValue(str, ref position)!); + var distributionStyle = Enum.Parse(ExtractValue(str, ref position)!); + var columnName = ExtractValue(str, ref position); + + return (distributionStrategy, distributeByColumnFunction, distributionStyle, columnName); + } + + private static void EscapeAndQuote(StringBuilder builder, object? value) + { + builder.Append("'"); + + if (value != null) + { + builder.Append(value.ToString()?.Replace("'", "''")); + } + + builder.Append("'"); + } + + private static string? ExtractValue(string value, ref int position) + { + position = value.IndexOf('\'', position) + 1; + + var end = value.IndexOf('\'', position); + + while (end + 1 < value.Length + && value[end + 1] == '\'') + { + end = value.IndexOf('\'', end + 2); + } + + var extracted = value.Substring(position, end - position).Replace("''", "'"); + position = end + 1; + + return extracted.Length == 0 ? null : extracted; + } + } +} diff --git a/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeByAnnotationNames.cs b/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeByAnnotationNames.cs new file mode 100644 index 000000000..7c2b8bb24 --- /dev/null +++ b/src/EFCore.PG/Metadata/Internal/PostgresXlDistributeByAnnotationNames.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal +{ + internal static class PostgresXlDistributeByAnnotationNames + { + public const string Prefix = NpgsqlAnnotationNames.Prefix + "PostgresXL:"; + + public const string DistributeBy = Prefix + "DistributeBy"; + } +} diff --git a/src/EFCore.PG/Metadata/PostgresXlDistributeByColumnFunction.cs b/src/EFCore.PG/Metadata/PostgresXlDistributeByColumnFunction.cs new file mode 100644 index 000000000..8292f0f64 --- /dev/null +++ b/src/EFCore.PG/Metadata/PostgresXlDistributeByColumnFunction.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore +{ + public enum PostgresXlDistributeByColumnFunction + { + None = 0, + Hash = 1, + Modulo = 2, + } +} diff --git a/src/EFCore.PG/Metadata/PostgresXlDistributeByStrategy.cs b/src/EFCore.PG/Metadata/PostgresXlDistributeByStrategy.cs new file mode 100644 index 000000000..550a926f1 --- /dev/null +++ b/src/EFCore.PG/Metadata/PostgresXlDistributeByStrategy.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore +{ + public enum PostgresXlDistributeByStrategy + { + None = 0, + Replication = 1, + RoundRobin = 2, + Randomly = 3, + } +} diff --git a/src/EFCore.PG/Metadata/PostgresXlDistributionStyle.cs b/src/EFCore.PG/Metadata/PostgresXlDistributionStyle.cs new file mode 100644 index 000000000..9e2460132 --- /dev/null +++ b/src/EFCore.PG/Metadata/PostgresXlDistributionStyle.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore +{ + public enum PostgresXlDistributionStyle + { + None = 0, + Even = 1, + Key = 2, + All = 3, + } +} diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index 97022f057..0a6d74aad 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Globalization; @@ -193,6 +194,69 @@ protected override void Generate( .Append(")"); } + // Postgres-XL - Distribute by (https://www.postgres-xl.org/documentation/sql-createtable.html) + if (operation[PostgresXlDistributeByAnnotationNames.DistributeBy] is string) + { + var distributeBy = new PostgresXlDistributeBy(operation); + var (distributionStrategy, distributeByColumnFunction, distributionStyle, distributeByColumnName) = distributeBy; + + ValidateTableDistributionProperties(distributionStrategy, distributeByColumnFunction, distributionStyle, distributeByColumnName); + + if (distributionStyle == PostgresXlDistributionStyle.None) + { + if (distributionStrategy == PostgresXlDistributeByStrategy.Replication || distributionStrategy == PostgresXlDistributeByStrategy.RoundRobin) + { + builder.AppendLine() + .Append("DISTRIBUTE BY ") + .Append(distributionStrategy.ToString().ToUpperInvariant()); + } + else if (distributeByColumnFunction != PostgresXlDistributeByColumnFunction.None) + { + builder.AppendLine() + .Append("DISTRIBUTE BY ") + .Append(distributeByColumnFunction.ToString().ToUpperInvariant()) + .Append(" (") + .Append(DelimitIdentifier(distributeByColumnName!)) + .Append(")"); + } + + if ((distributionStrategy == PostgresXlDistributeByStrategy.Randomly + || (distributeByColumnFunction == PostgresXlDistributeByColumnFunction.None + && !string.IsNullOrWhiteSpace(distributeByColumnName)))) + { + builder.AppendLine() + .Append("DISTRIBUTED "); + + if (distributionStrategy == PostgresXlDistributeByStrategy.Randomly) + { + builder.Append(PostgresXlDistributeByStrategy.Randomly.ToString().ToUpperInvariant()); + } + else if (!string.IsNullOrWhiteSpace(distributeByColumnName) + && distributeByColumnFunction == PostgresXlDistributeByColumnFunction.None) + { + builder.Append("BY (") + .Append(DelimitIdentifier(distributeByColumnName)) + .Append(")"); + } + } + } + else if (distributionStyle == PostgresXlDistributionStyle.Key) + { + builder.AppendLine() + .Append("DISTSTYLE ") + .Append(distributionStyle.ToString().ToUpperInvariant()) + .Append(" DISTKEY (") + .Append(DelimitIdentifier(distributeByColumnName!)) + .Append(")"); + } + else if (distributionStyle == PostgresXlDistributionStyle.All || distributionStyle == PostgresXlDistributionStyle.Even) + { + builder.AppendLine() + .Append("DISTSTYLE ") + .Append(distributionStyle.ToString().ToUpperInvariant()); + } + } + // Comment on the table if (operation.Comment != null) { @@ -225,6 +289,119 @@ protected override void Generate( builder.AppendLine(";"); EndStatement(builder); } + + static void ValidateTableDistributionProperties( + PostgresXlDistributeByStrategy distributionStrategy, + PostgresXlDistributeByColumnFunction distributeByColumnFunction, + PostgresXlDistributionStyle distributionStyle, + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + string? distributeByColumnName) + { + switch (distributionStrategy) + { + case PostgresXlDistributeByStrategy.Replication: + case PostgresXlDistributeByStrategy.RoundRobin: + case PostgresXlDistributeByStrategy.Randomly: + // If column is defined, throw + if (!string.IsNullOrWhiteSpace(distributeByColumnName)) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnName)} cannot be provided when {nameof(PostgresXlDistributeByStrategy)} is specified."); + } + + // If any others are defined, throw + if (distributeByColumnFunction != PostgresXlDistributeByColumnFunction.None) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnFunction)} cannot be provided when {nameof(PostgresXlDistributeByStrategy)} is specified."); + } + + if (distributionStyle != PostgresXlDistributionStyle.None) + { + throw new NotSupportedException( + $"{nameof(distributionStyle)} cannot be provided when {nameof(PostgresXlDistributeByStrategy)} is specified."); + } + + break; + } + + switch (distributeByColumnFunction) + { + case PostgresXlDistributeByColumnFunction.Hash: + case PostgresXlDistributeByColumnFunction.Modulo: + // If column is not defined, throw + if (string.IsNullOrWhiteSpace(distributeByColumnName)) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnName)} must be provided when {nameof(PostgresXlDistributeByColumnFunction)} is specified."); + } + + // If any others are defined, throw + if (distributionStrategy != PostgresXlDistributeByStrategy.None) + { + throw new NotSupportedException( + $"{nameof(distributionStrategy)} cannot be provided when {nameof(PostgresXlDistributeByColumnFunction)} is specified."); + } + + if (distributionStyle != PostgresXlDistributionStyle.None) + { + throw new NotSupportedException( + $"{nameof(distributionStyle)} cannot be provided when {nameof(PostgresXlDistributeByColumnFunction)} is specified."); + } + + break; + } + + switch (distributionStyle) + { + case PostgresXlDistributionStyle.Key: + // If column is not defined, throw + if (string.IsNullOrWhiteSpace(distributeByColumnName)) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnName)} must be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + // If any others are defined, throw + if (distributionStrategy != PostgresXlDistributeByStrategy.None) + { + throw new NotSupportedException( + $"{nameof(distributionStrategy)} cannot be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + if (distributeByColumnFunction != PostgresXlDistributeByColumnFunction.None) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnFunction)} cannot be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + break; + case PostgresXlDistributionStyle.Even: + case PostgresXlDistributionStyle.All: + // If column is defined, throw + if (!string.IsNullOrWhiteSpace(distributeByColumnName)) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnName)} must be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + // If any others are defined, throw + if (distributionStrategy != PostgresXlDistributeByStrategy.None) + { + throw new NotSupportedException( + $"{nameof(distributionStrategy)} cannot be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + if (distributeByColumnFunction != PostgresXlDistributeByColumnFunction.None) + { + throw new NotSupportedException( + $"{nameof(distributeByColumnFunction)} cannot be provided when {nameof(PostgresXlDistributionStyle)} is specified."); + } + + // If any others are defined, throw + break; + } + } } protected override void Generate(AlterTableOperation operation, IModel? model, MigrationCommandListBuilder builder) diff --git a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs index 7c011dcfb..d6b3225a8 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs @@ -516,6 +516,365 @@ PRIMARY KEY (""Id"") #endregion CockroachDB interleave-in-parent + #region Postgres-xl Distribute by + + [Fact] + public void CreateTableOperation_with_postgresxl_distribute_by_replication() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.Replication; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTE BY REPLICATION; +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_distribute_by_roundrobin() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.RoundRobin; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTE BY ROUNDROBIN; +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_distribute_by_hash_column() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.None; + distribution.DistributeByColumnFunction = PostgresXlDistributeByColumnFunction.Hash; + distribution.DistributeByPropertyName = "Id"; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTE BY HASH (""Id""); +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_distribute_by_modulo_column() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.None; + distribution.DistributeByColumnFunction = PostgresXlDistributeByColumnFunction.Modulo; + distribution.DistributeByPropertyName = "Id"; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTE BY MODULO (""Id""); +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_distributed_by_column() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.None; + distribution.DistributeByPropertyName = "Id"; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTED BY (""Id""); +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_distributed_randomly() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStrategy = PostgresXlDistributeByStrategy.Randomly; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTRIBUTED RANDOMLY; +"); + } + + [Fact] + public void CreateTableOperation_with_postgresxl_diststyle_even() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStyle = PostgresXlDistributionStyle.Even; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTSTYLE EVEN; +"); + } + [Fact] + public void CreateTableOperation_with_postgresxl_diststyle_key() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStyle = PostgresXlDistributionStyle.Key; + distribution.DistributeByPropertyName = "Id"; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTSTYLE KEY DISTKEY (""Id""); +"); + } + [Fact] + public void CreateTableOperation_with_postgresxl_diststyle_all() + { + var op = + new CreateTableOperation + { + Name = "People", + Schema = "dbo", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + Schema = "dbo", + ClrType = typeof(int), + IsNullable = false + }, + }, + PrimaryKey = new AddPrimaryKeyOperation + { + Columns = new[] { "Id" } + } + }; + + var distribution = new PostgresXlDistributeBy(op); + distribution.DistributionStyle = PostgresXlDistributionStyle.All; + + Generate(op); + + AssertSql( + @"CREATE TABLE dbo.""People"" ( + ""Id"" integer NOT NULL, + PRIMARY KEY (""Id"") +) +DISTSTYLE ALL; +"); + } + + #endregion Postgres-xl Distribute by + #pragma warning disable 618 [Fact] public virtual void AddColumnOperation_serial_old_annotation_throws()