Skip to content

Ensure scoped table retrieval for MySQL/MariaDB connections#1901

Merged
zaleslaw merged 4 commits into
masterfrom
issue-1746
Jun 18, 2026
Merged

Ensure scoped table retrieval for MySQL/MariaDB connections#1901
zaleslaw merged 4 commits into
masterfrom
issue-1746

Conversation

@zaleslaw

Copy link
Copy Markdown
Collaborator

Closes #1746

Fix #1746: readAllSqlTables leaks tables from other databases on MySQL/MariaDB

1. Problem

readAllSqlTables and DataFrameSchema.readAllSqlTables called without an explicit catalogue
incorrectly return tables from all accessible databases when connected to MySQL or MariaDB.

Root cause: both functions passed null directly to DatabaseMetaData.getTables(null, ...).
On MySQL/MariaDB, null means all catalogs, so a connection scoped to
jdbc:mysql://host/mydb would enumerate tables from every database visible to that user.
Subsequent SELECT * FROM table_from_another_db then fails with "table doesn't exist"
because the query runs in the wrong database context.

This is a MySQL/MariaDB-specific behavior: PostgreSQL and SQLite connections are inherently
single-database, so getTables(null, ...) there already returns only the current DB.

2. Solution

When catalogue is not provided by the caller, fall back to connection.catalog
(standard JDBC API — returns the database name from the JDBC URL).
The same pattern is already used in DbType.getTableColumnsMetadata.

val effectiveCatalogue = catalogue ?: try {
    connection.catalog.takeUnless { it.isNullOrBlank() }
} catch (_: Exception) { null }

If the URL has no database (jdbc:mysql://host without /dbname), connection.catalog
returns null/"", which falls through to null — preserving the existing
"all catalogs" behavior for that case.

Fixed in two places:

  • readJdbc.ktDataFrame.readAllSqlTables(connection, ...)
  • readDataFrameSchema.ktDataFrameSchema.readAllSqlTables(connection, ...) (same bug, hardcoded null)

3. Situation by Database

Database Affected Reason
MySQL Fixed getTables(null,...) returns all databases; connection.catalog = DB from URL
MariaDB Fixed Same behavior as MySQL
SQL Server Fixed Same catalog/database model; same fix applies
PostgreSQL No change Cross-database access impossible; getTables already scoped to current DB
SQLite No change Single-file = single catalog; connection.catalog returns null
H2 No change In-process, single DB per connection; not affected
DuckDB No change connection.catalog returns null for default in-memory catalog

Tests added (all @Ignore, require local server) in:

  • local/mysqlTest.kt
  • local/mariadbTest.kt
  • local/mssqlTest.kt

Each test creates a second database with a unique table, connects to DB1 by URL,
calls readAllSqlTables without catalogue, and asserts the second DB's table is absent.

Updated table retrieval logic to use `connection.catalog` when no specific catalog is provided. Added tests to verify tables are limited to the active database in MySQL, MariaDB, and MSSQL scenarios. Fixed connection URL constants in test files.
@zaleslaw zaleslaw requested a review from AndreiKingsley June 16, 2026 12:37
// tables from all MySQL/MariaDB databases when no specific catalog is given.
val effectiveCatalog = try {
connection.catalog.takeUnless { it.isNullOrBlank() }
} catch (_: Exception) { null }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if you're not interested in the exception, maybe you could try runCatching { ... }.getOrNull(). Just an alternative way to write it, but both are valid of course :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agree, it could be better for reading or handling

@zaleslaw zaleslaw merged commit 7bf0a4d into master Jun 18, 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.

readAllSqlTables doesn't work for MySQL with multiple databases

3 participants