Skip to content

Commit

Permalink
feat: supported uuid YDB type (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
KirillKurdyukov authored Oct 30, 2024
1 parent 676c69e commit f200f69
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Supported UUID (Guid)

## v0.7.3
- Fixed YdbDataReader: extract Json / Yson types

Expand Down
2 changes: 2 additions & 0 deletions src/Ydb.Sdk/src/Ado/YdbDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ public override System.Type GetFieldType(int ordinal)
typeof(string),
YdbTypeId.String => typeof(byte[]),
YdbTypeId.DecimalType => typeof(decimal),
YdbTypeId.Uuid => typeof(Guid),
_ => throw new YdbException($"Unsupported ydb type {type}")
};

Expand Down Expand Up @@ -339,6 +340,7 @@ public override object GetValue(int ordinal)
YdbTypeId.Yson => ydbValue.GetYson(),
YdbTypeId.String => ydbValue.GetString(),
YdbTypeId.DecimalType => ydbValue.GetDecimal(),
YdbTypeId.Uuid => ydbValue.GetUuid(),
_ => throw new YdbException($"Unsupported ydb type {ydbValue.TypeId}")
};
}
Expand Down
6 changes: 4 additions & 2 deletions src/Ydb.Sdk/src/Ado/YdbParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public sealed class YdbParameter : DbParameter
{ DbType.DateTime2, YdbValue.MakeOptionalTimestamp() },
{ DbType.DateTimeOffset, YdbValue.MakeOptionalTimestamp() },
{ DbType.Decimal, YdbValue.MakeOptionalDecimal() },
{ DbType.Currency, YdbValue.MakeOptionalDecimal() }
{ DbType.Currency, YdbValue.MakeOptionalDecimal() },
{ DbType.Guid, YdbValue.MakeOptionalUuid() }
};

private string _parameterName = string.Empty;
Expand Down Expand Up @@ -165,7 +166,8 @@ string valueString when DbType is DbType.String or DbType.AnsiString or DbType.A
_ => ThrowInvalidCast()
},
byte[] bytesValue when DbType is DbType.Binary or DbType.Object => YdbValue.MakeString(bytesValue),
_ when DbType is DbType.VarNumeric or DbType.Xml or DbType.Guid or DbType.Time =>
Guid guidValue when DbType is DbType.Guid or DbType.Object => YdbValue.MakeUuid(guidValue),
_ when DbType is DbType.VarNumeric or DbType.Xml or DbType.Time =>
throw new YdbException($"Ydb don't supported this DbType: {DbType}"),
_ => ThrowInvalidCast()
};
Expand Down
24 changes: 18 additions & 6 deletions src/Ydb.Sdk/src/Value/YdbValueBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ public static YdbValue MakeJsonDocument(string value)
new Ydb.Value { TextValue = value });
}

public static YdbValue MakeUuid(Guid guid)
{
var bytes = guid.ToByteArray();

var low = BitConverter.ToUInt64(bytes, 0);
var high = BitConverter.ToUInt64(bytes, 8);

return new YdbValue(MakePrimitiveType(Type.Types.PrimitiveTypeId.Uuid),
new Ydb.Value { Low128 = low, High128 = high });
}

private static byte GetDecimalScale(decimal value)
{
var bits = decimal.GetBits(value);
Expand Down Expand Up @@ -237,9 +248,7 @@ public static YdbValue MakeList(IReadOnlyList<YdbValue> values)
var value = new Ydb.Value();
value.Items.Add(values.Select(v => v._protoValue));

return new YdbValue(
new Type { ListType = new ListType { Item = values[0]._protoType } },
value);
return new YdbValue(new Type { ListType = new ListType { Item = values[0]._protoType } }, value);
}

public static YdbValue MakeTuple(IReadOnlyList<YdbValue> values)
Expand All @@ -254,9 +263,7 @@ public static YdbValue MakeTuple(IReadOnlyList<YdbValue> values)
var value = new Ydb.Value();
value.Items.Add(values.Select(v => v._protoValue));

return new YdbValue(
type,
value);
return new YdbValue(type, value);
}

