Skip to content

SNOW-3234503 ODBC Connection initialization#662

Closed
sfc-gh-daniszewski wants to merge 1 commit intomainfrom
daniszewski-SNOW-3234503-conn-init
Closed

SNOW-3234503 ODBC Connection initialization#662
sfc-gh-daniszewski wants to merge 1 commit intomainfrom
daniszewski-SNOW-3234503-conn-init

Conversation

@sfc-gh-daniszewski
Copy link
Copy Markdown
Contributor

@sfc-gh-daniszewski sfc-gh-daniszewski commented Mar 20, 2026

Description

This pull request implements comprehensive ODBC connection functionality by adding support for SQLConnect, SQLBrowseConnect, and DSN (Data Source Name) configuration loading.

Key Changes

DSN Configuration Support

  • Added dsn.rs module with platform-specific DSN loading
  • Unix implementation reads from odbc.ini files ($ODBCINI, ~/.odbc.ini, /etc/odbc.ini)
  • Windows implementation placeholder for registry-based DSN lookup
  • Case-insensitive DSN section matching with uppercase key normalization

Enhanced Connection Functions

  • Implemented SQLConnect to connect via DSN with optional explicit credentials
  • Implemented SQLBrowseConnect with stateful attribute accumulation and connection templates
  • Enhanced SQLDriverConnect to support DSN references and return completed connection strings
  • Added DSN attribute merging where connection string parameters override DSN values

Connection String Processing

  • Improved connection string parsing with proper key/value extraction and trimming
  • Added connection string completion for output buffers with sensitive value filtering
  • Enhanced parameter mapping with case-insensitive key matching
  • Added support for DRIVER, DSN, DESCRIPTION, SSL, LOCALE, and TRACING parameter filtering

Error Handling

  • Added DsnNotFoundSnafu error type with appropriate SQLSTATE IM002
  • Enhanced connection error reporting and diagnostic information

State Management

  • Added browse_connect_attrs field to Connection struct for SQLBrowseConnect state
  • Improved connection state transitions and cleanup

Testing

  • Added comprehensive unit tests for DSN INI file parsing including case sensitivity, section boundaries, and comment handling
  • Added end-to-end tests for SQLConnect with DSN-based connections and unknown DSN error handling
  • Added SQLBrowseConnect tests for connection template generation and SQL_NEED_DATA responses
  • Added feature definitions covering successful DSN connections, error scenarios, and browse connect workflows

Copilot AI review requested due to automatic review settings March 20, 2026 13:42
Copy link
Copy Markdown
Contributor Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge_queue - adds this PR to the back of the merge queue
  • priority_merge_queue - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds initial ODBC connection initialization coverage by implementing DSN-backed SQLConnect and introducing SQLBrowseConnect support, along with new end-to-end tests and a Gherkin feature definition.

Changes:

  • Implement SQLConnect{,W} by routing through the existing connection pipeline and adding DSN config loading.
  • Add SQLBrowseConnect{,W} with per-connection accumulation of browse-connect attributes and SQL_NEED_DATA templating.
  • Add new e2e tests (and feature definition) covering DSN connect, unknown DSN SQLSTATE, and browse-connect template behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/definitions/odbc/connection/sql_connect.feature New feature scenarios for SQLConnect/SQLBrowseConnect behaviors.
odbc/src/c_api.rs Updates SQLDriverConnect{,W} parameters and exports SQLBrowseConnect{,W} C entrypoints.
odbc/src/api/types/odbc_types.rs Adds browse_connect_attrs state to the connection handle.
odbc/src/api/mod.rs Registers new dsn module.
odbc/src/api/handle_allocation.rs Initializes new browse_connect_attrs field.
odbc/src/api/error.rs Adds DsnNotFound error variant and maps it to SQLSTATE IM002.
odbc/src/api/dsn.rs Introduces DSN config loading (unix odbc.ini parsing; Windows stub).
odbc/src/api/connection.rs Implements SQLConnect and SQLBrowseConnect logic; adds DSN merge + completed-string generation.
odbc_tests/tests/e2e/connection/sql_connect.cpp New Catch2 e2e tests for DSN connect, unknown DSN, and browse-connect template.
odbc_tests/tests/e2e/CMakeLists.txt Adds the new e2e test target.

