From 11d92db0198c70ce53e79aa49e783da531aaaba3 Mon Sep 17 00:00:00 2001 From: XmasApple Date: Thu, 12 Oct 2023 16:09:32 +0300 Subject: [PATCH 1/5] DescribeTable method --- src/Ydb.Sdk/src/Driver.cs | 2 + .../src/Services/Table/DescribeTable.cs | 578 ++++++++++++++++++ src/Ydb.Sdk/src/Services/Table/TableClient.cs | 5 + src/Ydb.Sdk/tests/Table/TestDescribeTable.cs | 59 ++ src/Ydb.Sdk/tests/Utils.cs | 16 + 5 files changed, 660 insertions(+) create mode 100644 src/Ydb.Sdk/src/Services/Table/DescribeTable.cs create mode 100644 src/Ydb.Sdk/tests/Table/TestDescribeTable.cs diff --git a/src/Ydb.Sdk/src/Driver.cs b/src/Ydb.Sdk/src/Driver.cs index f818d30f..b91806c0 100644 --- a/src/Ydb.Sdk/src/Driver.cs +++ b/src/Ydb.Sdk/src/Driver.cs @@ -19,6 +19,8 @@ public class Driver : IDisposable, IAsyncDisposable private readonly ChannelsCache _channels; private bool _disposed; + internal string Database => _config.Database; + public Driver(DriverConfig config, ILoggerFactory? loggerFactory = null) { LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; diff --git a/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs b/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs new file mode 100644 index 00000000..1f439326 --- /dev/null +++ b/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs @@ -0,0 +1,578 @@ +using Ydb.Scheme; +using Ydb.Sdk.Client; +using Ydb.Sdk.Value; +using Ydb.Table; +using Ydb.Table.V1; + +namespace Ydb.Sdk.Services.Table; + +public enum FeatureFlagStatus +{ + Unspecified, + Enabled, + Disabled +} + +public enum IndexType +{ + None, + GlobalIndex, + GlobalAsyncIndex +} + +internal static class TableEnumConverter +{ + internal static FeatureFlagStatus FromProto(this FeatureFlag.Types.Status proto) + { + return proto switch + { + FeatureFlag.Types.Status.Unspecified => FeatureFlagStatus.Unspecified, + FeatureFlag.Types.Status.Enabled => FeatureFlagStatus.Enabled, + FeatureFlag.Types.Status.Disabled => FeatureFlagStatus.Disabled, + _ => throw new ArgumentOutOfRangeException() + }; + } + + internal static FeatureFlag.Types.Status GetProto(this FeatureFlagStatus status) + { + return status switch + { + FeatureFlagStatus.Unspecified => FeatureFlag.Types.Status.Unspecified, + FeatureFlagStatus.Enabled => FeatureFlag.Types.Status.Enabled, + FeatureFlagStatus.Disabled => FeatureFlag.Types.Status.Disabled, + _ => throw new ArgumentOutOfRangeException(nameof(status), status, null) + }; + } +} + +public class PartitionStats +{ + public ulong RowsEstimate { get; } + public ulong StoreSize { get; } + + internal PartitionStats(Ydb.Table.PartitionStats proto) + { + RowsEstimate = proto.RowsEstimate; + StoreSize = proto.StoreSize; + } +} + +public class TableStats +{ + public IReadOnlyList? PartitionStats { get; } + public ulong RowsEstimate { get; } + public ulong StoreSize { get; } + public ulong Partitions { get; } + public DateTime CreationTime { get; } + public DateTime ModificationTime { get; } + + internal TableStats(Ydb.Table.TableStats? proto) + { + if (proto == null) + { + return; + } + + PartitionStats = proto.PartitionStats.Select(partitionStats => new PartitionStats(partitionStats)).ToList(); + RowsEstimate = proto.RowsEstimate; + StoreSize = proto.StoreSize; + Partitions = proto.Partitions; + CreationTime = proto.CreationTime.ToDateTime(); + ModificationTime = proto.ModificationTime.ToDateTime(); + } +} + +public class DateTypeColumnModeSettings +{ + public string ColumnName { get; } + public uint ExpireAfterSeconds { get; } + + public DateTypeColumnModeSettings(uint expireAfterSeconds, string columnName) + { + ExpireAfterSeconds = expireAfterSeconds; + ColumnName = columnName; + } + + public DateTypeColumnModeSettings(Ydb.Table.DateTypeColumnModeSettings proto) + { + ColumnName = proto.ColumnName; + ExpireAfterSeconds = proto.ExpireAfterSeconds; + } + + public Ydb.Table.DateTypeColumnModeSettings GetProto() + { + return new Ydb.Table.DateTypeColumnModeSettings + { + ColumnName = ColumnName, + ExpireAfterSeconds = ExpireAfterSeconds + }; + } +} + +public class ValueSinceUnixEpochModeSettings +{ + public enum Unit + { + Unspecified = 0, + Seconds = 1, + Milliseconds = 2, + Microseconds = 3, + Nanoseconds = 4 + } + + public string ColumnName { get; } + public Unit ColumnUnit { get; } + public uint ExpireAfterSeconds { get; } + + public ValueSinceUnixEpochModeSettings(string columnName, Unit columnUnit, uint expireAfterSeconds) + { + ColumnName = columnName; + ColumnUnit = columnUnit; + ExpireAfterSeconds = expireAfterSeconds; + } + + public ValueSinceUnixEpochModeSettings(Ydb.Table.ValueSinceUnixEpochModeSettings proto) + { + ColumnName = proto.ColumnName; + ColumnUnit = proto.ColumnUnit switch + { + Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Unspecified => Unit.Unspecified, + Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Seconds => Unit.Seconds, + Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Milliseconds => Unit.Milliseconds, + Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Microseconds => Unit.Microseconds, + Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Nanoseconds => Unit.Nanoseconds, + _ => throw new ArgumentOutOfRangeException() + }; + ExpireAfterSeconds = proto.ExpireAfterSeconds; + } + + public Ydb.Table.ValueSinceUnixEpochModeSettings GetProto() + { + return new Ydb.Table.ValueSinceUnixEpochModeSettings + { + ColumnName = ColumnName, + ColumnUnit = GetProtoUnit(ColumnUnit), + ExpireAfterSeconds = ExpireAfterSeconds + }; + } + + private static Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit GetProtoUnit(Unit unit) + { + return unit switch + { + Unit.Unspecified => Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Unspecified, + Unit.Seconds => Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Seconds, + Unit.Milliseconds => Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Milliseconds, + Unit.Microseconds => Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Microseconds, + Unit.Nanoseconds => Ydb.Table.ValueSinceUnixEpochModeSettings.Types.Unit.Nanoseconds, + _ => throw new ArgumentOutOfRangeException(nameof(unit), unit, null) + }; + } +} + +public class TtlSettingsMode +{ + public enum ModeType + { + None, + DateTypeColumn, + ValueSinceUnixEpoch + } + + public ModeType Type { get; } + public DateTypeColumnModeSettings? DateTypeColumnModeSettings { get; } + public ValueSinceUnixEpochModeSettings? ValueSinceUnixEpochModeSettings { get; } + + public TtlSettingsMode(DateTypeColumnModeSettings settings) + { + Type = ModeType.DateTypeColumn; + DateTypeColumnModeSettings = settings; + } + + public TtlSettingsMode(ValueSinceUnixEpochModeSettings settings) + { + Type = ModeType.DateTypeColumn; + ValueSinceUnixEpochModeSettings = settings; + } +} + +public class TtlSettings +{ + public TtlSettingsMode Mode { get; } + public uint RunIntervalSeconds { get; } + + private TtlSettings(TtlSettingsMode mode, uint runIntervalSeconds) + { + Mode = mode; + RunIntervalSeconds = runIntervalSeconds; + } + + public static TtlSettings? FromProto(Ydb.Table.TtlSettings? proto) + { + if (proto is null) + return null; + return proto.ModeCase switch + { + Ydb.Table.TtlSettings.ModeOneofCase.DateTypeColumn => new TtlSettings( + new TtlSettingsMode(new DateTypeColumnModeSettings(proto.DateTypeColumn)), + proto.RunIntervalSeconds), + Ydb.Table.TtlSettings.ModeOneofCase.ValueSinceUnixEpoch => new TtlSettings( + new TtlSettingsMode(new ValueSinceUnixEpochModeSettings(proto.ValueSinceUnixEpoch)), + proto.RunIntervalSeconds), + Ydb.Table.TtlSettings.ModeOneofCase.None => null, + _ => throw new ArgumentOutOfRangeException() + }; + } + + public Ydb.Table.TtlSettings GetProto() + { + var proto = new Ydb.Table.TtlSettings + { + RunIntervalSeconds = RunIntervalSeconds + }; + switch (Mode.Type) + { + case TtlSettingsMode.ModeType.DateTypeColumn: + proto.DateTypeColumn = Mode.DateTypeColumnModeSettings?.GetProto(); + break; + case TtlSettingsMode.ModeType.ValueSinceUnixEpoch: + proto.ValueSinceUnixEpoch = Mode.ValueSinceUnixEpochModeSettings?.GetProto(); + break; + case TtlSettingsMode.ModeType.None: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return proto; + } +} + +public class TableIndexDescription +{ + public enum TableIndexDescriptionStatus + { + Unspecified, + Ready, + Building + } + + public string Name { get; } + public IReadOnlyList IndexColumns { get; } + public TableIndexDescriptionStatus Status { get; } + public IReadOnlyList DataColumns { get; } + public ulong SizeBytes { get; } + + public IndexType IndexType { get; } + + internal TableIndexDescription(Ydb.Table.TableIndexDescription proto) + { + Name = proto.Name; + IndexColumns = proto.IndexColumns; + Status = proto.Status switch + { + Ydb.Table.TableIndexDescription.Types.Status.Unspecified => TableIndexDescriptionStatus.Unspecified, + Ydb.Table.TableIndexDescription.Types.Status.Ready => TableIndexDescriptionStatus.Ready, + Ydb.Table.TableIndexDescription.Types.Status.Building => TableIndexDescriptionStatus.Building, + _ => throw new ArgumentOutOfRangeException() + }; + DataColumns = proto.DataColumns; + SizeBytes = proto.SizeBytes; + + IndexType = proto.TypeCase switch + { + Ydb.Table.TableIndexDescription.TypeOneofCase.GlobalIndex => IndexType.GlobalIndex, + Ydb.Table.TableIndexDescription.TypeOneofCase.GlobalAsyncIndex => IndexType.GlobalAsyncIndex, + Ydb.Table.TableIndexDescription.TypeOneofCase.None => IndexType.None, + _ => throw new ArgumentOutOfRangeException() + }; + } +} + +public class StoragePool +{ + public string? Media { get; } + + public StoragePool(string media) + { + Media = media; + } + + public StoragePool(Ydb.Table.StoragePool? proto) + { + Media = proto?.Media; + } + + public Ydb.Table.StoragePool GetProto() + { + return new Ydb.Table.StoragePool { Media = Media }; + } +} + +public class StorageSettings +{ + public StoragePool TabletCommitLog0 { get; } + public StoragePool TabletCommitLog1 { get; } + public StoragePool External { get; } + public FeatureFlagStatus StoreExternalBlobs { get; } + + public StorageSettings(StoragePool tabletCommitLog0, StoragePool tabletCommitLog1, StoragePool external, + FeatureFlagStatus storeExternalBlobs) + { + TabletCommitLog0 = tabletCommitLog0; + TabletCommitLog1 = tabletCommitLog1; + External = external; + StoreExternalBlobs = storeExternalBlobs; + } + + public StorageSettings(Ydb.Table.StorageSettings proto) + { + TabletCommitLog0 = new StoragePool(proto.TabletCommitLog0); + TabletCommitLog1 = new StoragePool(proto.TabletCommitLog1); + External = new StoragePool(proto.External); + StoreExternalBlobs = proto.StoreExternalBlobs.FromProto(); + } + + public Ydb.Table.StorageSettings GetProto() + { + return new Ydb.Table.StorageSettings + { + TabletCommitLog0 = TabletCommitLog0.GetProto(), + TabletCommitLog1 = TabletCommitLog1.GetProto(), + External = External.GetProto(), + StoreExternalBlobs = StoreExternalBlobs.GetProto() + }; + } +} + +public class PartitioningSettings +{ + public List PartitionBy { get; } + public FeatureFlagStatus PartitioningBySize { get; } + public ulong PartitionSizeMb { get; } + public FeatureFlagStatus PartitioningByLoad { get; } + public ulong MinPartitionsCount { get; } + public ulong MaxPartitionsCount { get; } + + public PartitioningSettings(List partitionBy, FeatureFlagStatus partitioningBySize, ulong partitionSizeMb, + FeatureFlagStatus partitioningByLoad, ulong minPartitionsCount, ulong maxPartitionsCount) + { + PartitionBy = partitionBy; + PartitioningBySize = partitioningBySize; + PartitionSizeMb = partitionSizeMb; + PartitioningByLoad = partitioningByLoad; + MinPartitionsCount = minPartitionsCount; + MaxPartitionsCount = maxPartitionsCount; + } + + public PartitioningSettings(Ydb.Table.PartitioningSettings proto) + { + PartitionBy = proto.PartitionBy.ToList(); + PartitioningBySize = proto.PartitioningBySize.FromProto(); + PartitionSizeMb = proto.PartitionSizeMb; + PartitioningByLoad = proto.PartitioningByLoad.FromProto(); + MinPartitionsCount = proto.MinPartitionsCount; + MaxPartitionsCount = proto.MaxPartitionsCount; + } + + public Ydb.Table.PartitioningSettings GetProto() + { + return new Ydb.Table.PartitioningSettings + { + PartitionBy = { PartitionBy }, + PartitioningBySize = PartitioningBySize.GetProto(), + PartitionSizeMb = PartitionSizeMb, + PartitioningByLoad = PartitioningByLoad.GetProto(), + MinPartitionsCount = MinPartitionsCount, + MaxPartitionsCount = MaxPartitionsCount + }; + } +} + +public class ReadReplicasSettings +{ + public enum SettingsType + { + None, + PerAzReadReplicasCount, + AnyAzReadReplicasCount + } + + public SettingsType Type { get; } + public ulong Settings { get; } + + + public ReadReplicasSettings(SettingsType type, ulong settings) + { + Type = type; + Settings = settings; + } + + public ReadReplicasSettings(Ydb.Table.ReadReplicasSettings? proto) + { + if (proto is null) + { + return; + } + + switch (proto.SettingsCase) + { + case Ydb.Table.ReadReplicasSettings.SettingsOneofCase.None: + Type = SettingsType.None; + Settings = default; + break; + case Ydb.Table.ReadReplicasSettings.SettingsOneofCase.PerAzReadReplicasCount: + Type = SettingsType.PerAzReadReplicasCount; + Settings = proto.PerAzReadReplicasCount; + break; + case Ydb.Table.ReadReplicasSettings.SettingsOneofCase.AnyAzReadReplicasCount: + Type = SettingsType.AnyAzReadReplicasCount; + Settings = proto.AnyAzReadReplicasCount; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public Ydb.Table.ReadReplicasSettings GetProto() + { + return Type switch + { + SettingsType.None => new Ydb.Table.ReadReplicasSettings(), + SettingsType.PerAzReadReplicasCount => new Ydb.Table.ReadReplicasSettings + { PerAzReadReplicasCount = Settings }, + SettingsType.AnyAzReadReplicasCount => new Ydb.Table.ReadReplicasSettings + { AnyAzReadReplicasCount = Settings }, + _ => throw new ArgumentOutOfRangeException() + }; + } +} + +public class DescribeTableSettings : OperationRequestSettings +{ + public bool IncludeShardKeyBounds { get; private set; } + public bool IncludeTableStats { get; private set; } + public bool IncludePartitionStats { get; private set; } + + public DescribeTableSettings WithShardKeyBounds(bool include = true) + { + IncludeShardKeyBounds = include; + return this; + } + + public DescribeTableSettings WithTableStats(bool include = true) + { + IncludeTableStats = include; + return this; + } + + public DescribeTableSettings WithPartitionStats(bool include = true) + { + IncludePartitionStats = include; + return this; + } +} + +public class DescribeTableResponse : ResponseWithResultBase +{ + internal DescribeTableResponse(Status status, ResultData? result = null) : base(status, result) + { + } + + public class ResultData + { + public Entry Self { get; } + public IReadOnlyList Columns { get; } + public IReadOnlyList PrimaryKey { get; } + public IReadOnlyList ShardKeyBounds { get; } + public IReadOnlyList Indexes { get; } + public TableStats TableStats { get; } + public TtlSettings? TtlSettings { get; } + public StorageSettings StorageSettings { get; } + public IReadOnlyList ColumnFamilies { get; } + public IReadOnlyDictionary Attributes { get; } + public PartitioningSettings PartitioningSettings { get; } + public FeatureFlagStatus KeyBloomFilter { get; } + public ReadReplicasSettings ReadReplicasSettings { get; } + + public ResultData(Entry self, IReadOnlyList columns, IReadOnlyList primaryKey, + IReadOnlyList shardKeyBounds, IReadOnlyList indexes, + TableStats tableStats, TtlSettings? ttlSettings, StorageSettings storageSettings, + IReadOnlyList columnFamilies, IReadOnlyDictionary attributes, + PartitioningSettings partitioningSettings, FeatureFlagStatus keyBloomFilter, + ReadReplicasSettings readReplicasSettings) + { + Self = self; + Columns = columns; + PrimaryKey = primaryKey; + ShardKeyBounds = shardKeyBounds; + Indexes = indexes; + TableStats = tableStats; + TtlSettings = ttlSettings; + StorageSettings = storageSettings; + ColumnFamilies = columnFamilies; + Attributes = attributes; + PartitioningSettings = partitioningSettings; + KeyBloomFilter = keyBloomFilter; + ReadReplicasSettings = readReplicasSettings; + } + + internal static ResultData FromProto(DescribeTableResult resultProto) + { + return new ResultData( + self: resultProto.Self, + columns: resultProto.Columns.Select(proto => new ColumnMeta(proto)).ToList(), + primaryKey: resultProto.PrimaryKey.ToList(), + shardKeyBounds: resultProto.ShardKeyBounds.Select(proto => new YdbValue(proto.Type, proto.Value)) + .ToList(), + indexes: resultProto.Indexes.Select(proto => new TableIndexDescription(proto)).ToList(), + tableStats: new TableStats(resultProto.TableStats), + ttlSettings: TtlSettings.FromProto(resultProto.TtlSettings), + storageSettings: new StorageSettings(resultProto.StorageSettings), + columnFamilies: resultProto.ColumnFamilies.Select(proto => new ColumnFamily(proto)).ToList(), + attributes: new Dictionary(resultProto.Attributes), + partitioningSettings: new PartitioningSettings(resultProto.PartitioningSettings), + keyBloomFilter: resultProto.KeyBloomFilter.FromProto(), + readReplicasSettings: new ReadReplicasSettings(resultProto.ReadReplicasSettings) + ); + } + } +} + +public partial class TableClient +{ + public async Task DescribeTable(string tablePath, DescribeTableSettings? settings = null) + { + settings ??= new DescribeTableSettings(); + var request = new DescribeTableRequest + { + OperationParams = MakeOperationParams(settings), + Path = MakeTablePath(tablePath), + IncludeShardKeyBounds = settings.IncludeShardKeyBounds, + IncludeTableStats = settings.IncludeTableStats, + IncludePartitionStats = settings.IncludePartitionStats + }; + + try + { + var response = await Driver.UnaryCall( + method: TableService.DescribeTableMethod, + request: request, + settings: settings); + + var status = UnpackOperation(response.Data.Operation, out DescribeTableResult? resultProto); + DescribeTableResponse.ResultData? result = null; + + if (status.IsSuccess && resultProto is not null) + { + result = DescribeTableResponse.ResultData.FromProto(resultProto); + } + + return new DescribeTableResponse(status, result); + } + catch (Driver.TransportException e) + { + return new DescribeTableResponse(e.Status); + } + } +} \ No newline at end of file diff --git a/src/Ydb.Sdk/src/Services/Table/TableClient.cs b/src/Ydb.Sdk/src/Services/Table/TableClient.cs index 3ea510ad..b3652791 100644 --- a/src/Ydb.Sdk/src/Services/Table/TableClient.cs +++ b/src/Ydb.Sdk/src/Services/Table/TableClient.cs @@ -53,4 +53,9 @@ private void Dispose(bool disposing) _disposed = true; } + + internal string MakeTablePath(string path) + { + return path.StartsWith('/') ? path : $"{Driver.Database}/{path}"; + } } \ No newline at end of file diff --git a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs new file mode 100644 index 00000000..906bb3de --- /dev/null +++ b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; +using Ydb.Sdk.Services.Table; +using Ydb.Sdk.Tests.Auth; + +namespace Ydb.Sdk.Tests.Table; + +[Trait("Category", "Integration")] +public class TestDescribeTable +{ + private readonly ILoggerFactory? _loggerFactory; + private readonly ILogger _logger; + + private readonly DriverConfig _driverConfig = new( + endpoint: "grpc://localhost:2136", + database: "/local" + ); + + public TestDescribeTable() + { + _loggerFactory = Utils.GetLoggerFactory() ?? NullLoggerFactory.Instance; + _logger = _loggerFactory.CreateLogger(); + } + + + [Fact] + public async Task DescribeNotExisting() + { + _logger.LogInformation("Initializing driver and client"); + await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); + using var tableClient = new TableClient(driver); + _logger.LogInformation("Driver and client was initialized"); + + var response = await tableClient.DescribeTable("/local/not_exists"); + Assert.Equal(StatusCode.SchemeError, response.Status.StatusCode); + } + + + [Fact] + public async Task CreateAndDescribe() + { + _logger.LogInformation("Initializing driver and client"); + await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); + using var tableClient = new TableClient(driver); + _logger.LogInformation("Driver and client was initialized"); + + var tablePath = $"t{Guid.NewGuid():n}"; + const string columnName = "myColumnName"; + + await Utils.CreateSimpleTable(tableClient, tablePath, columnName); + + var describeResponse = await tableClient.DescribeTable(tablePath); + describeResponse.Status.EnsureSuccess(); + Assert.True(describeResponse.Result.PrimaryKey.SequenceEqual(new[] { columnName })); + + await Utils.DropTable(tableClient, tablePath); + } +} \ No newline at end of file diff --git a/src/Ydb.Sdk/tests/Utils.cs b/src/Ydb.Sdk/tests/Utils.cs index dacd9438..bf70e150 100644 --- a/src/Ydb.Sdk/tests/Utils.cs +++ b/src/Ydb.Sdk/tests/Utils.cs @@ -53,4 +53,20 @@ internal static ServiceProvider GetServiceProvider() var serviceProvider = GetServiceProvider(); return serviceProvider.GetService(); } + + + internal static async Task CreateSimpleTable( + TableClient tableClient, string tableName, string columnName = "key") + { + return await ExecuteSchemeQuery( + tableClient, + query: $"CREATE TABLE {tableName} ({columnName} Uint64, PRIMARY KEY ({columnName}))"); + } + + internal static async Task DropTable(TableClient tableClient, string tableName) + { + return await ExecuteSchemeQuery( + tableClient, + query: $"DROP TABLE {tableName}"); + } } \ No newline at end of file From 7d390cd2299eb27360737cf32e9b7b34ed5a4048 Mon Sep 17 00:00:00 2001 From: XmasApple Date: Thu, 12 Oct 2023 16:25:35 +0300 Subject: [PATCH 2/5] CopyTableMethod --- src/Ydb.Sdk/src/Services/Table/CopyTable.cs | 107 +++++++++++++++++++ src/Ydb.Sdk/tests/Table/TestCopyTable.cs | 82 ++++++++++++++ src/Ydb.Sdk/tests/Table/TestDescribeTable.cs | 8 -- 3 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 src/Ydb.Sdk/src/Services/Table/CopyTable.cs create mode 100644 src/Ydb.Sdk/tests/Table/TestCopyTable.cs diff --git a/src/Ydb.Sdk/src/Services/Table/CopyTable.cs b/src/Ydb.Sdk/src/Services/Table/CopyTable.cs new file mode 100644 index 00000000..a1a41167 --- /dev/null +++ b/src/Ydb.Sdk/src/Services/Table/CopyTable.cs @@ -0,0 +1,107 @@ +using Ydb.Sdk.Client; +using Ydb.Table; +using Ydb.Table.V1; + +namespace Ydb.Sdk.Services.Table; + +public class CopyTableItem +{ + public string SourcePath { get; } + public string DestinationPath { get; } + public bool OmitIndexes { get; } + + public CopyTableItem(string sourcePath, string destinationPath, bool omitIndexes) + { + SourcePath = sourcePath; + DestinationPath = destinationPath; + OmitIndexes = omitIndexes; + } + + public Ydb.Table.CopyTableItem GetProto(TableClient tableClient) + { + return new Ydb.Table.CopyTableItem + { + SourcePath = tableClient.MakeTablePath(SourcePath), + DestinationPath = tableClient.MakeTablePath(DestinationPath), + OmitIndexes = OmitIndexes + }; + } +} + +public class CopyTableSettings : OperationRequestSettings +{ +} + +public class CopyTablesSettings : OperationRequestSettings +{ +} + +public class CopyTableResponse : ResponseBase +{ + internal CopyTableResponse(Status status) : base(status) + { + } +} + +public class CopyTablesResponse : ResponseBase +{ + internal CopyTablesResponse(Status status) : base(status) + { + } +} + +public partial class TableClient +{ + public async Task CopyTable(string sourcePath, string destinationPath, + CopyTableSettings? settings = null) + { + settings ??= new CopyTableSettings(); + var request = new CopyTableRequest + { + OperationParams = MakeOperationParams(settings), + SourcePath = MakeTablePath(sourcePath), + DestinationPath = MakeTablePath(destinationPath) + }; + + try + { + var response = await Driver.UnaryCall( + method: TableService.CopyTableMethod, + request: request, + settings: settings); + + var status = UnpackOperation(response.Data.Operation); + return new CopyTableResponse(status); + } + catch (Driver.TransportException e) + { + return new CopyTableResponse(e.Status); + } + } + + public async Task CopyTables(List tableItems, + CopyTablesSettings? settings = null) + { + settings ??= new CopyTablesSettings(); + var request = new CopyTablesRequest + { + OperationParams = MakeOperationParams(settings) + }; + request.Tables.AddRange(tableItems.Select(item => item.GetProto(this))); + + try + { + var response = await Driver.UnaryCall( + method: TableService.CopyTablesMethod, + request: request, + settings: settings); + + var status = UnpackOperation(response.Data.Operation); + return new CopyTablesResponse(status); + } + catch (Driver.TransportException e) + { + return new CopyTablesResponse(e.Status); + } + } +} \ No newline at end of file diff --git a/src/Ydb.Sdk/tests/Table/TestCopyTable.cs b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs new file mode 100644 index 00000000..03ac5164 --- /dev/null +++ b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs @@ -0,0 +1,82 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; +using Ydb.Sdk.Services.Table; + +namespace Ydb.Sdk.Tests.Table; + +[Trait("Category", "Integration")] +public class TestCopyTable +{ + private readonly ILoggerFactory? _loggerFactory; + + private readonly DriverConfig _driverConfig = new( + endpoint: "grpc://localhost:2136", + database: "/local" + ); + + public TestCopyTable() + { + _loggerFactory = Utils.GetLoggerFactory() ?? NullLoggerFactory.Instance; + } + + [Fact] + public async Task CopyNotExisting() + { + await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); + using var tableClient = new TableClient(driver); + + const string source = "notExists"; + const string dest = "new"; + + var response = await tableClient.CopyTable(source, dest); + Assert.Equal(StatusCode.SchemeError, response.Status.StatusCode); + } + + [Fact] + public async Task CopyTable() + { + await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); + using var tableClient = new TableClient(driver); + + var source = $"t{Guid.NewGuid():n}"; + var dest = $"t{Guid.NewGuid():n}"; + await Utils.CreateSimpleTable(tableClient, source); + + var response = await tableClient.CopyTable(source, dest); + response.EnsureSuccess(); + + await Utils.DropTable(tableClient, source); + await Utils.DropTable(tableClient, dest); + } + + [Fact] + public async Task CopyTables() + { + await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); + using var tableClient = new TableClient(driver); + + var pairs = new List<(string, string)>(); + for (var i = 0; i < 5; i++) + { + pairs.Add(($"t{Guid.NewGuid():n}", $"t{Guid.NewGuid():n}")); + } + + var items = new List(); + foreach (var (source, dest) in pairs) + { + await Utils.CreateSimpleTable(tableClient, source); + items.Add(new CopyTableItem(source, dest, false)); + } + + var response = await tableClient.CopyTables(items); + response.EnsureSuccess(); + + + foreach (var (source, dest) in pairs) + { + await Utils.DropTable(tableClient, source); + await Utils.DropTable(tableClient, dest); + } + } +} \ No newline at end of file diff --git a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs index 906bb3de..f5ad5018 100644 --- a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs +++ b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Xunit; using Ydb.Sdk.Services.Table; -using Ydb.Sdk.Tests.Auth; namespace Ydb.Sdk.Tests.Table; @@ -10,8 +9,6 @@ namespace Ydb.Sdk.Tests.Table; public class TestDescribeTable { private readonly ILoggerFactory? _loggerFactory; - private readonly ILogger _logger; - private readonly DriverConfig _driverConfig = new( endpoint: "grpc://localhost:2136", database: "/local" @@ -20,17 +17,14 @@ public class TestDescribeTable public TestDescribeTable() { _loggerFactory = Utils.GetLoggerFactory() ?? NullLoggerFactory.Instance; - _logger = _loggerFactory.CreateLogger(); } [Fact] public async Task DescribeNotExisting() { - _logger.LogInformation("Initializing driver and client"); await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); using var tableClient = new TableClient(driver); - _logger.LogInformation("Driver and client was initialized"); var response = await tableClient.DescribeTable("/local/not_exists"); Assert.Equal(StatusCode.SchemeError, response.Status.StatusCode); @@ -40,10 +34,8 @@ public async Task DescribeNotExisting() [Fact] public async Task CreateAndDescribe() { - _logger.LogInformation("Initializing driver and client"); await using var driver = await Driver.CreateInitialized(_driverConfig, _loggerFactory); using var tableClient = new TableClient(driver); - _logger.LogInformation("Driver and client was initialized"); var tablePath = $"t{Guid.NewGuid():n}"; const string columnName = "myColumnName"; From d56e2115e68de3b4e84fbd3a09cf8e5e766488f1 Mon Sep 17 00:00:00 2001 From: XmasApple Date: Thu, 12 Oct 2023 16:42:52 +0300 Subject: [PATCH 3/5] Fix styles --- src/Ydb.Sdk/tests/Table/TestCopyTable.cs | 2 +- src/Ydb.Sdk/tests/Table/TestDescribeTable.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ydb.Sdk/tests/Table/TestCopyTable.cs b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs index 03ac5164..689481b0 100644 --- a/src/Ydb.Sdk/tests/Table/TestCopyTable.cs +++ b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs @@ -8,7 +8,7 @@ namespace Ydb.Sdk.Tests.Table; [Trait("Category", "Integration")] public class TestCopyTable { - private readonly ILoggerFactory? _loggerFactory; + private readonly ILoggerFactory _loggerFactory; private readonly DriverConfig _driverConfig = new( endpoint: "grpc://localhost:2136", diff --git a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs index f5ad5018..dc788ada 100644 --- a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs +++ b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs @@ -8,7 +8,8 @@ namespace Ydb.Sdk.Tests.Table; [Trait("Category", "Integration")] public class TestDescribeTable { - private readonly ILoggerFactory? _loggerFactory; + private readonly ILoggerFactory _loggerFactory; + private readonly DriverConfig _driverConfig = new( endpoint: "grpc://localhost:2136", database: "/local" From 9c53d037268827609dd56e9f4617c27971b29947 Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Thu, 14 Dec 2023 09:53:41 +0300 Subject: [PATCH 4/5] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f25bbaa..ae407b59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Added MakeTablePath, CopyTable, CopyTables, DescribeTable methods for TableClient + ## v0.1.5 - Fix timeout error on create session - Fix transport error on delete session From 54a5d69d70d0ae74a8a17604b531b8987ed039ee Mon Sep 17 00:00:00 2001 From: Timofey Koolin Date: Thu, 14 Dec 2023 15:41:11 +0300 Subject: [PATCH 5/5] Fix linter issues --- src/Ydb.Sdk/src/Services/Table/CopyTable.cs | 2 +- src/Ydb.Sdk/src/Services/Table/DescribeTable.cs | 2 +- src/Ydb.Sdk/tests/Table/TestCopyTable.cs | 2 +- src/Ydb.Sdk/tests/Table/TestDescribeTable.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ydb.Sdk/src/Services/Table/CopyTable.cs b/src/Ydb.Sdk/src/Services/Table/CopyTable.cs index a1a41167..747450ef 100644 --- a/src/Ydb.Sdk/src/Services/Table/CopyTable.cs +++ b/src/Ydb.Sdk/src/Services/Table/CopyTable.cs @@ -104,4 +104,4 @@ public async Task CopyTables(List tableItems, return new CopyTablesResponse(e.Status); } } -} \ No newline at end of file +} diff --git a/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs b/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs index 1f439326..29453b0f 100644 --- a/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs +++ b/src/Ydb.Sdk/src/Services/Table/DescribeTable.cs @@ -575,4 +575,4 @@ public async Task DescribeTable(string tablePath, Describ return new DescribeTableResponse(e.Status); } } -} \ No newline at end of file +} diff --git a/src/Ydb.Sdk/tests/Table/TestCopyTable.cs b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs index 689481b0..572b41e6 100644 --- a/src/Ydb.Sdk/tests/Table/TestCopyTable.cs +++ b/src/Ydb.Sdk/tests/Table/TestCopyTable.cs @@ -79,4 +79,4 @@ public async Task CopyTables() await Utils.DropTable(tableClient, dest); } } -} \ No newline at end of file +} diff --git a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs index dc788ada..9a6e6a40 100644 --- a/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs +++ b/src/Ydb.Sdk/tests/Table/TestDescribeTable.cs @@ -49,4 +49,4 @@ public async Task CreateAndDescribe() await Utils.DropTable(tableClient, tablePath); } -} \ No newline at end of file +}