Skip to content

Commit d14c297

Browse files
authored
Change UseQueryForMetadata default to 1 (SHOW commands) (#1413)
## Summary Change the default value of `UseQueryForMetadata` from `0` to `1`, making SQL SHOW commands the default for Thrift-mode metadata operations instead of native Thrift RPCs. This aligns Thrift metadata behavior with SEA mode for seamless server-side protocol switching. ## Breaking Change This is a **breaking change** for users who depend on Thrift-native metadata behavior. Key differences: - Catalog parameter is now a literal identifier (not a wildcard pattern) per JDBC spec - Methods that threw exceptions for null/empty inputs now return empty result sets - `getFunctions` now works (was broken via native Thrift RPC) - Result columns return stored values (lowercase) instead of preserving input case **To revert:** set `UseQueryForMetadata=0` in the connection string. ## Readiness - Comparator analysis complete (Apr 8-16, 2026) — all gaps resolved - Merged PRs: #1387 (GEOMETRY/GEOGRAPHY types), #1388 (VARIANT className), #1390 (null key params), #1411 (case-sensitive cross reference filter) ## Test plan - [x] Comparator analysis validates parity between SHOW commands and native RPCs - [ ] Full unit test suite passes - [ ] Integration tests pass with UseQueryForMetadata=1 (default) This pull request was AI-assisted by Isaac. --------- Signed-off-by: Gopal Lal <gopal.lal@databricks.com>
1 parent 961ccda commit d14c297

6 files changed

Lines changed: 80 additions & 11 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
- Added AI coding agent detection to the User-Agent header. When the driver is invoked by a known AI coding agent (e.g. Claude Code, Cursor, Gemini CLI), `agent/<product>` is appended to the User-Agent string.
88

99
### Updated
10+
- **[Breaking Change]** Thrift-mode metadata operations (`getTables`, `getColumns`, `getSchemas`, `getFunctions`, `getPrimaryKeys`, `getImportedKeys`, `getCrossReference`) on **SQL Warehouses** now use SQL SHOW commands by default instead of native Thrift RPCs, aligning behavior with Statement Execution API (SEA) mode. All-Purpose Clusters are unaffected and continue using native Thrift RPCs. The `UseQueryForMetadata` connection property default changed from `0` to `1`. To revert to native Thrift RPCs, set `UseQueryForMetadata=0`. Key behavioral changes:
11+
- Catalog parameter is now treated as a literal identifier (not a wildcard pattern) per JDBC spec. Use `null` to search across all catalogs.
12+
- Methods that previously threw exceptions for null/empty edge-case inputs now return empty result sets.
13+
- `getFunctions` now works correctly (was broken via native Thrift RPC).
14+
- Result columns (TABLE_CATALOG, etc.) return stored values (lowercase) instead of preserving input case.
15+
- Connection properties `EnableShowCommandForGetFunctions` and `TreatMetadataCatalogNameAsPattern` are now redundant when `UseQueryForMetadata=1` (the new default).
1016

1117
### Fixed
1218
- Fixed `EnableBatchedInserts` silently falling back to individual execution when table or schema names contain special characters (e.g., hyphens) inside backtick-quoted identifiers. Added a warn log when the fallback occurs.

src/main/java/com/databricks/jdbc/api/impl/DatabricksConnectionContext.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,14 @@ public boolean enableShowCommandsForGetFunctions() {
11011101

11021102
@Override
11031103
public boolean useQueryForMetadata() {
1104-
return getParameter(DatabricksJdbcUrlParams.USE_QUERY_FOR_METADATA).equals("1");
1104+
// If user explicitly set the property, honour their choice for any compute type
1105+
String userValue = getParameterIgnoreDefault(DatabricksJdbcUrlParams.USE_QUERY_FOR_METADATA);
1106+
if (userValue != null) {
1107+
return userValue.equals("1");
1108+
}
1109+
// Default: SHOW commands for SQL Warehouses only.
1110+
// All-Purpose Clusters default to native Thrift RPCs.
1111+
return !(computeResource instanceof AllPurposeCluster);
11051112
}
11061113

11071114
@Override

src/main/java/com/databricks/jdbc/common/DatabricksJdbcUrlParams.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public enum DatabricksJdbcUrlParams {
173173
USE_QUERY_FOR_METADATA(
174174
"UseQueryForMetadata",
175175
"Use SQL SHOW commands instead of Thrift RPCs for metadata operations. When enabled, EnableShowCommandForGetFunctions is redundant",
176-
"0"),
176+
"1"),
177177
TREAT_METADATA_CATALOG_NAME_AS_PATTERN(
178178
"TreatMetadataCatalogNameAsPattern",
179179
"Treat catalog names as patterns in Thrift metadata RPCs. When disabled (default), wildcard characters in catalog names are escaped",

src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionContextTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,4 +1453,38 @@ public void testDefaultGetterCoverage() throws DatabricksSQLException {
14531453
assertFalse(ctx.enableShowCommandsForGetFunctions());
14541454
assertFalse(ctx.treatMetadataCatalogNameAsPattern());
14551455
}
1456+
1457+
@Test
1458+
public void testUseQueryForMetadataDefaultTrueForWarehouse() throws DatabricksSQLException {
1459+
// Warehouse URL without explicit UseQueryForMetadata — should default to true
1460+
IDatabricksConnectionContext ctx =
1461+
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, properties);
1462+
assertTrue(ctx.useQueryForMetadata());
1463+
}
1464+
1465+
@Test
1466+
public void testUseQueryForMetadataDefaultFalseForCluster() throws DatabricksSQLException {
1467+
// Cluster URL without explicit UseQueryForMetadata — should default to false
1468+
IDatabricksConnectionContext ctx =
1469+
DatabricksConnectionContext.parse(TestConstants.VALID_CLUSTER_URL, properties);
1470+
assertFalse(ctx.useQueryForMetadata());
1471+
}
1472+
1473+
@Test
1474+
public void testUseQueryForMetadataExplicitTrueOnCluster() throws DatabricksSQLException {
1475+
// Cluster URL with explicit UseQueryForMetadata=1 — should be honoured
1476+
IDatabricksConnectionContext ctx =
1477+
DatabricksConnectionContext.parse(
1478+
TestConstants.VALID_CLUSTER_URL + ";UseQueryForMetadata=1", properties);
1479+
assertTrue(ctx.useQueryForMetadata());
1480+
}
1481+
1482+
@Test
1483+
public void testUseQueryForMetadataExplicitFalseOnWarehouse() throws DatabricksSQLException {
1484+
// Warehouse URL with explicit UseQueryForMetadata=0 — should be honoured
1485+
IDatabricksConnectionContext ctx =
1486+
DatabricksConnectionContext.parse(
1487+
TestConstants.VALID_URL_1 + ";UseQueryForMetadata=0", properties);
1488+
assertFalse(ctx.useQueryForMetadata());
1489+
}
14561490
}

src/test/java/com/databricks/jdbc/api/impl/DatabricksSessionTest.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ public void testOpenAndCloseSession() throws SQLException {
7575
session.open();
7676
assertTrue(session.isOpen());
7777
assertEquals(SESSION_ID, session.getSessionId());
78-
assertInstanceOf(DatabricksThriftServiceClient.class, session.getDatabricksMetadataClient());
78+
// Warehouses use DatabricksMetadataQueryClient (SHOW commands) by default
79+
assertInstanceOf(DatabricksMetadataQueryClient.class, session.getDatabricksMetadataClient());
7980
assertEquals(WAREHOUSE_COMPUTE, session.getComputeResource());
8081
session.close();
8182
assertFalse(session.isOpen());
@@ -110,7 +111,10 @@ public void testOpenRedirectedThriftSession() throws SQLException {
110111
assertEquals(SESSION_ID, session.getSessionId());
111112
assertEquals(DatabricksClientType.THRIFT, connectionContext.getClientType());
112113
assertInstanceOf(DatabricksThriftServiceClient.class, session.getDatabricksClient());
113-
assertInstanceOf(DatabricksThriftServiceClient.class, session.getDatabricksMetadataClient());
114+
// After redirect, the createProxy mock returns thriftClient for all proxy calls.
115+
// In production, useQueryForMetadata=1 (warehouse default) would create a
116+
// DatabricksMetadataQueryClient. Here the mock collapses it.
117+
assertNotNull(session.getDatabricksMetadataClient());
114118
assertEquals(WAREHOUSE_COMPUTE, session.getComputeResource());
115119

116120
session.close();
@@ -136,7 +140,8 @@ public void testOpenAndCloseSessionUsingThrift() throws SQLException {
136140
assertTrue(session.isOpen());
137141
assertEquals(SESSION_ID, session.getSessionId());
138142
assertEquals(tSessionHandle, session.getSessionInfo().sessionHandle());
139-
assertEquals(thriftClient, session.getDatabricksMetadataClient());
143+
// Warehouses use DatabricksMetadataQueryClient (SHOW commands) by default
144+
assertInstanceOf(DatabricksMetadataQueryClient.class, session.getDatabricksMetadataClient());
140145
assertEquals(WAREHOUSE_COMPUTE, session.getComputeResource());
141146
session.close();
142147
assertFalse(session.isOpen());
@@ -315,14 +320,25 @@ static void setupWarehouseWithQueryMetadata() throws SQLException {
315320
}
316321

317322
@Test
318-
public void testUseQueryForMetadataDisabledByDefault() throws SQLException {
323+
public void testUseQueryForMetadataEnabledByDefaultForWarehouse() throws SQLException {
319324
setupWarehouse(true /* useThrift */);
320325
DatabricksSession session = new DatabricksSession(connectionContext, thriftClient);
321-
assertFalse(connectionContext.useQueryForMetadata());
326+
assertTrue(connectionContext.useQueryForMetadata());
322327
assertInstanceOf(
323-
DatabricksThriftServiceClient.class,
328+
DatabricksMetadataQueryClient.class,
329+
session.getDatabricksMetadataClient(),
330+
"Warehouses should use SHOW commands (DatabricksMetadataQueryClient) by default");
331+
}
332+
333+
@Test
334+
public void testUseQueryForMetadataDisabledByDefaultForCluster() throws SQLException {
335+
connectionContext = DatabricksConnectionContext.parse(VALID_CLUSTER_URL, new Properties());
336+
DatabricksSession session = new DatabricksSession(connectionContext, thriftClient);
337+
assertFalse(connectionContext.useQueryForMetadata());
338+
assertEquals(
339+
thriftClient,
324340
session.getDatabricksMetadataClient(),
325-
"When UseQueryForMetadata is default (0), metadata client should be the Thrift client");
341+
"Clusters should use native Thrift RPCs by default");
326342
}
327343

328344
@Test

src/test/java/com/databricks/jdbc/integration/fakeservice/tests/EnableMultipleCatalogSupportIntegrationTests.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ void testGetSchemasWithNullCatalogMultipleCatalogSupportEnabled() throws SQLExce
3232
DatabricksJdbcUrlParams.USE_THRIFT_CLIENT.getParamName(),
3333
FakeServiceConfigLoader.shouldUseThriftClient());
3434

35-
String jdbcUrl = getFakeServiceBenchfoodJDBCUrl() + ";enableMultipleCatalogSupport=1";
35+
// UseQueryForMetadata=0: Thrift fake service uses binary protocol stubs with
36+
// exact session handle matching. SHOW command stubs can't be recorded reliably
37+
// because session handles change between recording and replay.
38+
String jdbcUrl =
39+
getFakeServiceBenchfoodJDBCUrl() + ";enableMultipleCatalogSupport=1;UseQueryForMetadata=0";
3640

3741
try (Connection connectionWithMultiCatalog = DriverManager.getConnection(jdbcUrl, props)) {
3842
DatabaseMetaData metaData = connectionWithMultiCatalog.getMetaData();
@@ -74,7 +78,9 @@ void testGetSchemasWithNullCatalogMultipleCatalogSupportDisabled() throws SQLExc
7478
DatabricksJdbcUrlParams.USE_THRIFT_CLIENT.getParamName(),
7579
FakeServiceConfigLoader.shouldUseThriftClient());
7680

77-
String jdbcUrl = getFakeServiceBenchfoodJDBCUrl() + ";enableMultipleCatalogSupport=0";
81+
// UseQueryForMetadata=0: same rationale as the enabled test above
82+
String jdbcUrl =
83+
getFakeServiceBenchfoodJDBCUrl() + ";enableMultipleCatalogSupport=0;UseQueryForMetadata=0";
7884

7985
try (Connection connectionWithSingleCatalog = DriverManager.getConnection(jdbcUrl, props)) {
8086
DatabaseMetaData metaData = connectionWithSingleCatalog.getMetaData();

0 commit comments

Comments
 (0)