This is an automated email from the ASF dual-hosted git repository.
curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 191402012 feat(csharp/src/Drivers/Apache): Add escape underscore
parameter to metadata command (#2920)
191402012 is described below
commit 191402012ff7da41a614cad536104e1b9ee27c79
Author: eric-wang-1990 <[email protected]>
AuthorDate: Thu Jun 5 12:59:50 2025 -0700
feat(csharp/src/Drivers/Apache): Add escape underscore parameter to
metadata command (#2920)
# Add support for escaping underscores in metadata queries
## Why
For current Power BI ODBC connection we are escaping all the underscore.
I've thinking about change in the connector side to always pass in the
escaped name, but it is not feasible since later we will introduce sql
text based metadata query like DESCRIBE TABLE `xxx` as json, where we
would expect xxx not be escaped since it will be treated as text.
Thus the best way is to introduce a new parameter in the metadata api to
specify whether the client want to escape the underscore, then on the
driver side based on different calling method(Thrift API or sql text) we
can perform differently.
## Description
This PR adds support for escaping underscores in metadata query
parameters through a new parameter
`adbc.get_metadata.escape_underscore`. When enabled, underscores in
catalog, schema, table, and column names will be treated as literal
characters rather than SQL wildcards.
## Changes
- Added new parameter `EscapeUnderscore` to control underscore escaping
behavior
- Added `EscapeUnderscoreInName` helper method to handle underscore
escaping
- Updated all metadata query methods to use the escaping functionality:
- GetCatalogsAsync
- GetSchemasAsync
- GetTablesAsync
- GetColumnsAsync
- GetPrimaryKeysAsync
- GetCrossReferenceAsync
- GetCrossReferenceAsForeignTableAsync
## Testing
- Added test case in `CanGetColumnsExtended` to verify the escaping
functionality
- Verified that null values are handled correctly
- Verified that escaping only occurs when the flag is enabled
## Usage
To enable underscore escaping, set the parameter:
```csharp
statement.SetOption(ApacheParameters.EscapeUnderscore, "true");
```
## Impact
This change allows users to query metadata for objects that contain
underscores in their names without the underscore being interpreted as a
SQL wildcard character.
---
csharp/src/Drivers/Apache/ApacheParameters.cs | 5 ++
.../Drivers/Apache/Hive2/HiveServer2Statement.cs | 56 ++++++++++++++--------
csharp/test/Drivers/Databricks/StatementTests.cs | 1 +
3 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/csharp/src/Drivers/Apache/ApacheParameters.cs
b/csharp/src/Drivers/Apache/ApacheParameters.cs
index 4629df98f..c63442772 100644
--- a/csharp/src/Drivers/Apache/ApacheParameters.cs
+++ b/csharp/src/Drivers/Apache/ApacheParameters.cs
@@ -75,5 +75,10 @@ namespace Apache.Arrow.Adbc.Drivers.Apache
/// The table name (or pattern) of the foreign (child) table for
GetCrossReference metadata command query.
/// </summary>
public const string ForeignTableName =
"adbc.get_metadata.foreign_target_table";
+
+ /// <summary>
+ /// Whether to escape underscore to treat as literal rather than
wildcard. Default to false.
+ /// </summary>
+ public const string EscapeUnderscore =
"adbc.get_metadata.escape_underscore";
}
}
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
b/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
index e4d272d0e..3203502e5 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
@@ -254,6 +254,12 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
case ApacheParameters.ForeignTableName:
this.ForeignTableName = value;
break;
+ case ApacheParameters.EscapeUnderscore:
+ if (ApacheUtility.BooleanIsValid(key, value, out bool
escapeUnderscore))
+ {
+ this.EscapeUnderscore = escapeUnderscore;
+ }
+ break;
default:
throw AdbcException.NotImplemented($"Option '{key}' is not
implemented.");
}
@@ -311,6 +317,7 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
protected internal string? ForeignCatalogName { get; set; }
protected internal string? ForeignSchemaName { get; set; }
protected internal string? ForeignTableName { get; set; }
+ protected internal bool EscapeUnderscore { get; set; } = false;
protected internal TSparkDirectResults? _directResults { get; set; }
public HiveServer2Connection Connection { get; private set; }
@@ -328,6 +335,13 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
? batchSize
: throw new ArgumentOutOfRangeException(key, value, $"The value
'{value}' for option '{key}' is invalid. Must be a numeric value greater than
zero.");
+ private string? EscapeUnderscoreInName(string? name)
+ {
+ if (!EscapeUnderscore || name == null)
+ return name;
+ return name.Replace("_", "\\_");
+ }
+
public override void Dispose()
{
if (OperationHandle != null)
@@ -382,9 +396,9 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
null,
null,
null,
- CatalogName,
- SchemaName,
- TableName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
+ EscapeUnderscoreInName(TableName),
cancellationToken);
OperationHandle = resp.OperationHandle;
@@ -394,12 +408,12 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
protected virtual async Task<QueryResult>
GetCrossReferenceAsync(CancellationToken cancellationToken = default)
{
TGetCrossReferenceResp resp = await
Connection.GetCrossReferenceAsync(
- CatalogName,
- SchemaName,
- TableName,
- ForeignCatalogName,
- ForeignSchemaName,
- ForeignTableName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
+ EscapeUnderscoreInName(TableName),
+ EscapeUnderscoreInName(ForeignCatalogName),
+ EscapeUnderscoreInName(ForeignSchemaName),
+ EscapeUnderscoreInName(ForeignTableName),
cancellationToken);
OperationHandle = resp.OperationHandle;
@@ -409,9 +423,9 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
protected virtual async Task<QueryResult>
GetPrimaryKeysAsync(CancellationToken cancellationToken = default)
{
TGetPrimaryKeysResp resp = await Connection.GetPrimaryKeysAsync(
- CatalogName,
- SchemaName,
- TableName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
+ EscapeUnderscoreInName(TableName),
cancellationToken);
OperationHandle = resp.OperationHandle;
@@ -429,8 +443,8 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
protected virtual async Task<QueryResult>
GetSchemasAsync(CancellationToken cancellationToken = default)
{
TGetSchemasResp resp = await Connection.GetSchemasAsync(
- CatalogName,
- SchemaName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
cancellationToken);
OperationHandle = resp.OperationHandle;
@@ -441,9 +455,9 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
{
List<string>? tableTypesList =
this.TableTypes?.Split(',').ToList();
TGetTablesResp resp = await Connection.GetTablesAsync(
- CatalogName,
- SchemaName,
- TableName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
+ EscapeUnderscoreInName(TableName),
tableTypesList,
cancellationToken);
OperationHandle = resp.OperationHandle;
@@ -454,10 +468,10 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
protected virtual async Task<QueryResult>
GetColumnsAsync(CancellationToken cancellationToken = default)
{
TGetColumnsResp resp = await Connection.GetColumnsAsync(
- CatalogName,
- SchemaName,
- TableName,
- ColumnName,
+ EscapeUnderscoreInName(CatalogName),
+ EscapeUnderscoreInName(SchemaName),
+ EscapeUnderscoreInName(TableName),
+ EscapeUnderscoreInName(ColumnName),
cancellationToken);
OperationHandle = resp.OperationHandle;
diff --git a/csharp/test/Drivers/Databricks/StatementTests.cs
b/csharp/test/Drivers/Databricks/StatementTests.cs
index cd5254a60..d6ac50e1d 100644
--- a/csharp/test/Drivers/Databricks/StatementTests.cs
+++ b/csharp/test/Drivers/Databricks/StatementTests.cs
@@ -343,6 +343,7 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Databricks
statement.SetOption(ApacheParameters.CatalogName,
TestConfiguration.Metadata.Catalog);
statement.SetOption(ApacheParameters.SchemaName,
TestConfiguration.Metadata.Schema);
statement.SetOption(ApacheParameters.TableName,
TestConfiguration.Metadata.Table);
+ statement.SetOption(ApacheParameters.EscapeUnderscore, "true");
statement.SqlQuery = "GetColumnsExtended";
QueryResult queryResult = await statement.ExecuteQueryAsync();