Comment on lines +116 to +120
write_string_bytes::<E>(
&completed,
out_connection_string,
out_buffer_length,
out_string_length,
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

SQLDriverConnect{,W} buffer-length parameters are in character units (and pcbConnStrOut is a character count), but this uses write_string_bytes, which treats buffer_length / *out_string_length as bytes and will under-fill/truncate and report the wrong length for the wide (W) variant. Consider using write_string_chars here (or a dedicated helper that follows the SQLDriverConnect length semantics).

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +203
if !has_server {
tracing::debug!("browse_connect: insufficient attrs, returning NEED_DATA");
write_string_bytes::<E>(
BROWSE_CONNECT_TEMPLATE,
out_connection_string,
out_buffer_length,
out_string_length,
None,
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

SQLBrowseConnect{,W} uses write_string_bytes for the output template / completed connection string, but ODBC defines out_buffer_length and *out_string_length for this API in character units. This will misreport lengths and truncate early for the wide (W) variant. Use write_string_chars (or an API-specific helper) to match the expected length units.

Copilot uses AI. Check for mistakes.
Comment thread odbc/src/api/dsn.rs
Comment on lines +105 to +117

pub fn load(dsn_name: &str) -> Option<HashMap<String, String>> {
// DSN entries live at:
// HKCU\SOFTWARE\ODBC\ODBC.INI\<dsn> (user DSN)
// HKLM\SOFTWARE\ODBC\ODBC.INI\<dsn> (system DSN)
//
// TODO: implement Windows registry lookup
tracing::warn!(
"dsn: Windows registry DSN loading not yet implemented for {:?}",
dsn_name
);
None
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The Windows DSN loader is currently a stub that always returns None (and only logs a warning). With SQLConnect now delegating through DSN resolution, DSN-based connections will always fail on Windows (and the new e2e SQLConnect-via-DSN scenario is likely to fail in Windows CI). Implement the registry lookup (HKCU/HKLM ...\ODBC.INI\<dsn>) or gate the SQLConnect DSN path on cfg(windows) until it’s supported.

Suggested change
pub fn load(dsn_name: &str) -> Option<HashMap<String, String>> {
// DSN entries live at:
// HKCU\SOFTWARE\ODBC\ODBC.INI\<dsn> (user DSN)
// HKLM\SOFTWARE\ODBC\ODBC.INI\<dsn> (system DSN)
//
// TODO: implement Windows registry lookup
tracing::warn!(
"dsn: Windows registry DSN loading not yet implemented for {:?}",
dsn_name
);
None
}
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY};
use winreg::RegKey;
pub fn load(dsn_name: &str) -> Option<HashMap<String, String>> {
// DSN entries live at:
// HKCU\SOFTWARE\ODBC\ODBC.INI\<dsn> (user DSN)
// HKLM\SOFTWARE\ODBC\ODBC.INI\<dsn> (system DSN)
let subkey = format!(r"SOFTWARE\\ODBC\\ODBC.INI\\{dsn_name}");
tracing::debug!("dsn: loading Windows registry config for {:?}", dsn_name);
// Prefer user DSN (HKCU) over system DSN (HKLM), mirroring typical ODBC behaviour.
if let Some(attrs) = load_from_hive(HKEY_CURRENT_USER, &subkey) {
return Some(attrs);
}
if let Some(attrs) = load_from_hive(HKEY_LOCAL_MACHINE, &subkey) {
return Some(attrs);
}
tracing::debug!("dsn: {:?} not found in Windows registry", dsn_name);
None
}
fn load_from_hive(root: HKEY, subkey: &str) -> Option<HashMap<String, String>> {
let hive = RegKey::predef(root);
let key = match hive.open_subkey(subkey) {
Ok(k) => k,
Err(e) => {
tracing::trace!(
"dsn: Windows registry key {:?}\\{:?} not accessible: {}",
root,
subkey,
e
);
return None;
}
};
let mut attrs: HashMap<String, String> = HashMap::new();
// Enumerate all values under the DSN key and read them as strings.
for value_result in key.enum_values() {
let (name, _) = match value_result {
Ok(pair) => pair,
Err(e) => {
tracing::trace!("dsn: failed to enumerate registry value: {}", e);
continue;
}
};
match key.get_value::<String, _>(&name) {
Ok(val) => {
attrs.insert(name.to_uppercase(), val);
}
Err(e) => {
tracing::trace!(
"dsn: failed to read registry value {:?} from {:?}: {}",
name,
subkey,
e
);
}
}
}
if attrs.is_empty() {
None
} else {
Some(attrs)
}
}

Copilot uses AI. Check for mistakes.
) -> OdbcResult<String> {
if ptr.is_null() {
return Ok(String::new());
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

read_optional_string calls E::read_string for any non-null pointer. E::read_string currently treats non-SQL_NTS lengths <= 0 as invalid, so callers passing an explicit empty string with length = 0 (a common pattern for optional SQLConnect args) will get InvalidBufferLength instead of an empty value. Consider treating length == 0 as Ok(String::new()) in read_optional_string.

Suggested change
}
}
if length == 0 {
return Ok(String::new());
}

Copilot uses AI. Check for mistakes.
Comment on lines +208 to +225
// Attempt the connection with the accumulated attributes.
match driver_connect_core(connection_handle, &conn_str) {
Ok(completed) => {
// Reset accumulated state on success.
conn_from_handle(connection_handle)
.browse_connect_attrs
.clear();
write_string_bytes::<E>(
&completed,
out_connection_string,
out_buffer_length,
out_string_length,
None,
);
Ok(BrowseConnectOutcome::Connected)
}
Err(e) => Err(e),
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

browse_connect_attrs is documented as being cleared when the browse-connect loop completes (success or reset), but the current implementation only clears it on success. If driver_connect_core returns an error (or if the caller wants to restart the browse-connect sequence), the accumulated attributes will persist and can leak into later attempts. Consider clearing the map on error and/or explicitly handling a reset condition (e.g., empty input) to match the documented behavior.

Copilot uses AI. Check for mistakes.
@sfc-gh-daniszewski sfc-gh-daniszewski deleted the daniszewski-SNOW-3234503-conn-init branch April 23, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants