From b0dc132ee01b10b65ec5904a05370d517860d09a Mon Sep 17 00:00:00 2001 From: Kirill Kurdyukov Date: Thu, 5 Sep 2024 15:13:49 +0300 Subject: [PATCH] YdbValue.Null -> Optional(Null) (#179) --- CHANGELOG.md | 2 + src/Ydb.Sdk/src/Ado/YdbParameter.cs | 173 ++++++++++++-------- src/Ydb.Sdk/src/Value/YdbValueBuilder.cs | 121 ++++++++------ src/Ydb.Sdk/tests/Ado/YdbConnectionTests.cs | 85 ++++++++++ src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs | 29 ++-- src/Ydb.Sdk/tests/Value/BasicUnitTests.cs | 42 ++--- 6 files changed, 295 insertions(+), 157 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afe87fa5..41ee10be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Fixed bug: parameter type mismatch, when setting optional with null + ## v0.6.2 - Fixed bug: adding correct placeholders to all logging calls with parameters diff --git a/src/Ydb.Sdk/src/Ado/YdbParameter.cs b/src/Ydb.Sdk/src/Ado/YdbParameter.cs index c2cb8176..289961e2 100644 --- a/src/Ydb.Sdk/src/Ado/YdbParameter.cs +++ b/src/Ydb.Sdk/src/Ado/YdbParameter.cs @@ -3,13 +3,38 @@ using System.Data.Common; using System.Diagnostics.CodeAnalysis; using Ydb.Sdk.Value; -using static System.String; namespace Ydb.Sdk.Ado; public sealed class YdbParameter : DbParameter { - private string _parameterName = Empty; + private static readonly Dictionary YdbNullByDbType = new() + { + { DbType.String, YdbValue.MakeOptionalUtf8() }, + { DbType.AnsiString, YdbValue.MakeOptionalUtf8() }, + { DbType.AnsiStringFixedLength, YdbValue.MakeOptionalUtf8() }, + { DbType.StringFixedLength, YdbValue.MakeOptionalUtf8() }, + { DbType.Int32, YdbValue.MakeOptionalInt32() }, + { DbType.Int64, YdbValue.MakeOptionalInt64() }, + { DbType.Boolean, YdbValue.MakeOptionalBool() }, + { DbType.UInt32, YdbValue.MakeOptionalUint32() }, + { DbType.UInt64, YdbValue.MakeOptionalUint64() }, + { DbType.SByte, YdbValue.MakeOptionalInt8() }, + { DbType.Int16, YdbValue.MakeOptionalInt16() }, + { DbType.UInt16, YdbValue.MakeOptionalUint16() }, + { DbType.Double, YdbValue.MakeOptionalDouble() }, + { DbType.Single, YdbValue.MakeOptionalFloat() }, + { DbType.Date, YdbValue.MakeOptionalDate() }, + { DbType.DateTime, YdbValue.MakeOptionalDatetime() }, + { DbType.Binary, YdbValue.MakeOptionalString() }, + { DbType.Byte, YdbValue.MakeOptionalUint8() }, + { DbType.DateTime2, YdbValue.MakeOptionalTimestamp() }, + { DbType.DateTimeOffset, YdbValue.MakeOptionalTimestamp() }, + { DbType.Decimal, YdbValue.MakeOptionalDecimal() }, + { DbType.Currency, YdbValue.MakeOptionalDecimal() } + }; + + private string _parameterName = string.Empty; public YdbParameter() { @@ -46,7 +71,7 @@ public override string ParameterName set => _parameterName = value ?? throw new YdbException("ParameterName must not be null!"); } - [AllowNull] [DefaultValue("")] public override string SourceColumn { get; set; } = Empty; + [AllowNull] [DefaultValue("")] public override string SourceColumn { get; set; } = string.Empty; public override object? Value { get; set; } public override bool SourceColumnNullMapping { get; set; } public override int Size { get; set; } @@ -55,44 +80,84 @@ internal YdbValue YdbValue { get { - if (Value is YdbValue ydbValue) + return Value switch { - return ydbValue; - } - - return DbType switch - { - DbType.Object when Value is not null => PrepareThenReturnYdbValue(), - DbType.String or DbType.AnsiString or DbType.AnsiStringFixedLength or DbType.StringFixedLength when - Value is string valueString => YdbValue.MakeUtf8(valueString), - DbType.Int32 when Value is int or sbyte or byte or short or ushort => - YdbValue.MakeInt32(Convert.ToInt32(Value)), - DbType.Int64 when Value is long or sbyte or byte or short or ushort or int or uint => - YdbValue.MakeInt64(Convert.ToInt64(Value)), - DbType.Boolean when Value is bool boolValue => YdbValue.MakeBool(boolValue), - DbType.UInt32 when Value is uint or byte or ushort => YdbValue.MakeUint32(Convert.ToUInt32(Value)), - DbType.UInt64 - when Value is ulong or byte or ushort or uint => YdbValue.MakeUint64(Convert.ToUInt64(Value)), - DbType.SByte when Value is sbyte sbyteValue => YdbValue.MakeInt8(sbyteValue), - DbType.Int16 when Value is short or sbyte or byte => YdbValue.MakeInt16(Convert.ToInt16(Value)), - DbType.UInt16 when Value is ushort or byte => YdbValue.MakeUint16(Convert.ToUInt16(Value)), - DbType.Double when Value is double or float => YdbValue.MakeDouble(Convert.ToDouble(Value)), - DbType.Single when Value is float floatValue => YdbValue.MakeFloat(floatValue), - DbType.Date when Value is DateTime dateTimeValue => YdbValue.MakeDate(dateTimeValue), - DbType.Time or DbType.DateTime - when Value is DateTime dateTimeValue => YdbValue.MakeDatetime(dateTimeValue), - DbType.DateTime2 when Value is DateTime dateTime => YdbValue.MakeTimestamp(dateTime), - DbType.DateTimeOffset when Value is DateTimeOffset dateTimeOffset => + YdbValue ydbValue => ydbValue, + null or DBNull when YdbNullByDbType.TryGetValue(DbType, out var value) => value, + string valueString when DbType is DbType.String or DbType.AnsiString or DbType.AnsiStringFixedLength + or DbType.StringFixedLength or DbType.Object => YdbValue.MakeUtf8(valueString), + bool boolValue when DbType is DbType.Boolean or DbType.Object => YdbValue.MakeBool(boolValue), + DateTime dateTimeValue => DbType switch + { + DbType.Date => YdbValue.MakeDate(dateTimeValue), + DbType.DateTime => YdbValue.MakeDatetime(dateTimeValue), + DbType.DateTime2 or DbType.Object => YdbValue.MakeTimestamp(dateTimeValue), + _ => ThrowInvalidCast() + }, + DateTimeOffset dateTimeOffset when DbType is DbType.DateTimeOffset or DbType.Object => YdbValue.MakeTimestamp(dateTimeOffset.UtcDateTime), - DbType.Decimal or DbType.Currency - when Value is decimal decimalValue => YdbValue.MakeDecimal(decimalValue), - DbType.Binary when Value is byte[] bytes => YdbValue.MakeString(bytes), - DbType.Byte when Value is byte valueByte => YdbValue.MakeUint8(valueByte), - DbType.VarNumeric or DbType.Xml or DbType.Guid => throw new YdbException( - $"Ydb don't supported this DbType: {DbType}"), - _ when !Enum.IsDefined(typeof(DbType), DbType) => - throw new ArgumentOutOfRangeException(nameof(DbType), DbType, null), - _ when Value is null => YdbValue.Null, + float floatValue => DbType switch + { + DbType.Single or DbType.Object => YdbValue.MakeFloat(floatValue), + DbType.Double => YdbValue.MakeDouble(floatValue), + _ => ThrowInvalidCast() + }, + double doubleValue when DbType is DbType.Double or DbType.Object => YdbValue.MakeDouble(doubleValue), + int intValue => DbType switch + { + DbType.Int32 or DbType.Object => YdbValue.MakeInt32(intValue), + DbType.Int64 => YdbValue.MakeInt64(intValue), + _ => ThrowInvalidCast() + }, + long longValue when DbType is DbType.Int64 or DbType.Object => YdbValue.MakeInt64(longValue), + decimal decimalValue when DbType is DbType.Decimal or DbType.Currency or DbType.Object => + YdbValue.MakeDecimal(decimalValue), + ulong ulongValue when DbType is DbType.UInt64 or DbType.Object => YdbValue.MakeUint64(ulongValue), + uint uintValue => DbType switch + { + DbType.UInt32 or DbType.Object => YdbValue.MakeUint32(uintValue), + DbType.UInt64 => YdbValue.MakeUint64(uintValue), + DbType.Int64 => YdbValue.MakeInt64(uintValue), + _ => ThrowInvalidCast() + }, + byte byteValue => DbType switch + { + DbType.Byte or DbType.Object => YdbValue.MakeUint8(byteValue), + DbType.Int64 => YdbValue.MakeInt64(byteValue), + DbType.Int32 => YdbValue.MakeInt32(byteValue), + DbType.Int16 => YdbValue.MakeInt16(byteValue), + DbType.UInt64 => YdbValue.MakeUint64(byteValue), + DbType.UInt32 => YdbValue.MakeUint32(byteValue), + DbType.UInt16 => YdbValue.MakeUint16(byteValue), + _ => ThrowInvalidCast() + }, + sbyte sbyteValue => DbType switch + { + DbType.SByte or DbType.Object => YdbValue.MakeInt8(sbyteValue), + DbType.Int64 => YdbValue.MakeInt64(sbyteValue), + DbType.Int32 => YdbValue.MakeInt32(sbyteValue), + DbType.Int16 => YdbValue.MakeInt16(sbyteValue), + _ => ThrowInvalidCast() + }, + ushort ushortValue => DbType switch + { + DbType.UInt16 or DbType.Object => YdbValue.MakeUint16(ushortValue), + DbType.Int64 => YdbValue.MakeInt64(ushortValue), + DbType.Int32 => YdbValue.MakeInt32(ushortValue), + DbType.UInt64 => YdbValue.MakeUint64(ushortValue), + DbType.UInt32 => YdbValue.MakeUint32(ushortValue), + _ => ThrowInvalidCast() + }, + short shortValue => DbType switch + { + DbType.Int16 or DbType.Object => YdbValue.MakeInt16(shortValue), + DbType.Int64 => YdbValue.MakeInt64(shortValue), + DbType.Int32 => YdbValue.MakeInt32(shortValue), + _ => 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 => + throw new YdbException($"Ydb don't supported this DbType: {DbType}"), _ => ThrowInvalidCast() }; } @@ -101,34 +166,6 @@ DbType.Decimal or DbType.Currency private YdbValue ThrowInvalidCast() { throw new InvalidCastException( - $"Writing values of '{Value?.GetType()}' is not supported for parameters having DbType '{DbType}'"); - } - - private YdbValue PrepareThenReturnYdbValue() - { - DbType = Value switch - { - string => DbType.String, - int => DbType.Int32, - uint => DbType.UInt32, - long => DbType.Int64, - ulong => DbType.UInt64, - bool => DbType.Boolean, - byte => DbType.Byte, - sbyte => DbType.SByte, - float => DbType.Single, - double => DbType.Double, - short => DbType.Int16, - ushort => DbType.UInt16, - decimal => DbType.Decimal, - byte[] => DbType.Binary, - Guid => DbType.Guid, - DateTime => DbType.DateTime, - DateTimeOffset => DbType.DateTimeOffset, - _ => throw new YdbException($"Error converting {Value?.GetType().ToString() ?? "null"} to YdbValue") - }; - IsNullable = false; - - return YdbValue; + $"Writing value of '{Value?.GetType().ToString() ?? "null"}' is not supported for parameters having DbType '{DbType}'"); } } diff --git a/src/Ydb.Sdk/src/Value/YdbValueBuilder.cs b/src/Ydb.Sdk/src/Value/YdbValueBuilder.cs index 6f0f3259..8f8b3e76 100644 --- a/src/Ydb.Sdk/src/Value/YdbValueBuilder.cs +++ b/src/Ydb.Sdk/src/Value/YdbValueBuilder.cs @@ -5,11 +5,6 @@ namespace Ydb.Sdk.Value; public partial class YdbValue { - internal static readonly YdbValue Null = new( - new Type { OptionalType = new OptionalType { Item = new Type { VoidType = NullValue.NullValue } } }, - new Ydb.Value { NullFlagValue = new NullValue() } - ); - public static YdbValue MakeBool(bool value) { return new YdbValue(MakePrimitiveType(Type.Types.PrimitiveTypeId.Bool), new Ydb.Value { BoolValue = value }); @@ -304,114 +299,142 @@ private static void EnsurePrimitiveTypeId(YdbTypeId typeId) } } + public static YdbValue MakeOptionalBool(bool? value = null) + { + return MakeOptionalOf(value, YdbTypeId.Bool, MakeBool); + } - private static YdbValue MakeOptionalOf(T? value, Func func) where T : struct + public static YdbValue MakeOptionalInt8(sbyte? value = null) { - return value is null ? Null : MakeOptional(func((T)value)); + return MakeOptionalOf(value, YdbTypeId.Int8, MakeInt8); } - public static YdbValue MakeOptionalBool(bool? value) + public static YdbValue MakeOptionalUint8(byte? value = null) { - return MakeOptionalOf(value, MakeBool); + return MakeOptionalOf(value, YdbTypeId.Uint8, MakeUint8); } - public static YdbValue MakeOptionalInt8(sbyte? value) + public static YdbValue MakeOptionalInt16(short? value = null) { - return MakeOptionalOf(value, MakeInt8); + return MakeOptionalOf(value, YdbTypeId.Int16, MakeInt16); } - public static YdbValue MakeOptionalUint8(byte? value) + public static YdbValue MakeOptionalUint16(ushort? value = null) { - return MakeOptionalOf(value, MakeUint8); + return MakeOptionalOf(value, YdbTypeId.Uint16, MakeUint16); } - public static YdbValue MakeOptionalInt16(short? value) + public static YdbValue MakeOptionalInt32(int? value = null) { - return MakeOptionalOf(value, MakeInt16); + return MakeOptionalOf(value, YdbTypeId.Int32, MakeInt32); } - public static YdbValue MakeOptionalUint16(ushort? value) + public static YdbValue MakeOptionalUint32(uint? value = null) { - return MakeOptionalOf(value, MakeUint16); + return MakeOptionalOf(value, YdbTypeId.Uint32, MakeUint32); } - public static YdbValue MakeOptionalInt32(int? value) + public static YdbValue MakeOptionalInt64(long? value = null) { - return MakeOptionalOf(value, MakeInt32); + return MakeOptionalOf(value, YdbTypeId.Int64, MakeInt64); } - public static YdbValue MakeOptionalUint32(uint? value) + public static YdbValue MakeOptionalUint64(ulong? value = null) { - return MakeOptionalOf(value, MakeUint32); + return MakeOptionalOf(value, YdbTypeId.Uint64, MakeUint64); } - public static YdbValue MakeOptionalInt64(long? value) + public static YdbValue MakeOptionalFloat(float? value = null) { - return MakeOptionalOf(value, MakeInt64); + return MakeOptionalOf(value, YdbTypeId.Float, MakeFloat); } - public static YdbValue MakeOptionalUint64(ulong? value) + public static YdbValue MakeOptionalDouble(double? value = null) { - return MakeOptionalOf(value, MakeUint64); + return MakeOptionalOf(value, YdbTypeId.Double, MakeDouble); } - public static YdbValue MakeOptionalFloat(float? value) + public static YdbValue MakeOptionalDate(DateTime? value = null) { - return MakeOptionalOf(value, MakeFloat); + return MakeOptionalOf(value, YdbTypeId.Date, MakeDate); } - public static YdbValue MakeOptionalDouble(double? value) + public static YdbValue MakeOptionalDatetime(DateTime? value = null) { - return MakeOptionalOf(value, MakeDouble); + return MakeOptionalOf(value, YdbTypeId.Datetime, MakeDatetime); } - public static YdbValue MakeOptionalDate(DateTime? value) + public static YdbValue MakeOptionalTimestamp(DateTime? value = null) { - return MakeOptionalOf(value, MakeDate); + return MakeOptionalOf(value, YdbTypeId.Timestamp, MakeTimestamp); } - public static YdbValue MakeOptionalDatetime(DateTime? value) + public static YdbValue MakeOptionalInterval(TimeSpan? value = null) { - return MakeOptionalOf(value, MakeDatetime); + return MakeOptionalOf(value, YdbTypeId.Interval, MakeInterval); } - public static YdbValue MakeOptionalTimestamp(DateTime? value) + public static YdbValue MakeOptionalString(byte[]? value = null) { - return MakeOptionalOf(value, MakeTimestamp); + return MakeOptionalOf(value, YdbTypeId.String, MakeString); } - public static YdbValue MakeOptionalInterval(TimeSpan? value) + public static YdbValue MakeOptionalUtf8(string? value = null) { - return MakeOptionalOf(value, MakeInterval); + return MakeOptionalOf(value, YdbTypeId.Utf8, MakeUtf8); } - public static YdbValue MakeOptionalString(byte[]? value) + public static YdbValue MakeOptionalYson(byte[]? value = null) { - return value is null ? Null : MakeOptional(MakeString(value)); + return MakeOptionalOf(value, YdbTypeId.Yson, MakeYson); } - public static YdbValue MakeOptionalUtf8(string? value) + public static YdbValue MakeOptionalJson(string? value = null) { - return value is null ? Null : MakeOptional(MakeUtf8(value)); + return MakeOptionalOf(value, YdbTypeId.Json, MakeJson); } - public static YdbValue MakeOptionalYson(byte[]? value) + public static YdbValue MakeOptionalJsonDocument(string? value = null) { - return value is null ? Null : MakeOptional(MakeYson(value)); + return MakeOptionalOf(value, YdbTypeId.JsonDocument, MakeJsonDocument); } - public static YdbValue MakeOptionalJson(string? value) + public static YdbValue MakeOptionalDecimal(decimal? value = null) { - return value is null ? Null : MakeOptional(MakeJson(value)); + return MakeOptionalOf(value, YdbTypeId.DecimalType, MakeDecimal); } - public static YdbValue MakeOptionalJsonDocument(string? value) + private static YdbValue MakeOptionalOf(T? value, YdbTypeId type, Func func) where T : struct { - return value is null ? Null : MakeOptional(MakeJsonDocument(value)); + return value is null ? MakeEmptyOptional(type) : MakeOptional(func((T)value)); } - public static YdbValue MakeOptionalDecimal(decimal? value) + private static YdbValue MakeOptionalOf(T? value, YdbTypeId type, Func func) where T : class { - return MakeOptionalOf(value, MakeDecimal); + return value is null ? MakeEmptyOptional(type) : MakeOptional(func(value)); + } + + private static YdbValue MakeEmptyOptional(YdbTypeId typeId) + { + if (IsPrimitiveTypeId(typeId)) + { + return new YdbValue( + new Type { OptionalType = new OptionalType { Item = MakePrimitiveType(typeId) } }, + new Ydb.Value { NullFlagValue = new NullValue() }); + } + + if (typeId == YdbTypeId.DecimalType) + { + return new YdbValue( + new Type + { + OptionalType = new OptionalType + { Item = new Type { DecimalType = new DecimalType { Scale = 9, Precision = 22 } } } + }, + new Ydb.Value { NullFlagValue = new NullValue() } + ); + } + + throw new ArgumentException($"This type is not supported: {typeId}", nameof(typeId)); } } diff --git a/src/Ydb.Sdk/tests/Ado/YdbConnectionTests.cs b/src/Ydb.Sdk/tests/Ado/YdbConnectionTests.cs index 4f293cfd..5339318e 100644 --- a/src/Ydb.Sdk/tests/Ado/YdbConnectionTests.cs +++ b/src/Ydb.Sdk/tests/Ado/YdbConnectionTests.cs @@ -1,5 +1,7 @@ +using System.Data; using Xunit; using Ydb.Sdk.Ado; +using Ydb.Sdk.Value; namespace Ydb.Sdk.Tests.Ado; @@ -133,6 +135,89 @@ public async Task ClosedYdbDataReader_WhenConnectionIsClosed_ThrowException() Assert.False(await reader.ReadAsync()); } + [Fact] + public async Task SetNulls_WhenTableAllTypes_SussesSet() + { + var ydbConnection = new YdbConnection(); + await ydbConnection.OpenAsync(); + var ydbCommand = ydbConnection.CreateCommand(); + var tableName = "AllTypes_" + Random.Shared.Next(); + + ydbCommand.CommandText = @$" +CREATE TABLE {tableName} ( + id INT32, + bool_column BOOL, + bigint_column INT64, + smallint_column INT16, + tinyint_column INT8, + float_column FLOAT, + double_column DOUBLE, + decimal_column DECIMAL(22,9), + uint8_column UINT8, + uint16_column UINT16, + uint32_column UINT32, + uint64_column UINT64, + text_column TEXT, + binary_column BYTES, + json_column JSON, + jsondocument_column JSONDOCUMENT, + date_column DATE, + datetime_column DATETIME, + timestamp_column TIMESTAMP, + interval_column INTERVAL, + PRIMARY KEY (id) +) +"; + await ydbCommand.ExecuteNonQueryAsync(); + ydbCommand.CommandText = @$" +INSERT INTO {tableName} + (id, bool_column, bigint_column, smallint_column, tinyint_column, float_column, double_column, decimal_column, + uint8_column, uint16_column, uint32_column, uint64_column, text_column, binary_column, json_column, + jsondocument_column, date_column, datetime_column, timestamp_column, interval_column) VALUES +($name1, $name2, $name3, $name4, $name5, $name6, $name7, $name8, $name9, $name10, $name11, $name12, $name13, $name14, + $name15, $name16, $name17, $name18, $name19, $name20); +"; + + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name1", DbType = DbType.Int32, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name2", DbType = DbType.Boolean, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name3", DbType = DbType.Int64, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name4", DbType = DbType.Int16, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name5", DbType = DbType.SByte, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name6", DbType = DbType.Single, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name7", DbType = DbType.Double, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name8", DbType = DbType.Decimal, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name9", DbType = DbType.Byte, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name10", DbType = DbType.UInt16, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name11", DbType = DbType.UInt32, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name12", DbType = DbType.UInt64, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name13", DbType = DbType.String, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name14", DbType = DbType.Binary, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name15", Value = YdbValue.MakeOptionalJson() }); + ydbCommand.Parameters.Add(new YdbParameter + { ParameterName = "$name16", Value = YdbValue.MakeOptionalJsonDocument() }); + ydbCommand.Parameters.Add(new YdbParameter { ParameterName = "$name17", DbType = DbType.Date, Value = null }); + ydbCommand.Parameters.Add( + new YdbParameter { ParameterName = "$name18", DbType = DbType.DateTime, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter + { ParameterName = "$name19", DbType = DbType.DateTime2, Value = null }); + ydbCommand.Parameters.Add(new YdbParameter + { ParameterName = "$name20", Value = YdbValue.MakeOptionalInterval() }); + + await ydbCommand.ExecuteNonQueryAsync(); + ydbCommand.CommandText = $"SELECT NULL, t.* FROM {tableName} t"; + var ydbDataReader = await ydbCommand.ExecuteReaderAsync(); + Assert.True(await ydbDataReader.ReadAsync()); + for (var i = 0; i < 21; i++) + { + Assert.True(ydbDataReader.IsDBNull(i)); + } + + Assert.False(await ydbDataReader.ReadAsync()); + + ydbCommand.CommandText = $"DROP TABLE {tableName}"; + await ydbCommand.ExecuteNonQueryAsync(); + } + private List GenerateTasks() { return Enumerable.Range(0, 100).Select(async i => diff --git a/src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs b/src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs index e96f670c..e4f93e1a 100644 --- a/src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs +++ b/src/Ydb.Sdk/tests/Ado/YdbParameterTests.cs @@ -13,9 +13,10 @@ public class YdbParameterTests [Fact] public void YdbValue_WhenValueIsNullAndDbTypeIsObject_ThrowException() { - Assert.Equal(YdbValue.Null, new YdbParameter().YdbValue); - Assert.Equal("Error converting System.Object to YdbValue", Assert.Throws(() => - new YdbParameter("$param", new object()).YdbValue).Message); + Assert.Equal("Writing value of 'null' is not supported for parameters having DbType 'Object'", + Assert.Throws(() => new YdbParameter().YdbValue).Message); + Assert.Equal("Writing value of 'System.Object' is not supported for parameters having DbType 'Object'", + Assert.Throws(() => new YdbParameter("$param", new object()).YdbValue).Message); } [Theory] @@ -25,7 +26,7 @@ public void YdbValue_WhenSetDbValue_ReturnYdbValue(Data data) Assert.Equal(data.Expected, data.FetchFun(new YdbParameter("$parameter", data.DbType, data.Expected) { IsNullable = data.IsNullable }.YdbValue)); - if (!data.IsNullable && data.DbType != DbType.DateTime2 && data.DbType != DbType.Date && data.Expected != null) + if (!data.IsNullable && data.DbType != DbType.DateTime && data.DbType != DbType.Date && data.Expected != null) { Assert.Equal(data.Expected, data.FetchFun(new YdbParameter("$parameter", data.Expected).YdbValue)); } @@ -57,14 +58,14 @@ public void YdbValue_WhenYdbValueIsSet_ReturnThis() [Fact] public void YdbValue_WhenUnCastTypes_ThrowInvalidCastException() { - Assert.Equal("Writing values of 'System.Int32' is not supported for parameters having DbType 'Boolean'", + Assert.Equal("Writing value of 'System.Int32' is not supported for parameters having DbType 'Boolean'", Assert.Throws(() => new YdbParameter("$var", DbType.Boolean, 1).YdbValue).Message); - Assert.Equal("Writing values of 'System.Int32' is not supported for parameters having DbType 'SByte'", + Assert.Equal("Writing value of 'System.Int32' is not supported for parameters having DbType 'SByte'", Assert.Throws(() => new YdbParameter("$var", DbType.SByte, 1).YdbValue).Message); - Assert.Equal("Writing values of 'System.String' is not supported for parameters having DbType 'Boolean'", + Assert.Equal("Writing value of 'System.String' is not supported for parameters having DbType 'Boolean'", Assert.Throws(() => new YdbParameter("$parameter", DbType.Boolean) { Value = "true" }.YdbValue).Message); - Assert.Equal("Writing values of 'System.Double' is not supported for parameters having DbType 'Single'", + Assert.Equal("Writing value of 'System.Double' is not supported for parameters having DbType 'Single'", Assert.Throws(() => new YdbParameter("$var", DbType.Single, 1.1).YdbValue).Message); } @@ -72,6 +73,7 @@ public void YdbValue_WhenUnCastTypes_ThrowInvalidCastException() [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) { Assert.Equal("Ydb don't supported this DbType: " + name, @@ -191,17 +193,6 @@ public class TestDataGenerator : IEnumerable }, new object[] { new Data(DbType.DateTime, null, value => value.GetOptionalDatetime()) }, new object[] - { - new Data(DbType.Time, new DateTime(2021, 08, 21, 23, 30, 47), - value => value.GetDatetime()) - }, - new object[] - { - new Data(DbType.Time, new DateTime(2021, 08, 21, 23, 30, 47), - value => value.GetDatetime(), true) - }, - new object[] { new Data(DbType.Time, null, value => value.GetOptionalDatetime()) }, - new object[] { new Data(DbType.DateTime2, DateTime.Parse("2029-08-03T06:59:44.8578730Z"), value => value.GetTimestamp()) diff --git a/src/Ydb.Sdk/tests/Value/BasicUnitTests.cs b/src/Ydb.Sdk/tests/Value/BasicUnitTests.cs index 27605555..9022bdff 100644 --- a/src/Ydb.Sdk/tests/Value/BasicUnitTests.cs +++ b/src/Ydb.Sdk/tests/Value/BasicUnitTests.cs @@ -183,26 +183,26 @@ public void OptionalPrimitiveTypesMakeGet() var valueJsonDocument = "{\"type\": \"jsondoc\"}"; Assert.Equal(valueJsonDocument, YdbValue.MakeOptionalJsonDocument(valueJsonDocument).GetOptionalJsonDocument()); - Assert.Null(YdbValue.MakeOptionalBool(null).GetOptionalBool()); - Assert.Null(YdbValue.MakeOptionalInt8(null).GetOptionalInt8()); - Assert.Null(YdbValue.MakeOptionalUint8(null).GetOptionalUint8()); - Assert.Null(YdbValue.MakeOptionalInt16(null).GetOptionalInt16()); - Assert.Null(YdbValue.MakeOptionalUint16(null).GetOptionalUint16()); - Assert.Null(YdbValue.MakeOptionalInt32(null).GetOptionalInt32()); - Assert.Null(YdbValue.MakeOptionalUint32(null).GetOptionalUint32()); - Assert.Null(YdbValue.MakeOptionalInt64(null).GetOptionalInt64()); - Assert.Null(YdbValue.MakeOptionalUint64(null).GetOptionalUint64()); - Assert.Null(YdbValue.MakeOptionalFloat(null).GetOptionalFloat()); - Assert.Null(YdbValue.MakeOptionalDouble(null).GetOptionalDouble()); - Assert.Null(YdbValue.MakeOptionalDate(null).GetOptionalDate()); - Assert.Null(YdbValue.MakeOptionalDatetime(null).GetOptionalDatetime()); - Assert.Null(YdbValue.MakeOptionalTimestamp(null).GetOptionalTimestamp()); - Assert.Null(YdbValue.MakeOptionalInterval(null).GetOptionalInterval()); - Assert.Null(YdbValue.MakeOptionalString(null).GetOptionalString()); - Assert.Null(YdbValue.MakeOptionalUtf8(null).GetOptionalUtf8()); - Assert.Null(YdbValue.MakeOptionalYson(null).GetOptionalYson()); - Assert.Null(YdbValue.MakeOptionalJson(null).GetOptionalJson()); - Assert.Null(YdbValue.MakeOptionalJsonDocument(null).GetOptionalJsonDocument()); + Assert.Null(YdbValue.MakeOptionalBool().GetOptionalBool()); + Assert.Null(YdbValue.MakeOptionalInt8().GetOptionalInt8()); + Assert.Null(YdbValue.MakeOptionalUint8().GetOptionalUint8()); + Assert.Null(YdbValue.MakeOptionalInt16().GetOptionalInt16()); + Assert.Null(YdbValue.MakeOptionalUint16().GetOptionalUint16()); + Assert.Null(YdbValue.MakeOptionalInt32().GetOptionalInt32()); + Assert.Null(YdbValue.MakeOptionalUint32().GetOptionalUint32()); + Assert.Null(YdbValue.MakeOptionalInt64().GetOptionalInt64()); + Assert.Null(YdbValue.MakeOptionalUint64().GetOptionalUint64()); + Assert.Null(YdbValue.MakeOptionalFloat().GetOptionalFloat()); + Assert.Null(YdbValue.MakeOptionalDouble().GetOptionalDouble()); + Assert.Null(YdbValue.MakeOptionalDate().GetOptionalDate()); + Assert.Null(YdbValue.MakeOptionalDatetime().GetOptionalDatetime()); + Assert.Null(YdbValue.MakeOptionalTimestamp().GetOptionalTimestamp()); + Assert.Null(YdbValue.MakeOptionalInterval().GetOptionalInterval()); + Assert.Null(YdbValue.MakeOptionalString().GetOptionalString()); + Assert.Null(YdbValue.MakeOptionalUtf8().GetOptionalUtf8()); + Assert.Null(YdbValue.MakeOptionalYson().GetOptionalYson()); + Assert.Null(YdbValue.MakeOptionalJson().GetOptionalJson()); + Assert.Null(YdbValue.MakeOptionalJsonDocument().GetOptionalJsonDocument()); } [Fact] @@ -283,7 +283,7 @@ public void DecimalType() Assert.Equal(excepted, (decimal?)(YdbValue)(decimal?)value); } - Assert.Null(YdbValue.MakeOptionalDecimal(null).GetOptionalDecimal()); + Assert.Null(YdbValue.MakeOptionalDecimal().GetOptionalDecimal()); Assert.Null((decimal?)(YdbValue)(decimal?)null); Assert.Equal("Decimal with precision (30, 0) can't fit into (22, 9)",