private object TryConvertTo(object data, ClickHouseType type) { switch (type.TypeCode) { case ClickHouseTypeCode.Nullable: return(data is null || data is DBNull ? data : TryConvertTo(data, ((NullableType)type).UnderlyingType)); case ClickHouseTypeCode.UUID: return(Guid.TryParse((string)data, out var guid) ? guid : data); case ClickHouseTypeCode.IPv4: case ClickHouseTypeCode.IPv6: return(IPAddress.TryParse((string)data, out var address) ? address : data); case ClickHouseTypeCode.Date: return(DateTime.TryParseExact((string)data, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) ? date : data); case ClickHouseTypeCode.DateTime: case ClickHouseTypeCode.DateTime64: return(DateTime.Parse((string)data, CultureInfo.InvariantCulture, DateTimeStyles.None)); case ClickHouseTypeCode.Tuple: var tt = (TupleType)type; return(tt.MakeTuple((object[])data)); default: break; } return(data); }
private object ConvertString(string item, ClickHouseType typeInfo) { switch (typeInfo) { case ArrayType ati: return(item .Trim('[', ']') .Split(',') .Select(v => ConvertString(v, ati.UnderlyingType)) .ToArray()); case TupleType tti: return(ParseTuple(item, tti)); case NothingType ti: return(item == "\\N" ? DBNull.Value : throw new InvalidOperationException()); case NullableType nti: return(item == "NULL" ? DBNull.Value : ConvertString(item, nti.UnderlyingType)); case PlainDataType <Guid> _: return(new Guid(item)); case PlainDataType <IPAddress> _: return(IPAddress.Parse(item)); default: return(Convert.ChangeType(Regex.Unescape(item), typeInfo.FrameworkType, CultureInfo.InvariantCulture)); } }
private void ReadHeaders() { // Read starting tag AssertEquals(true, jsonReader.Read()); AssertEquals(JsonToken.StartObject, jsonReader.TokenType); // Read 'meta' property AssertEquals(true, jsonReader.Read()); AssertEquals(JsonToken.PropertyName, jsonReader.TokenType); AssertEquals("meta", jsonReader.Value); AssertEquals(true, jsonReader.Read()); var columns = serializer.Deserialize <JsonColumnRecord[]>(jsonReader); FieldNames = new string[columns.Length]; RawTypes = new ClickHouseType[columns.Length]; for (var i = 0; i < columns.Length; i++) { FieldNames[i] = columns[i].Name; RawTypes[i] = TypeConverter.ParseClickHouseType(columns[i].Type); } // Read start of 'data' property AssertEquals(true, jsonReader.Read()); AssertEquals(JsonToken.PropertyName, jsonReader.TokenType); AssertEquals("data", jsonReader.Value); // Read start of data array tag AssertEquals(true, jsonReader.Read()); AssertEquals(JsonToken.StartArray, jsonReader.TokenType); AssertEquals(true, jsonReader.Read()); hasMore = jsonReader.TokenType == JsonToken.StartArray; }
internal static string Format(ClickHouseType type, object value) { return(type.TypeCode switch { var simpleType when simpleType == ClickHouseTypeCode.UInt8 || simpleType == ClickHouseTypeCode.UInt16 || simpleType == ClickHouseTypeCode.UInt32 || simpleType == ClickHouseTypeCode.UInt64 || simpleType == ClickHouseTypeCode.Int8 || simpleType == ClickHouseTypeCode.Int16 || simpleType == ClickHouseTypeCode.Int32 || simpleType == ClickHouseTypeCode.Int64 => value.ToString(), var floatType when floatType == ClickHouseTypeCode.Float32 || floatType == ClickHouseTypeCode.Float64 => FormatFloat(value), ClickHouseTypeCode.Decimal => FormatDecimal(type, value), var stringType when stringType == ClickHouseTypeCode.String || stringType == ClickHouseTypeCode.FixedString || stringType == ClickHouseTypeCode.LowCardinality || stringType == ClickHouseTypeCode.Enum8 || stringType == ClickHouseTypeCode.Enum16 || stringType == ClickHouseTypeCode.UUID => value.ToString().Escape(), ClickHouseTypeCode.Nothing => $"null", ClickHouseTypeCode.Date when value is DateTime date => $"'{date:yyyy-MM-dd}'", ClickHouseTypeCode.DateTime when type is AbstractDateTimeType dateTimeType && value is DateTime dateTime => dateTimeType.TimeZone == null ? $"'{dateTime:yyyy-MM-dd HH:mm:ss}'" : $"'{dateTime.ToUniversalTime():yyyy-MM-dd HH:mm:ss}'", ClickHouseTypeCode.DateTime64 when type is DateTime64Type dateTimeType && value is DateTime dateTime => dateTimeType.TimeZone == null ? $"'{dateTime:yyyy-MM-dd HH:mm:ss.fff}'" : $"'{dateTime.ToUniversalTime():yyyy-MM-dd HH:mm:ss.fff}'", ClickHouseTypeCode.IPv4 when value is IPAddress iPAddressValue => $"toIPv4({iPAddressValue.ToString().Escape()})", ClickHouseTypeCode.IPv6 when value is IPAddress iPAddressValue => $"toIPv6({iPAddressValue.ToString().Escape()})", ClickHouseTypeCode.Nullable when type is NullableType nullableType => value is null || value == DBNull.Value ? "null" : $"{Format(nullableType.UnderlyingType, value)}", ClickHouseTypeCode.Array when type is ArrayType arrayType && value is IEnumerable enumerable => $"[{string.Join(",", enumerable.Cast<object>().Select(obj => Format(arrayType.UnderlyingType, obj)))}]", ClickHouseTypeCode.Tuple when type is TupleType tupleType && value is ITuple tuple => $"({string.Join(",", tupleType.UnderlyingTypes.Select((x, i) => Format(x, tuple[i])))})", _ => throw new NotSupportedException($"Cannot convert value {value} to type {type.TypeCode}") });
internal static ClickHouseType[] GetClickHouseColumnTypes(this ClickHouseDataReader reader) { var count = reader.FieldCount; var names = new ClickHouseType[count]; for (int i = 0; i < count; i++) { names[i] = reader.GetClickHouseType(i); } return(names); }
internal static string Format(ClickHouseType type, object value) { return(type.TypeCode switch { var simpleType when simpleType == ClickHouseTypeCode.UInt8 || simpleType == ClickHouseTypeCode.UInt16 || simpleType == ClickHouseTypeCode.UInt32 || simpleType == ClickHouseTypeCode.UInt64 || simpleType == ClickHouseTypeCode.Int8 || simpleType == ClickHouseTypeCode.Int16 || simpleType == ClickHouseTypeCode.Int32 || simpleType == ClickHouseTypeCode.Int64 => Convert.ToString(value, CultureInfo.InvariantCulture), var floatType when floatType == ClickHouseTypeCode.Float32 || floatType == ClickHouseTypeCode.Float64 => FormatFloat(value), ClickHouseTypeCode.Decimal when value is decimal decimalValue => decimalValue.ToString(CultureInfo.InvariantCulture), var stringType when stringType == ClickHouseTypeCode.String || stringType == ClickHouseTypeCode.FixedString || stringType == ClickHouseTypeCode.LowCardinality || stringType == ClickHouseTypeCode.Enum8 || stringType == ClickHouseTypeCode.Enum16 || stringType == ClickHouseTypeCode.UUID || stringType == ClickHouseTypeCode.IPv4 || stringType == ClickHouseTypeCode.IPv6 => value.ToString(), ClickHouseTypeCode.Nothing => $"null", ClickHouseTypeCode.Date when value is DateTime date => $"{date:yyyy-MM-dd}", ClickHouseTypeCode.DateTime when type is DateTimeType dateTimeType && value is DateTime dateTime => dateTimeType.TimeZone == null ? $"{dateTime:yyyy-MM-dd HH:mm:ss}" : $"{dateTime.ToUniversalTime():yyyy-MM-dd HH:mm:ss}", ClickHouseTypeCode.DateTime64 when type is DateTime64Type dateTimeType && value is DateTime dateTime => dateTimeType.TimeZone == null ? $"{dateTime:yyyy-MM-dd HH:mm:ss.fffffff}" : $"{dateTime.ToUniversalTime():yyyy-MM-dd HH:mm:ss.fffffff}", ClickHouseTypeCode.Nullable when type is NullableType nullableType => value is null || value is DBNull ? "null" : $"{Format(nullableType.UnderlyingType, value)}", ClickHouseTypeCode.Array when type is ArrayType arrayType && value is IEnumerable enumerable => $"[{string.Join(",", enumerable.Cast<object>().Select(obj => InlineParameterFormatter.Format(arrayType.UnderlyingType, obj)))}]", ClickHouseTypeCode.Tuple when type is TupleType tupleType && value is ITuple tuple => $"({string.Join(",", tupleType.UnderlyingTypes.Select((x, i) => InlineParameterFormatter.Format(x, tuple[i])))})", _ => throw new NotSupportedException($"Cannot convert value {value} to type {type.TypeCode}") });
private void ReadHeaders() { var count = reader.Read7BitEncodedInt(); FieldNames = new string[count]; RawTypes = new ClickHouseType[count]; CurrentRow = new object[count]; for (var i = 0; i < count; i++) { FieldNames[i] = reader.ReadString(); } for (var i = 0; i < count; i++) { var chType = reader.ReadString(); RawTypes[i] = TypeConverter.ParseClickHouseType(chType); } }
private void ReadHeaders() { var names = inputReader.ReadLine().Split('\t'); var types = inputReader.ReadLine().Split('\t'); if (names.Length != types.Length) { throw new InvalidOperationException($"Count mismatch between names ({names.Length}) and types ({types.Length})"); } var fieldCount = names.Length; RawTypes = new ClickHouseType[fieldCount]; FieldNames = new string[fieldCount]; names.CopyTo(FieldNames, 0); for (var i = 0; i < fieldCount; i++) { RawTypes[i] = TypeConverter.ParseClickHouseType(Regex.Unescape(types[i])); } }
internal static string Format(ClickHouseType type, object value) { switch (type.TypeCode) { case ClickHouseTypeCode.UInt8: case ClickHouseTypeCode.UInt16: case ClickHouseTypeCode.UInt32: case ClickHouseTypeCode.UInt64: case ClickHouseTypeCode.Int8: case ClickHouseTypeCode.Int16: case ClickHouseTypeCode.Int32: case ClickHouseTypeCode.Int64: return(Convert.ToString(value, CultureInfo.InvariantCulture)); case ClickHouseTypeCode.Float32: case ClickHouseTypeCode.Float64: return(FormatFloat(value)); case ClickHouseTypeCode.Decimal: return(FormatDecimal(type, value)); case ClickHouseTypeCode.String: case ClickHouseTypeCode.FixedString: case ClickHouseTypeCode.LowCardinality: case ClickHouseTypeCode.Enum8: case ClickHouseTypeCode.Enum16: return(value.ToString().Escape()); case ClickHouseTypeCode.UUID: return($"toUUID({value.ToString().Escape()})"); case ClickHouseTypeCode.Nothing: return("null"); case ClickHouseTypeCode.Date when value is DateTime: return($"toDate('{value:yyyy-MM-dd}')"); case ClickHouseTypeCode.DateTime when type is AbstractDateTimeType dateTimeType && value is DateTime dateTime: if (dateTimeType.TimeZone != null) { dateTime = dateTime.ToUniversalTime(); } return($"toDateTime('{dateTime:yyyy-MM-dd HH:mm:ss}')"); case ClickHouseTypeCode.DateTime64 when type is DateTime64Type dateTimeType && value is DateTime dateTime: if (dateTimeType.TimeZone != null) { dateTime = dateTime.ToUniversalTime(); } return($"toDateTime64('{dateTime:yyyy-MM-dd HH:mm:ss.fffffff}', 7)"); case ClickHouseTypeCode.IPv4: return($"toIPv4({FormatIPAddress(value)})"); case ClickHouseTypeCode.IPv6: return($"toIPv6({FormatIPAddress(value)})"); case ClickHouseTypeCode.Nullable: var nullableType = (NullableType)type; return(value is null || value == DBNull.Value ? "null" : $"{Format(nullableType.UnderlyingType, value)}"); case ClickHouseTypeCode.Array: var arrayType = (ArrayType)type; var array = ((IEnumerable)value).Cast <object>().Select(obj => Format(arrayType.UnderlyingType, obj)); return($"[{string.Join(",", array)}]"); case ClickHouseTypeCode.Tuple: var tupleType = (TupleType)type; var tuple = (ITuple)value; return($"({string.Join(",", tupleType.UnderlyingTypes.Select((x, i) => Format(x, tuple[i])))})"); default: throw new NotSupportedException($"Cannot convert value {value} to type {type.TypeCode}"); } }
public object ReadValue(ClickHouseType databaseType, bool nullAsDbNull) { switch (databaseType.TypeCode) { case ClickHouseTypeCode.UInt8: return(reader.ReadByte()); case ClickHouseTypeCode.UInt16: return(reader.ReadUInt16()); case ClickHouseTypeCode.UInt32: return(reader.ReadUInt32()); case ClickHouseTypeCode.UInt64: return(reader.ReadUInt64()); case ClickHouseTypeCode.Int8: return(reader.ReadSByte()); case ClickHouseTypeCode.Int16: return(reader.ReadInt16()); case ClickHouseTypeCode.Int32: return(reader.ReadInt32()); case ClickHouseTypeCode.Int64: return(reader.ReadInt64()); case ClickHouseTypeCode.Float32: return(reader.ReadSingle()); case ClickHouseTypeCode.Float64: return(reader.ReadDouble()); case ClickHouseTypeCode.String: return(reader.ReadString()); case ClickHouseTypeCode.FixedString: var stringInfo = (FixedStringType)databaseType; return(Encoding.UTF8.GetString(reader.ReadBytes(stringInfo.Length))); case ClickHouseTypeCode.Array: var arrayTypeInfo = (ArrayType)databaseType; var length = reader.Read7BitEncodedInt(); var data = new object[length]; for (var i = 0; i < length; i++) { data[i] = ReadValue(arrayTypeInfo.UnderlyingType, nullAsDbNull); } return(data); case ClickHouseTypeCode.Nullable: var nullableTypeInfo = (NullableType)databaseType; if (reader.ReadByte() > 0) { return(nullAsDbNull ? DBNull.Value : null); } else { return(ReadValue(nullableTypeInfo.UnderlyingType, nullAsDbNull)); } case ClickHouseTypeCode.Date: var days = reader.ReadUInt16(); return(TypeConverter.DateTimeEpochStart.AddDays(days)); case ClickHouseTypeCode.DateTime: var seconds = reader.ReadUInt32(); return(TypeConverter.DateTimeEpochStart.AddSeconds(seconds)); case ClickHouseTypeCode.DateTime64: var dt64t = (DateTime64Type)databaseType; var chTicks = reader.ReadInt64(); // 7 is a 'magic constant' - Log10 of TimeSpan.TicksInSecond return(TypeConverter.DateTimeEpochStart.AddTicks((long)MathUtils.ShiftDecimalPlaces(chTicks, 7 - dt64t.Scale))); case ClickHouseTypeCode.UUID: // Byte manipulation because of ClickHouse's weird GUID implementation var bytes = new byte[16]; reader.Read(bytes, 6, 2); reader.Read(bytes, 4, 2); reader.Read(bytes, 0, 4); reader.Read(bytes, 8, 8); Array.Reverse(bytes, 8, 8); return(new Guid(bytes)); case ClickHouseTypeCode.IPv4: var ipv4bytes = reader.ReadBytes(4); Array.Reverse(ipv4bytes); return(new IPAddress(ipv4bytes)); case ClickHouseTypeCode.IPv6: var ipv6bytes = reader.ReadBytes(16); return(new IPAddress(ipv6bytes)); case ClickHouseTypeCode.Tuple: var tupleTypeInfo = (TupleType)databaseType; var count = tupleTypeInfo.UnderlyingTypes.Length; var contents = new object[count]; for (var i = 0; i < count; i++) { // Underlying data in Tuple should always be null, not DBNull contents[i] = ReadValue(tupleTypeInfo.UnderlyingTypes[i], false); } return(tupleTypeInfo.MakeTuple(contents)); case ClickHouseTypeCode.Decimal: var decimalTypeInfo = (DecimalType)databaseType; var value = new BigInteger(reader.ReadBytes(decimalTypeInfo.Size)); return(MathUtils.ShiftDecimalPlaces((decimal)value, -decimalTypeInfo.Scale)); case ClickHouseTypeCode.Nothing: break; case ClickHouseTypeCode.Nested: throw new NotSupportedException("Nested types cannot be read directly"); case ClickHouseTypeCode.Enum8: var enum8TypeInfo = (EnumType)databaseType; return(enum8TypeInfo.Lookup(reader.ReadSByte())); case ClickHouseTypeCode.Enum16: var enum16TypeInfo = (EnumType)databaseType; return(enum16TypeInfo.Lookup(reader.ReadInt16())); case ClickHouseTypeCode.LowCardinality: var lcCardinality = (LowCardinalityType)databaseType; return(ReadValue(lcCardinality.UnderlyingType, nullAsDbNull)); } throw new NotImplementedException($"Reading of {databaseType.TypeCode} is not implemented"); }
public object Read(ClickHouseType type) => type.AcceptRead(this);
internal void Write(ClickHouseType clickHouseType, object value) => clickHouseType.AcceptWrite(this, value);
internal static string Format(ClickHouseType type, object value) { switch (type) { case IntegerType it: return(Convert.ToString(value, CultureInfo.InvariantCulture)); case FloatType ft: return(FormatFloat(value)); case DecimalType dt: return(FormatDecimal(dt, value)); case LowCardinalityType lt: return(Format(lt.UnderlyingType, value)); case StringType st: case FixedStringType tt: case Enum8Type e8t: case Enum16Type e16t: return(value.ToString().Escape()); case UuidType ut: return($"toUUID({value.ToString().Escape()})"); case NothingType nt: return("NULL"); case DateType dt when value is DateTime dtv: return($"toDate('{dtv:yyyy-MM-dd}')"); case DateTimeType dtt when value is DateTime dtv: return(dtt.TimeZone == null ? $"toDateTime('{dtv:yyyy-MM-dd HH:mm:ss}')" : $"toDateTime('{dtv:yyyy-MM-dd HH:mm:ss}', '{dtt.TimeZone.Id}')"); case DateTimeType dtt when value is DateTimeOffset dto: return(dtt.TimeZone == null ? $"toDateTime('{dto:yyyy-MM-dd HH:mm:ss}')" : $"toDateTime('{dto:yyyy-MM-dd HH:mm:ss}', '{dtt.TimeZone.Id}')"); case DateTime64Type dtt when value is DateTime dtv: var @string = dtt.ToZonedDateTime(dtv).ToString("yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture); return(dtt.TimeZone != null ? $"toDateTime64('{@string}', 7, '{dtt.TimeZone}')" : $"toDateTime64('{@string}', 7)"); case DateTime64Type dtt when value is DateTimeOffset dto: var @str2 = dtt.ToZonedDateTime(dto.DateTime).ToString("yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.InvariantCulture); return(dtt.TimeZone != null ? $"toDateTime64('{@str2}', 7, '{dtt.TimeZone}')" : $"toDateTime64('{@str2}', 7)"); case IPv4Type it: return($"toIPv4({FormatIPAddress(value)})"); case IPv6Type it: return($"toIPv6({FormatIPAddress(value)})"); case NullableType nullableType: return(value is null || value is DBNull ? "null" : $"{Format(nullableType.UnderlyingType, value)}"); case ArrayType arrayType when value is IEnumerable enumerable: var array = enumerable.Cast <object>().Select(obj => Format(arrayType.UnderlyingType, obj)); return($"[{string.Join(",", array)}]"); case TupleType tupleType when value is ITuple tuple: return($"({string.Join(",", tupleType.UnderlyingTypes.Select((x, i) => Format(x, tuple[i])))})"); case MapType mapType when value is IDictionary dict: var strings = dict.Keys.Cast <object>().Select(k => $"{Format(mapType.KeyType, k)},{Format(mapType.ValueType, dict[k])}"); return($"map({string.Join(",", strings)})"); default: throw new NotSupportedException($"Cannot convert value {value} to ClickHouse type {type}"); } }
public void WriteValue(object data, ClickHouseType databaseType) { switch (databaseType.TypeCode) { case ClickHouseTypeCode.UInt8: writer.Write(Convert.ToByte(data)); break; case ClickHouseTypeCode.UInt16: writer.Write(Convert.ToUInt16(data)); break; case ClickHouseTypeCode.UInt32: writer.Write(Convert.ToUInt32(data)); break; case ClickHouseTypeCode.UInt64: writer.Write(Convert.ToUInt64(data)); break; case ClickHouseTypeCode.Int8: writer.Write(Convert.ToSByte(data)); break; case ClickHouseTypeCode.Int16: writer.Write(Convert.ToInt16(data)); break; case ClickHouseTypeCode.Int32: writer.Write(Convert.ToInt32(data)); break; case ClickHouseTypeCode.Int64: writer.Write(Convert.ToInt64(data)); break; case ClickHouseTypeCode.Float32: writer.Write(Convert.ToSingle(data)); break; case ClickHouseTypeCode.Float64: writer.Write(Convert.ToDouble(data)); break; case ClickHouseTypeCode.Decimal: var dti = (DecimalType)databaseType; var value = new BigInteger(MathUtils.ShiftDecimalPlaces(Convert.ToDecimal(data), dti.Scale)); var dbytes = new byte[dti.Size]; value.ToByteArray().CopyTo(dbytes, 0); writer.Write(dbytes); break; case ClickHouseTypeCode.String: writer.Write(Convert.ToString(data)); break; case ClickHouseTypeCode.FixedString: var @string = (string)data; var stringInfo = (FixedStringType)databaseType; var stringBytes = new byte[stringInfo.Length]; Encoding.UTF8.GetBytes(@string, 0, @string.Length, stringBytes, 0); writer.Write(stringBytes); break; case ClickHouseTypeCode.Array: var arrayTypeInfo = (ArrayType)databaseType; var collection = (IList)data; writer.Write7BitEncodedInt(collection.Count); for (var i = 0; i < collection.Count; i++) { WriteValue(collection[i], arrayTypeInfo.UnderlyingType); } break; case ClickHouseTypeCode.Nullable: var nullableTypeInfo = (NullableType)databaseType; if (data == null || data is DBNull) { writer.Write((byte)1); } else { writer.Write((byte)0); WriteValue(data, nullableTypeInfo.UnderlyingType); } break; case ClickHouseTypeCode.Tuple: var tupleType = (TupleType)databaseType; var tuple = (ITuple)data; for (var i = 0; i < tuple.Length; i++) { WriteValue(tuple[i], tupleType.UnderlyingTypes[i]); } break; case ClickHouseTypeCode.Nested: var nestedType = (NestedType)databaseType; var tuples = ((IEnumerable)data).Cast <ITuple>().ToList(); writer.Write7BitEncodedInt(tuples.Count); foreach (var ntuple in tuples) { for (int i = 0; i < ntuple.Length; i++) { WriteValue(ntuple[i], nestedType.UnderlyingTypes[i]); } } break; case ClickHouseTypeCode.UUID: var guid = ExtractGuid(data); var bytes = guid.ToByteArray(); Array.Reverse(bytes, 8, 8); writer.Write(bytes, 6, 2); writer.Write(bytes, 4, 2); writer.Write(bytes, 0, 4); writer.Write(bytes, 8, 8); break; case ClickHouseTypeCode.IPv4: var address4 = ExtractIPAddress(data); if (address4.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { throw new ArgumentException($"Expected IPv4, got {address4.ToString()}"); } var ipv4bytes = address4.GetAddressBytes(); Array.Reverse(ipv4bytes); writer.Write(ipv4bytes, 0, ipv4bytes.Length); break; case ClickHouseTypeCode.IPv6: var address6 = ExtractIPAddress(data); if (address6.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6) { throw new ArgumentException($"Expected IPv4, got {address6.ToString()}"); } var ipv6bytes = address6.GetAddressBytes(); writer.Write(ipv6bytes, 0, ipv6bytes.Length); break; case ClickHouseTypeCode.Date: var days = (ushort)(((DateTime)data).Date - TypeConverter.DateTimeEpochStart).TotalDays; writer.Write(days); break; case ClickHouseTypeCode.DateTime: var dtType = (DateTimeType)databaseType; var dto = dtType.ToDateTimeOffset((DateTime)data); var seconds = (uint)(dto.UtcDateTime - TypeConverter.DateTimeEpochStart).TotalSeconds; writer.Write(seconds); break; case ClickHouseTypeCode.DateTime64: var dt64type = (DateTime64Type)databaseType; var dto64 = dt64type.ToDateTimeOffset((DateTime)data); var ticks = (dto64.UtcDateTime - TypeConverter.DateTimeEpochStart).Ticks; // 7 is a 'magic constant' - Log10 of TimeSpan.TicksInSecond writer.Write(MathUtils.ShiftDecimalPlaces(ticks, dt64type.Scale - 7)); break; case ClickHouseTypeCode.Nothing: break; case ClickHouseTypeCode.Enum8: var enum8TypeInfo = (EnumType)databaseType; var enum8Index = data is string enum8Str ? (sbyte)enum8TypeInfo.Lookup(enum8Str) : Convert.ToSByte(data); writer.Write(enum8Index); break; case ClickHouseTypeCode.Enum16: var enum16TypeInfo = (EnumType)databaseType; var enum16Index = data is string enum16Str ? (short)enum16TypeInfo.Lookup(enum16Str) : Convert.ToInt16(data); writer.Write(enum16Index); break; case ClickHouseTypeCode.LowCardinality: var lcCardinality = (LowCardinalityType)databaseType; WriteValue(data, lcCardinality.UnderlyingType); break; default: throw new NotImplementedException($"{databaseType.TypeCode} not supported yet"); } }
internal static string Format(ClickHouseType type, object value) { switch (type) { case NothingType nt: return(NullValueString); case IntegerType it: case FloatType ft: return(Convert.ToString(value, CultureInfo.InvariantCulture)); case DecimalType dt: return(Convert.ToDecimal(value).ToString(CultureInfo.InvariantCulture)); case DateType dt when value is DateTimeOffset @do: return(@do.Date.ToString("yyyy-MM-dd")); case DateType dt: return(Convert.ToDateTime(value).ToString("yyyy-MM-dd")); case StringType st: case FixedStringType tt: case Enum8Type e8t: case Enum16Type e16t: case IPv4Type ip4: case IPv6Type ip6: case UuidType uuidType: return(value.ToString()); case LowCardinalityType lt: return(Format(lt.UnderlyingType, value)); case DateTimeType dtt when value is DateTime dt: return(dt.ToString("s", CultureInfo.InvariantCulture)); case DateTimeType dtt when value is DateTimeOffset dto: return(dto.ToString("s", CultureInfo.InvariantCulture)); case DateTime64Type dtt when value is DateTime dtv: return($"{dtv:yyyy-MM-dd HH:mm:ss.fffffff}"); case DateTime64Type dtt when value is DateTimeOffset dto: return($"{dto:yyyy-MM-dd HH:mm:ss.fffffff}"); case NullableType nt: return(value is null || value is DBNull ? NullValueString : $"{Format(nt.UnderlyingType, value)}"); case ArrayType arrayType when value is IEnumerable enumerable: return($"[{string.Join(",", enumerable.Cast<object>().Select(obj => InlineParameterFormatter.Format(arrayType.UnderlyingType, obj)))}]"); case TupleType tupleType when value is ITuple tuple: return($"({string.Join(",", tupleType.UnderlyingTypes.Select((x, i) => InlineParameterFormatter.Format(x, tuple[i])))})"); case MapType mapType when value is IDictionary dict: var strings = string.Join(",", dict.Keys.Cast <object>().Select(k => $"{InlineParameterFormatter.Format(mapType.KeyType, k)} : {InlineParameterFormatter.Format(mapType.ValueType, dict[k])}")); return($"{{{string.Join(",", strings)}}}"); default: throw new Exception($"Cannot convert {value} to {type}"); } }