Skip to content

Commit db0e4b9

Browse files
authored
Fix enable multiple catalog support to match existing driver behaviour (databricks#1181)
## Description <!-- Provide a brief summary of the changes made and the issue they aim to address.--> Modified the logic for enableMultipleCatalogSupport parameter to only return results when the catalog provided in metadata calls is null or is equal to the current catalog when the param is disabled. This matches the behaviour with existing driver. ## Testing <!-- Describe how the changes have been tested--> Tested locally ## Additional Notes to the Reviewer <!-- Share any additional context or insights that may help the reviewer understand the changes better. This could include challenges faced, limitations, or compromises made during the development process. Also, mention any areas of the code that you would like the reviewer to focus on specifically. -->
1 parent 1893a40 commit db0e4b9

8 files changed

Lines changed: 379 additions & 49 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- Geospatial column type names now include SRID information (e.g., `GEOMETRY(4326)` instead of `GEOMETRY`).
1313
- Changed default value of `IgnoreTransactions` from `0` to `1` to disable multi-statement transactions by default. Preview participants can opt-in by setting `IgnoreTransactions=0`. Also updated `supportsTransactions()` to respect this flag.
1414
- Implemented lazy loading for inline Arrow results, fetching arrow batches on demand instead of all at once. This improves memory usage and initial response time for large result sets when using the Thrift protocol with Arrow format.
15+
- **Enhanced `enableMultipleCatalogSupport` behavior**: When this parameter is disabled (`enableMultipleCatalogSupport=0`), metadata operations (such as `getSchemas()`, `getTables()`, `getColumns()`, etc.) now return results only when the catalog parameter is either `null` or matches the current catalog. For any other catalog name, an empty result set is returned. This ensures metadata queries are restricted to the current catalog context. When enabled (`enableMultipleCatalogSupport=1`), metadata operations continue to work across all accessible catalogs.
1516

1617
### Fixed
1718
- Fixed complex data type metadata support when retrieving 0 rows in Arrow format

src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111

1212
import com.databricks.jdbc.api.impl.DatabricksResultSet;
1313
import com.databricks.jdbc.api.internal.IDatabricksConnectionContext;
14+
import com.databricks.jdbc.api.internal.IDatabricksSession;
1415
import com.databricks.jdbc.common.CommandName;
1516
import com.databricks.jdbc.common.Nullable;
1617
import com.databricks.jdbc.common.StatementType;
1718
import com.databricks.jdbc.exception.DatabricksSQLException;
19+
import com.databricks.jdbc.log.JdbcLogger;
20+
import com.databricks.jdbc.log.JdbcLoggerFactory;
1821
import com.databricks.jdbc.model.core.ColumnMetadata;
1922
import com.databricks.jdbc.model.core.ResultColumn;
2023
import com.databricks.jdbc.model.core.StatementStatus;
@@ -29,6 +32,8 @@
2932
import java.util.stream.Collectors;
3033

3134
public class MetadataResultSetBuilder {
35+
private static final JdbcLogger LOGGER =
36+
JdbcLoggerFactory.getLogger(MetadataResultSetBuilder.class);
3237
private static final IDatabricksResultSetAdapter defaultAdapter =
3338
new DefaultDatabricksResultSetAdapter();
3439
private static final IDatabricksResultSetAdapter importedKeysAdapter =
@@ -39,6 +44,31 @@ public MetadataResultSetBuilder(IDatabricksConnectionContext ctx) {
3944
this.ctx = ctx;
4045
}
4146

47+
public boolean shouldAllowCatalogAccess(
48+
String catalog, String currentCatalog, IDatabricksSession session) throws SQLException {
49+
if (ctx == null || ctx.getEnableMultipleCatalogSupport()) {
50+
return true;
51+
}
52+
53+
if (catalog == null) {
54+
return true;
55+
}
56+
57+
if (currentCatalog == null) {
58+
currentCatalog = session.getCurrentCatalog();
59+
}
60+
61+
if (currentCatalog != null && currentCatalog.equals(catalog)) {
62+
return true;
63+
}
64+
65+
LOGGER.debug(
66+
"Catalog access denied for catalog '{}' when enableMultipleCatalogSupport=false. Current catalog is '{}'",
67+
catalog,
68+
currentCatalog);
69+
return false;
70+
}
71+
4272
public DatabricksResultSet getFunctionsResult(DatabricksResultSet resultSet, String catalog)
4373
throws SQLException {
4474
List<List<Object>> rows = getRowsForFunctions(resultSet, FUNCTION_COLUMNS, catalog);

src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java

Lines changed: 90 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.databricks.jdbc.dbclient.impl.sqlexec;
22

33
import static com.databricks.jdbc.common.MetadataResultConstants.*;
4-
import static com.databricks.jdbc.dbclient.impl.common.CommandConstants.GET_TABLES_STATEMENT_ID;
54
import static com.databricks.jdbc.dbclient.impl.common.CommandConstants.METADATA_STATEMENT_ID;
65
import static com.databricks.jdbc.dbclient.impl.sqlexec.ResultConstants.TYPE_INFO_RESULT;
76

87
import com.databricks.jdbc.api.impl.DatabricksResultSet;
98
import com.databricks.jdbc.api.internal.IDatabricksSession;
10-
import com.databricks.jdbc.common.MetadataResultConstants;
119
import com.databricks.jdbc.common.StatementType;
1210
import com.databricks.jdbc.common.util.JdbcThreadUtils;
1311
import com.databricks.jdbc.common.util.WildcardUtil;
@@ -54,8 +52,11 @@ public DatabricksResultSet listCatalogs(IDatabricksSession session) throws SQLEx
5452
// If multiple catalog support is disabled, return only the current catalog
5553
if (isMultipleCatalogSupportDisabled()) {
5654
String currentCatalog = session.getCurrentCatalog();
57-
if (currentCatalog == null) {
58-
currentCatalog = "";
55+
if (currentCatalog == null || currentCatalog.isEmpty()) {
56+
currentCatalog = "spark";
57+
LOGGER.debug(
58+
"Current catalog is null or empty when multiple catalog support is disabled. Using default catalog: {}",
59+
currentCatalog);
5960
}
6061
String SQL = String.format("SELECT '%s' AS catalog", currentCatalog);
6162
LOGGER.debug("SQL command to fetch catalogs: {}", SQL);
@@ -71,7 +72,13 @@ public DatabricksResultSet listCatalogs(IDatabricksSession session) throws SQLEx
7172
@Override
7273
public DatabricksResultSet listSchemas(
7374
IDatabricksSession session, String catalog, String schemaNamePattern) throws SQLException {
74-
catalog = autoFillCatalog(catalog, session);
75+
// Only fetch currentCatalog if multiple catalog support is disabled
76+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
77+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
78+
return metadataResultSetBuilder.getSchemasResult(new ArrayList<>());
79+
}
80+
81+
catalog = autoFillCatalog(catalog, currentCatalog);
7582

7683
// Return empty result set if catalog is an empty string
7784
if (catalog != null && catalog.isEmpty()) {
@@ -111,11 +118,19 @@ public DatabricksResultSet listTables(
111118
String tableNamePattern,
112119
String[] tableTypes)
113120
throws SQLException {
114-
catalog = autoFillCatalog(catalog, session);
115121
String[] validatedTableTypes =
116122
Optional.ofNullable(tableTypes)
117123
.filter(types -> types.length > 0)
118124
.orElse(DEFAULT_TABLE_TYPES);
125+
126+
// Only fetch currentCatalog if multiple catalog support is disabled
127+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
128+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
129+
return metadataResultSetBuilder.getTablesResult(
130+
catalog, validatedTableTypes, new ArrayList<>());
131+
}
132+
133+
catalog = autoFillCatalog(catalog, currentCatalog);
119134
CommandBuilder commandBuilder =
120135
new CommandBuilder(catalog, session)
121136
.setSchemaPattern(schemaNamePattern)
@@ -132,11 +147,8 @@ public DatabricksResultSet listTables(
132147
// Gracefully handles the case where an older DBSQL version doesn't support all catalogs in
133148
// the SHOW TABLES command.
134149
LOGGER.debug("SQL command failed with syntax error. Returning empty result set.");
135-
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
136-
MetadataResultConstants.TABLE_COLUMNS,
137-
new ArrayList<>(),
138-
GET_TABLES_STATEMENT_ID,
139-
com.databricks.jdbc.common.CommandName.LIST_TABLES);
150+
return metadataResultSetBuilder.getTablesResult(
151+
catalog, validatedTableTypes, new ArrayList<>());
140152
} else {
141153
throw e;
142154
}
@@ -157,7 +169,13 @@ public DatabricksResultSet listColumns(
157169
String tableNamePattern,
158170
String columnNamePattern)
159171
throws SQLException {
160-
catalog = autoFillCatalog(catalog, session);
172+
// Only fetch currentCatalog if multiple catalog support is disabled
173+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
174+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
175+
return metadataResultSetBuilder.getColumnsResult(new ArrayList<>());
176+
}
177+
178+
catalog = autoFillCatalog(catalog, currentCatalog);
161179

162180
// Fetch columns from all catalogs if catalog is null
163181
if (catalog == null) {
@@ -183,7 +201,13 @@ public DatabricksResultSet listFunctions(
183201
String schemaNamePattern,
184202
String functionNamePattern)
185203
throws SQLException {
186-
catalog = autoFillCatalog(catalog, session);
204+
// Only fetch currentCatalog if multiple catalog support is disabled
205+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
206+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
207+
return metadataResultSetBuilder.getFunctionsResult(catalog, new ArrayList<>());
208+
}
209+
210+
catalog = autoFillCatalog(catalog, currentCatalog);
187211

188212
// Fetch current catalog if catalog is null
189213
if (catalog == null) {
@@ -212,7 +236,13 @@ public DatabricksResultSet listFunctions(
212236
@Override
213237
public DatabricksResultSet listPrimaryKeys(
214238
IDatabricksSession session, String catalog, String schema, String table) throws SQLException {
215-
catalog = autoFillCatalog(catalog, session);
239+
// Only fetch currentCatalog if multiple catalog support is disabled
240+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
241+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
242+
return metadataResultSetBuilder.getPrimaryKeysResult(new ArrayList<>());
243+
}
244+
245+
catalog = autoFillCatalog(catalog, currentCatalog);
216246

217247
// Return empty result set if catalog, schema, or table is null
218248
if (catalog == null || schema == null || table == null) {
@@ -222,7 +252,7 @@ public DatabricksResultSet listPrimaryKeys(
222252
schema,
223253
table);
224254
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
225-
MetadataResultConstants.PRIMARY_KEYS_COLUMNS,
255+
PRIMARY_KEYS_COLUMNS,
226256
new ArrayList<>(),
227257
METADATA_STATEMENT_ID,
228258
com.databricks.jdbc.common.CommandName.LIST_PRIMARY_KEYS);
@@ -239,7 +269,14 @@ public DatabricksResultSet listPrimaryKeys(
239269
public DatabricksResultSet listImportedKeys(
240270
IDatabricksSession session, String catalog, String schema, String table) throws SQLException {
241271
LOGGER.debug("public ResultSet listImportedKeys() using SDK");
242-
catalog = autoFillCatalog(catalog, session);
272+
273+
// Only fetch currentCatalog if multiple catalog support is disabled
274+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
275+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
276+
return metadataResultSetBuilder.getImportedKeys(new ArrayList<>());
277+
}
278+
279+
catalog = autoFillCatalog(catalog, currentCatalog);
243280

244281
// Return empty result set if catalog, schema, or table is null
245282
if (catalog == null || schema == null || table == null) {
@@ -249,7 +286,7 @@ public DatabricksResultSet listImportedKeys(
249286
schema,
250287
table);
251288
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
252-
MetadataResultConstants.IMPORTED_KEYS_COLUMNS,
289+
IMPORTED_KEYS_COLUMNS,
253290
new ArrayList<>(),
254291
METADATA_STATEMENT_ID,
255292
com.databricks.jdbc.common.CommandName.GET_IMPORTED_KEYS);
@@ -265,11 +302,7 @@ public DatabricksResultSet listImportedKeys(
265302
// This is a workaround for the issue where the SQL command fails with "syntax error at or
266303
// near "foreign""
267304
LOGGER.debug("SQL command failed with syntax error. Returning empty result set.");
268-
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
269-
MetadataResultConstants.IMPORTED_KEYS_COLUMNS,
270-
new ArrayList<>(),
271-
METADATA_STATEMENT_ID,
272-
com.databricks.jdbc.common.CommandName.GET_IMPORTED_KEYS);
305+
return metadataResultSetBuilder.getImportedKeys(new ArrayList<>());
273306
} else {
274307
throw e;
275308
}
@@ -280,13 +313,17 @@ public DatabricksResultSet listImportedKeys(
280313
public DatabricksResultSet listExportedKeys(
281314
IDatabricksSession session, String catalog, String schema, String table) throws SQLException {
282315
LOGGER.debug("public ResultSet listExportedKeys() using SDK");
283-
catalog = autoFillCatalog(catalog, session);
316+
317+
// Only fetch currentCatalog if multiple catalog support is disabled
318+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
319+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) {
320+
return metadataResultSetBuilder.getExportedKeys(new ArrayList<>());
321+
}
322+
323+
catalog = autoFillCatalog(catalog, currentCatalog);
324+
284325
// Exported keys not tracked in DBSQL. Returning empty result set
285-
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
286-
MetadataResultConstants.EXPORTED_KEYS_COLUMNS,
287-
new ArrayList<>(),
288-
METADATA_STATEMENT_ID,
289-
com.databricks.jdbc.common.CommandName.GET_EXPORTED_KEYS);
326+
return metadataResultSetBuilder.getExportedKeys(new ArrayList<>());
290327
}
291328

292329
@Override
@@ -300,6 +337,15 @@ public DatabricksResultSet listCrossReferences(
300337
String foreignTable)
301338
throws SQLException {
302339
LOGGER.debug("public ResultSet listCrossReferences() using SDK");
340+
341+
// Only fetch currentCatalog if multiple catalog support is disabled
342+
String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null;
343+
if (!metadataResultSetBuilder.shouldAllowCatalogAccess(parentCatalog, currentCatalog, session)
344+
|| !metadataResultSetBuilder.shouldAllowCatalogAccess(
345+
foreignCatalog, currentCatalog, session)) {
346+
return metadataResultSetBuilder.getCrossRefsResult(new ArrayList<>());
347+
}
348+
303349
CommandBuilder commandBuilder =
304350
new CommandBuilder(foreignCatalog, session).setSchema(foreignSchema).setTable(foreignTable);
305351
String SQL = commandBuilder.getSQLString(CommandName.LIST_FOREIGN_KEYS);
@@ -312,11 +358,7 @@ public DatabricksResultSet listCrossReferences(
312358
// near "foreign""
313359
// This is a known issue in Databricks for older DBSQL versions
314360
LOGGER.debug("SQL command failed with syntax error. Returning empty result set.");
315-
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
316-
MetadataResultConstants.CROSS_REFERENCE_COLUMNS,
317-
new ArrayList<>(),
318-
METADATA_STATEMENT_ID,
319-
com.databricks.jdbc.common.CommandName.GET_CROSS_REFERENCE);
361+
return metadataResultSetBuilder.getCrossRefsResult(new ArrayList<>());
320362
} else {
321363
LOGGER.error(
322364
e,
@@ -333,10 +375,20 @@ private boolean isMultipleCatalogSupportDisabled() {
333375
&& !sdkClient.getConnectionContext().getEnableMultipleCatalogSupport();
334376
}
335377

336-
private String autoFillCatalog(String catalog, IDatabricksSession session) throws SQLException {
337-
if (isMultipleCatalogSupportDisabled()) {
338-
String currentCatalog = session.getCurrentCatalog();
339-
return (currentCatalog != null && !currentCatalog.isEmpty()) ? currentCatalog : "";
378+
/**
379+
* Auto-fills the catalog parameter if multiple catalog support is disabled and catalog is null.
380+
*
381+
* @param catalog the catalog parameter to auto-fill
382+
* @param currentCatalog the current catalog from the session
383+
* @return the auto-filled catalog or the original catalog if no auto-fill is needed
384+
*/
385+
private String autoFillCatalog(String catalog, String currentCatalog) {
386+
if (isMultipleCatalogSupportDisabled() && catalog == null) {
387+
String result =
388+
(currentCatalog != null && !currentCatalog.isEmpty()) ? currentCatalog : "spark";
389+
LOGGER.debug(
390+
"Auto-filling null catalog with '{}' when multiple catalog support is disabled", result);
391+
return result;
340392
}
341393
return catalog;
342394
}
@@ -446,7 +498,7 @@ private DatabricksResultSet fetchColumnsAcrossCatalogs(
446498

447499
// Convert combined data into a result set
448500
return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns(
449-
MetadataResultConstants.COLUMN_COLUMNS,
501+
COLUMN_COLUMNS,
450502
columnRows,
451503
METADATA_STATEMENT_ID,
452504
com.databricks.jdbc.common.CommandName.LIST_COLUMNS);

0 commit comments

Comments
 (0)