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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions driver/catalog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,9 @@ SQLRETURN foreign_keys_i_s(SQLHSTMT hstmt,
MYSQL *mysql= stmt->dbc->mysql;
char tmpbuff[1024]; /* This should be big enough. */
const char *update_rule, *delete_rule, *ref_constraints_join;
const bool is_matrixone =
mysql->server_version != nullptr &&
strstr(mysql->server_version, "MatrixOne") != nullptr;
SQLRETURN rc;
std::string query, pk_db, fk_db, order_by;
query.reserve(4096);
Expand All @@ -1635,7 +1638,14 @@ SQLRETURN foreign_keys_i_s(SQLHSTMT hstmt,
/*
With 5.1, we can use REFERENTIAL_CONSTRAINTS to get even more info.
*/
if (is_minimum_version(mysql->server_version, "5.1"))
if (is_matrixone)
{
/* MatrixOne currently supports NO ACTION semantics and older releases do
not consistently expose REFERENTIAL_CONSTRAINTS. */
update_rule= delete_rule= "3";
ref_constraints_join= "";
}
else if (is_minimum_version(mysql->server_version, "5.1"))
{
update_rule= "CASE"
" WHEN R.UPDATE_RULE = 'CASCADE' THEN 0"
Expand Down Expand Up @@ -1691,12 +1701,22 @@ SQLRETURN foreign_keys_i_s(SQLHSTMT hstmt,
"A.CONSTRAINT_NAME AS FK_NAME,"
"'PRIMARY' AS PK_NAME,"
"7 AS DEFERRABILITY"
" FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A"
" JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE D"
" ON (D.TABLE_SCHEMA=A.REFERENCED_TABLE_SCHEMA AND D.TABLE_NAME=A.REFERENCED_TABLE_NAME"
" AND D.COLUMN_NAME=A.REFERENCED_COLUMN_NAME)");
query.append(ref_constraints_join).append(" WHERE D.CONSTRAINT_NAME");
query.append(" IS NOT NULL ");
" FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A");
if (!is_matrixone)
{
/* MySQL bug #51422 can expose non-key referenced columns, so retain the
historical self-join there. MatrixOne does not expose primary-key rows
in KEY_COLUMN_USAGE; its foreign-key rows already identify both sides. */
query.append(" JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE D"
" ON (D.TABLE_SCHEMA=A.REFERENCED_TABLE_SCHEMA AND D.TABLE_NAME=A.REFERENCED_TABLE_NAME"
" AND D.COLUMN_NAME=A.REFERENCED_COLUMN_NAME)");
}
query.append(ref_constraints_join);
if (is_matrixone)
query.append(" WHERE A.REFERENCED_TABLE_NAME IS NOT NULL"
" AND A.REFERENCED_COLUMN_NAME IS NOT NULL ");
else
query.append(" WHERE D.CONSTRAINT_NAME IS NOT NULL ");

if (pk_table && pk_table[0])
{
Expand Down Expand Up @@ -1751,6 +1771,7 @@ SQLRETURN foreign_keys_i_s(SQLHSTMT hstmt,
}

query.append(order_by);
MYLOG_QUERY(stmt, query.c_str());
rc= MySQLPrepare(hstmt, (SQLCHAR *)query.c_str(), (SQLINTEGER)(query.length()),
true, false);

Expand Down
80 changes: 69 additions & 11 deletions test/mo_odbc_deep.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,16 @@ INSERT INTO mo_odbc_deep.type_matrix VALUES
"mo_odbc_deep.type_matrix(c_varchar)");
execute(dbc, "CREATE VIEW mo_odbc_deep.type_view AS SELECT id,c_varchar "
"FROM mo_odbc_deep.type_matrix");
execute(dbc, "CREATE TABLE mo_odbc_deep.meta_under_score (id INT)");
execute(dbc, "CREATE TABLE mo_odbc_deep.metaXunderXscore (id INT)");
execute(dbc, "CREATE TABLE mo_odbc_deep.meta_under_score (id INT)");
execute(dbc, "CREATE TABLE mo_odbc_deep.metaXunderXscore (id INT)");
execute(dbc, "CREATE TABLE mo_odbc_deep.fk_parent (id INT PRIMARY KEY)");
execute(dbc, R"SQL(
CREATE TABLE mo_odbc_deep.fk_child (
id INT PRIMARY KEY,
parent_id INT,
CONSTRAINT fk_deep_parent FOREIGN KEY (parent_id)
REFERENCES mo_odbc_deep.fk_parent(id)
))SQL");
execute(dbc, R"SQL(
CREATE TABLE mo_odbc_deep.param_values (
id BIGINT PRIMARY KEY,
Expand Down Expand Up @@ -344,9 +352,10 @@ void test_connection_capabilities(SQLHDBC dbc) {
expect(!driver_version.empty(), "SQL_DRIVER_VER is empty");

for (SQLUSMALLINT api : {SQL_API_SQLBINDPARAMETER, SQL_API_SQLDESCRIBEPARAM,
SQL_API_SQLNUMPARAMS, SQL_API_SQLTABLES,
SQL_API_SQLCOLUMNS, SQL_API_SQLPRIMARYKEYS,
SQL_API_SQLSTATISTICS, SQL_API_SQLGETTYPEINFO}) {
SQL_API_SQLNUMPARAMS, SQL_API_SQLTABLES,
SQL_API_SQLCOLUMNS, SQL_API_SQLPRIMARYKEYS,
SQL_API_SQLFOREIGNKEYS, SQL_API_SQLSTATISTICS,
SQL_API_SQLGETTYPEINFO}) {
SQLUSMALLINT supported = SQL_FALSE;
check(SQLGetFunctions(dbc, api, &supported), SQL_HANDLE_DBC, dbc,
"SQLGetFunctions " + std::to_string(api));
Expand Down Expand Up @@ -388,9 +397,9 @@ SQLLEN count_rows(SQLHSTMT stmt) {
void test_catalog_metadata(SQLHDBC dbc) {
const bool unicode_driver =
info_string(dbc, SQL_DRIVER_NAME).find("9w") != std::string::npos;
{
Statement stmt(dbc);
SQLCHAR catalog[] = "mo_odbc_deep";
{
Statement stmt(dbc);
SQLCHAR catalog[] = "mo_odbc_deep";
SQLCHAR table[] = "type_matrix";
SQLCHAR type[] = "TABLE";
check(SQLTables(stmt.handle(), catalog, SQL_NTS, nullptr, 0, table,
Expand Down Expand Up @@ -542,9 +551,58 @@ void test_catalog_metadata(SQLHDBC dbc) {
SQL_HANDLE_STMT, stmt.handle(), "SQLPrimaryKeys COLUMN_NAME");
expect(std::string(reinterpret_cast<const char *>(column)) == "id",
"SQLPrimaryKeys did not report id");
expect(SQLFetch(stmt.handle()) == SQL_NO_DATA,
"SQLPrimaryKeys returned more than one key column");
}
expect(SQLFetch(stmt.handle()) == SQL_NO_DATA,
"SQLPrimaryKeys returned more than one key column");
}
{
Statement stmt(dbc);
SQLCHAR catalog[] = "mo_odbc_deep";
SQLCHAR table[] = "fk_child";
check(SQLForeignKeys(stmt.handle(), nullptr, 0, nullptr, 0, nullptr, 0,
catalog, SQL_NTS, nullptr, 0, table, SQL_NTS),
SQL_HANDLE_STMT, stmt.handle(), "SQLForeignKeys");
SQLRETURN foreign_key_fetch = SQLFetch(stmt.handle());
if (foreign_key_fetch == SQL_NO_DATA &&
scalar_int(
dbc,
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE "
"WHERE TABLE_SCHEMA='mo_odbc_deep' AND TABLE_NAME='fk_child' "
"AND REFERENCED_TABLE_NAME IS NOT NULL") == 0) {
throw KnownIssue(
"older MatrixOne releases do not expose foreign-key rows through "
"INFORMATION_SCHEMA.KEY_COLUMN_USAGE; matrixorigin/matrixone#25103");
}
check(foreign_key_fetch, SQL_HANDLE_STMT, stmt.handle(),
"SQLForeignKeys fetch");

auto get_text = [&](SQLUSMALLINT column) {
SQLCHAR value[256] = {};
SQLLEN length = 0;
check(SQLGetData(stmt.handle(), column, SQL_C_CHAR, value, sizeof(value),
&length),
SQL_HANDLE_STMT, stmt.handle(),
"SQLForeignKeys column " + std::to_string(column));
return std::string(reinterpret_cast<const char *>(value));
};
SQLSMALLINT key_seq = 0;
SQLLEN key_seq_length = 0;
expect(get_text(3) == "fk_parent",
"SQLForeignKeys reported the wrong primary-key table");
expect(get_text(4) == "id",
"SQLForeignKeys reported the wrong primary-key column");
expect(get_text(7) == "fk_child",
"SQLForeignKeys reported the wrong foreign-key table");
expect(get_text(8) == "parent_id",
"SQLForeignKeys reported the wrong foreign-key column");
check(SQLGetData(stmt.handle(), 9, SQL_C_SSHORT, &key_seq,
sizeof(key_seq), &key_seq_length),
SQL_HANDLE_STMT, stmt.handle(), "SQLForeignKeys KEY_SEQ");
expect(key_seq == 1, "SQLForeignKeys KEY_SEQ should be 1");
expect(get_text(12) == "fk_deep_parent",
"SQLForeignKeys reported the wrong foreign-key name");
expect(SQLFetch(stmt.handle()) == SQL_NO_DATA,
"SQLForeignKeys returned more than one key column");
}
{
Statement stmt(dbc);
SQLCHAR catalog[] = "mo_odbc_deep";
Expand Down
Loading