-
Notifications
You must be signed in to change notification settings - Fork 151
SNOW-1488703: Add arrow support for structured types #1178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
SNOW-1488703: Add arrow support for structured types #1178
Conversation
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types # Conflicts: # Snowflake.Data.Tests/IntegrationTests/StructuredArraysIT.cs # Snowflake.Data.Tests/IntegrationTests/StructuredMapsIT.cs # Snowflake.Data.Tests/IntegrationTests/StructuredObjectsIT.cs
🎉 Snyk checks have passed. No issues have been found so far.✅ security/snyk check is complete. No issues have been found. (View Details) ✅ license/snyk check is complete. No issues have been found. (View Details) |
❌ 7 Tests Failed:
View the full list of 10 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
This reverts commit a034791.
This reverts commit 5add0a3.
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…ps://github.com/snowflakedb/snowflake-connector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
| case Decimal128Array decimals: return decimals.GetValue(index); | ||
| case Int32Array ints: return ints.GetValue(index); | ||
| case Int64Array longs: return longs.GetValue(index); | ||
| case StringArray strArray: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should there be a boolean array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added case for boolean arrays
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
| case DoubleArray doubles: return doubles.GetValue(index); | ||
| case FloatArray floats: return floats.GetValue(index); | ||
| case Decimal128Array decimals: return decimals.GetValue(index); | ||
| case Int32Array ints: return ints.GetValue(index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason for not including Int8Array, Int16Array, BinaryArray, Date32Array and FixedSizeListArray? I see all of them are handled in ExtractCell method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed them when going through the list of cases. They are added now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to make a test for FixedSizeListArray and found that ARRAY_CONSTRUCT and OBJECT_CONSTRUCT does not support VECTOR and that's why FixedSizeListArray wasn't initially included
Also just to let you know, currently the response always return number arrays in either Decimal128Array or DoubleArray regardless of the numeric data type they were assigned in ARRAY_CONSTRUCT
| var json = stringValue == null ? null : JObject.Parse(stringValue); | ||
| return JsonToStructuredTypeConverter.ConvertMap<TKey, TValue>(fields, json); | ||
| var val = GetValue(ordinal); | ||
| if (val is string stringValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Isn't this more readable? Here and in GetArray
switch (val)
{
case string stringValue:
{
var json = JObject.Parse(stringValue);
return JsonToStructuredTypeConverter.ConvertObject<T>(fields, json);
}
case Dictionary<string, object> structArray:
return ArrowConverter.ConvertObject<T>(structArray);
default:
return null;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced with switch case for all the Get methods
| return result; | ||
| } | ||
|
|
||
| private static object ConvertValue(object value, Type targetType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use switch case for clarity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| private static void MapPropertiesByNames(object obj, Dictionary<string, object> dict, Type type) | ||
| { | ||
| foreach (var kvp in dict) | ||
| { | ||
| var prop = type.GetProperty(kvp.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); | ||
| if (prop != null) | ||
| { | ||
| var converted = ConvertValue(kvp.Value, prop.PropertyType); | ||
| prop.SetValue(obj, converted); | ||
| } | ||
| else | ||
| { | ||
| foreach (var property in type.GetProperties()) | ||
| { | ||
| foreach (var attr in property.GetCustomAttributes().OfType<SnowflakeColumn>()) | ||
| { | ||
| if (attr?.Name == kvp.Key) | ||
| { | ||
| var converted = ConvertValue(kvp.Value, property.PropertyType); | ||
| property.SetValue(obj, converted); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private static void MapPropertiesByNames(object obj, Dictionary<string, object> dict, Type type) | |
| { | |
| foreach (var kvp in dict) | |
| { | |
| var prop = type.GetProperty(kvp.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); | |
| if (prop != null) | |
| { | |
| var converted = ConvertValue(kvp.Value, prop.PropertyType); | |
| prop.SetValue(obj, converted); | |
| } | |
| else | |
| { | |
| foreach (var property in type.GetProperties()) | |
| { | |
| foreach (var attr in property.GetCustomAttributes().OfType<SnowflakeColumn>()) | |
| { | |
| if (attr?.Name == kvp.Key) | |
| { | |
| var converted = ConvertValue(kvp.Value, property.PropertyType); | |
| property.SetValue(obj, converted); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private static void MapPropertiesByNames(object obj, Dictionary<string, object> dict, Type type) | |
| { | |
| foreach (var kvp in dict) | |
| { | |
| var prop = FindPropertyByName(type, kvp.Key); | |
| if (prop != null) | |
| { | |
| var converted = ConvertValue(kvp.Value, prop.PropertyType); | |
| prop.SetValue(obj, converted); | |
| } | |
| } | |
| } | |
| private static PropertyInfo FindPropertyByName(Type type, string name) | |
| { | |
| var prop = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); | |
| if (prop != null) | |
| return prop; | |
| return type.GetProperties() | |
| .FirstOrDefault(p => p.GetCustomAttributes() | |
| .OfType<SnowflakeColumn>() | |
| .Any(attr => attr?.Name == name)); | |
| } |
IMO, it's more readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| if (type.GetCustomAttributes(false).Any(attribute => attribute.GetType() == typeof(SnowflakeObject))) | ||
| { | ||
| var constructionMethod = JsonToStructuredTypeConverter.GetConstructionMethod(type); | ||
| if (constructionMethod == SnowflakeObjectConstructionMethod.PROPERTIES_NAMES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use switch case for nested if statements to improve clarity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored to switch case
| private static void MapPropertiesByOrder(object obj, Dictionary<string, object> dict, Type type) | ||
| { | ||
| var index = 0; | ||
| foreach (var property in type.GetProperties()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the order of properties guaranteed in type.GetProperties()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the docs, the order is only guaranteed for .NET 7 and above
I checked its JSON counterpart and it also uses type.GetProperties(). One option could be ordering by metadata
| if (index >= dict.Count) | ||
| break; | ||
|
|
||
| var attributes = property.GetCustomAttributes().OfType<SnowflakeColumn>().ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the order of the attributes guaranteed in property.GetCustomAttributes()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There doesn't seem to be a guarantee that GetCustomAttributes() will always list attributes in the same order on all .NET versions
It's replaced to get the first or default value instead of a list similar to JsonToStructuredTypeConverter
| var result = new T[list.Count]; | ||
| for (int i = 0; i < list.Count; i++) | ||
| { | ||
| if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it checked inside the for loop? Let's move it outside
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, it's now outside the for loop
| return CallMethod(targetType, objDict, nameof(ConvertObject)); | ||
| if (value is Dictionary<object, object> mapDict) | ||
| { | ||
| var genericArgs = targetType.GetGenericArguments(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we do some kind of validation here? For example, should we check that the targetType is, in fact, a dictionary and has 2 generic arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added validation for targetType and the length of the generic arguments
|
Please also update the changelog |
Updated |
…ps://github.com/snowflakedb/snowflake-connector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
…nector-net into SNOW-1488703-Arrow-Support-For-Structured-Types
Description
Add arrow support for structured types
Checklist
dotnet test)