Skip to content

[PECOBLR-1746] Implementing support for listing procedures#1238

Open
msrathore-db wants to merge 9 commits intodatabricks:mainfrom
msrathore-db:PECOBLR-1746
Open

[PECOBLR-1746] Implementing support for listing procedures#1238
msrathore-db wants to merge 9 commits intodatabricks:mainfrom
msrathore-db:PECOBLR-1746

Conversation

@msrathore-db
Copy link
Collaborator

@msrathore-db msrathore-db commented Feb 27, 2026

Description

Implements getProcedures and getProcedureColumns in DatabricksDatabaseMetaData by querying information_schema.routines and information_schema.parameters via SQL.

Unlike other metadata operations that use SHOW commands or Thrift RPCs, these use direct SQL SELECT queries against information_schema views. This works for both Thrift and SEA transports.

getProcedures:

  • Queries information_schema.routines filtered by routine_type = 'PROCEDURE'
  • Returns 9-column JDBC-spec result set (PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME, reserved x3, REMARKS, PROCEDURE_TYPE, SPECIFIC_NAME)
  • PROCEDURE_TYPE is always procedureNoResult (1)

getProcedureColumns:

  • Queries information_schema.parameters JOINed with information_schema.routines to filter for procedures
  • Returns 20-column JDBC-spec result set with parameter metadata
  • Maps parameter_mode (IN/OUT/INOUT) to JDBC COLUMN_TYPE constants (1/4/2)
  • Maps Databricks type names to java.sql.Types codes via existing getCode()

Catalog resolution:

  • NULL catalog → queries system.information_schema.routines (cross-catalog)
  • Specific catalog → queries <catalog>.information_schema.routines
  • Empty string → returns empty result set

Shared SQL builders in CommandConstants eliminate duplication between SDK and Thrift clients.

Testing

Unit tests (DatabricksMetadataSdkClientTest):

  • 4 parameterized tests for listProcedures SQL generation (catalog+schema+name, null schema, null name, null catalog)
  • 3 parameterized tests for listProcedureColumns SQL generation (all filters, partial filters, all nulls)

Integration test (MetadataIntegrationTests#testGetProceduresAndProcedureColumns):

  • Creates a procedure jdbc_test_compute_area(x DOUBLE, y DOUBLE, OUT area DOUBLE) with COMMENT
  • Verifies getProcedures returns correct name, schema, catalog, remarks, type
  • Verifies getProcedureColumns returns 3 params with correct COLUMN_TYPE (IN=1, OUT=4), DATA_TYPE (DOUBLE=8), ordinal positions
  • Tests column name filtering
  • Cleans up procedure after test
  • WireMock stubs recorded for REPLAY mode

Existing tests: All 248 DatabricksDatabaseMetaDataTest + 51 DatabricksMetadataSdkClientTest pass.

Additional Notes to the Reviewer

  • information_schema.parameters is used (not routine_columns) because routine_columns contains table-valued function output columns, not procedure parameters.
  • NULLABLE is always procedureNullableUnknown (2) since the server does not track parameter nullability.
  • When catalog is NULL, system.information_schema is queried which requires system table access permissions. If the user lacks this permission, the driver returns an empty result set. This will be addressed server-side in a future release.

msrathore-db and others added 7 commits February 27, 2026 19:30
…SQL queries

Implement JDBC-compliant getProcedures and getProcedureColumns by querying
information_schema.ROUTINES and information_schema.parameters via SQL.
This approach works for both thrift and SEA transports without using thrift RPC.

- getProcedures: queries ROUTINES filtered by routine_type='PROCEDURE'
- getProcedureColumns: queries parameters joined with ROUTINES for procedures
- Null catalog uses system.information_schema; specific catalog uses <catalog>.information_schema
- Shared SQL builders in CommandConstants to avoid duplication across SDK/Thrift clients
- Maps parameter_mode (IN/OUT/INOUT) to JDBC COLUMN_TYPE constants
- Maps Databricks type names to java.sql.Types via existing getCode()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…line helpers

- Extract table names, column lists, and filter into named constants in CommandConstants
- Remove getProcedureColumnPrecision/getProcedureColumnLength helpers with unused dataType param
- Inline precision/length logic directly in getRowsForProcedureColumns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ocedureColumns

Unit tests (DatabricksMetadataSdkClientTest):
- testListProcedures: parameterized tests for SQL generation with various
  catalog/schema/name combinations including null catalog (system prefix)
- testListProcedureColumns: parameterized tests for SQL generation with
  JOIN to routines table, all filter combinations

Integration test (MetadataIntegrationTests):
- testGetProceduresAndProcedureColumns: creates a test procedure with
  IN and OUT params, verifies getProcedures returns correct metadata
  (name, remarks, type), verifies getProcedureColumns returns correct
  parameter info (column types IN=1/OUT=4, data types, ordinal positions),
  tests column name filtering, cleans up procedure after test

Also renames ROUTINES -> routines in CommandConstants for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…I changes

Add GET_PROCEDURES and GET_PROCEDURE_COLUMNS to MetadataOperationType enum.
Update getResultSet/executeStatement calls to pass the new required
MetadataOperationType parameter after upstream API signature change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
String.format treats the single quote before %s as a flag character,
causing UnknownFormatConversion when patterns like '%' are passed.
Replace String.format with StringBuilder.append for SQL LIKE clauses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… '%'

The LOGGER.error(e, "message {}", e) call uses String.format internally.
When the exception message contains SQL with LIKE '%', the '%' is
interpreted as a format specifier causing UnknownFormatConversionException.
Use plain log message without exception interpolation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…m values

Add GET_PROCEDURES and GET_PROCEDURE_COLUMNS to the enum count assertion
and parameterized header value tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sql.append(" AND routine_schema LIKE '").append(schemaPattern).append("'");
}
if (procedureNamePattern != null) {
sql.append(" AND routine_name LIKE '").append(procedureNamePattern).append("'");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it possible to use parameterized statements here and provide user provided values as parameters? This will prevent SQL injection issues

}

