Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ public override MethodCallCodeFragment GenerateFluentApi(IIndex index, IAnnotati
{
if (annotation.Name == NpgsqlAnnotationNames.IndexMethod)
return new MethodCallCodeFragment(nameof(NpgsqlIndexBuilderExtensions.ForNpgsqlHasMethod), annotation.Value);
if (annotation.Name == NpgsqlAnnotationNames.IndexOperators)
return new MethodCallCodeFragment(nameof(NpgsqlIndexBuilderExtensions.ForNpgsqlHasOperators), annotation.Value);

return null;
}
Expand Down
19 changes: 19 additions & 0 deletions src/EFCore.PG/Extensions/NpgsqlIndexBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,24 @@ public static IndexBuilder ForNpgsqlHasMethod([NotNull] this IndexBuilder indexB

return indexBuilder;
}

/// <summary>
/// The PostgreSQL index operators to be used.
/// </summary>
/// <remarks>
/// https://www.postgresql.org/docs/current/static/indexes-opclass.html
/// </remarks>
/// <param name="indexBuilder"> The builder for the index being configured. </param>
/// <param name="operators"> The operators to use for each column. </param>
/// <returns> A builder to further configure the index. </returns>
public static IndexBuilder ForNpgsqlHasOperators([NotNull] this IndexBuilder indexBuilder, [CanBeNull] params string[] operators)
{
Check.NotNull(indexBuilder, nameof(indexBuilder));
Check.NullButNotEmpty(operators, nameof(operators));

indexBuilder.Metadata.Npgsql().Operators = operators;

return indexBuilder;
}
}
}
11 changes: 10 additions & 1 deletion src/EFCore.PG/Metadata/INpgsqlIndexAnnotations.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore.Metadata;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Metadata;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata
{
Expand All @@ -11,5 +12,13 @@ public interface INpgsqlIndexAnnotations : IRelationalIndexAnnotations
/// http://www.postgresql.org/docs/current/static/sql-createindex.html
/// </remarks>
string Method { get; }

/// <summary>
/// The PostgreSQL index operators to be used.
/// </summary>
/// <remarks>
/// https://www.postgresql.org/docs/current/static/indexes-opclass.html
/// </remarks>
IReadOnlyList<string> Operators { get; }
}
}
1 change: 1 addition & 0 deletions src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public static class NpgsqlAnnotationNames
public const string HiLoSequenceName = Prefix + "HiLoSequenceName";
public const string HiLoSequenceSchema = Prefix + "HiLoSequenceSchema";
public const string IndexMethod = Prefix + "IndexMethod";
public const string IndexOperators = Prefix + "IndexOperators";
public const string PostgresExtensionPrefix = Prefix + "PostgresExtension:";
public const string EnumPrefix = Prefix + "Enum:";
public const string RangePrefix = Prefix + "Range:";
Expand Down
19 changes: 18 additions & 1 deletion src/EFCore.PG/Metadata/NpgsqlIndexAnnotations.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal;

Expand Down Expand Up @@ -28,8 +29,24 @@ public string Method
set => SetMethod(value);
}

/// <summary>
/// The PostgreSQL index operators to be used.
/// </summary>
/// <remarks>
/// https://www.postgresql.org/docs/current/static/indexes-opclass.html
/// </remarks>
public string[] Operators
Comment thread
khellang marked this conversation as resolved.
{
get => (string[]) Annotations.Metadata[NpgsqlAnnotationNames.IndexOperators];
set => SetOperators(value);
}

IReadOnlyList<string> INpgsqlIndexAnnotations.Operators => Operators;

protected virtual bool SetMethod(string value)
=> Annotations.SetAnnotation(NpgsqlAnnotationNames.IndexMethod, value);

