Skip to content

Commit 77b146b

Browse files
chore fixes
1 parent cd0161c commit 77b146b

File tree

6 files changed

+219
-62
lines changed

6 files changed

+219
-62
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,4 @@ jobs:
9696
EmptyStatement,
9797
ObjectCreationAsStatement,
9898
ParameterOnlyUsedForPreconditionCheck.Local
99+
BitwiseOperatorOnEnumWithoutFlags

src/Ydb.Sdk/src/Ado/Internal/YdbTypeExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ internal static class YdbTypeExtensions
1010
internal static Type DecimalType(byte precision, byte scale) => precision == 0 && scale == 0
1111
? DefaultDecimalType
1212
: new Type { DecimalType = new DecimalType { Precision = precision, Scale = scale } };
13-
13+
1414
internal static Type ListType(this Type type) => new() { ListType = new ListType { Item = type } };
15-
15+
1616
internal static Type OptionalType(this Type type) => new() { OptionalType = new OptionalType { Item = type } };
1717
}

src/Ydb.Sdk/src/Ado/YdbParameter.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections;
2-
using System.Collections.Immutable;
32
using System.ComponentModel;
43
using System.Data;
54
using System.Data.Common;

src/Ydb.Sdk/src/Ado/YdbType/YdbDbType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ public enum YdbDbType
228228
/// another type. This value must be combined with another value from <see cref="YdbDbType"/>
229229
/// via a bit OR (e.g. YdbDbType.List | YdbDbType.Int32)
230230
/// </summary>
231-
List = int.MinValue,
231+
List = int.MinValue
232232
}
233233

234234
internal static class YdbDbTypeExtensions
@@ -291,6 +291,6 @@ DbType.AnsiStringFixedLength or
291291
};
292292

293293
internal static string ToYdbTypeName(this YdbDbType ydbDbType) => ydbDbType.HasFlag(YdbDbType.List)
294-
? $"List<${~YdbDbType.List & ydbDbType}>"
294+
? $"List<{~YdbDbType.List & ydbDbType}>"
295295
: ydbDbType.ToString();
296296
}

src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbCommandTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,24 @@ public async Task ExecuteReaderAsync_WhenParamsHaveDifferentTypes_ThrowArgumentE
221221
"Expected: { \"typeId\": \"UTF8\" }, actual: { \"typeId\": \"INT32\" }", ex.Message);
222222
}
223223

224+
[Fact]
225+
public async Task ExecuteReaderAsync_WhenParamsHaveNullOrNotNullTypes_ThrowArgumentException()
226+
{
227+
await using var ydbConnection = await CreateOpenConnectionAsync();
228+
var ex = await Assert.ThrowsAsync<ArgumentException>(() => new YdbCommand(ydbConnection)
229+
{
230+
CommandText = "SELECT * FROM T WHERE Ids in (@id1, @id2);",
231+
Parameters =
232+
{
233+
new YdbParameter("id1", DbType.Int32),
234+
new YdbParameter("id2", DbType.Int32, 1)
235+
}
236+
}.ExecuteReaderAsync());
237+
Assert.Equal("All elements in the list must have the same type. " +
238+
"Expected: { \"optionalType\": { \"item\": { \"typeId\": \"INT32\" } } }, " +
239+
"actual: { \"typeId\": \"INT32\" }", ex.Message);
240+
}
241+
224242
[Fact]
225243
public async Task ExecuteReaderAsync_WhenEmptyList_ReturnEmptyResultSet()
226244
{

src/Ydb.Sdk/test/Ydb.Sdk.Ado.Tests/YdbParameterTests.cs

Lines changed: 196 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -472,10 +472,14 @@ PRIMARY KEY (Int32Column)
472472
await new YdbCommand(ydbConnection) { CommandText = $"DROP TABLE {tableName}" }.ExecuteNonQueryAsync();
473473
}
474474

475-
public static TheoryData<object, IList> NotNullListParams => new()
475+
private static readonly DateTime SomeTimestamp = DateTime.Parse("2025-11-02T18:47:14.112353");
476+
private static readonly DateTime SomeDatetime = DateTime.Parse("2025-11-02T18:47");
477+
private static readonly DateTime SomeDate = DateTime.Parse("2025-11-02");
478+
479+
public static TheoryData<YdbDbType, IList> ListParams => new()
476480
{
477-
{ YdbDbType.Bool, new List<bool> { false, true } },
478-
{ YdbDbType.Bool, (bool[])[false, true] },
481+
{ YdbDbType.Bool, new List<bool> { false, true, false } },
482+
{ YdbDbType.Bool, (bool[])[false, true, false] },
479483
{ YdbDbType.Int8, new List<sbyte> { 1, 2, 3 } },
480484
{ YdbDbType.Int8, new sbyte[] { 1, 2, 3 } },
481485
{ YdbDbType.Int16, new List<short> { 1, 2, 3 } },
@@ -495,62 +499,24 @@ PRIMARY KEY (Int32Column)
495499
{ YdbDbType.Float, new float[] { 1, 2, 3 } },
496500
{ YdbDbType.Double, new List<double> { 1, 2, 3 } },
497501
{ YdbDbType.Double, new double[] { 1, 2, 3 } },
498-
{ "Decimal(22, 9)", new List<decimal> { 1, 2, 3 } },
499-
{ "Decimal(22, 9)", new decimal[] { 1, 2, 3 } },
502+
{ YdbDbType.Decimal, new List<decimal> { 1, 2, 3 } },
503+
{ YdbDbType.Decimal, new decimal[] { 1, 2, 3 } },
500504
{ YdbDbType.Text, new List<string> { "1", "2", "3" } },
501505
{ YdbDbType.Text, (string[])["1", "2", "3"] },
502506
{ YdbDbType.Bytes, new List<byte[]> { new byte[] { 1, 1 }, new byte[] { 2, 2 }, new byte[] { 3, 3 } } },
503507
{ YdbDbType.Bytes, (byte[][])[[1, 1], [2, 2], [3, 3]] },
504508
{
505509
YdbDbType.Timestamp,
506-
new List<DateTime> { DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3) }
510+
new List<DateTime> { SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3) }
507511
},
508512
{
509513
YdbDbType.Timestamp,
510-
(DateTime[])[DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3)]
514+
(DateTime[])[SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3)]
511515
},
512-
{
513-
YdbDbType.Interval, (TimeSpan[])[TimeSpan.FromDays(1), TimeSpan.FromDays(2), TimeSpan.FromDays(3)]
514-
}
515-
};
516-
517-
[Theory]
518-
[MemberData(nameof(NotNullListParams))]
519-
public async Task YdbParameter_WhenListOrArrayParams_AutoCastYdbList(object dbType, IList list)
520-
{
521-
await using var ydbConnection = await CreateOpenConnectionAsync();
522-
var testTable = $"auto_cast_ydb_list_{Guid.NewGuid()}";
523-
await new YdbCommand(ydbConnection)
524-
{ CommandText = $"CREATE TABLE `{testTable}`(id Uuid, type {dbType}, PRIMARY KEY(id));" }
525-
.ExecuteNonQueryAsync();
526-
await new YdbCommand(ydbConnection)
527-
{
528-
CommandText =
529-
$"INSERT INTO `{testTable}`(id, type) " +
530-
"SELECT id, type FROM AS_TABLE(ListMap($list, ($x) -> { RETURN <|id: RandomUuid($x), type: $x|> }));",
531-
Parameters = { new YdbParameter("list", list) }
532-
}.ExecuteNonQueryAsync();
533-
534-
var readerAsync = await new YdbCommand(ydbConnection)
535-
{
536-
CommandText = $"SELECT type FROM `{testTable}` WHERE type IN $list ORDER BY type",
537-
Parameters = { new YdbParameter("list", list) }
538-
}.ExecuteReaderAsync();
539-
540-
foreach (var id in list)
541-
{
542-
await readerAsync.ReadAsync();
543-
Assert.Equal(id, readerAsync.GetValue(0));
544-
}
545-
546-
Assert.False(await readerAsync.ReadAsync());
547-
await new YdbCommand(ydbConnection) { CommandText = $"DROP TABLE `{testTable}`" }.ExecuteNonQueryAsync();
548-
}
549-
550-
public static TheoryData<object, IList> NullListParams => new()
551-
{
552-
{ YdbDbType.Bool, new List<bool?> { false, true, null } },
553-
{ YdbDbType.Bool, (bool?[])[false, true, null] },
516+
{ YdbDbType.Interval, new List<TimeSpan> { TimeSpan.FromDays(1), TimeSpan.FromDays(2), TimeSpan.FromDays(3) } },
517+
{ YdbDbType.Interval, (TimeSpan[])[TimeSpan.FromDays(1), TimeSpan.FromDays(2), TimeSpan.FromDays(3)] },
518+
{ YdbDbType.Bool, new List<bool?> { false, true, false, null } },
519+
{ YdbDbType.Bool, (bool?[])[false, true, false, null] },
554520
{ YdbDbType.Int8, new List<sbyte?> { 1, 2, 3, null } },
555521
{ YdbDbType.Int8, new sbyte?[] { 1, 2, 3, null } },
556522
{ YdbDbType.Int16, new List<short?> { 1, 2, 3, null } },
@@ -570,27 +536,149 @@ public async Task YdbParameter_WhenListOrArrayParams_AutoCastYdbList(object dbTy
570536
{ YdbDbType.Float, new float?[] { 1, 2, 3, null } },
571537
{ YdbDbType.Double, new List<double?> { 1, 2, 3, null } },
572538
{ YdbDbType.Double, new double?[] { 1, 2, 3, null } },
573-
{ "Decimal(22, 9)", new List<decimal?> { 1, 2, 3, null } },
574-
{ "Decimal(22, 9)", new decimal?[] { 1, 2, 3, null } },
539+
{ YdbDbType.Decimal, new List<decimal?> { 1, 2, 3, null } },
540+
{ YdbDbType.Decimal, new decimal?[] { 1, 2, 3, null } },
575541
{ YdbDbType.Text, new List<string?> { "1", "2", "3", null } },
576542
{ YdbDbType.Text, (string?[])["1", "2", "3", null] },
577543
{ YdbDbType.Bytes, new List<byte[]?> { new byte[] { 1, 1 }, new byte[] { 2, 2 }, new byte[] { 3, 3 }, null } },
578544
{ YdbDbType.Bytes, (byte[]?[])[[1, 1], [2, 2], [3, 3], null] },
579545
{
580546
YdbDbType.Timestamp,
581-
(DateTime?[])[DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3), null]
547+
new List<DateTime?> { SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3), null }
548+
},
549+
{
550+
YdbDbType.Timestamp,
551+
(DateTime?[])[SomeTimestamp.AddDays(1), SomeTimestamp.AddDays(2), SomeTimestamp.AddDays(3), null]
552+
},
553+
{
554+
YdbDbType.Interval,
555+
new List<TimeSpan?> { TimeSpan.FromDays(1), TimeSpan.FromDays(2), TimeSpan.FromDays(3), null }
582556
},
583557
{ YdbDbType.Interval, (TimeSpan?[])[TimeSpan.FromDays(1), TimeSpan.FromDays(2), TimeSpan.FromDays(3), null] }
584558
};
585559

560+
public static TheoryData<YdbDbType, IList> ExtraParams = new()
561+
{
562+
{
563+
YdbDbType.Timestamp64, new List<DateTime>
564+
{ SomeTimestamp.AddYears(-100), SomeTimestamp.AddYears(200), SomeTimestamp.AddYears(-300) }
565+
},
566+
{
567+
YdbDbType.Timestamp64,
568+
(DateTime[])[SomeTimestamp.AddYears(-100), SomeTimestamp.AddYears(200), SomeTimestamp.AddYears(-300)]
569+
},
570+
{
571+
YdbDbType.Timestamp64, new List<DateTime?>
572+
{ SomeTimestamp.AddYears(-100), SomeTimestamp.AddYears(200), SomeTimestamp.AddYears(-300), null }
573+
},
574+
{
575+
YdbDbType.Timestamp64,
576+
(DateTime?[])[SomeTimestamp.AddYears(-100), SomeTimestamp.AddYears(200), SomeTimestamp.AddYears(-300), null]
577+
},
578+
{
579+
YdbDbType.Datetime64, new List<DateTime>
580+
{ SomeDatetime.AddYears(-100), SomeDatetime.AddYears(200), SomeDatetime.AddYears(-300) }
581+
},
582+
{
583+
YdbDbType.Datetime64,
584+
(DateTime[])[SomeDatetime.AddYears(-100), SomeDatetime.AddYears(200), SomeDatetime.AddYears(-300)]
585+
},
586+
{
587+
YdbDbType.Datetime64, new List<DateTime?>
588+
{ SomeDatetime.AddYears(-100), SomeDatetime.AddYears(200), SomeDatetime.AddYears(-300), null }
589+
},
590+
{
591+
YdbDbType.Datetime64,
592+
(DateTime?[])[SomeDatetime.AddYears(-100), SomeDatetime.AddYears(200), SomeDatetime.AddYears(-300), null]
593+
},
594+
{
595+
YdbDbType.Date32, new List<DateTime>
596+
{ SomeDate.AddYears(-100), SomeDate.AddDays(200), SomeDate.AddDays(-300) }
597+
},
598+
{
599+
YdbDbType.Date32,
600+
(DateTime[])[SomeDate.AddYears(-100), SomeDate.AddDays(200), SomeDate.AddDays(-300)]
601+
},
602+
{
603+
YdbDbType.Date32, new List<DateTime?>
604+
{ SomeDate.AddYears(-100), SomeDate.AddDays(200), SomeDate.AddDays(-300), null }
605+
},
606+
{
607+
YdbDbType.Date32,
608+
(DateTime?[])[SomeDate.AddYears(-100), SomeDate.AddDays(200), SomeDate.AddDays(-300), null]
609+
},
610+
{
611+
YdbDbType.Datetime, new List<DateTime>
612+
{ SomeDatetime.AddYears(1), SomeTimestamp.AddYears(2), SomeTimestamp.AddYears(3) }
613+
},
614+
{
615+
YdbDbType.Datetime,
616+
(DateTime[])[SomeDatetime.AddYears(1), SomeTimestamp.AddYears(2), SomeTimestamp.AddYears(3)]
617+
},
618+
{
619+
YdbDbType.Datetime, new List<DateTime?>
620+
{ SomeDatetime.AddYears(1), SomeTimestamp.AddYears(2), SomeTimestamp.AddYears(3), null }
621+
},
622+
{
623+
YdbDbType.Datetime,
624+
(DateTime?[])[SomeDatetime.AddYears(1), SomeTimestamp.AddYears(2), SomeTimestamp.AddYears(3), null]
625+
},
626+
{ YdbDbType.Date, new List<DateTime> { SomeDate.AddYears(1), SomeDate.AddYears(2), SomeDate.AddYears(3) } },
627+
{
628+
YdbDbType.Date,
629+
(DateTime[])[SomeDate.AddYears(1), SomeDate.AddYears(2), SomeDate.AddYears(3)]
630+
},
631+
{
632+
YdbDbType.Date,
633+
new List<DateTime?> { SomeDate.AddYears(1), SomeDate.AddYears(2), SomeDate.AddYears(3), null }
634+
},
635+
{
636+
YdbDbType.Date,
637+
(DateTime?[])[SomeDate.AddYears(1), SomeDate.AddYears(2), SomeDate.AddYears(3), null]
638+
},
639+
{
640+
YdbDbType.Interval64,
641+
new List<TimeSpan?> { TimeSpan.FromDays(-1), TimeSpan.FromDays(2), TimeSpan.FromDays(-3), null }
642+
},
643+
{
644+
YdbDbType.Interval64,
645+
(TimeSpan?[])[TimeSpan.FromDays(-1), TimeSpan.FromDays(2), TimeSpan.FromDays(-3), null]
646+
},
647+
{
648+
YdbDbType.Json,
649+
new List<string?> { "{\"type\": \"json1\"}", "{\"type\": \"json2\"}", "{\"type\": \"json3\"}", null }
650+
},
651+
{
652+
YdbDbType.Json,
653+
(string?[])["{\"type\": \"json1\"}", "{\"type\": \"json2\"}", "{\"type\": \"json3\"}", null]
654+
},
655+
{
656+
YdbDbType.JsonDocument,
657+
new List<string?> { "{\"type\": \"json1\"}", "{\"type\": \"json2\"}", "{\"type\": \"json3\"}", null }
658+
},
659+
{
660+
YdbDbType.JsonDocument,
661+
(string?[])["{\"type\": \"json1\"}", "{\"type\": \"json2\"}", "{\"type\": \"json3\"}", null]
662+
},
663+
{
664+
YdbDbType.Yson,
665+
new List<byte[]?> { "{a=1u}"u8.ToArray(), "{a=2u}"u8.ToArray(), null }
666+
},
667+
{
668+
YdbDbType.Yson,
669+
(byte[]?[])["{a=1u}"u8.ToArray(), "{a=2u}"u8.ToArray(), null]
670+
}
671+
};
672+
586673
[Theory]
587-
[MemberData(nameof(NullListParams))]
588-
public async Task YdbParameter_WhenNullableListOrArrayParams_AutoCastYdbList(object dbType, IList list)
674+
[MemberData(nameof(ListParams))]
675+
public async Task YdbParameter_SetValue_ArrayOrList_ConvertsToYdbList(YdbDbType ydbDbType, IList list)
589676
{
590677
await using var ydbConnection = await CreateOpenConnectionAsync();
591-
var testTable = $"auto_cast_ydb_nullable_list_{Guid.NewGuid()}";
678+
var testTable = $"auto_cast_ydb_list_{Guid.NewGuid()}";
679+
var dbTypeStr = ydbDbType == YdbDbType.Decimal ? "Decimal(22, 9)" : ydbDbType.ToString();
592680
await new YdbCommand(ydbConnection)
593-
{ CommandText = $"CREATE TABLE `{testTable}`(id Uuid, type {dbType}, PRIMARY KEY(id));" }
681+
{ CommandText = $"CREATE TABLE `{testTable}`(id Uuid, type {dbTypeStr}, PRIMARY KEY(id));" }
594682
.ExecuteNonQueryAsync();
595683
await new YdbCommand(ydbConnection)
596684
{
@@ -606,8 +694,59 @@ public async Task YdbParameter_WhenNullableListOrArrayParams_AutoCastYdbList(obj
606694
Parameters = { new YdbParameter("list", list) }
607695
}.ExecuteScalarAsync();
608696

609-
Assert.Equal((ulong)list.Count - 1, count);
697+
Assert.Equal(3ul, count);
698+
Assert.Equal(list.Count, await new YdbCommand(ydbConnection)
699+
{ CommandText = $"SELECT COUNT(*) FROM `{testTable}`" }.ExecuteNonQueryAsync());
610700

611701
await new YdbCommand(ydbConnection) { CommandText = $"DROP TABLE `{testTable}`" }.ExecuteNonQueryAsync();
612702
}
703+
704+
[Theory]
705+
[MemberData(nameof(ListParams))]
706+
[MemberData(nameof(ExtraParams))]
707+
public async Task YdbParameter_Value_WithYdbDbTypeList_ProducesListOfSpecifiedType(YdbDbType ydbDbType, IList list)
708+
{
709+
await using var ydbConnection = await CreateOpenConnectionAsync();
710+
var testTable = $"ydb_list_{Guid.NewGuid()}";
711+
var dbTypeStr = ydbDbType == YdbDbType.Decimal ? "Decimal(22, 9)" : ydbDbType.ToString();
712+
await new YdbCommand(ydbConnection)
713+
{ CommandText = $"CREATE TABLE `{testTable}`(id Uuid, type {dbTypeStr}, PRIMARY KEY(id));" }
714+
.ExecuteNonQueryAsync();
715+
await new YdbCommand(ydbConnection)
716+
{
717+
CommandText =
718+
$"INSERT INTO `{testTable}`(id, type) " +
719+
"SELECT id, type FROM AS_TABLE(ListMap($list, ($x) -> { RETURN <|id: RandomUuid($x), type: $x|> }));",
720+
Parameters = { new YdbParameter("list", YdbDbType.List | ydbDbType, list) }
721+
}.ExecuteNonQueryAsync();
722+
723+
if (ydbDbType is not (YdbDbType.Json or YdbDbType.JsonDocument or YdbDbType.Yson))
724+
{
725+
Assert.Equal(3ul, await new YdbCommand(ydbConnection)
726+
{
727+
CommandText = $"SELECT COUNT(*) FROM `{testTable}` WHERE type IN $list",
728+
Parameters = { new YdbParameter("list", YdbDbType.List | ydbDbType, list) }
729+
}.ExecuteScalarAsync());
730+
}
731+
732+
Assert.Equal((ulong)list.Count, await new YdbCommand(ydbConnection)
733+
{ CommandText = $"SELECT COUNT(*) FROM `{testTable}`" }.ExecuteScalarAsync());
734+
735+
await new YdbCommand(ydbConnection) { CommandText = $"DROP TABLE `{testTable}`" }.ExecuteNonQueryAsync();
736+
}
737+
738+
739+
[Fact]
740+
public void YdbParameter_SetValue_ListOrArray_InvalidInputs_Throws()
741+
{
742+
Assert.Equal("Writing value of 'System.Object[]' is not supported for parameters having YdbDbType 'List<Bool>'",
743+
Assert.Throws<InvalidOperationException>(() =>
744+
new YdbParameter("list", YdbDbType.List | YdbDbType.Bool, new object[] { true, false, "string" })
745+
.TypedValue).Message);
746+
747+
Assert.Equal(
748+
"Writing value of 'System.Object[]' is not supported for parameters having YdbDbType 'List<Decimal>'",
749+
Assert.Throws<InvalidOperationException>(() => new YdbParameter("list",
750+
YdbDbType.List | YdbDbType.Decimal, new object[] { 1.0m, false, 2.0m }).TypedValue).Message);
751+
}
613752
}

0 commit comments

Comments
 (0)