public static YdbValue MakeStruct(IReadOnlyDictionary<string, YdbValue> members)
Expand Down Expand Up @@ -399,6 +406,11 @@ public static YdbValue MakeOptionalJsonDocument(string? value = null)
return MakeOptionalOf(value, YdbTypeId.JsonDocument, MakeJsonDocument);
}

public static YdbValue MakeOptionalUuid(Guid? value = null)
{
return MakeOptionalOf(value, YdbTypeId.Uuid, MakeUuid);
}

public static YdbValue MakeOptionalDecimal(decimal? value = null)
{
return MakeOptionalOf(value, YdbTypeId.DecimalType, MakeDecimal);
Expand Down
5 changes: 5 additions & 0 deletions src/Ydb.Sdk/src/Value/YdbValueCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ public static explicit operator decimal(YdbValue value)
return GetOptionalPrimitive<decimal>(value);
}

public static explicit operator Guid?(YdbValue value)
{
return GetOptionalPrimitive<Guid>(value);
}

private static T? GetOptionalPrimitive<T>(YdbValue value) where T : struct
{
Expand Down Expand Up @@ -192,6 +196,7 @@ private static T GetObject<T>(YdbValue value)
YdbTypeId.Json => value.GetJson(),
YdbTypeId.JsonDocument => value.GetJsonDocument(),
YdbTypeId.DecimalType => value.GetDecimal(),
YdbTypeId.Uuid => value.GetUuid(),
_ => throw new InvalidCastException($"Cannot cast YDB type {value.TypeId} to {typeof(T).Name}.")
});
}
Expand Down
22 changes: 22 additions & 0 deletions src/Ydb.Sdk/src/Value/YdbValueParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,23 @@ public string GetJsonDocument()
return _protoValue.TextValue;
}

public Guid GetUuid()
{
EnsurePrimitiveTypeId(Type.Types.PrimitiveTypeId.Uuid);

var high = _protoValue.High128;
var low = _protoValue.Low128;

var lowBytes = BitConverter.GetBytes(low);
var highBytes = BitConverter.GetBytes(high);

var guidBytes = new byte[16];
Array.Copy(lowBytes, 0, guidBytes, 0, 8);
Array.Copy(highBytes, 0, guidBytes, 8, 8);

return new Guid(guidBytes);
}

public decimal GetDecimal()
{
EnsureType(Type.TypeOneofCase.DecimalType);
Expand Down Expand Up @@ -258,6 +275,11 @@ public decimal GetDecimal()
return GetOptional()?.GetJsonDocument();
}

public Guid? GetOptionalUuid()
{
return GetOptional()?.GetUuid();
}

public decimal? GetOptionalDecimal()
{
return GetOptional()?.GetDecimal();
Expand Down
64 changes: 64 additions & 0 deletions src/Ydb.Sdk/tests/Ado/YdbCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,33 @@ public async Task ExecuteScalarAsync_WhenSetYdbParameterThenPrepare_ReturnThisVa
Assert.Equal(data.Expected, await dbCommand.ExecuteScalarAsync());
}

[Theory]
[ClassData(typeof(YdbParameterTests.TestDataGenerator))]
public async Task ExecuteScalarAsync_WhenDbTypeIsObject_ReturnThisValue<T>(YdbParameterTests.Data<T> data)
{
if (data.IsNullable)
{
return;
}

await using var connection = new YdbConnection();
await connection.OpenAsync();

var dbCommand = connection.CreateCommand();

dbCommand.CommandText = "SELECT @var;";

var dbParameter = new YdbParameter
{
ParameterName = "@var",
Value = data.Expected,
IsNullable = data.IsNullable
};
dbCommand.Parameters.Add(dbParameter);

Assert.Equal(data.Expected, await dbCommand.ExecuteScalarAsync());
}

[Fact]
public async Task ExecuteScalarAsync_WhenNoDbTypeParameter_ReturnThisValue()
{
Expand Down Expand Up @@ -404,4 +431,41 @@ public async Task ExecuteScalar_WhenSelectNull_ReturnNull()

Assert.Null(await new YdbCommand(ydbConnection) { CommandText = "SELECT NULL" }.ExecuteScalarAsync());
}

