Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions csharp/src/Hive2/HiveServer2Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ protected internal string GetProductVersion()
return fileVersionInfo.ProductVersion ?? GetProductVersionDefault();
}

protected static Uri GetBaseAddress(string? uri, string? hostName, string? path, string? port, string hostOptionName, bool isTlsEnabled)
protected internal static Uri GetBaseAddress(string? uri, string? hostName, string? path, string? port, string hostOptionName, bool isTlsEnabled)
{
// Uri property takes precedent.
if (!string.IsNullOrWhiteSpace(uri))
Expand Down Expand Up @@ -874,7 +874,16 @@ protected static Uri GetBaseAddress(string? uri, string? hostName, string? path,
else
throw new ArgumentOutOfRangeException(nameof(port), portNumber, $"Port number is not in a valid range.");

Uri baseAddress = new UriBuilder(uriScheme, hostName, uriPort, path).Uri;
// UriBuilder percent-encodes '?' as '%3F' when a query string is embedded
// in the path argument (e.g. /sql/1.0/warehouses/<id>?o=<orgId>).
// Use the overload that takes extraValue so the query is passed correctly.
int queryIndex = path?.IndexOf('?') ?? -1;
Uri baseAddress = new UriBuilder(
uriScheme,
hostName,
uriPort,
queryIndex >= 0 ? path!.Substring(0, queryIndex) : path,
queryIndex >= 0 ? path!.Substring(queryIndex) : string.Empty).Uri;
return baseAddress;
}

Expand Down
93 changes: 93 additions & 0 deletions csharp/test/Hive2/HiveServer2ConnectionGetBaseAddressTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2025 ADBC Drivers Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using AdbcDrivers.HiveServer2.Hive2;
using AdbcDrivers.HiveServer2.Spark;
using Xunit;

namespace AdbcDrivers.Tests.HiveServer2.Hive2
{
/// <summary>
/// Unit tests for HiveServer2Connection.GetBaseAddress, focusing on correct
/// handling of HTTP paths that contain query parameters (e.g. ?o=orgId).
/// </summary>
public class HiveServer2ConnectionGetBaseAddressTest
{
/// <summary>
/// Verifies that a path containing a query parameter (e.g. Databricks warehouse
/// paths with ?o=orgId) is not percent-encoded. The '?' must remain a literal
/// query separator and not become %3F in the resulting URI.
/// </summary>
[Fact]
public void GetBaseAddress_PathWithQueryParam_DoesNotPercentEncodeQuestionMark()
{
Uri result = InvokeGetBaseAddress(
uri: null,
hostName: "test.server.com",
path: "/sql/1.0/warehouses/abc123?o=999888777",
port: "443",
isTlsEnabled: true);

Assert.DoesNotContain("%3F", result.ToString());
Assert.Equal("/sql/1.0/warehouses/abc123", result.AbsolutePath);
Assert.Equal("o=999888777", result.Query.TrimStart('?'));
Assert.Equal("https://test.server.com/sql/1.0/warehouses/abc123?o=999888777", result.ToString());
}

/// <summary>
/// Verifies that a plain path without a query parameter is unchanged.
/// </summary>
[Fact]
public void GetBaseAddress_PathWithoutQueryParam_ReturnsExpectedUri()
{
Uri result = InvokeGetBaseAddress(
uri: null,
hostName: "test.server.com",
path: "/sql/1.0/warehouses/abc123",
port: "443",
isTlsEnabled: true);

Assert.Equal("/sql/1.0/warehouses/abc123", result.AbsolutePath);
Assert.Equal(string.Empty, result.Query);
Comment thread
eric-wang-1990 marked this conversation as resolved.
Assert.Equal("https://test.server.com/sql/1.0/warehouses/abc123", result.ToString());
}

/// <summary>
/// Verifies that when a full URI is provided it is returned as-is, bypassing
/// the UriBuilder path entirely.
/// </summary>
[Fact]
public void GetBaseAddress_FullUri_ReturnsUriDirectly()
{
string fullUri = "https://test.server.com/sql/1.0/warehouses/abc123";

Uri result = InvokeGetBaseAddress(
uri: fullUri,
hostName: null,
path: null,
port: null,
isTlsEnabled: true);

Assert.Equal(new Uri(fullUri), result);
}

private static Uri InvokeGetBaseAddress(string? uri, string? hostName, string? path, string? port, bool isTlsEnabled)
{
return HiveServer2Connection.GetBaseAddress(uri, hostName, path, port, SparkParameters.HostName, isTlsEnabled);
}
}
}
Loading