protected virtual bool SetOperators(string[] value)
=> Annotations.SetAnnotation(NpgsqlAnnotationNames.IndexOperators, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public override IEnumerable<IAnnotation> For(IIndex index)
{
if (index.Npgsql().Method != null)
yield return new Annotation(NpgsqlAnnotationNames.IndexMethod, index.Npgsql().Method);
if (index.Npgsql().Operators != null)
yield return new Annotation(NpgsqlAnnotationNames.IndexOperators, index.Npgsql().Operators);
}

public override IEnumerable<IAnnotation> For(IModel model)
Expand Down
44 changes: 43 additions & 1 deletion src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ protected override void Generate(
Check.NotNull(builder, nameof(builder));

var method = (string)operation[NpgsqlAnnotationNames.IndexMethod];
var operators = (string[])operation[NpgsqlAnnotationNames.IndexOperators];
Comment thread
khellang marked this conversation as resolved.

builder.Append("CREATE ");

Expand All @@ -559,7 +560,7 @@ protected override void Generate(

builder
.Append(" (")
.Append(ColumnList(operation.Columns))
.Append(IndexColumnList(operation.Columns, operators))
.Append(")");

if (!string.IsNullOrEmpty(operation.Filter))
Expand Down Expand Up @@ -1133,6 +1134,47 @@ static string GetSchemaOrDefault([CanBeNull] string schema, [CanBeNull] IAnnotat
bool VersionAtLeast(int major, int minor)
=> _postgresVersion is null || new Version(major, minor) <= _postgresVersion;

string IndexColumnList(string[] columns, string[] operators)
Comment thread
austindrenski marked this conversation as resolved.
{
if (operators == null || operators.Length == 0)
return ColumnList(columns);

return string.Join(", ", columns.Select((v, i) =>
{
var identifier = Dependencies.SqlGenerationHelper.DelimitIdentifier(v);

if (i >= operators.Length)
return identifier;

var @operator = operators[i];

if (string.IsNullOrEmpty(@operator))
return identifier;

var delimitedOperator = TryParseSchema(@operator, out var name, out var schema)
? Dependencies.SqlGenerationHelper.DelimitIdentifier(name, schema)
: Dependencies.SqlGenerationHelper.DelimitIdentifier(@operator);

return string.Concat(identifier, " ", delimitedOperator);
}));
}

static bool TryParseSchema(string identifier, out string name, out string schema)
{
var index = identifier.IndexOf('.');

if (index >= 0)
{
schema = identifier.Substring(0, index);
name = identifier.Substring(index + 1);
return true;
}

schema = default;
name = default;
return false;
}

#endregion
}
}
13 changes: 13 additions & 0 deletions src/EFCore.PG/Utilities/Check.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ public static string NullButNotEmpty([CanBeNull] string value, [InvokerParameter
return value;
}

public static IReadOnlyCollection<T> NullButNotEmpty<T>([CanBeNull] IReadOnlyCollection<T> value, [InvokerParameterName] [NotNull] string parameterName)
Comment thread
khellang marked this conversation as resolved.
{
if (!ReferenceEquals(value, null)
&& (value.Count == 0))
{
NotEmpty(parameterName, nameof(parameterName));

throw new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName));
}

return value;
}

public static IReadOnlyList<T> HasNoNulls<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
where T : class
{
Expand Down
34 changes: 34 additions & 0 deletions test/EFCore.PG.FunctionalTests/NpgsqlMigrationSqlGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,40 @@ public void CreateIndexOperation_method()
Sql);
}

[Fact]
public void CreateIndexOperation_operations()
{
Generate(new CreateIndexOperation
{
Name = "IX_People_Name",
Table = "People",
Schema = "dbo",
Columns = new[] { "FirstName", "LastName" },
[NpgsqlAnnotationNames.IndexOperators] = new[] { "text_pattern_ops" }
});

Assert.Equal(
"CREATE INDEX \"IX_People_Name\" ON dbo.\"People\" (\"FirstName\" text_pattern_ops, \"LastName\");" + EOL,
Sql);
}

[Fact]
public void CreateIndexOperation_schema_qualified_operations()
{
Generate(new CreateIndexOperation
{
Name = "IX_People_Name",
Table = "People",
Schema = "dbo",
Columns = new[] { "FirstName" },
[NpgsqlAnnotationNames.IndexOperators] = new[] { "myschema.TextOperation" }
});

Assert.Equal(
"CREATE INDEX \"IX_People_Name\" ON dbo.\"People\" (\"FirstName\" myschema.\"TextOperation\");" + EOL,
Sql);
}

[Fact]
public void RenameIndexOperation()
{
Expand Down