Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct ConnProps {
std::string psc;
GCD gcd;
std::string impersonated_email;
std::string partner_token;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest creating new struct for "connection properties". partner_token doesn't logically belong to oauth

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This struct is now named ConnProps for readability. Is it fine now?

};

// Returns true if all required BYOID properties are set.
Expand Down
18 changes: 14 additions & 4 deletions google/cloud/odbc/bq_client_interface/odbc_bq_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "google/cloud/odbc/bq_client_interface/projects.h"
#include "google/cloud/odbc/bq_client_interface/storage.h"
#include "google/cloud/odbc/bq_client_interface/tables.h"
#include "google/cloud/odbc/bq_client_interface/utils.h"
#include "google/cloud/odbc/internal/status_record_or.h"
#include "google/cloud/odbc/internal/version.h"
#include "google/cloud/common_options.h"
Expand Down Expand Up @@ -192,8 +193,18 @@ StatusRecordOr<std::shared_ptr<ODBCBQClient>> ODBCBQClient::CreateBQClient(
.set_password(conn_props.proxy_options.password)
.set_scheme("http"));

options.set<google::cloud::UserAgentProductsOption>(
{"Google-Bigquery-ODBC/" + std::string(DRIVER_VERSION)});
auto partner_token_or =
::google::cloud::odbc_bigquery_client_interface::ParsePartnerToken(
conn_props.partner_token);
if (!partner_token_or) {
LOG(ERROR) << "CreateBQClient::ParsePartnerToken:: "
<< partner_token_or.GetStatusRecord().message;
return partner_token_or.GetStatusRecord();
}
std::string partner_token = *partner_token_or;
std::string user_agent =
"Google-Bigquery-ODBC/" + std::string(DRIVER_VERSION) + partner_token;
options.set<google::cloud::UserAgentProductsOption>({user_agent});

