Skip to content

Commit 02c583f

Browse files
committed
Improve code
1 parent 8116268 commit 02c583f

File tree

1 file changed

+76
-69
lines changed

1 file changed

+76
-69
lines changed

DuckDB.NET.Data/Internal/Writer/EnumVectorDataWriter.cs

Lines changed: 76 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,97 +7,104 @@ namespace DuckDB.NET.Data.Internal.Writer;
77

88
internal sealed unsafe class EnumVectorDataWriter : VectorDataWriterBase
99
{
10+
private readonly DuckDBLogicalType logicalType;
11+
1012
private readonly DuckDBType enumType;
1113

12-
private readonly Dictionary<string, long> enumValueIndexDictionary;
14+
private readonly uint enumDictionarySize;
15+
16+
private Dictionary<string, uint>? enumValues;
1317

1418
public EnumVectorDataWriter(IntPtr vector, void* vectorData, DuckDBLogicalType logicalType, DuckDBType columnType) : base(vector, vectorData, columnType)
1519
{
20+
this.logicalType = logicalType;
1621
enumType = NativeMethods.LogicalType.DuckDBEnumInternalType(logicalType);
17-
uint size = NativeMethods.LogicalType.DuckDBEnumDictionarySize(logicalType);
18-
enumValueIndexDictionary = [];
19-
for (long index = 0; index < size; index++)
22+
enumDictionarySize = NativeMethods.LogicalType.DuckDBEnumDictionarySize(logicalType);
23+
24+
uint maxEnumDictionarySize = enumType switch
25+
{
26+
DuckDBType.UnsignedTinyInt => byte.MaxValue,
27+
DuckDBType.UnsignedSmallInt => ushort.MaxValue,
28+
DuckDBType.UnsignedInteger => uint.MaxValue,
29+
_ => throw new NotSupportedException($"The internal enum type must be utinyint, usmallint, or uinteger."),
30+
};
31+
if (enumDictionarySize > maxEnumDictionarySize)
2032
{
21-
string enumValue = NativeMethods.LogicalType.DuckDBEnumDictionaryValue(logicalType, index).ToManagedString();
22-
enumValueIndexDictionary.Add(enumValue, index);
33+
// This exception should only be thrown if the DuckDB library has a bug.
34+
throw new InvalidOperationException($"The internal enum type is \"{enumType}\" but the enum dictionary size is greater than {maxEnumDictionarySize}.");
2335
}
36+
37+
enumValues = null;
2438
}
2539

2640
internal override bool AppendString(string value, int rowIndex)
2741
{
28-
switch (enumType)
42+
// lazy initialization
43+
if (enumValues == null)
2944
{
30-
case DuckDBType.UnsignedTinyInt:
31-
{
32-
if (enumValueIndexDictionary.TryGetValue(value, out long enumValueIndex) &&
33-
enumValueIndex >= byte.MinValue && enumValueIndex <= byte.MaxValue)
34-
{
35-
return AppendValueInternal((byte)enumValueIndex, rowIndex);
36-
}
37-
38-
return false;
39-
}
40-
case DuckDBType.UnsignedSmallInt:
41-
{
42-
if (enumValueIndexDictionary.TryGetValue(value, out long enumValueIndex) &&
43-
enumValueIndex >= ushort.MinValue && enumValueIndex <= ushort.MaxValue)
44-
{
45-
return AppendValueInternal((ushort)enumValueIndex, rowIndex);
46-
}
47-
48-
return false;
49-
}
50-
case DuckDBType.UnsignedInteger:
51-
{
52-
if (enumValueIndexDictionary.TryGetValue(value, out long enumValueIndex) &&
53-
enumValueIndex >= uint.MinValue && enumValueIndex <= uint.MaxValue)
54-
{
55-
return AppendValueInternal((uint)enumValueIndex, rowIndex);
56-
}
45+
enumValues = [];
46+
for (uint index = 0; index < enumDictionarySize; index++)
47+
{
48+
string enumValueName = NativeMethods.LogicalType.DuckDBEnumDictionaryValue(logicalType, index).ToManagedString();
49+
enumValues.Add(enumValueName, index);
50+
}
51+
}
5752

58-
return false;
59-
}
60-
default:
61-
return false;
53+
if (enumValues.TryGetValue(value, out uint enumValue))
54+
{
55+
// The following casts to byte and ushort are safe because we ensure in the constructor that the value enumDictionarySize is not too high.
56+
return enumType switch
57+
{
58+
DuckDBType.UnsignedTinyInt => AppendValueInternal((byte)enumValue, rowIndex),
59+
DuckDBType.UnsignedSmallInt => AppendValueInternal((ushort)enumValue, rowIndex),
60+
DuckDBType.UnsignedInteger => AppendValueInternal(enumValue, rowIndex),
61+
_ => throw new InvalidOperationException($"Failed to write Enum column because the internal enum type must be utinyint, usmallint, or uinteger."),
62+
};
6263
}
64+
65+
throw new InvalidOperationException($"Failed to write Enum column because the value \"{value}\" is not valid.");
6366
}
6467

6568
internal override bool AppendEnum<TEnum>(TEnum value, int rowIndex)
6669
{
67-
switch (enumType)
70+
ulong enumValue = ConvertEnumValueToUInt64(value);
71+
if (enumValue <= enumDictionarySize)
6872
{
69-
case DuckDBType.UnsignedTinyInt:
70-
{
71-
long enumValueIndex = Convert.ToInt64(value);
72-
if (enumValueIndex >= byte.MinValue && enumValueIndex <= byte.MaxValue)
73-
{
74-
return AppendValueInternal((byte)enumValueIndex, rowIndex);
75-
}
73+
// The following casts to byte, ushort and uint are safe because we ensure in the constructor that the value enumDictionarySize is not too high.
74+
return enumType switch
75+
{
76+
DuckDBType.UnsignedTinyInt => AppendValueInternal((byte)enumValue, rowIndex),
77+
DuckDBType.UnsignedSmallInt => AppendValueInternal((ushort)enumValue, rowIndex),
78+
DuckDBType.UnsignedInteger => AppendValueInternal((uint)enumValue, rowIndex),
79+
_ => throw new InvalidOperationException($"Failed to write Enum column because the internal enum type must be utinyint, usmallint, or uinteger."),
80+
};
81+
}
7682

77-
return false;
78-
}
79-
case DuckDBType.UnsignedSmallInt:
80-
{
81-
long enumValueIndex = Convert.ToInt64(value);
82-
if (enumValueIndex >= ushort.MinValue && enumValueIndex <= ushort.MaxValue)
83-
{
84-
return AppendValueInternal((ushort)enumValueIndex, rowIndex);
85-
}
83+
throw new InvalidOperationException($"Failed to write Enum column because the value is outside the range (0-{enumDictionarySize}).");
84+
}
8685

87-
return false;
88-
}
89-
case DuckDBType.UnsignedInteger:
90-
{
91-
long enumValueIndex = Convert.ToInt64(value);
92-
if (enumValueIndex >= uint.MinValue && enumValueIndex <= uint.MaxValue)
93-
{
94-
return AppendValueInternal((uint)enumValueIndex, rowIndex);
95-
}
86+
private static ulong ConvertEnumValueToUInt64<TEnum>(TEnum value) where TEnum : Enum
87+
{
88+
switch (Convert.GetTypeCode(value))
89+
{
90+
case TypeCode.SByte:
91+
return (ulong)Convert.ToSByte(value);
92+
case TypeCode.Byte:
93+
return Convert.ToByte(value);
94+
case TypeCode.Int16:
95+
return (ulong)Convert.ToInt16(value);
96+
case TypeCode.UInt16:
97+
return Convert.ToUInt16(value);
98+
case TypeCode.Int32:
99+
return (ulong)Convert.ToInt32(value);
100+
case TypeCode.UInt32:
101+
return Convert.ToUInt32(value);
102+
case TypeCode.Int64:
103+
return (ulong)Convert.ToInt64(value);
104+
case TypeCode.UInt64:
105+
return Convert.ToUInt64(value);
106+
};
96107

97-
return false;
98-
}
99-
default:
100-
return false;
101-
}
108+
throw new InvalidOperationException($"Failed to convert the enum value {value} to ulong.");
102109
}
103110
}

0 commit comments

Comments
 (0)