Skip to content

fix: return MatrixOne foreign key metadata - #21

Merged
iamlinjunhong merged 3 commits into
mainfrom
codex/pyodbc-deep-20260820
Aug 20, 2026
Merged

fix: return MatrixOne foreign key metadata#21
iamlinjunhong merged 3 commits into
mainfrom
codex/pyodbc-deep-20260820

Conversation

@iamlinjunhong

@iamlinjunhong iamlinjunhong commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Problem

SQLForeignKeys returns an empty result against MatrixOne even when the requested table has a valid foreign-key constraint. This breaks pyodbc Cursor.foreignKeys() and other ODBC schema-introspection clients.

Tested with:

  • MatrixOne main commit c6b2ac9c1eaff0db867e845723a8ce1d2102928f
  • matrixone-odbc base bcc10fec25e5cbc7f1febb70f280c0d94bcac2c3
  • Windows 11 x64, pyodbc 5.3.0

Root cause

The inherited MySQL catalog query self-joins INFORMATION_SCHEMA.KEY_COLUMN_USAGE foreign-key row A to a second row D representing the referenced primary-key column, then requires D.CONSTRAINT_NAME IS NOT NULL.

MatrixOne exposes the foreign-key row, including all REFERENCED_* fields, but does not expose a separate primary-key row in KEY_COLUMN_USAGE. The self-join therefore removes every valid foreign key. SHOW CREATE TABLE and INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS both confirm that the constraint exists.

Fix

  • Detect a MatrixOne server through its server version string.
  • On MatrixOne, use the complete foreign-key row directly and require non-null referenced table/column fields.
  • Avoid depending on REFERENTIAL_CONSTRAINTS on MatrixOne releases that do not expose it consistently; report the currently supported NO ACTION rule.
  • Preserve the historical MySQL self-join, which protects against MySQL bug #51422.
  • Log the generated metadata query when query logging is enabled.

Reproduction

CREATE DATABASE fk_repro;
CREATE TABLE fk_repro.parent(id INT PRIMARY KEY);
CREATE TABLE fk_repro.child(
  id INT PRIMARY KEY,
  parent_id INT,
  CONSTRAINT fk_parent FOREIGN KEY(parent_id) REFERENCES fk_repro.parent(id)
);
rows = list(cursor.foreignKeys(
    foreignTable="child", foreignCatalog="fk_repro"
))
assert len(rows) == 1

Before this change, rows is empty. The same result occurs when calling the ANSI/Unicode ODBC API directly.

Before / after

  • Before (v9.7.0-mo.3): five valid SQLForeignKeys argument combinations each returned 0 rows.
  • After (this branch): direct loading of the newly built myodbc9w.dll returned exactly one row with parent.id -> child.parent_id, KEY_SEQ=1, and FK_NAME=fk_direct.

Regression coverage

mo_odbc_deep now:

  • creates a parent/child foreign key fixture;
  • verifies that SQLGetFunctions reports SQL_API_SQLFOREIGNKEYS;
  • calls SQLForeignKeys and asserts table names, column names, key sequence, foreign-key name, and exact row count.
  • distinguishes an older MatrixOne server that exposes no foreign-key row in KEY_COLUMN_USAGE (server known issue) from a driver returning no row when the source metadata exists.

Test scope

  • Full Visual Studio 2022 x64 RelWithDebInfo build: PASS; Unicode and ANSI driver targets compile.
  • MatrixOne end-to-end catalog SQL against latest main: PASS.
  • Direct DLL API verification (bypassing Driver Manager registration):
    • SQLForeignKeysW: PASS
    • query timeout SQLSTATE HYT00: PASS, 1.002 s
    • statement cancellation SQLSTATE HY008: PASS, 0.531 s
  • Existing pyodbc/pandas deep suite outside this focused fix: connection, types, Unicode, LOBs, parameters, transactions, bulk fetch, concurrency, network interruption, and server restart were exercised separately.

No Power BI scenario or Power BI-owned environment was modified by this change.

@iamlinjunhong
iamlinjunhong merged commit 8f1ca36 into main Aug 20, 2026
4 checks passed
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.

1 participant