Skip to content

Commit

Permalink
Add support for variant #435 (#436)
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkWanderer authored Feb 13, 2024
1 parent cfae784 commit 8c19be4
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 14 deletions.
28 changes: 14 additions & 14 deletions ClickHouse.Client.Tests/TestUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ public static ClickHouseConnection GetTestClickHouseConnection(bool compression
builder["set_session_timeout"] = 1; // Expire sessions quickly after test
builder["set_allow_experimental_geo_types"] = 1; // Allow support for geo types

// Version 21.7 requires this flag for Map type
if (SupportedFeatures.HasFlag(Feature.Map))
{
builder["set_allow_experimental_map_type"] = 1;
}
if (SupportedFeatures.HasFlag(Feature.Variant))
{
builder["set_allow_experimental_variant_type"] = 1;
}
return new ClickHouseConnection(builder.ConnectionString);
}

Expand All @@ -70,20 +73,12 @@ public static ClickHouseConnectionStringBuilder GetConnectionStringBuilder()
return new ClickHouseConnectionStringBuilder(devConnectionString);
}

public readonly struct DataTypeSample
public readonly struct DataTypeSample(string clickHouseType, Type frameworkType, string exampleExpression, object exampleValue)
{
public readonly string ClickHouseType;
public readonly Type FrameworkType;
public readonly string ExampleExpression;
public readonly object ExampleValue;

public DataTypeSample(string clickHouseType, Type frameworkType, string exampleExpression, object exampleValue)
{
ClickHouseType = clickHouseType;
FrameworkType = frameworkType;
ExampleExpression = exampleExpression;
ExampleValue = exampleValue;
}
public readonly string ClickHouseType = clickHouseType;
public readonly Type FrameworkType = frameworkType;
public readonly string ExampleExpression = exampleExpression;
public readonly object ExampleValue = exampleValue;
}

public static IEnumerable<DataTypeSample> GetDataTypeSamples()
Expand Down Expand Up @@ -223,6 +218,11 @@ public static IEnumerable<DataTypeSample> GetDataTypeSamples()
{
//yield return new DataTypeSample("Json", typeof(string), "'{\"a\": \"b\", \"c\": 3}'", "{\"a\": \"b\", \"c\": 3}");
}

if (SupportedFeatures.HasFlag(Feature.Variant))
{
yield return new DataTypeSample("Variant(UInt64, String, Array(UInt64))", typeof(string), "'Hello, World!'::Variant(UInt64, String, Array(UInt64))", "Hello, World!");
}
}

public static object[] GetEnsureSingleRow(this DbDataReader reader)
Expand Down
3 changes: 3 additions & 0 deletions ClickHouse.Client/ADO/Feature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ public enum Feature
[SinceVersion("22.8")]
AsyncInsert = 8192,

[SinceVersion("24.1")]
Variant = 16384,

All = ~None, // Special value
}
4 changes: 4 additions & 0 deletions ClickHouse.Client/Formats/HttpParameterFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ internal static string Format(ClickHouseType type, object value, bool quote)
var strings = string.Join(",", dict.Keys.Cast<object>().Select(k => $"{Format(mapType.KeyType, k, true)} : {Format(mapType.ValueType, dict[k], true)}"));
return $"{{{string.Join(",", strings)}}}";

case VariantType variantType:
var (_,chType) = variantType.GetMatchingType(value);

Check warning on line 94 in ClickHouse.Client/Formats/HttpParameterFormatter.cs

View workflow job for this annotation

GitHub Actions / Short

return Format(chType, value, quote);

default:
throw new ArgumentException($"Cannot convert {value} to {type}");
}
Expand Down
1 change: 1 addition & 0 deletions ClickHouse.Client/Types/TypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ static TypeConverter()
RegisterParameterizedType<Enum16Type>();
RegisterParameterizedType<SimpleAggregateFunctionType>();
RegisterParameterizedType<MapType>();
RegisterParameterizedType<VariantType>();

// Geo types
RegisterPlainType<PointType>();
Expand Down
57 changes: 57 additions & 0 deletions ClickHouse.Client/Types/VariantType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ClickHouse.Client.Formats;
using ClickHouse.Client.Types.Grammar;

namespace ClickHouse.Client.Types;

internal class VariantType : ParameterizedType
{
public ClickHouseType[] UnderlyingTypes { get; private set; }

public override Type FrameworkType => typeof(object);

public override string Name => "Variant";

public override ParameterizedType Parse(SyntaxTreeNode node, Func<SyntaxTreeNode, ClickHouseType> parseClickHouseTypeFunc, TypeSettings settings)
{
return new VariantType
{
UnderlyingTypes = node.ChildNodes.Select(parseClickHouseTypeFunc).ToArray(),
};
}

public override string ToString() => $"{Name}({string.Join(",", UnderlyingTypes.Select(t => t.ToString()))})";

public override object Read(ExtendedBinaryReader reader)
{
var typeIndex = reader.ReadByte();
var type = UnderlyingTypes[typeIndex];

return type.Read(reader);
}

public (int, ClickHouseType) GetMatchingType(object value)
{
var valueType = value?.GetType() ?? typeof(DBNull);
for (int i = 0; i < UnderlyingTypes.Length; i++)
{
var type = UnderlyingTypes[i];
if (type.FrameworkType == valueType)
{
return (i, type);
}
}
throw new ArgumentException("Could not find matching type for variant", nameof(value));
}

public override void Write(ExtendedBinaryWriter writer, object value)
{
var (index, type) = GetMatchingType(value);
writer.Write((byte)index);
type.Write(writer, value);
}
}

0 comments on commit 8c19be4

Please sign in to comment.