-
Notifications
You must be signed in to change notification settings - Fork 229
/
PostgresCollation.cs
146 lines (120 loc) · 5.21 KB
/
PostgresCollation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
public class PostgresCollation
{
private readonly IReadOnlyAnnotatable _annotatable;
private readonly string _annotationName;
internal PostgresCollation(IReadOnlyAnnotatable annotatable, string annotationName)
{
_annotatable = Check.NotNull(annotatable, nameof(annotatable));
_annotationName = Check.NotNull(annotationName, nameof(annotationName));
}
public static PostgresCollation GetOrAddCollation(
IMutableAnnotatable annotatable,
string? schema,
string name,
string lcCollate,
string lcCtype,
string? provider = null,
bool? deterministic = null)
{
Check.NotNull(annotatable, nameof(annotatable));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotEmpty(name, nameof(name));
if (FindCollation(annotatable, schema, name) is { } collation)
{
return collation;
}
var annotationName = BuildAnnotationName(schema, name);
return new PostgresCollation(annotatable, annotationName)
{
LcCollate = lcCollate,
LcCtype = lcCtype,
Provider = provider,
IsDeterministic = deterministic
};
}
public static PostgresCollation? FindCollation(
IReadOnlyAnnotatable annotatable,
string? schema,
string name)
{
Check.NotNull(annotatable, nameof(annotatable));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotEmpty(name, nameof(name));
var annotationName = BuildAnnotationName(schema, name);
return annotatable[annotationName] is null ? null : new PostgresCollation(annotatable, annotationName);
}
private static string BuildAnnotationName(string? schema, string name)
=> schema is not null
? $"{NpgsqlAnnotationNames.CollationDefinitionPrefix}{schema}.{name}"
: $"{NpgsqlAnnotationNames.CollationDefinitionPrefix}{name}";
public static IEnumerable<PostgresCollation> GetCollations(IReadOnlyAnnotatable annotatable)
=> Check.NotNull(annotatable, nameof(annotatable))
.GetAnnotations()
.Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.CollationDefinitionPrefix, StringComparison.Ordinal))
.Select(a => new PostgresCollation(annotatable, a.Name));
public virtual Annotatable Annotatable => (Annotatable)_annotatable;
public virtual string? Schema => GetData().Schema;
public virtual string Name => GetData().Name!;
public virtual string LcCollate
{
get => GetData().LcCollate!;
set => SetData(lcCollate: value);
}
public virtual string LcCtype
{
get => GetData().LcCtype!;
set => SetData(lcCtype: value);
}
public virtual string? Provider
{
get => GetData().Provider;
set => SetData(provider: value);
}
public virtual bool? IsDeterministic
{
get => GetData().IsDeterministic;
set => SetData(deterministic: value);
}
private (string? Schema, string? Name, string? LcCollate, string? LcCtype, string? Provider, bool? IsDeterministic) GetData()
=> Deserialize(Annotatable.FindAnnotation(_annotationName));
private void SetData(string? lcCollate = null, string? lcCtype = null, string? provider = null, bool? deterministic = null)
=> Annotatable[_annotationName] =
$"{lcCollate ?? LcCollate},{lcCtype ?? LcCtype},{provider ?? Provider},{deterministic ?? IsDeterministic}";
private static (string? Schema, string? Name, string? LcCollate, string? LcCtype, string? Provider, bool? IsDeterministic)
Deserialize(IAnnotation? annotation)
{
if (annotation is null || !(annotation.Value is string value) || string.IsNullOrEmpty(value))
{
return (null, null!, null!, null!, null, null);
}
string?[] elements = value.Split(',');
if (elements.Length != 4)
{
throw new ArgumentException($"Cannot parse collation annotation value: {value}");
}
for (var i = 0; i < 4; i++)
{
if (elements[i] == "")
{
elements[i] = null;
}
}
var isDeterministic = elements[3] is { } isDeterminsticString
? bool.Parse(isDeterminsticString)
: (bool?)null;
// TODO: This would be a safer operation if we stored schema and name in the annotation value (see Sequence.cs).
// Yes, this doesn't support dots in the schema/collation name, let somebody complain first.
var schemaAndName = annotation.Name.Substring(NpgsqlAnnotationNames.CollationDefinitionPrefix.Length).Split('.');
switch (schemaAndName.Length)
{
case 1:
return (null, schemaAndName[0], elements[0], elements[1], elements[2], isDeterministic);
case 2:
return (schemaAndName[0], schemaAndName[1], elements[0], elements[1], elements[2], isDeterministic);
default:
throw new ArgumentException($"Cannot parse collation name from annotation: {annotation.Name}");
}
}
}