Skip to content

Commit f55151e

Browse files
msrathore-dbclaude
andcommitted
refactor(csharp): convert SEA statement metadata to async, fix CTS leak
- Convert all 7 statement metadata methods (GetCatalogs, GetSchemas, GetTables, GetColumns, GetColumnsExtended, GetPrimaryKeys, GetCrossReference) from sync to async with CancellationToken - Convert ExecuteMetadataCommand → ExecuteMetadataCommandAsync - Use TraceActivityAsync + await ExecuteMetadataSqlAsync instead of sync ExecuteMetadataSql wrapper (eliminates nested sync-over-async) - Propagate CancellationToken from ExecuteQueryAsync through the entire metadata chain (matches Thrift pattern) - Fix CancellationTokenSource leak: CreateMetadataTimeoutToken → CreateMetadataTimeoutCts returns disposable CTS; callers use 'using' - Silently ignore readonly statement options (PollTimeMilliseconds, BatchSize, BatchSizeStopCondition, QueryTimeoutSeconds) to avoid NotImplemented exceptions in PowerBI compatibility - Move Show*Command classes to MetadataCommands/ subfolder - Fix test using directive for new MetadataCommands namespace Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0ce7a95 commit f55151e

File tree

12 files changed

+163
-155
lines changed

12 files changed

+163
-155
lines changed

csharp/hiveserver2

csharp/src/ColumnMetadataHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal static class ColumnMetadataHelper
3636
{ "INTEGER", (short)ColumnTypeId.INTEGER },
3737
{ "BIGINT", (short)ColumnTypeId.BIGINT },
3838
{ "FLOAT", (short)ColumnTypeId.FLOAT },
39-
{ "REAL", (short)ColumnTypeId.REAL },
39+
{ "REAL", (short)ColumnTypeId.FLOAT },
4040
{ "DOUBLE", (short)ColumnTypeId.DOUBLE },
4141
{ "DECIMAL", (short)ColumnTypeId.DECIMAL },
4242
{ "NUMERIC", (short)ColumnTypeId.NUMERIC },

csharp/src/Result/DescTableExtendedResult.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,7 @@ internal class ColumnInfo
8585
/// See the list of type names from https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-datatypes
8686
/// </summary>
8787
[JsonIgnore]
88-
public ColumnTypeId DataType
89-
{
90-
get
91-
{
92-
var code = (ColumnTypeId)ColumnMetadataHelper.GetDataTypeCode(Type.Name);
93-
// REAL maps to FLOAT for backward compatibility
94-
return code == ColumnTypeId.REAL ? ColumnTypeId.FLOAT : code;
95-
}
96-
}
88+
public ColumnTypeId DataType => (ColumnTypeId)ColumnMetadataHelper.GetDataTypeCode(Type.Name);
9789

9890
[JsonIgnore]
9991
public bool IsNumber => ColumnMetadataHelper.GetNumPrecRadix(Type.Name) != null;

csharp/src/StatementExecution/MetadataCommandBase.cs renamed to csharp/src/StatementExecution/MetadataCommands/MetadataCommandBase.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
using System.Text;
1818

19-
namespace AdbcDrivers.Databricks.StatementExecution
19+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2020
{
2121
internal abstract class MetadataCommandBase
2222
{
@@ -84,6 +84,11 @@ protected static string ConvertPattern(string? pattern)
8484
}
8585
}
8686

87+
if (escapeNext)
88+
{
89+
result.Append('\\');
90+
}
91+
8792
return result.ToString();
8893
}
8994

csharp/src/StatementExecution/ShowCatalogsCommand.cs renamed to csharp/src/StatementExecution/MetadataCommands/ShowCatalogsCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
using System.Text;
1818