private static String getCatalogPrefix(String catalog) {
return (catalog == null) ? "system" : "`" + catalog + "`";
Copy link
Collaborator

Choose a reason for hiding this comment

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

what if catalogName already contains a backtick character? Should we escape that?

return (short) procedureColumnUnknown;
}
switch (parameterMode.toUpperCase()) {
case "IN":
Copy link
Collaborator

Choose a reason for hiding this comment

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

declare as constants

try {
Object val = resultSet.getObject(columnName);
return val != null ? val.toString() : null;
} catch (SQLException e) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

add logging

if (val == null) return null;
if (val instanceof Number) return ((Number) val).intValue();
return Integer.parseInt(val.toString());
} catch (SQLException | NumberFormatException e) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

add logging

if (val == null) return null;
if (val instanceof Number) return ((Number) val).shortValue();
return Short.parseShort(val.toString());
} catch (SQLException | NumberFormatException e) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

add logging

Integer charMaxLength = getIntOrNull(resultSet, "character_maximum_length");
Integer charOctetLength = getIntOrNull(resultSet, "character_octet_length");
row.add(numericPrecision != null ? numericPrecision : charMaxLength); // PRECISION
row.add(charOctetLength != null ? charOctetLength : numericPrecision); // LENGTH
Copy link
Collaborator

Choose a reason for hiding this comment

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

but numericPrecision can be null also

Integer numericPrecision = getIntOrNull(resultSet, "numeric_precision");
Integer charMaxLength = getIntOrNull(resultSet, "character_maximum_length");
Integer charOctetLength = getIntOrNull(resultSet, "character_octet_length");
row.add(numericPrecision != null ? numericPrecision : charMaxLength); // PRECISION
Copy link
Collaborator

Choose a reason for hiding this comment

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

charMaxLength itself can be null

Integer charMaxLength = getIntOrNull(resultSet, "character_maximum_length");
Integer charOctetLength = getIntOrNull(resultSet, "character_octet_length");
row.add(numericPrecision != null ? numericPrecision : charMaxLength); // PRECISION
row.add(charOctetLength != null ? charOctetLength : numericPrecision); // LENGTH
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is length being fallback to precision?

Arguments.of(
"SELECT p.specific_catalog, p.specific_schema, p.specific_name,"
+ " p.parameter_name, p.parameter_mode, p.is_result,"
+ " p.data_type, p.full_data_type,"
Copy link
Collaborator

Choose a reason for hiding this comment

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

where is full_data_type used?

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.

2 participants