GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder - #50853

Open
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix
Open

GH-50852: [C++][FlightRPC][ODBC] Decode wide connection/descriptor string attributes with the wide decoder#50853
vikrantpuppala wants to merge 1 commit into
apache:mainfrom
vikrantpuppala:GH-wide-attr-decode-fix

Conversation

@vikrantpuppala

@vikrantpuppalavikrantpuppala commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Two string attributes in the ODBC driver are decoded with the byte-wise
decoder even when the value arrives through a wide (Unicode / *W) entry point,
so the wide buffer is misread and corrupted.

ODBCConnection::SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its two decode
branches swapped:

if (is_unicode) {
SetAttributeUTF8(value, string_length, catalog); // byte-wise
} else {
SetAttributeSQLWCHAR(value, string_length, catalog); // wide
}

is_unicode selects the buffer width: a unicode (*W) call passes a wide
SQLWCHAR buffer that must be decoded with SetAttributeSQLWCHAR; a non-unicode
call passes a byte string decoded with SetAttributeUTF8. With the branches
swapped, a wide catalog name (e.g. UTF-16 "my_catalog") is reinterpreted
byte-wise and stored with the wide encoding's embedded NULs
("m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"), so catalog scoping operates on a corrupt
name. This is the inverse of the mapping the getter already uses:
GetStringAttribute maps is_unicode -> GetAttributeSQLWCHAR.

ODBCDescriptor::SetField(SQL_DESC_NAME) is the same class of bug: it
unconditionally used the byte-wise SetAttributeUTF8, even though the matching
getter (GetField / SQL_DESC_NAME) reads the field back with
GetAttributeSQLWCHAR.

How this happened (history):

What changes are included in this PR?

  • odbc_connection.cc: swap the SQL_ATTR_CURRENT_CATALOG decode branches so a
    unicode call uses SetAttributeSQLWCHAR and a non-unicode call uses
    SetAttributeUTF8, matching GetStringAttribute.
  • odbc_descriptor.cc: decode SQL_DESC_NAME with SetAttributeSQLWCHAR to
    match its getter.
  • connection_attr_test.cc: add TestSQLSetGetConnectAttrCurrentCatalogWide, a
    TYPED_TEST (mock + remote fixtures) that sets a multi-character catalog
    through the wide entry point and asserts the full name round-trips back.
  • odbc_descriptor_test.cc (new): add ODBCDescriptorTest.SetGetNameWideRoundTrips,
    a SetField/GetField round-trip on SQL_DESC_NAME with a multi-character
    wide name. There is no SQLSetDescField entry point in this tree, so this
    covers the descriptor change with a direct unit test on ODBCDescriptor.

Are these changes tested?

Yes. Each change has a round-trip test that distinguishes the fixed and unfixed
code:

checkresult
catalog test, with fix (mock fixture)PASS — out_catalog == "my_catalog"
catalog test, without fix (mock fixture)FAIL — out_catalog == "m\0y\0_\0c\0a\0t\0a\0l\0o\0g\0"
descriptor test, with fixPASS — name reads back as "my_column"
descriptor test, without fixFAIL — byte-wise decode corrupts the stored UTF-16
arrow-odbc-spi-impl-test (unit suite)85/85 pass
ConnectionAttributeTest/0.* (mock suite)25/25 pass
clang-formatclean on all changed files

The connection catalog test uses the mock fixture (FlightSQLODBCMockTestBase,
an in-process SQLite Flight SQL server); its remote variant runs when
ARROW_FLIGHT_SQL_ODBC_CONN is set. The descriptor test is a standalone unit
test. Verified locally against the mock fixture and the unit suite.

Are there any user-facing changes?

Yes. A catalog name (and descriptor SQL_DESC_NAME) set through the wide entry
point is now decoded correctly instead of being corrupted. Applications that set
a multi-character catalog through SQLSetConnectAttrW now scope to the intended
catalog.

AI usage

Per the AI-generated code guidance:
the diagnosis, the fix, and the test were produced with the assistance of an AI
coding agent and reviewed and verified by me. Correctness was checked by
(1) confirming the getter path (GetStringAttribute) maps is_unicode to the
wide decoder, establishing the intended mapping the setters violated, and
(2) running the new round-trip test against both the fixed and unfixed driver to
confirm it distinguishes them (clean name vs. embedded-NUL corruption).


🤖 Drafted by Claude Code (an AI agent) and reviewed & verified by vikrantpuppala.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50852has been automatically assigned in GitHub to PR creator.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Corrects wide-string decoding for ODBC catalog and descriptor attributes.

Changes:

  • Uses the wide decoder for Unicode catalog and descriptor names.
  • Preserves UTF-8 decoding for non-Unicode catalog values.
  • Adds catalog round-trip coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
connection_attr_test.ccTests wide catalog round-tripping.
odbc_descriptor.ccCorrects descriptor-name decoding.
odbc_connection.ccCorrects catalog decoder selection.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

break;
case SQL_DESC_NAME:
SetAttributeUTF8(value, buffer_length, record.name);
SetAttributeSQLWCHAR(value, buffer_length, record.name);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — added ODBCDescriptorTest.SetGetNameWideRoundTrips in odbc_descriptor_test.cc, a direct SetField/GetField round-trip on SQL_DESC_NAME with a multi-character wide name. It passes with the fix and fails without it (the byte-wise decode corrupts the stored UTF-16, which the wide getter then rejects). A black-box test isn't possible here since there's no SQLSetDescField entry point wired up, so the unit test on ODBCDescriptor covers it directly.

…tor string attributes with the wide decoder
SetConnectAttr(SQL_ATTR_CURRENT_CATALOG) had its is_unicode decode branches
swapped, and SetField(SQL_DESC_NAME) unconditionally used the byte-wise
SetAttributeUTF8. In both cases a wide SQLWCHAR buffer was misread, corrupting
the stored value. Decode wide buffers with SetAttributeSQLWCHAR, matching the
mapping the getters already use (GetStringAttribute / GetAttributeSQLWCHAR).
Add a round-trip TYPED_TEST for SQL_ATTR_CURRENT_CATALOG through the wide entry
point (connection_attr_test.cc) and a SetField/GetField round-trip unit test
for SQL_DESC_NAME (odbc_descriptor_test.cc).
Co-authored-by: Isaac
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we consolidate into an existing file?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The tricky part is SQL_DESC_NAME can't be exercised through a public entry point. I had tried a black-box test via SQLGetStmtAttr(SQL_ATTR_APP_ROW_DESC) + SQLSetDescField(ard, 1, SQL_DESC_NAME, …), but that path returns SQL_ERROR (the field isn't settable on the ARD through the DM), so the only way to reach ODBCDescriptor::SetField(SQL_DESC_NAME) is a direct unit test on ODBCDescriptor.

There's no existing descriptor unit test in odbc_impl/ to merge into, and the unit tests there follow a 1:1 source↔test naming convention (util.cc↔util_test.cc, json_converter.cc↔json_converter_test.cc, etc.), so odbc_descriptor_test.cc fit that pattern. Happy to fold it elsewhere if you'd prefer though, e.g. into util_test.cc.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Aug 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vikrantpuppala@lidavidm