Skip to content

Commit ae784b6

Browse files
feat: added support columns collection (#262)
1 parent ef4f6dd commit ae784b6

File tree

5 files changed

+256
-80
lines changed

5 files changed

+256
-80
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
* Fixed: YdbDataReader.GetDataTypeName for optional values.
2+
* Added support for "Columns" collectionName in YdbConnection.GetSchema(Async).
3+
14
## v0.12.0
25
* GetUint64(int ordinal) returns a ulong for Uint8, Uint16, Uint32, Uint64 YDB types.
36
* GetInt64(int ordinal) returns a int for Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32 YDB types.

src/Ydb.Sdk/src/Ado/YdbDataReader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ private static void CheckOffsets<T>(long dataOffset, T[]? buffer, int bufferOffs
181181

182182
public override string GetDataTypeName(int ordinal)
183183
{
184-
return ReaderMetadata.GetColumn(ordinal).Type.TypeId.ToString();
184+
return ReaderMetadata.GetColumn(ordinal).Type.YqlTableType();
185185
}
186186

187187
public override DateTime GetDateTime(int ordinal)

src/Ydb.Sdk/src/Ado/YdbSchema.cs

Lines changed: 123 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System.Collections.Immutable;
12
using System.Data;
23
using System.Data.Common;
4+
using System.Globalization;
35
using Ydb.Scheme;
46
using Ydb.Scheme.V1;
57
using Ydb.Sdk.Services.Table;
@@ -28,6 +30,7 @@ public static Task<DataTable> GetSchemaAsync(
2830

2931
// Ydb specific Schema Collections
3032
"TABLES" => GetTables(ydbConnection, restrictions, cancellationToken),
33+
"COLUMNS" => GetColumns(ydbConnection, restrictions, cancellationToken),
3134
"TABLESWITHSTATS" => GetTablesWithStats(ydbConnection, restrictions, cancellationToken),
3235

3336
_ => throw new ArgumentOutOfRangeException(nameof(collectionName), collectionName,
@@ -40,18 +43,24 @@ private static async Task<DataTable> GetTables(
4043
string?[] restrictions,
4144
CancellationToken cancellationToken)
4245
{
43-
var table = new DataTable("Tables");
44-
table.Columns.Add("table_name", typeof(string));
45-
table.Columns.Add("table_type", typeof(string));
46+
var table = new DataTable("Tables")
47+
{
48+
Locale = CultureInfo.InvariantCulture,
49+
Columns =
50+
{
51+
new DataColumn("table_name"),
52+
new DataColumn("table_type")
53+
}
54+
};
4655

4756
var tableName = restrictions[0];
4857
var tableType = restrictions[1];
4958
var database = ydbConnection.Database;
5059

5160
if (tableName == null) // tableName isn't set
5261
{
53-
foreach (var tupleTable in await ListTables(ydbConnection.Session.Driver,
54-
WithSuffix(database), database, tableType, cancellationToken))
62+
foreach (var tupleTable in
63+
await ListTables(ydbConnection, WithSuffix(database), database, tableType, cancellationToken))
5564
{
5665
table.Rows.Add(tupleTable.TableName, tupleTable.TableType);
5766
}
@@ -61,7 +70,6 @@ private static async Task<DataTable> GetTables(
6170
await AppendDescribeTable(
6271
ydbConnection: ydbConnection,
6372
describeTableSettings: new DescribeTableSettings { CancellationToken = cancellationToken },
64-
database: database,
6573
tableName: tableName,
6674
tableType: tableType,
6775
(_, type) => { table.Rows.Add(tableName, type); });
@@ -75,27 +83,32 @@ private static async Task<DataTable> GetTablesWithStats(
7583
string?[] restrictions,
7684
CancellationToken cancellationToken)
7785
{
78-
var table = new DataTable("TablesWithStats");
79-
table.Columns.Add("table_name", typeof(string));
80-
table.Columns.Add("table_type", typeof(string));
81-
table.Columns.Add("rows_estimate", typeof(ulong));
82-
table.Columns.Add("creation_time", typeof(DateTime));
83-
table.Columns.Add("modification_time", typeof(DateTime));
86+
var table = new DataTable("TablesWithStats")
87+
{
88+
Locale = CultureInfo.InvariantCulture,
89+
Columns =
90+
{
91+
new DataColumn("table_name"),
92+
new DataColumn("table_type"),
93+
new DataColumn("rows_estimate", typeof(ulong)),
94+
new DataColumn("creation_time", typeof(DateTime)),
95+
new DataColumn("modification_time", typeof(DateTime))
96+
}
97+
};
8498

8599
var tableName = restrictions[0];
86100
var tableType = restrictions[1];
87101
var database = ydbConnection.Database;
88102

89103
if (tableName == null) // tableName isn't set
90104
{
91-
foreach (var tupleTable in await ListTables(ydbConnection.Session.Driver,
92-
WithSuffix(database), database, tableType, cancellationToken))
105+
foreach (var tupleTable in
106+
await ListTables(ydbConnection, WithSuffix(database), database, tableType, cancellationToken))
93107
{
94108
await AppendDescribeTable(
95109
ydbConnection: ydbConnection,
96110
describeTableSettings: new DescribeTableSettings { CancellationToken = cancellationToken }
97111
.WithTableStats(),
98-
database: database,
99112
tableName: tupleTable.TableName,
100113
tableType: tableType,
101114
(describeTableResult, type) =>
@@ -117,7 +130,6 @@ await AppendDescribeTable(
117130
ydbConnection: ydbConnection,
118131
describeTableSettings: new DescribeTableSettings { CancellationToken = cancellationToken }
119132
.WithTableStats(),
120-
database: database,
121133
tableName: tableName,
122134
tableType: tableType,
123135
(describeTableResult, type) =>
@@ -136,18 +148,74 @@ await AppendDescribeTable(
136148
return table;
137149
}
138150

151+
private static async Task<DataTable> GetColumns(
152+
YdbConnection ydbConnection,
153+
string?[] restrictions,
154+
CancellationToken cancellationToken)
155+
{
156+
var table = new DataTable("Columns")
157+
{
158+
Locale = CultureInfo.InvariantCulture,
159+
Columns =
160+
{
161+
new DataColumn("table_name"),
162+
new DataColumn("column_name"),
163+
new DataColumn("ordinal_position", typeof(int)),
164+
new DataColumn("is_nullable"),
165+
new DataColumn("data_type"),
166+
new DataColumn("family_name")
167+
}
168+
};
169+
var tableNameRestriction = restrictions[0];
170+
var columnName = restrictions[1];
171+
172+
var tableNames = await ListTableNames(ydbConnection, tableNameRestriction, cancellationToken);
173+
foreach (var tableName in tableNames)
174+
{
175+
await AppendDescribeTable(
176+
ydbConnection,
177+
new DescribeTableSettings { CancellationToken = cancellationToken },
178+
tableName,
179+
null,
180+
(result, _) =>
181+
{
182+
for (var ordinal = 0; ordinal < result.Columns.Count; ordinal++)
183+
{
184+
var column = result.Columns[ordinal];
185+
186+
if (!column.Name.IsPattern(columnName))
187+
{
188+
continue;
189+
}
190+
191+
var row = table.Rows.Add();
192+
var type = column.Type;
193+
194+
row["table_name"] = tableName;
195+
row["column_name"] = column.Name;
196+
row["ordinal_position"] = ordinal;
197+
row["is_nullable"] = type.TypeCase == Type.TypeOneofCase.OptionalType ? "YES" : "NO";
198+
row["data_type"] = type.YqlTableType();
199+
row["family_name"] = column.Family;
200+
}
201+
}
202+
);
203+
}
204+
205+
return table;
206+
}
207+
139208
private static async Task AppendDescribeTable(
140209
YdbConnection ydbConnection,
141210
DescribeTableSettings describeTableSettings,
142-
string database,
143211
string tableName,
144212
string? tableType,
145213
Action<DescribeTableResult, string> appendInTable)
146214
{
147215
try
148216
{
149217
var describeResponse = await ydbConnection.Session
150-
.DescribeTable(WithSuffix(database) + tableName, describeTableSettings);
218+
.DescribeTable(WithSuffix(ydbConnection.Database) + tableName, describeTableSettings);
151219

152220
if (describeResponse.Operation.Status == StatusIds.Types.StatusCode.SchemeError)
153221
{
@@ -174,7 +242,7 @@ private static async Task AppendDescribeTable(
174242
_ => throw new YdbException($"Unexpected entry type for Table: {describeRes.Self.Type}")
175243
};
176244

177-
if (type.IsTableType(tableType))
245+
if (type.IsPattern(tableType))
178246
{
179247
appendInTable(describeRes, type);
180248
}
@@ -187,8 +255,26 @@ private static async Task AppendDescribeTable(
187255
}
188256
}
189257

190-
private static async Task<List<(string TableName, string TableType)>> ListTables(
191-
Driver driver,
258+
private static async Task<IReadOnlyCollection<string>> ListTableNames(
259+
YdbConnection ydbConnection,
260+
string? tableName,
261+
CancellationToken cancellationToken)
262+
{
263+
var database = ydbConnection.Database;
264+
265+
return tableName != null
266+
? new List<string> { tableName }
267+
: (await ListTables(
268+
ydbConnection,
269+
WithSuffix(database),
270+
database,
271+
null,
272+
cancellationToken
273+
)).Select(tuple => tuple.TableName).ToImmutableList();
274+
}
275+
276+
private static async Task<IReadOnlyCollection<(string TableName, string TableType)>> ListTables(
277+
YdbConnection ydbConnection,
192278
string databasePath,
193279
string path,
194280
string? tableType,
@@ -198,7 +284,7 @@ private static async Task AppendDescribeTable(
198284
{
199285
var fullPath = WithSuffix(path);
200286
var tables = new List<(string, string)>();
201-
var response = await driver.UnaryCall(
287+
var response = await ydbConnection.Session.Driver.UnaryCall(
202288
SchemeService.ListDirectoryMethod,
203289
new ListDirectoryRequest { Path = fullPath },
204290
new GrpcRequestSettings { CancellationToken = cancellationToken }
@@ -220,22 +306,23 @@ private static async Task AppendDescribeTable(
220306
{
221307
case Entry.Types.Type.Table:
222308
var type = tablePath.IsSystem() ? "SYSTEM_TABLE" : "TABLE";
223-
if (type.IsTableType(tableType))
309+
if (type.IsPattern(tableType))
224310
{
225311
tables.Add((tablePath, type));
226312
}
227313

228314
break;
229315
case Entry.Types.Type.ColumnTable:
230-
if ("COLUMN_TABLE".IsTableType(tableType))
316+
if ("COLUMN_TABLE".IsPattern(tableType))
231317
{
232318
tables.Add((tablePath, "COLUMN_TABLE"));
233319
}
234320

235321
break;
236322
case Entry.Types.Type.Directory:
237323
tables.AddRange(
238-
await ListTables(driver, databasePath, fullPath + entry.Name, tableType, cancellationToken)
324+
await ListTables(ydbConnection, databasePath, fullPath + entry.Name, tableType,
325+
cancellationToken)
239326
);
240327
break;
241328
case Entry.Types.Type.Unspecified:
@@ -333,6 +420,7 @@ private static DataTable GetMetaDataCollections()
333420
// Ydb Specific Schema Collections
334421
table.Rows.Add("Tables", 2, 1);
335422
table.Rows.Add("TablesWithStats", 2, 1);
423+
table.Rows.Add("Columns", 2, 2);
336424

337425
return table;
338426
}
@@ -350,6 +438,8 @@ private static DataTable GetRestrictions()
350438
table.Rows.Add("Tables", "TableType", "TABLE_TYPE", 2);
351439
table.Rows.Add("TablesWithStats", "Table", "TABLE_NAME", 1);
352440
table.Rows.Add("TablesWithStats", "TableType", "TABLE_TYPE", 2);
441+
table.Rows.Add("Columns", "Table", "TABLE_NAME", 1);
442+
table.Rows.Add("Columns", "Column", "COLUMN_NAME", 2);
353443

354444
return table;
355445
}
@@ -366,8 +456,15 @@ private static bool IsSystem(this string tablePath)
366456
|| tablePath.StartsWith(".sys_health_dev/");
367457
}
368458

369-
private static bool IsTableType(this string tableType, string? expectedTableType)
459+
private static bool IsPattern(this string tableType, string? expectedTableType)
370460
{
371461
return expectedTableType == null || expectedTableType.Equals(tableType, StringComparison.OrdinalIgnoreCase);
372462
}
463+
464+
internal static string YqlTableType(this Type type)
465+
{
466+
return type.TypeCase == Type.TypeOneofCase.OptionalType
467+
? type.OptionalType.Item.TypeId.ToString()
468+
: type.TypeId.ToString();
469+
}
373470
}

src/Ydb.Sdk/tests/Ado/YdbCommandTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ public async Task ExecuteDbDataReader_WhenSelectManyResultSet_ReturnYdbDataReade
196196
Assert.True(ydbDataReader.HasRows);
197197
// Read 2 result set
198198
Assert.True(await ydbDataReader.NextResultAsync());
199+
Assert.Equal("Bool", ydbDataReader.GetDataTypeName(0));
200+
Assert.Equal("Double", ydbDataReader.GetDataTypeName(1));
201+
Assert.Equal("Int32", ydbDataReader.GetDataTypeName(2));
199202
for (var i = 0; i < 1500; i++)
200203
{
201204
// Read meta info
@@ -214,12 +217,19 @@ public async Task ExecuteDbDataReader_WhenSelectManyResultSet_ReturnYdbDataReade
214217

215218
// Read 3 result set
216219
Assert.True(await ydbDataReader.NextResultAsync());
220+
Assert.Equal("Int8", ydbDataReader.GetDataTypeName(0));
221+
Assert.Equal("null_field", ydbDataReader.GetName(0));
217222
Assert.True(await ydbDataReader.ReadAsync());
218223
Assert.True(ydbDataReader.IsDBNull(0));
224+
Assert.Equal(DBNull.Value, ydbDataReader.GetValue(0));
219225
Assert.False(await ydbDataReader.ReadAsync());
220226

221227
// Read 4 result set
222228
Assert.True(await ydbDataReader.NextResultAsync());
229+
Assert.Equal("Datetime", ydbDataReader.GetDataTypeName(0));
230+
Assert.Equal("Key", ydbDataReader.GetName(0));
231+
Assert.Equal("Timestamp", ydbDataReader.GetDataTypeName(1));
232+
Assert.Equal("Value", ydbDataReader.GetName(1));
223233
Assert.True(await ydbDataReader.ReadAsync());
224234
Assert.Equal(dateTime, ydbDataReader.GetDateTime(0));
225235
Assert.Equal(timestamp, ydbDataReader.GetDateTime(1));

0 commit comments

Comments
 (0)