if (conn_props.gcd.enable_gcd &&
conn_props.gcd.universe_domain != "googleapis.com") {
Expand Down Expand Up @@ -282,8 +293,7 @@ StatusRecordOr<std::shared_ptr<ODBCBQClient>> ODBCBQClient::CreateBQClient(
// account impersonation) will hang waiting on completion events.

grpc::ChannelArguments channel_arguments;
channel_arguments.SetUserAgentPrefix("Google-Bigquery-ODBC/" +
std::string(DRIVER_VERSION));
channel_arguments.SetUserAgentPrefix(user_agent);
channel_arguments.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS,
std::chrono::minutes(1).count());
channel_arguments.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS,
Expand Down
31 changes: 30 additions & 1 deletion google/cloud/odbc/bq_client_interface/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
#ifndef CPP_BIGQUERY_ODBC_GOOGLE_CLOUD_ODBC_BQ_CLIENT_INTERFACE_UTILS_H
#define CPP_BIGQUERY_ODBC_GOOGLE_CLOUD_ODBC_BQ_CLIENT_INTERFACE_UTILS_H

#include "google/cloud/odbc/internal/sql_state_constants.h"
#include "google/cloud/odbc/internal/status_record_or.h"
#include "google/cloud/internal/backoff_policy.h"
#include "absl/log/log.h"
#include <cctype>
#include <cstdint>
#include <iomanip>
#include <regex>
#include <sstream>
#include <string>
#include <thread>
Expand Down Expand Up @@ -56,7 +60,7 @@ auto RetryLoop(Functor&& functor, std::string const& operation_name,
using ReturnType = decltype(functor());
ReturnType response;

google::cloud::ExponentialBackoffPolicy backoff_policy(
google::cloud::internal::ExponentialBackoffPolicy backoff_policy(
std::chrono::milliseconds(initial_delay_ms),
std::chrono::milliseconds(max_delay_ms), backoff_multiplier);

Expand Down Expand Up @@ -95,5 +99,30 @@ auto RetryLoop(Functor&& functor, std::string const& operation_name,

return response;
}

inline google::cloud::odbc_internal::StatusRecordOr<std::string>
ParsePartnerToken(std::string const& raw_token) {
if (raw_token.empty()) {
return std::string("");
}
std::regex pattern(R"(\(\s*(GPN:[^;]*?)\s*(?:;\s*([^)]*?))?\s*\))");
std::smatch match;
if (std::regex_search(raw_token, match, pattern)) {
std::string gpn_part = match[1].str();
std::string env_part = match[2].str();
std::string partner_token = " (";
partner_token += gpn_part;
if (!env_part.empty()) {
partner_token += "; ";
partner_token += env_part;
}
partner_token += ")";
return partner_token;
}
return google::cloud::odbc_internal::StatusRecord{
google::cloud::odbc_internal::SQLStates::k_HY024(),
"Invalid PartnerToken format."};
}

} // namespace google::cloud::odbc_bigquery_client_interface
#endif // CPP_BIGQUERY_ODBC_GOOGLE_CLOUD_ODBC_BQ_CLIENT_INTERFACE_UTILS_H
63 changes: 63 additions & 0 deletions google/cloud/odbc/bq_client_interface/utils_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2026 Google LLC
//
// 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
//
// https://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.

#include "google/cloud/odbc/bq_client_interface/utils.h"
#include <gtest/gtest.h>
#include <string>

namespace google::cloud::odbc_bigquery_client_interface {
namespace {

TEST(ParsePartnerToken, ValidFull) {
std::string raw = "(GPN:PartnerName;Environment)";
auto result = ParsePartnerToken(raw);
EXPECT_TRUE(result.Ok());
EXPECT_EQ(*result, " (GPN:PartnerName; Environment)");
}

TEST(ParsePartnerToken, ValidShort) {
std::string raw = "(GPN:PartnerName)";
auto result = ParsePartnerToken(raw);
EXPECT_TRUE(result.Ok());
EXPECT_EQ(*result, " (GPN:PartnerName)");
}

TEST(ParsePartnerToken, ValidWithSpaces) {
std::string raw = " ( GPN:PartnerName ; Environment ) ";
auto result = ParsePartnerToken(raw);
EXPECT_TRUE(result.Ok());
EXPECT_EQ(*result, " (GPN:PartnerName; Environment)");
}

TEST(ParsePartnerToken, InvalidNoGpn) {
std::string raw = "(PartnerName; Environment)";
auto result = ParsePartnerToken(raw);
EXPECT_FALSE(result.Ok());
}

TEST(ParsePartnerToken, Empty) {
std::string raw;
auto result = ParsePartnerToken(raw);
EXPECT_TRUE(result.Ok());
EXPECT_EQ(*result, "");
}

TEST(ParsePartnerToken, NoMatch) {
std::string raw = "invalid";
auto result = ParsePartnerToken(raw);
EXPECT_FALSE(result.Ok());
}

} // namespace
} // namespace google::cloud::odbc_bigquery_client_interface
1 change: 1 addition & 0 deletions google/cloud/odbc/bq_driver/internal/odbc_conn_handle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ void ConnectionHandle::SetUp(Section& dsn_section,
}

dsn_.impersonated_email = dsn_section["SERVICEACCOUNTIMPERSONATIONEMAIL"];
dsn_.partner_token = dsn_section["PARTNERTOKEN"];

// Populate HTAPI related configurations
std::string use_default_large_results_dataset =
Expand Down
1 change: 1 addition & 0 deletions google/cloud/odbc/bq_driver/internal/odbc_conn_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ struct Dsn {
bool enable_gcd;
std::string universe_domain;
std::string impersonated_email;
std::string partner_token;
};

class EnvironmentHandle;
Expand Down
1 change: 1 addition & 0 deletions google/cloud/odbc/bq_driver/odbc_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Authentication CreateAuth(Dsn const& dsn) {
auth.conn_props.gcd.enable_gcd = dsn.enable_gcd;
auth.conn_props.gcd.universe_domain = dsn.universe_domain;
auth.conn_props.impersonated_email = dsn.impersonated_email;
auth.conn_props.partner_token = dsn.partner_token;
return auth;
}

Expand Down
Loading