[Theory]
[InlineData("123e4567-e89b-12d3-a456-426614174000")]
[InlineData("2d9e498b-b746-9cfb-084d-de4e1cb4736e")]
[InlineData("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B")]
public async Task Guid_WhenSelectUuid_ReturnThisUuid(string guid)
{
await using var ydbConnection = new YdbConnection();
await ydbConnection.OpenAsync();

var actualGuid = await new YdbCommand(ydbConnection)
{ CommandText = $"SELECT CAST('{guid}' AS UUID);" }
.ExecuteScalarAsync();

Assert.Equal(new Guid(guid), actualGuid);
Assert.Equal(guid.ToLower(), actualGuid?.ToString()); // Guid.ToString() method represents lowercase
}

[Theory]
[InlineData("123e4567-e89b-12d3-a456-426614174000")]
[InlineData("2d9e498b-b746-9cfb-084d-de4e1cb4736e")]
[InlineData("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B")]
public async Task Guid_WhenSetUuid_ReturnThisUtf8Uuid(string guid)
{
await using var ydbConnection = new YdbConnection();
await ydbConnection.OpenAsync();

var ydbCommand = new YdbCommand(ydbConnection)
{
CommandText = "SELECT CAST(@guid AS Text);"
};
ydbCommand.Parameters.Add(new YdbParameter("guid", DbType.Guid, new Guid(guid)));

var actualGuidText = await ydbCommand.ExecuteScalarAsync();

Assert.Equal(guid.ToLower(), actualGuidText); // Guid.ToString() method represents lowercase
}
}
12 changes: 11 additions & 1 deletion src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ public void YdbValue_WhenUnCastTypes_ThrowInvalidCastException()
[Theory]
[InlineData(DbType.VarNumeric, "VarNumeric")]
[InlineData(DbType.Xml, "Xml")]
[InlineData(DbType.Guid, "Guid")]
[InlineData(DbType.Time, "Time")]
public void YdbValue_WhenNoSupportedDbType_ThrowException(DbType dbType, string name)
{
Expand Down Expand Up @@ -183,6 +182,17 @@ public class TestDataGenerator : IEnumerable<object[]>
new object[] { new Data<double>(DbType.Double, 123.45, value => value.GetDouble()) },
new object[] { new Data<double?>(DbType.Double, 123.45, value => value.GetDouble(), true) },
new object[] { new Data<double?>(DbType.Double, null, value => value.GetOptionalDouble()) },
new object[]
{
new Data<Guid>(DbType.Guid, new Guid("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B"),
value => value.GetUuid())
},
new object[]
{
new Data<Guid?>(DbType.Guid, new Guid("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B"),
value => value.GetUuid(), true)
},
new object[] { new Data<Guid?>(DbType.Guid, null, value => value.GetOptionalUuid()) },
new object[] { new Data<DateTime>(DbType.Date, new DateTime(2021, 08, 21), value => value.GetDate()) },
new object[]
{
Expand Down
11 changes: 11 additions & 0 deletions src/Ydb.Sdk/tests/Value/YdbValueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ private class TestDataGenerator : IEnumerable<object[]>
new object[] { new Data<double>(123.45, YdbValue.MakeDouble, value => value.GetDouble()) },
new object[] { new Data<double?>(123.45, YdbValue.MakeOptionalDouble, value => value.GetOptionalDouble()) },
new object[] { new Data<double?>(null, YdbValue.MakeOptionalDouble, value => value.GetOptionalDouble()) },
new object[]
{
new Data<Guid>(new Guid("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B"), YdbValue.MakeUuid,
value => value.GetUuid())
},
new object[]
{
new Data<Guid?>(new Guid("6E73B41C-4EDE-4D08-9CFB-B7462D9E498B"), YdbValue.MakeOptionalUuid,
value => value.GetOptionalUuid())
},
new object[] { new Data<Guid?>(null, YdbValue.MakeOptionalUuid, value => value.GetOptionalUuid()) },
new object[]
{ new Data<DateTime>(new DateTime(2021, 08, 21), YdbValue.MakeDate, value => value.GetDate()) },
new object[]
Expand Down

0 comments on commit f200f69

Please sign in to comment.