19-
namespace AdbcDrivers.Databricks.StatementExecution
19+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2020
{
2121
internal class ShowCatalogsCommand : MetadataCommandBase
2222
{

csharp/src/StatementExecution/ShowColumnsCommand.cs renamed to csharp/src/StatementExecution/MetadataCommands/ShowColumnsCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
using System.Text;
1818

19-
namespace AdbcDrivers.Databricks.StatementExecution
19+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2020
{
2121
internal class ShowColumnsCommand : MetadataCommandBase
2222
{

csharp/src/StatementExecution/ShowKeysCommand.cs renamed to csharp/src/StatementExecution/MetadataCommands/ShowKeysCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
using System;
1818
using System.Text;
1919

20-
namespace AdbcDrivers.Databricks.StatementExecution
20+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2121
{
2222
internal class ShowKeysCommand : MetadataCommandBase
2323
{

csharp/src/StatementExecution/ShowSchemasCommand.cs renamed to csharp/src/StatementExecution/MetadataCommands/ShowSchemasCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
using System.Text;
1818

19-
namespace AdbcDrivers.Databricks.StatementExecution
19+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2020
{
2121
internal class ShowSchemasCommand : MetadataCommandBase
2222
{

csharp/src/StatementExecution/ShowTablesCommand.cs renamed to csharp/src/StatementExecution/MetadataCommands/ShowTablesCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
using System.Text;
1818

19-
namespace AdbcDrivers.Databricks.StatementExecution
19+
namespace AdbcDrivers.Databricks.StatementExecution.MetadataCommands
2020
{
2121
internal class ShowTablesCommand : MetadataCommandBase
2222
{

csharp/src/StatementExecution/StatementExecutionConnection.cs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using System.Threading.Tasks;
2323
using AdbcDrivers.Databricks.Http;
2424
using AdbcDrivers.HiveServer2.Hive2;
25+
using AdbcDrivers.Databricks.StatementExecution.MetadataCommands;
2526
using AdbcDrivers.HiveServer2.Spark;
2627
using Apache.Arrow;
2728
using Apache.Arrow.Adbc;
@@ -392,9 +393,11 @@ public override IArrowArrayStream GetObjects(GetObjectsDepth depth, string? cata
392393
activity?.SetTag("table_pattern", tableNamePattern ?? "(none)");
393394
activity?.SetTag("column_pattern", columnNamePattern ?? "(none)");
394395

396+
using var cts = CreateMetadataTimeoutCts();
395397
return GetObjectsResultBuilder.BuildGetObjectsResultAsync(
396398
this, depth, catalogPattern, schemaPattern,
397-
tableNamePattern, tableTypes, columnNamePattern).GetAwaiter().GetResult();
399+
tableNamePattern, tableTypes, columnNamePattern,
400+
cts.Token).GetAwaiter().GetResult();
398401
}, nameof(GetObjects));
399402
}
400403

@@ -451,11 +454,11 @@ public override Schema GetTableSchema(string? catalog, string? dbSchema, string
451454
activity?.SetTag("db_schema", dbSchema ?? "(none)");
452455
activity?.SetTag("table_name", tableName);
453456

454-
var cancellationToken = CreateMetadataTimeoutToken();
457+
using var cts = CreateMetadataTimeoutCts();
455458
string sql = new ShowColumnsCommand(
456459
catalog ?? _catalog, dbSchema, tableName).Build();
457460
activity?.SetTag("sql_query", sql);
458-
var batches = ExecuteMetadataSql(sql, cancellationToken);
461+
var batches = ExecuteMetadataSql(sql, cts.Token);
459462

460463
var fields = new List<Field>();
461464
foreach (var batch in batches)
@@ -488,12 +491,10 @@ public override Schema GetTableSchema(string? catalog, string? dbSchema, string
488491

489492
// IGetObjectsDataProvider implementation
490493

491-
async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(string? catalogPattern)
494+
async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(string? catalogPattern, CancellationToken cancellationToken)
492495
{
493-
var cancellationToken = CreateMetadataTimeoutToken();
494496
string sql = new ShowCatalogsCommand(catalogPattern).Build();
495-
var batches = ExecuteMetadataSql(sql, cancellationToken);
496-
await Task.CompletedTask.ConfigureAwait(false);
497+
var batches = await ExecuteMetadataSqlAsync(sql, cancellationToken).ConfigureAwait(false);
497498
var result = new List<string>();
498499
foreach (var batch in batches)
499500
{
@@ -508,12 +509,10 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
508509
return result;
509510
}
510511

511-
async Task<IReadOnlyList<(string catalog, string schema)>> IGetObjectsDataProvider.GetSchemasAsync(string? catalogPattern, string? schemaPattern)
512+
async Task<IReadOnlyList<(string catalog, string schema)>> IGetObjectsDataProvider.GetSchemasAsync(string? catalogPattern, string? schemaPattern, CancellationToken cancellationToken)
512513
{
513-
var cancellationToken = CreateMetadataTimeoutToken();
514514
string sql = new ShowSchemasCommand(catalogPattern, schemaPattern).Build();
515-
var batches = ExecuteMetadataSql(sql, cancellationToken);
516-
await Task.CompletedTask.ConfigureAwait(false);
515+
var batches = await ExecuteMetadataSqlAsync(sql, cancellationToken).ConfigureAwait(false);
517516
var result = new List<(string, string)>();
518517
foreach (var batch in batches)
519518
{
@@ -537,12 +536,10 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
537536
}
538537

539538
async Task<IReadOnlyList<(string catalog, string schema, string table, string tableType)>> IGetObjectsDataProvider.GetTablesAsync(
540-
string? catalogPattern, string? schemaPattern, string? tableNamePattern, IReadOnlyList<string>? tableTypes)
539+
string? catalogPattern, string? schemaPattern, string? tableNamePattern, IReadOnlyList<string>? tableTypes, CancellationToken cancellationToken)
541540
{
542-
var cancellationToken = CreateMetadataTimeoutToken();
543541
string sql = new ShowTablesCommand(catalogPattern, schemaPattern, tableNamePattern).Build();
544-
var batches = ExecuteMetadataSql(sql, cancellationToken);
545-
await Task.CompletedTask.ConfigureAwait(false);
542+
var batches = await ExecuteMetadataSqlAsync(sql, cancellationToken).ConfigureAwait(false);
546543
var result = new List<(string, string, string, string)>();
547544
foreach (var batch in batches)
548545
{
@@ -577,12 +574,11 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
577574

578575
async Task IGetObjectsDataProvider.PopulateColumnInfoAsync(string? catalogPattern, string? schemaPattern,
579576
string? tablePattern, string? columnPattern,
580-
Dictionary<string, Dictionary<string, Dictionary<string, TableInfo>>> catalogMap)
577+
Dictionary<string, Dictionary<string, Dictionary<string, TableInfo>>> catalogMap,
578+
CancellationToken cancellationToken)
581579
{
582-
var cancellationToken = CreateMetadataTimeoutToken();
583580
string sql = new ShowColumnsCommand(catalogPattern, schemaPattern, tablePattern, columnPattern).Build();
584-
var batches = ExecuteMetadataSql(sql, cancellationToken);
585-
await Task.CompletedTask.ConfigureAwait(false);
581+
var batches = await ExecuteMetadataSqlAsync(sql, cancellationToken).ConfigureAwait(false);
586582

587583
var tablePositions = new Dictionary<string, int>();
588584

@@ -659,29 +655,33 @@ async Task IGetObjectsDataProvider.PopulateColumnInfoAsync(string? catalogPatter
659655
}
660656
}
661657

662-
internal List<RecordBatch> ExecuteMetadataSql(string sql, CancellationToken cancellationToken = default)
658+
internal async Task<List<RecordBatch>> ExecuteMetadataSqlAsync(string sql, CancellationToken cancellationToken = default)
663659
{
664660
var batches = new List<RecordBatch>();
665661
using var stmt = (StatementExecutionStatement)CreateStatement();
666662
stmt.SqlQuery = sql;
667663
stmt.IsMetadataExecution = true;
668-
var result = stmt.ExecuteQuery();
664+
var result = await stmt.ExecuteQueryAsync(cancellationToken).ConfigureAwait(false);
669665
using var stream = result.Stream;
670666
if (stream == null) return batches;
671667
while (true)
672668
{
673669
cancellationToken.ThrowIfCancellationRequested();
674-
var batch = stream.ReadNextRecordBatchAsync(cancellationToken).AsTask().GetAwaiter().GetResult();
670+
var batch = await stream.ReadNextRecordBatchAsync(cancellationToken).ConfigureAwait(false);
675671
if (batch == null) break;
676672
batches.Add(batch);
677673
}
678674
return batches;
679675
}
680676

681-
internal CancellationToken CreateMetadataTimeoutToken()
677+
internal List<RecordBatch> ExecuteMetadataSql(string sql, CancellationToken cancellationToken = default)
678+
{
679+
return ExecuteMetadataSqlAsync(sql, cancellationToken).GetAwaiter().GetResult();
680+
}
681+
682+
internal CancellationTokenSource CreateMetadataTimeoutCts()
682683
{
683-
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(_waitTimeoutSeconds));
684-
return cts.Token;
684+
return new CancellationTokenSource(TimeSpan.FromSeconds(_waitTimeoutSeconds));
685685
}
686686

687687
/// <summary>

0 commit comments

Comments
 (0)