-
-
Notifications
You must be signed in to change notification settings - Fork 156
/
Copy pathDataField.cs
193 lines (158 loc) · 7.04 KB
/
DataField.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using System;
using Parquet.Encodings;
using Parquet.Extensions;
using Parquet.File;
namespace Parquet.Schema {
/// <summary>
/// Field containing actual data, unlike fields containing metadata.
/// </summary>
public class DataField : Field {
private bool _isNullable;
private bool _isArray;
/// <summary>
/// When true, this element is allowed to have nulls. Bad naming, probably should be something like IsNullable.
/// Changes <see cref="ClrNullableIfHasNullsType"/> property accordingly.
/// </summary>
public override bool IsNullable {
get => _isNullable; internal set {
_isNullable = value;
ClrNullableIfHasNullsType = value ? ClrType.GetNullable() : ClrType;
}
}
/// <summary>
/// When true, this element is allowed to have nulls. Bad naming, probably should be something like IsNullable.
/// </summary>
[Obsolete("Use IsNullable instead.")]
public bool HasNulls => IsNullable;
/// <summary>
/// When true, the value is an array rather than a single value.
/// </summary>
public bool IsArray {
get => _isArray; internal set {
_isArray = value;
MaxRepetitionLevel = value ? 1 : 0;
}
}
/// <summary>
/// CLR type of this column. For nullable columns this type is not nullable.
/// </summary>
public Type ClrType { get; private set; }
/// <summary>
/// Unsupported, use at your own risk!
/// </summary>
public Type ClrNullableIfHasNullsType { get; set; } = typeof(void);
/// <summary>
/// Creates a new instance of <see cref="DataField"/> by name and CLR type.
/// </summary>
/// <param name="name">Field name</param>
/// <param name="clrType">CLR type of this field. The type is internally discovered and expanded into appropriate Parquet flags.</param>
/// <param name="isNullable">When set, will override <see cref="IsNullable"/> attribute regardless whether passed type was nullable or not.</param>
/// <param name="isArray">When set, will override <see cref="IsArray"/> attribute regardless whether passed type was an array or not.</param>
/// <param name="propertyName">When set, uses this property to get the field's data. When not set, uses the property that matches the name parameter.</param>
public DataField(string name, Type clrType, bool? isNullable = null, bool? isArray = null, string? propertyName = null)
: base(name, SchemaType.Data) {
Discover(clrType, out Type baseType, out bool discIsArray, out bool discIsNullable);
ClrType = baseType;
if(!SchemaEncoder.IsSupported(ClrType)) {
if(baseType == typeof(DateTimeOffset)) {
throw new NotSupportedException($"{nameof(DateTimeOffset)} support was dropped due to numerous ambiguity issues, please use {nameof(DateTime)} from now on.");
}
else {
throw new NotSupportedException($"type {clrType} is not supported");
}
}
IsNullable = isNullable ?? discIsNullable;
IsArray = isArray ?? discIsArray;
ClrPropName = propertyName ?? name;
MaxRepetitionLevel = IsArray ? 1 : 0;
}
internal override FieldPath? PathPrefix {
set => Path = value + new FieldPath(Name);
}
internal bool IsAttachedToSchema { get; set; } = false;
internal void EnsureAttachedToSchema(string argName) {
if(IsAttachedToSchema)
return;
throw new ArgumentException(
$"Field [{this}] is not attached to any schema. You need to construct a schema passing in this field first.",
argName);
}
internal override void PropagateLevels(int parentRepetitionLevel, int parentDefinitionLevel) {
MaxRepetitionLevel = parentRepetitionLevel;
if(IsArray)
MaxRepetitionLevel++;
MaxDefinitionLevel = parentDefinitionLevel;
// can't be both array and nullable
if(IsArray)
MaxDefinitionLevel++;
else if(IsNullable)
MaxDefinitionLevel++;
IsAttachedToSchema = true;
}
/// <summary>
/// Creates non-nullable uninitialised array to hold this data type.
/// </summary>
/// <param name="length">Exact array size</param>
/// <returns></returns>
internal Array CreateArray(int length) => Array.CreateInstance(ClrType, length);
internal Array UnpackDefinitions(Array definedData, Span<int> definitionLevels) {
if(IsNullable) {
Array result = Array.CreateInstance(ClrNullableIfHasNullsType, definitionLevels.Length);
definedData.UnpackNullsFast(definitionLevels, MaxDefinitionLevel, result);
return result;
} else {
return definedData;
}
}
internal override bool IsAtomic => true;
internal bool IsDeltaEncodable => DeltaBinaryPackedEncoder.IsSupported(ClrType);
/// <inheritdoc/>
public override string ToString() =>
$"{Path} ({ClrType}{(_isNullable ? "?" : "")}{(_isArray ? "[]" : "")})";
private Type BaseClrType {
get {
#if NET6_0_OR_GREATER
if(ClrType == typeof(DateOnly))
return typeof(DateTime);
if(ClrType == typeof(TimeOnly))
return typeof(TimeSpan);
#endif
return ClrType;
}
}
/// <summary>
/// Basic equality check
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object? obj) {
if(obj is not DataField other)
return false;
return base.Equals(obj) &&
BaseClrType == other.BaseClrType &&
IsNullable == other.IsNullable &&
IsArray == other.IsArray;
}
/// <inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
#region [ Type Resolution ]
private static void Discover(Type t, out Type baseType, out bool isArray, out bool isNullable) {
baseType = t;
isArray = false;
isNullable = false;
//throw a useful hint
if(t.IsGenericIDictionary()) {
throw new NotSupportedException($"cannot declare a dictionary this way, please use {nameof(MapField)}.");
}
if(t.TryExtractIEnumerableType(out Type? enumItemType)) {
baseType = enumItemType!;
isArray = true;
}
if(baseType.IsNullable()) {
baseType = baseType.GetNonNullable();
isNullable = true;
}
}
#endregion
}
}