From 4a2b52d8201c4e736b41183b75cd3b8cd24dc163 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 19:30:36 +0530 Subject: [PATCH 1/9] [PECOBLR-1746] Implement getProcedures and getProcedureColumns using 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 .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) --- .../api/impl/DatabricksDatabaseMetaData.java | 76 ++------ .../databricks/jdbc/common/CommandName.java | 3 +- .../jdbc/common/MetadataResultConstants.java | 23 ++- .../dbclient/IDatabricksMetadataClient.java | 36 ++++ .../impl/common/CommandConstants.java | 75 ++++++++ .../impl/common/MetadataResultSetBuilder.java | 165 ++++++++++++++++++ .../DatabricksEmptyMetadataClient.java | 23 +++ .../sqlexec/DatabricksMetadataSdkClient.java | 41 +++++ .../thrift/DatabricksThriftServiceClient.java | 66 +++++++ .../impl/DatabricksDatabaseMetaDataTest.java | 6 + 10 files changed, 453 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java index 62170b80a2..e283b00442 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java @@ -892,21 +892,6 @@ public boolean supportsSharding() throws SQLException { return false; } - /** - * Builds the result set for stored procedures metadata. - * - *

The result set structure is defined based on the JDBC driver specifications to ensure - * consistency. The following columns are included in the result set: - * - *

    - *
  • PROCEDURE_CAT: The catalog of the procedure (String) - *
  • PROCEDURE_SCHEM: The schema of the procedure (String) - *
  • PROCEDURE_NAME: The name of the procedure (String) - *
  • REMARKS: A description or remarks about the procedure (String) - *
  • PROCEDURE_TYPE: The type of procedure (e.g., FUNCTION, PROCEDURE) (String) - *
  • SPECIFIC_NAME: The specific name for the procedure (String) - *
- */ @Override public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { @@ -916,44 +901,14 @@ public ResultSet getProcedures(String catalog, String schemaPattern, String proc schemaPattern, procedureNamePattern); throwExceptionIfConnectionIsClosed(); - return new DatabricksResultSet( - new StatementStatus().setState(StatementState.SUCCEEDED), - new StatementId("getprocedures-metadata"), - Arrays.asList( - "PROCEDURE_CAT", - "PROCEDURE_SCHEM", - "PROCEDURE_NAME", - "NUM_INPUT_PARAMS", - "NUM_OUTPUT_PARAMS", - "NUM_RESULT_SETS", - "REMARKS", - "PROCEDURE_TYPE", - "SPECIFIC_NAME"), - Arrays.asList( - "VARCHAR", - "VARCHAR", - "VARCHAR", - "INTEGER", - "INTEGER", - "INTEGER", - "VARCHAR", - "SMALLINT", - "VARCHAR"), - new int[] { - Types.VARCHAR, - Types.VARCHAR, - Types.VARCHAR, - Types.INTEGER, - Types.INTEGER, - Types.INTEGER, - Types.VARCHAR, - Types.SMALLINT, - Types.VARCHAR - }, - new int[] {128, 128, 128, 10, 10, 10, 254, 5, 128}, - new int[] {1, 1, 0, 1, 1, 1, 1, 1, 0}, - new Object[0][0], - StatementType.METADATA); + try { + return session + .getDatabricksMetadataClient() + .listProcedures(session, catalog, schemaPattern, procedureNamePattern); + } catch (Exception e) { + LOGGER.error(e, "Unable to fetch procedures, returning empty result set {}", e); + return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); + } } @Override @@ -967,12 +922,15 @@ public ResultSet getProcedureColumns( procedureNamePattern, columnNamePattern); throwExceptionIfConnectionIsClosed(); - - return metadataResultSetBuilder.getResultSetWithGivenRowsAndColumns( - PROCEDURE_COLUMNS_COLUMNS, - new ArrayList<>(), - METADATA_STATEMENT_ID, - CommandName.GET_PROCEDURES_COLUMNS); + try { + return session + .getDatabricksMetadataClient() + .listProcedureColumns( + session, catalog, schemaPattern, procedureNamePattern, columnNamePattern); + } catch (Exception e) { + LOGGER.error(e, "Unable to fetch procedure columns, returning empty result set {}", e); + return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); + } } @Override diff --git a/src/main/java/com/databricks/jdbc/common/CommandName.java b/src/main/java/com/databricks/jdbc/common/CommandName.java index 3e4b3a85ea..6a9527ebca 100644 --- a/src/main/java/com/databricks/jdbc/common/CommandName.java +++ b/src/main/java/com/databricks/jdbc/common/CommandName.java @@ -22,7 +22,8 @@ public enum CommandName { GET_TABLE_PRIVILEGES, GET_VERSION_COLUMNS, GET_SUPER_TYPES, - GET_PROCEDURES_COLUMNS, + LIST_PROCEDURES, + LIST_PROCEDURE_COLUMNS, GET_INDEX_INFO, GET_SUPER_TABLES, GET_FUNCTION_COLUMNS, diff --git a/src/main/java/com/databricks/jdbc/common/MetadataResultConstants.java b/src/main/java/com/databricks/jdbc/common/MetadataResultConstants.java index 6adc4d9b3f..f53ad7d1de 100644 --- a/src/main/java/com/databricks/jdbc/common/MetadataResultConstants.java +++ b/src/main/java/com/databricks/jdbc/common/MetadataResultConstants.java @@ -195,6 +195,14 @@ public class MetadataResultConstants { private static final ResultColumn RADIX = new ResultColumn("RADIX", "radix", Types.SMALLINT); private static final ResultColumn NULLABLE_SHORT = new ResultColumn("NULLABLE", "nullable", Types.SMALLINT); + private static final ResultColumn NUM_INPUT_PARAMS = + new ResultColumn("NUM_INPUT_PARAMS", "numInputParams", Types.INTEGER); + private static final ResultColumn NUM_OUTPUT_PARAMS = + new ResultColumn("NUM_OUTPUT_PARAMS", "numOutputParams", Types.INTEGER); + private static final ResultColumn NUM_RESULT_SETS = + new ResultColumn("NUM_RESULT_SETS", "numResultSets", Types.INTEGER); + private static final ResultColumn PROCEDURE_TYPE = + new ResultColumn("PROCEDURE_TYPE", "procedureType", Types.SMALLINT); private static final ResultColumn NON_UNIQUE = new ResultColumn("NON_UNIQUE", "nonUnique", Types.BOOLEAN); private static final ResultColumn INDEX_QUALIFIER = @@ -225,6 +233,18 @@ public class MetadataResultConstants { FUNCTION_TYPE_COLUMN, SPECIFIC_NAME_COLUMN); + public static final List PROCEDURES_COLUMNS = + List.of( + PROCEDURE_CAT, + PROCEDURE_SCHEM, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS_COLUMN, + PROCEDURE_TYPE, + SPECIFIC_NAME_COLUMN); + public static List COLUMN_COLUMNS = List.of( CATALOG_COLUMN, @@ -618,8 +638,9 @@ public class MetadataResultConstants { CommandName.GET_VERSION_COLUMNS, List.of(SCOPE, COL_NAME_COLUMN, DATA_TYPE_COLUMN, TYPE_NAME_COLUMN, PSEUDO_COLUMN)); put(CommandName.GET_SUPER_TYPES, List.of(TYPE_NAME_COLUMN, SUPERTYPE_NAME)); + put(CommandName.LIST_PROCEDURES, List.of(PROCEDURE_NAME, SPECIFIC_NAME_COLUMN)); put( - CommandName.GET_PROCEDURES_COLUMNS, + CommandName.LIST_PROCEDURE_COLUMNS, List.of( PROCEDURE_NAME, COLUMN_NAME_COLUMN, diff --git a/src/main/java/com/databricks/jdbc/dbclient/IDatabricksMetadataClient.java b/src/main/java/com/databricks/jdbc/dbclient/IDatabricksMetadataClient.java index ed01dd44de..d8929f9db8 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/IDatabricksMetadataClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/IDatabricksMetadataClient.java @@ -132,6 +132,42 @@ DatabricksResultSet listImportedKeys( DatabricksResultSet listExportedKeys( IDatabricksSession session, String catalog, String schema, String table) throws SQLException; + /** + * Returns the list of stored procedures + * + * @param session underlying session + * @param catalog catalogName; null means use system catalog + * @param schemaNamePattern schema name pattern (can be a LIKE pattern) + * @param procedureNamePattern procedure name pattern (can be a LIKE pattern) + * @return a DatabricksResultSet representing list of procedures + */ + @DatabricksMetricsTimed + DatabricksResultSet listProcedures( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern) + throws SQLException; + + /** + * Returns the list of stored procedure columns/parameters + * + * @param session underlying session + * @param catalog catalogName; null means use system catalog + * @param schemaNamePattern schema name pattern (can be a LIKE pattern) + * @param procedureNamePattern procedure name pattern (can be a LIKE pattern) + * @param columnNamePattern column/parameter name pattern (can be a LIKE pattern) + * @return a DatabricksResultSet representing list of procedure columns + */ + @DatabricksMetricsTimed + DatabricksResultSet listProcedureColumns( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern, + String columnNamePattern) + throws SQLException; + /** * Returns the list of cross references between a parent table and a foreign table * diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java index be80667c46..7f241b5085 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java @@ -6,6 +6,8 @@ public class CommandConstants { public static final String GET_CATALOGS_STATEMENT_ID = "getcatalogs-metadata"; public static final String GET_TABLE_TYPE_STATEMENT_ID = "gettabletype-metadata"; public static final String GET_FUNCTIONS_STATEMENT_ID = "getfunctions-metadata"; + public static final String GET_PROCEDURES_STATEMENT_ID = "getprocedures-metadata"; + public static final String GET_PROCEDURE_COLUMNS_STATEMENT_ID = "getprocedurecolumns-metadata"; public static final String SHOW_CATALOGS_SQL = "SHOW CATALOGS"; public static final String SHOW_TABLE_TYPES_SQL = "SHOW TABLE_TYPES"; public static final String IN_CATALOG_SQL = " IN CATALOG `%s`"; @@ -26,4 +28,77 @@ public class CommandConstants { "SHOW KEYS" + IN_CATALOG_SQL + IN_ABSOLUTE_SCHEMA_SQL + IN_ABSOLUTE_TABLE_SQL; public static final String SHOW_FOREIGN_KEYS_SQL = "SHOW FOREIGN KEYS" + IN_CATALOG_SQL + IN_ABSOLUTE_SCHEMA_SQL + IN_ABSOLUTE_TABLE_SQL; + + /** + * Builds a SQL query to fetch stored procedures from information_schema.ROUTINES. + * + * @param catalog the catalog name; null means use system catalog + * @param schemaPattern schema LIKE pattern; null means no filter + * @param procedureNamePattern procedure name LIKE pattern; null means no filter + */ + public static String buildProceduresSQL( + String catalog, String schemaPattern, String procedureNamePattern) { + String catalogPrefix = getCatalogPrefix(catalog); + StringBuilder sql = new StringBuilder(); + sql.append( + String.format( + "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + + " FROM %s.information_schema.ROUTINES" + + " WHERE routine_type = 'PROCEDURE'", + catalogPrefix)); + if (schemaPattern != null) { + sql.append(String.format(" AND routine_schema LIKE '%s'", schemaPattern)); + } + if (procedureNamePattern != null) { + sql.append(String.format(" AND routine_name LIKE '%s'", procedureNamePattern)); + } + sql.append(" ORDER BY routine_catalog, routine_schema, routine_name"); + return sql.toString(); + } + + /** + * Builds a SQL query to fetch stored procedure columns/parameters from + * information_schema.parameters joined with ROUTINES. + * + * @param catalog the catalog name; null means use system catalog + * @param schemaPattern schema LIKE pattern; null means no filter + * @param procedureNamePattern procedure name LIKE pattern; null means no filter + * @param columnNamePattern column/parameter name LIKE pattern; null means no filter + */ + public static String buildProcedureColumnsSQL( + String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) { + String catalogPrefix = getCatalogPrefix(catalog); + StringBuilder sql = new StringBuilder(); + sql.append( + String.format( + "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," + + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," + + " p.character_maximum_length, p.character_octet_length," + + " p.ordinal_position, p.parameter_default, p.comment" + + " FROM %s.information_schema.parameters p" + + " JOIN %s.information_schema.ROUTINES r" + + " ON p.specific_catalog = r.specific_catalog" + + " AND p.specific_schema = r.specific_schema" + + " AND p.specific_name = r.specific_name" + + " WHERE r.routine_type = 'PROCEDURE'", + catalogPrefix, catalogPrefix)); + if (schemaPattern != null) { + sql.append(String.format(" AND p.specific_schema LIKE '%s'", schemaPattern)); + } + if (procedureNamePattern != null) { + sql.append(String.format(" AND p.specific_name LIKE '%s'", procedureNamePattern)); + } + if (columnNamePattern != null) { + sql.append(String.format(" AND p.parameter_name LIKE '%s'", columnNamePattern)); + } + sql.append( + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position"); + return sql.toString(); + } + + private static String getCatalogPrefix(String catalog) { + return (catalog == null) ? "system" : String.format("`%s`", catalog); + } } diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java index 7ef3c938b6..989f941fea 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java @@ -510,6 +510,44 @@ public DatabricksResultSet getFunctionsResult(DatabricksResultSet resultSet, Str CommandName.LIST_FUNCTIONS); } + public DatabricksResultSet getProceduresResult(DatabricksResultSet resultSet) + throws SQLException { + List> rows = getRowsForProcedures(resultSet); + return buildResultSet( + PROCEDURES_COLUMNS, + rows, + GET_PROCEDURES_STATEMENT_ID, + resultSet.getMetaData(), + CommandName.LIST_PROCEDURES); + } + + public DatabricksResultSet getProceduresResult(List> rows) { + return buildResultSet( + PROCEDURES_COLUMNS, + rows != null ? rows : new ArrayList<>(), + GET_PROCEDURES_STATEMENT_ID, + CommandName.LIST_PROCEDURES); + } + + public DatabricksResultSet getProcedureColumnsResult(DatabricksResultSet resultSet) + throws SQLException { + List> rows = getRowsForProcedureColumns(resultSet); + return buildResultSet( + PROCEDURE_COLUMNS_COLUMNS, + rows, + GET_PROCEDURE_COLUMNS_STATEMENT_ID, + resultSet.getMetaData(), + CommandName.LIST_PROCEDURE_COLUMNS); + } + + public DatabricksResultSet getProcedureColumnsResult(List> rows) { + return buildResultSet( + PROCEDURE_COLUMNS_COLUMNS, + rows != null ? rows : new ArrayList<>(), + GET_PROCEDURE_COLUMNS_STATEMENT_ID, + CommandName.LIST_PROCEDURE_COLUMNS); + } + public DatabricksResultSet getColumnsResult(DatabricksResultSet resultSet) throws SQLException { List> rows = getRows(resultSet, COLUMN_COLUMNS, defaultAdapter); return buildResultSet( @@ -1125,6 +1163,133 @@ private List> getRowsForFunctions( return rows; } + private List> getRowsForProcedures(DatabricksResultSet resultSet) + throws SQLException { + List> rows = new ArrayList<>(); + while (resultSet.next()) { + List row = new ArrayList<>(); + row.add(getStringOrNull(resultSet, "routine_catalog")); // PROCEDURE_CAT + row.add(getStringOrNull(resultSet, "routine_schema")); // PROCEDURE_SCHEM + row.add(getStringOrNull(resultSet, "routine_name")); // PROCEDURE_NAME + row.add(null); // NUM_INPUT_PARAMS (reserved) + row.add(null); // NUM_OUTPUT_PARAMS (reserved) + row.add(null); // NUM_RESULT_SETS (reserved) + row.add(getStringOrNull(resultSet, "comment")); // REMARKS + row.add((short) procedureNoResult); // PROCEDURE_TYPE + row.add(getStringOrNull(resultSet, "specific_name")); // SPECIFIC_NAME + rows.add(row); + } + return rows; + } + + private List> getRowsForProcedureColumns(DatabricksResultSet resultSet) + throws SQLException { + List> rows = new ArrayList<>(); + while (resultSet.next()) { + String dataType = getStringOrNull(resultSet, "data_type"); + String parameterMode = getStringOrNull(resultSet, "parameter_mode"); + String isResult = getStringOrNull(resultSet, "is_result"); + + List row = new ArrayList<>(); + row.add(getStringOrNull(resultSet, "specific_catalog")); // PROCEDURE_CAT + row.add(getStringOrNull(resultSet, "specific_schema")); // PROCEDURE_SCHEM + row.add(getStringOrNull(resultSet, "specific_name")); // PROCEDURE_NAME + row.add(getStringOrNull(resultSet, "parameter_name")); // COLUMN_NAME + row.add(mapParameterModeToColumnType(parameterMode, isResult)); // COLUMN_TYPE + row.add( + dataType != null + ? getCode(stripBaseTypeName(dataType.toUpperCase())) + : null); // DATA_TYPE + row.add(dataType != null ? dataType.toUpperCase() : null); // TYPE_NAME + row.add(getProcedureColumnPrecision(resultSet, dataType)); // PRECISION + row.add(getProcedureColumnLength(resultSet, dataType)); // LENGTH + row.add(getShortOrNull(resultSet, "numeric_scale")); // SCALE + row.add(getShortOrNull(resultSet, "numeric_precision_radix")); // RADIX + row.add((short) procedureNullableUnknown); // NULLABLE + row.add(getStringOrNull(resultSet, "comment")); // REMARKS + row.add(getStringOrNull(resultSet, "parameter_default")); // COLUMN_DEF + row.add(null); // SQL_DATA_TYPE (reserved) + row.add(null); // SQL_DATETIME_SUB (reserved) + row.add(getIntOrNull(resultSet, "character_octet_length")); // CHAR_OCTET_LENGTH + row.add(getIntOrNull(resultSet, "ordinal_position")); // ORDINAL_POSITION + row.add(""); // IS_NULLABLE (unknown) + row.add(getStringOrNull(resultSet, "specific_name")); // SPECIFIC_NAME + rows.add(row); + } + return rows; + } + + private static short mapParameterModeToColumnType(String parameterMode, String isResult) { + if ("YES".equalsIgnoreCase(isResult)) { + return (short) procedureColumnReturn; + } + if (parameterMode == null) { + return (short) procedureColumnUnknown; + } + switch (parameterMode.toUpperCase()) { + case "IN": + return (short) procedureColumnIn; + case "INOUT": + return (short) procedureColumnInOut; + case "OUT": + return (short) procedureColumnOut; + default: + return (short) procedureColumnUnknown; + } + } + + private Object getProcedureColumnPrecision(DatabricksResultSet resultSet, String dataType) + throws SQLException { + Object numericPrecision = getIntOrNull(resultSet, "numeric_precision"); + if (numericPrecision != null) { + return numericPrecision; + } + return getIntOrNull(resultSet, "character_maximum_length"); + } + + private Object getProcedureColumnLength(DatabricksResultSet resultSet, String dataType) + throws SQLException { + Object charOctetLength = getIntOrNull(resultSet, "character_octet_length"); + if (charOctetLength != null) { + return charOctetLength; + } + return getIntOrNull(resultSet, "numeric_precision"); + } + + private static String getStringOrNull(DatabricksResultSet resultSet, String columnName) + throws SQLException { + try { + Object val = resultSet.getObject(columnName); + return val != null ? val.toString() : null; + } catch (SQLException e) { + return null; + } + } + + private static Integer getIntOrNull(DatabricksResultSet resultSet, String columnName) + throws SQLException { + try { + Object val = resultSet.getObject(columnName); + if (val == null) return null; + if (val instanceof Number) return ((Number) val).intValue(); + return Integer.parseInt(val.toString()); + } catch (SQLException | NumberFormatException e) { + return null; + } + } + + private static Short getShortOrNull(DatabricksResultSet resultSet, String columnName) + throws SQLException { + try { + Object val = resultSet.getObject(columnName); + if (val == null) return null; + if (val instanceof Number) return ((Number) val).shortValue(); + return Short.parseShort(val.toString()); + } catch (SQLException | NumberFormatException e) { + return null; + } + } + private List> getRowsForSchemas( DatabricksResultSet resultSet, List columns, diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksEmptyMetadataClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksEmptyMetadataClient.java index 048142dff8..8993838bce 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksEmptyMetadataClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksEmptyMetadataClient.java @@ -81,6 +81,29 @@ public DatabricksResultSet listFunctions( return metadataResultSetBuilder.getFunctionsResult("", null); } + @Override + public DatabricksResultSet listProcedures( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern) + throws SQLException { + LOGGER.warn("Empty metadata implementation for listProcedures."); + return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); + } + + @Override + public DatabricksResultSet listProcedureColumns( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern, + String columnNamePattern) + throws SQLException { + LOGGER.warn("Empty metadata implementation for listProcedureColumns."); + return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); + } + @Override public DatabricksResultSet listPrimaryKeys( IDatabricksSession session, String catalog, String schema, String table) throws SQLException { diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java index adcfe64216..eb97bb902e 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java @@ -10,6 +10,7 @@ import com.databricks.jdbc.common.util.WildcardUtil; import com.databricks.jdbc.dbclient.IDatabricksClient; import com.databricks.jdbc.dbclient.IDatabricksMetadataClient; +import com.databricks.jdbc.dbclient.impl.common.CommandConstants; import com.databricks.jdbc.dbclient.impl.common.MetadataResultSetBuilder; import com.databricks.jdbc.log.JdbcLogger; import com.databricks.jdbc.log.JdbcLoggerFactory; @@ -232,6 +233,46 @@ public DatabricksResultSet listFunctions( return metadataResultSetBuilder.getFunctionsResult(getResultSet(SQL, session), catalog); } + @Override + public DatabricksResultSet listProcedures( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern) + throws SQLException { + String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null; + if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) { + return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); + } + + catalog = autoFillCatalog(catalog, currentCatalog); + String SQL = + CommandConstants.buildProceduresSQL(catalog, schemaNamePattern, procedureNamePattern); + LOGGER.debug("SQL command to fetch procedures: {}", SQL); + return metadataResultSetBuilder.getProceduresResult(getResultSet(SQL, session)); + } + + @Override + public DatabricksResultSet listProcedureColumns( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern, + String columnNamePattern) + throws SQLException { + String currentCatalog = isMultipleCatalogSupportDisabled() ? session.getCurrentCatalog() : null; + if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, currentCatalog, session)) { + return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); + } + + catalog = autoFillCatalog(catalog, currentCatalog); + String SQL = + CommandConstants.buildProcedureColumnsSQL( + catalog, schemaNamePattern, procedureNamePattern, columnNamePattern); + LOGGER.debug("SQL command to fetch procedure columns: {}", SQL); + return metadataResultSetBuilder.getProcedureColumnsResult(getResultSet(SQL, session)); + } + @Override public DatabricksResultSet listPrimaryKeys( IDatabricksSession session, String catalog, String schema, String table) throws SQLException { diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java index 04c37f4ed0..9cb60ce7bf 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java @@ -19,6 +19,7 @@ import com.databricks.jdbc.common.util.ProtocolFeatureUtil; import com.databricks.jdbc.dbclient.IDatabricksClient; import com.databricks.jdbc.dbclient.IDatabricksMetadataClient; +import com.databricks.jdbc.dbclient.impl.common.CommandConstants; import com.databricks.jdbc.dbclient.impl.common.MetadataResultSetBuilder; import com.databricks.jdbc.dbclient.impl.common.StatementId; import com.databricks.jdbc.dbclient.impl.sqlexec.CommandBuilder; @@ -592,6 +593,71 @@ public DatabricksResultSet listFunctions( catalog, extractRowsFromColumnar(response.getResults())); } + @Override + public DatabricksResultSet listProcedures( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern) + throws SQLException { + LOGGER.debug( + "Fetching procedures using SQL via Thrift client. Session {}, catalog {}, schemaPattern {}, procedureNamePattern {}.", + session.toString(), + catalog, + schemaNamePattern, + procedureNamePattern); + DatabricksThreadContextHolder.setSessionId(session.getSessionId()); + + if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, null, session)) { + return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); + } + + String sql = + CommandConstants.buildProceduresSQL(catalog, schemaNamePattern, procedureNamePattern); + return metadataResultSetBuilder.getProceduresResult( + executeStatement( + sql, + session.getComputeResource(), + new HashMap<>(), + StatementType.METADATA, + session, + null)); + } + + @Override + public DatabricksResultSet listProcedureColumns( + IDatabricksSession session, + String catalog, + String schemaNamePattern, + String procedureNamePattern, + String columnNamePattern) + throws SQLException { + LOGGER.debug( + "Fetching procedure columns using SQL via Thrift client. Session {}, catalog {}, schemaPattern {}, procedureNamePattern {}, columnNamePattern {}.", + session.toString(), + catalog, + schemaNamePattern, + procedureNamePattern, + columnNamePattern); + DatabricksThreadContextHolder.setSessionId(session.getSessionId()); + + if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, null, session)) { + return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); + } + + String sql = + CommandConstants.buildProcedureColumnsSQL( + catalog, schemaNamePattern, procedureNamePattern, columnNamePattern); + return metadataResultSetBuilder.getProcedureColumnsResult( + executeStatement( + sql, + session.getComputeResource(), + new HashMap<>(), + StatementType.METADATA, + session, + null)); + } + @Override public DatabricksResultSet listPrimaryKeys( IDatabricksSession session, String catalog, String schema, String table) throws SQLException { diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaDataTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaDataTest.java index 4ea945272a..55bc3dbe86 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaDataTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaDataTest.java @@ -13,6 +13,7 @@ import com.databricks.jdbc.dbclient.IDatabricksMetadataClient; import com.databricks.jdbc.exception.DatabricksSQLException; import java.sql.*; +import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.stream.Stream; @@ -68,6 +69,11 @@ public void setup() throws SQLException { .thenReturn(Mockito.mock(DatabricksResultSet.class)); when(metadataClient.listCrossReferences(any(), any(), any(), any(), any(), any(), any())) .thenReturn(Mockito.mock(DatabricksResultSet.class)); + when(metadataClient.listProcedures(any(), any(), any(), any())) + .thenAnswer(invocation -> metadataResultSetBuilder.getProceduresResult(new ArrayList<>())); + when(metadataClient.listProcedureColumns(any(), any(), any(), any(), any())) + .thenAnswer( + invocation -> metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>())); when(connection.getConnection()).thenReturn(Mockito.mock(Connection.class)); when(session.isOpen()).thenReturn(true); } From 36b8208a2505ae0fb7e932131e4157451e1ddace Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 20:57:03 +0530 Subject: [PATCH 2/9] [PECOBLR-1746] Clean up procedure metadata: extract SQL variables, inline 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) --- .../impl/common/CommandConstants.java | 67 +++++++++---------- .../impl/common/MetadataResultSetBuilder.java | 25 ++----- 2 files changed, 35 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java index 7f241b5085..ebc761c7f3 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java @@ -29,23 +29,30 @@ public class CommandConstants { public static final String SHOW_FOREIGN_KEYS_SQL = "SHOW FOREIGN KEYS" + IN_CATALOG_SQL + IN_ABSOLUTE_SCHEMA_SQL + IN_ABSOLUTE_TABLE_SQL; - /** - * Builds a SQL query to fetch stored procedures from information_schema.ROUTINES. - * - * @param catalog the catalog name; null means use system catalog - * @param schemaPattern schema LIKE pattern; null means no filter - * @param procedureNamePattern procedure name LIKE pattern; null means no filter - */ + private static final String INFORMATION_SCHEMA_ROUTINES = "information_schema.ROUTINES"; + private static final String INFORMATION_SCHEMA_PARAMETERS = "information_schema.parameters"; + private static final String PROCEDURE_TYPE_FILTER = "routine_type = 'PROCEDURE'"; + + private static final String ROUTINES_SELECT_COLUMNS = + "routine_catalog, routine_schema, routine_name, comment, specific_name"; + + private static final String PARAMETERS_SELECT_COLUMNS = + "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," + + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," + + " p.character_maximum_length, p.character_octet_length," + + " p.ordinal_position, p.parameter_default, p.comment"; + public static String buildProceduresSQL( String catalog, String schemaPattern, String procedureNamePattern) { String catalogPrefix = getCatalogPrefix(catalog); + String routinesTable = catalogPrefix + "." + INFORMATION_SCHEMA_ROUTINES; + StringBuilder sql = new StringBuilder(); - sql.append( - String.format( - "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" - + " FROM %s.information_schema.ROUTINES" - + " WHERE routine_type = 'PROCEDURE'", - catalogPrefix)); + sql.append("SELECT ").append(ROUTINES_SELECT_COLUMNS); + sql.append(" FROM ").append(routinesTable); + sql.append(" WHERE ").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { sql.append(String.format(" AND routine_schema LIKE '%s'", schemaPattern)); } @@ -56,34 +63,20 @@ public static String buildProceduresSQL( return sql.toString(); } - /** - * Builds a SQL query to fetch stored procedure columns/parameters from - * information_schema.parameters joined with ROUTINES. - * - * @param catalog the catalog name; null means use system catalog - * @param schemaPattern schema LIKE pattern; null means no filter - * @param procedureNamePattern procedure name LIKE pattern; null means no filter - * @param columnNamePattern column/parameter name LIKE pattern; null means no filter - */ public static String buildProcedureColumnsSQL( String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) { String catalogPrefix = getCatalogPrefix(catalog); + String parametersTable = catalogPrefix + "." + INFORMATION_SCHEMA_PARAMETERS + " p"; + String routinesTable = catalogPrefix + "." + INFORMATION_SCHEMA_ROUTINES + " r"; + StringBuilder sql = new StringBuilder(); - sql.append( - String.format( - "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," - + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," - + " p.character_maximum_length, p.character_octet_length," - + " p.ordinal_position, p.parameter_default, p.comment" - + " FROM %s.information_schema.parameters p" - + " JOIN %s.information_schema.ROUTINES r" - + " ON p.specific_catalog = r.specific_catalog" - + " AND p.specific_schema = r.specific_schema" - + " AND p.specific_name = r.specific_name" - + " WHERE r.routine_type = 'PROCEDURE'", - catalogPrefix, catalogPrefix)); + sql.append("SELECT ").append(PARAMETERS_SELECT_COLUMNS); + sql.append(" FROM ").append(parametersTable); + sql.append(" JOIN ").append(routinesTable); + sql.append(" ON p.specific_catalog = r.specific_catalog"); + sql.append(" AND p.specific_schema = r.specific_schema"); + sql.append(" AND p.specific_name = r.specific_name"); + sql.append(" WHERE r.").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { sql.append(String.format(" AND p.specific_schema LIKE '%s'", schemaPattern)); } diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java index 989f941fea..a49bff4f5b 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java @@ -1201,8 +1201,11 @@ private List> getRowsForProcedureColumns(DatabricksResultSet result ? getCode(stripBaseTypeName(dataType.toUpperCase())) : null); // DATA_TYPE row.add(dataType != null ? dataType.toUpperCase() : null); // TYPE_NAME - row.add(getProcedureColumnPrecision(resultSet, dataType)); // PRECISION - row.add(getProcedureColumnLength(resultSet, dataType)); // LENGTH + 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 + row.add(charOctetLength != null ? charOctetLength : numericPrecision); // LENGTH row.add(getShortOrNull(resultSet, "numeric_scale")); // SCALE row.add(getShortOrNull(resultSet, "numeric_precision_radix")); // RADIX row.add((short) procedureNullableUnknown); // NULLABLE @@ -1238,24 +1241,6 @@ private static short mapParameterModeToColumnType(String parameterMode, String i } } - private Object getProcedureColumnPrecision(DatabricksResultSet resultSet, String dataType) - throws SQLException { - Object numericPrecision = getIntOrNull(resultSet, "numeric_precision"); - if (numericPrecision != null) { - return numericPrecision; - } - return getIntOrNull(resultSet, "character_maximum_length"); - } - - private Object getProcedureColumnLength(DatabricksResultSet resultSet, String dataType) - throws SQLException { - Object charOctetLength = getIntOrNull(resultSet, "character_octet_length"); - if (charOctetLength != null) { - return charOctetLength; - } - return getIntOrNull(resultSet, "numeric_precision"); - } - private static String getStringOrNull(DatabricksResultSet resultSet, String columnName) throws SQLException { try { From 7e44096eb7462584f9daa1afd4685559a425ebfb Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 21:24:48 +0530 Subject: [PATCH 3/9] [PECOBLR-1746] Add unit and integration tests for getProcedures/getProcedureColumns 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) --- .../impl/common/CommandConstants.java | 2 +- .../com/databricks/jdbc/TestConstants.java | 2 + .../DatabricksMetadataSdkClientTest.java | 193 ++++++++++++++++++ .../tests/MetadataIntegrationTests.java | 81 ++++++++ ...-ff17bdaf-78a9-4a58-8d34-ce77f1589736.json | 25 +++ ...-71fda4a6-6195-4ecb-b6e0-1294445d3f5c.json | 25 +++ ...-8b56451a-af84-4083-a0ed-cd195cb8da96.json | 25 +++ ...-34a44455-4998-432f-a241-3c63efcd1770.json | 25 +++ ...-b9f97ecd-5477-47de-ad83-6eaa1a00f57c.json | 38 ++++ ...-0e1eaea3-fa89-4465-8a41-1e25e840dfb7.json | 32 +++ ...-4ecc7065-13f6-4a73-975f-f41897b1bc4f.json | 38 ++++ ...-508838af-30ec-411f-86e2-830b55318032.json | 38 ++++ ...-870d642a-3155-4d9d-8eca-93f608f9daa5.json | 38 ++++ ...-b21e9430-296e-4023-8328-f28887f37db8.json | 38 ++++ ...-eca6b1d3-04fe-4462-a75f-e1b2ff30bb16.json | 38 ++++ ...-f16fdfd9-2ade-477e-a5e4-247ed86f8041.json | 38 ++++ ...-153bfd8f-90e1-4df4-824d-9b12a426bb50.json | 32 +++ ...-f747f76f-4540-4278-8a5a-fcd039748a17.json | 32 +++ ...-be7e8572-6195-4608-b385-9344c6b044f6.json | 33 +++ 19 files changed, 772 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164916z_689e65a4-0eb8-4021-a1b9-ba57027605dc-ff17bdaf-78a9-4a58-8d34-ce77f1589736.json create mode 100644 src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164920z_07f2c854-dc4a-4f97-ba88-49725454824a-71fda4a6-6195-4ecb-b6e0-1294445d3f5c.json create mode 100644 src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164925z_4eeb4f54-014b-4555-a062-1fe9c646c5ae-8b56451a-af84-4083-a0ed-cd195cb8da96.json create mode 100644 src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164928z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb-34a44455-4998-432f-a241-3c63efcd1770.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions-b9f97ecd-5477-47de-ad83-6eaa1a00f57c.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions_01f113f3-dbfa-1457-b8fa-f01e05a73cd0-0e1eaea3-fa89-4465-8a41-1e25e840dfb7.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-4ecc7065-13f6-4a73-975f-f41897b1bc4f.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-508838af-30ec-411f-86e2-830b55318032.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-870d642a-3155-4d9d-8eca-93f608f9daa5.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-b21e9430-296e-4023-8328-f28887f37db8.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-eca6b1d3-04fe-4462-a75f-e1b2ff30bb16.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-f16fdfd9-2ade-477e-a5e4-247ed86f8041.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-dcc3-1501-987b-3a6cc9773eee-153bfd8f-90e1-4df4-824d-9b12a426bb50.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-e7d6-14de-9e32-e020b45e7c9e-f747f76f-4540-4278-8a5a-fcd039748a17.json create mode 100644 src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oidc_.well-known_oauth-authorization-server-be7e8572-6195-4608-b385-9344c6b044f6.json diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java index ebc761c7f3..d954a84eec 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java @@ -29,7 +29,7 @@ public class CommandConstants { public static final String SHOW_FOREIGN_KEYS_SQL = "SHOW FOREIGN KEYS" + IN_CATALOG_SQL + IN_ABSOLUTE_SCHEMA_SQL + IN_ABSOLUTE_TABLE_SQL; - private static final String INFORMATION_SCHEMA_ROUTINES = "information_schema.ROUTINES"; + private static final String INFORMATION_SCHEMA_ROUTINES = "information_schema.routines"; private static final String INFORMATION_SCHEMA_PARAMETERS = "information_schema.parameters"; private static final String PROCEDURE_TYPE_FILTER = "routine_type = 'PROCEDURE'"; diff --git a/src/test/java/com/databricks/jdbc/TestConstants.java b/src/test/java/com/databricks/jdbc/TestConstants.java index e4381a83d2..5e51a4ab81 100644 --- a/src/test/java/com/databricks/jdbc/TestConstants.java +++ b/src/test/java/com/databricks/jdbc/TestConstants.java @@ -31,6 +31,8 @@ public class TestConstants { public static final String TEST_FOREIGN_SCHEMA = "foreignSchema"; public static final String TEST_FOREIGN_TABLE = "foreignTable"; public static final String TEST_FUNCTION_PATTERN = "functionPattern"; + public static final String TEST_PROCEDURE_PATTERN = "procedurePattern"; + public static final String TEST_COLUMN_PATTERN = "columnPattern"; public static final String TEST_STRING = "test"; public static final String TEST_STRING_2 = "test2"; public static final String TEST_USER = "testUser"; diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java index 71a7298e23..f929be3ecb 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java @@ -1254,4 +1254,197 @@ void testNoUnnecessaryGetCurrentCatalogCallWhenSupportEnabled() throws SQLExcept // Verify getCurrentCatalog was NEVER called when support is enabled verify(session, never()).getCurrentCatalog(); } + + // ==================== listProcedures tests ==================== + + private static Stream listProceduresTestParams() { + return Stream.of( + Arguments.of( + "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + + " FROM `catalog1`.information_schema.routines" + + " WHERE routine_type = 'PROCEDURE'" + + " AND routine_schema LIKE 'testSchema'" + + " AND routine_name LIKE 'procedurePattern'" + + " ORDER BY routine_catalog, routine_schema, routine_name", + TEST_CATALOG, + TEST_SCHEMA, + TEST_PROCEDURE_PATTERN, + "test for get procedures with catalog, schema and name pattern"), + Arguments.of( + "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + + " FROM `catalog1`.information_schema.routines" + + " WHERE routine_type = 'PROCEDURE'" + + " AND routine_name LIKE 'procedurePattern'" + + " ORDER BY routine_catalog, routine_schema, routine_name", + TEST_CATALOG, + null, + TEST_PROCEDURE_PATTERN, + "test for get procedures without schema"), + Arguments.of( + "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + + " FROM `catalog1`.information_schema.routines" + + " WHERE routine_type = 'PROCEDURE'" + + " AND routine_schema LIKE 'testSchema'" + + " ORDER BY routine_catalog, routine_schema, routine_name", + TEST_CATALOG, + TEST_SCHEMA, + null, + "test for get procedures without name pattern"), + Arguments.of( + "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + + " FROM system.information_schema.routines" + + " WHERE routine_type = 'PROCEDURE'" + + " ORDER BY routine_catalog, routine_schema, routine_name", + null, + null, + null, + "test for get procedures with null catalog")); + } + + @ParameterizedTest + @MethodSource("listProceduresTestParams") + void testListProcedures( + String sql, String catalog, String schema, String procedurePattern, String description) + throws SQLException { + when(session.getComputeResource()).thenReturn(WAREHOUSE_COMPUTE); + DatabricksMetadataSdkClient metadataClient = new DatabricksMetadataSdkClient(mockClient); + when(mockClient.executeStatement( + sql, + WAREHOUSE_COMPUTE, + new HashMap(), + StatementType.METADATA, + session, + null)) + .thenReturn(mockedResultSet); + when(mockedResultSet.next()).thenReturn(true, false); + when(mockedResultSet.getObject("routine_catalog")).thenReturn("main"); + when(mockedResultSet.getObject("routine_schema")).thenReturn("default"); + when(mockedResultSet.getObject("routine_name")).thenReturn("test_proc"); + when(mockedResultSet.getObject("comment")).thenReturn(null); + when(mockedResultSet.getObject("specific_name")).thenReturn("test_proc"); + doReturn(5).when(mockedMetaData).getColumnCount(); + when(mockedResultSet.getMetaData()).thenReturn(mockedMetaData); + DatabricksResultSet actualResult = + metadataClient.listProcedures(session, catalog, schema, procedurePattern); + assertEquals( + StatementState.SUCCEEDED, actualResult.getStatementStatus().getState(), description); + assertEquals(GET_PROCEDURES_STATEMENT_ID, actualResult.getStatementId(), description); + assertEquals( + 1, ((DatabricksResultSetMetaData) actualResult.getMetaData()).getTotalRows(), description); + } + + // ==================== listProcedureColumns tests ==================== + + private static Stream listProcedureColumnsTestParams() { + return Stream.of( + 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," + + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," + + " p.character_maximum_length, p.character_octet_length," + + " p.ordinal_position, p.parameter_default, p.comment" + + " FROM `catalog1`.information_schema.parameters p" + + " JOIN `catalog1`.information_schema.routines r" + + " ON p.specific_catalog = r.specific_catalog" + + " AND p.specific_schema = r.specific_schema" + + " AND p.specific_name = r.specific_name" + + " WHERE r.routine_type = 'PROCEDURE'" + + " AND p.specific_schema LIKE 'testSchema'" + + " AND p.specific_name LIKE 'procedurePattern'" + + " AND p.parameter_name LIKE 'columnPattern'" + + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position", + TEST_CATALOG, + TEST_SCHEMA, + TEST_PROCEDURE_PATTERN, + TEST_COLUMN_PATTERN, + "test for get procedure columns with all filters"), + 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," + + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," + + " p.character_maximum_length, p.character_octet_length," + + " p.ordinal_position, p.parameter_default, p.comment" + + " FROM `catalog1`.information_schema.parameters p" + + " JOIN `catalog1`.information_schema.routines r" + + " ON p.specific_catalog = r.specific_catalog" + + " AND p.specific_schema = r.specific_schema" + + " AND p.specific_name = r.specific_name" + + " WHERE r.routine_type = 'PROCEDURE'" + + " AND p.specific_name LIKE 'procedurePattern'" + + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position", + TEST_CATALOG, + null, + TEST_PROCEDURE_PATTERN, + null, + "test for get procedure columns without schema and column pattern"), + 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," + + " p.numeric_precision, p.numeric_precision_radix, p.numeric_scale," + + " p.character_maximum_length, p.character_octet_length," + + " p.ordinal_position, p.parameter_default, p.comment" + + " FROM system.information_schema.parameters p" + + " JOIN system.information_schema.routines r" + + " ON p.specific_catalog = r.specific_catalog" + + " AND p.specific_schema = r.specific_schema" + + " AND p.specific_name = r.specific_name" + + " WHERE r.routine_type = 'PROCEDURE'" + + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position", + null, + null, + null, + null, + "test for get procedure columns with null catalog and no filters")); + } + + @ParameterizedTest + @MethodSource("listProcedureColumnsTestParams") + void testListProcedureColumns( + String sql, + String catalog, + String schema, + String procedurePattern, + String columnPattern, + String description) + throws SQLException { + when(session.getComputeResource()).thenReturn(WAREHOUSE_COMPUTE); + DatabricksMetadataSdkClient metadataClient = new DatabricksMetadataSdkClient(mockClient); + when(mockClient.executeStatement( + sql, + WAREHOUSE_COMPUTE, + new HashMap(), + StatementType.METADATA, + session, + null)) + .thenReturn(mockedResultSet); + when(mockedResultSet.next()).thenReturn(true, false); + when(mockedResultSet.getObject("specific_catalog")).thenReturn("main"); + when(mockedResultSet.getObject("specific_schema")).thenReturn("default"); + when(mockedResultSet.getObject("specific_name")).thenReturn("test_proc"); + when(mockedResultSet.getObject("parameter_name")).thenReturn("x"); + when(mockedResultSet.getObject("parameter_mode")).thenReturn("IN"); + when(mockedResultSet.getObject("is_result")).thenReturn("NO"); + when(mockedResultSet.getObject("data_type")).thenReturn("INT"); + when(mockedResultSet.getObject("numeric_precision")).thenReturn(null); + when(mockedResultSet.getObject("numeric_precision_radix")).thenReturn(2); + when(mockedResultSet.getObject("numeric_scale")).thenReturn(null); + when(mockedResultSet.getObject("character_maximum_length")).thenReturn(null); + when(mockedResultSet.getObject("character_octet_length")).thenReturn(null); + when(mockedResultSet.getObject("ordinal_position")).thenReturn(0); + when(mockedResultSet.getObject("parameter_default")).thenReturn(null); + when(mockedResultSet.getObject("comment")).thenReturn(null); + doReturn(16).when(mockedMetaData).getColumnCount(); + when(mockedResultSet.getMetaData()).thenReturn(mockedMetaData); + DatabricksResultSet actualResult = + metadataClient.listProcedureColumns( + session, catalog, schema, procedurePattern, columnPattern); + assertEquals( + StatementState.SUCCEEDED, actualResult.getStatementStatus().getState(), description); + assertEquals(GET_PROCEDURE_COLUMNS_STATEMENT_ID, actualResult.getStatementId(), description); + assertEquals( + 1, ((DatabricksResultSetMetaData) actualResult.getMetaData()).getTotalRows(), description); + } } diff --git a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/MetadataIntegrationTests.java b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/MetadataIntegrationTests.java index 48159a63a9..693c11848b 100644 --- a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/MetadataIntegrationTests.java +++ b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/MetadataIntegrationTests.java @@ -198,6 +198,87 @@ void testCatalogAndSchemaInformation() throws SQLException { } } + @Test + void testGetProceduresAndProcedureColumns() throws SQLException { + assumeTrue(isSqlExecSdkClient(), "This test only runs for SQL Execution API"); + + DatabaseMetaData metaData = connection.getMetaData(); + String catalog = "main"; + String schema = getDatabricksSchema(); + String procName = "jdbc_test_compute_area"; + + // Create a test procedure with IN and OUT parameters + executeSQL( + connection, + "CREATE OR REPLACE PROCEDURE " + + catalog + + "." + + schema + + "." + + procName + + "(x DOUBLE, y DOUBLE, OUT area DOUBLE)\n" + + "LANGUAGE SQL\n" + + "SQL SECURITY INVOKER\n" + + "COMMENT 'Test procedure for JDBC integration tests'\n" + + "AS BEGIN\n" + + " SET area = x * y;\n" + + "END"); + + try { + // Test getProcedures - find our procedure by exact name + try (ResultSet procedures = metaData.getProcedures(catalog, schema, procName)) { + assertTrue(procedures.next(), "Should find the created procedure"); + assertEquals(catalog, procedures.getString("PROCEDURE_CAT")); + assertEquals(schema, procedures.getString("PROCEDURE_SCHEM")); + assertEquals(procName, procedures.getString("PROCEDURE_NAME")); + assertEquals("Test procedure for JDBC integration tests", procedures.getString("REMARKS")); + assertEquals(1, procedures.getShort("PROCEDURE_TYPE"), "Should be SQL_PT_PROCEDURE"); + assertEquals(procName, procedures.getString("SPECIFIC_NAME")); + assertFalse(procedures.next(), "Should be exactly one match"); + } + + // Test getProcedures - pattern matching + try (ResultSet procedures = metaData.getProcedures(catalog, schema, "jdbc_test_%")) { + assertTrue(procedures.next(), "Pattern should match our procedure"); + } + + // Test getProcedureColumns - all parameters + try (ResultSet columns = metaData.getProcedureColumns(catalog, schema, procName, "%")) { + // Parameter x (IN, DOUBLE) + assertTrue(columns.next(), "Should have parameter x"); + assertEquals(procName, columns.getString("PROCEDURE_NAME")); + assertEquals("x", columns.getString("COLUMN_NAME")); + assertEquals(1, columns.getShort("COLUMN_TYPE"), "x should be SQL_PARAM_INPUT"); + assertEquals(8, columns.getInt("DATA_TYPE"), "DOUBLE maps to SQL type code 8"); + assertEquals("DOUBLE", columns.getString("TYPE_NAME")); + assertEquals(0, columns.getInt("ORDINAL_POSITION")); + + // Parameter y (IN, DOUBLE) + assertTrue(columns.next(), "Should have parameter y"); + assertEquals("y", columns.getString("COLUMN_NAME")); + assertEquals(1, columns.getShort("COLUMN_TYPE"), "y should be SQL_PARAM_INPUT"); + + // Parameter area (OUT, DOUBLE) + assertTrue(columns.next(), "Should have parameter area"); + assertEquals("area", columns.getString("COLUMN_NAME")); + assertEquals(4, columns.getShort("COLUMN_TYPE"), "area should be SQL_PARAM_OUTPUT"); + + assertFalse(columns.next(), "Should have exactly 3 parameters"); + } + + // Test getProcedureColumns - filter by column name + try (ResultSet columns = metaData.getProcedureColumns(catalog, schema, procName, "area")) { + assertTrue(columns.next(), "Should find the 'area' parameter"); + assertEquals("area", columns.getString("COLUMN_NAME")); + assertEquals(4, columns.getShort("COLUMN_TYPE"), "area should be SQL_PARAM_OUTPUT"); + assertFalse(columns.next(), "Should be exactly one match"); + } + + } finally { + executeSQL(connection, "DROP PROCEDURE IF EXISTS " + catalog + "." + schema + "." + procName); + } + } + @Test void testMetadataOperationsWithHyphenatedIdentifiers() throws SQLException { assumeTrue(isSqlExecSdkClient(), "This test only runs for SQL Execution API"); diff --git a/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164916z_689e65a4-0eb8-4021-a1b9-ba57027605dc-ff17bdaf-78a9-4a58-8d34-ce77f1589736.json b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164916z_689e65a4-0eb8-4021-a1b9-ba57027605dc-ff17bdaf-78a9-4a58-8d34-ce77f1589736.json new file mode 100644 index 0000000000..090df9a6f7 --- /dev/null +++ b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164916z_689e65a4-0eb8-4021-a1b9-ba57027605dc-ff17bdaf-78a9-4a58-8d34-ce77f1589736.json @@ -0,0 +1,25 @@ +{ + "id" : "ff17bdaf-78a9-4a58-8d34-ce77f1589736", + "name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164916z_689e65a4-0eb8-4021-a1b9-ba57027605dc", + "request" : { + "url" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A16Z_689e65a4-0eb8-4021-a1b9-ba57027605dc?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154916Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=591a240a91d37cd1d8851014c0bb3685a56dd09b3acefdbe5a0387c53f65bce7", + "method" : "GET" + }, + "response" : { + "status" : 200, + "base64Body" : "BCJNGHRwjvsAAACg/////4gBAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEA8QMABQAAABABAADAAAAAhAAAAFA2AKQAABb///8UAAAABAAAKwACZAACAgD1EAT///8NAAAAc3BlY2lmaWNfbmFtZQASABgAFAATABJwABcSRAAATABIAAAFAUgA/wJM////BwAAAGNvbW1lbnQAjngADEB8////zABycm91dGluZXcATwAAAMY4AAxXtP///w44AGBzY2hlbWGQAAK0AD8AABO0AAIQGCMAMAAABSIABAIAEQQCADcAAA9QAIBjYXRhbG9nACmPNjgAAAAA7SBirAQiTRh0cI5RAQAAov////+YAQAAFAABANAMABYABgAFAAgADAAMEwCAAwQAGAAAAMgMAAECAPEICgAYAAwABAAIAAoAAAAMAQAAEAAAAAEcAAICABIPBwABAgAEGAARCA4ABggAADQAAAIAEAQFACEAAGQAAAIABDAAECANAAcwABEoDgAGOAAQOA0AB3gAEUAOAAYwABBIDQBBAAAAFgYAMAAAYAUABzAAEWgOAAYwABBwDQBBAAAAKQYAMAAAoAUABzAAEagOAAYwABCwDQAHYAAAAgAXBTAABAIABNAABAIADxAAKQAsAUBtYWluTAAISAAATAH4AWpkYmNfdGVzdF9zY2hlbWEgAAAEAQYgAMpjb21wdXRlX2FyZWFIAAD8APAVVGVzdCBwcm9jZWR1cmUgZm9yIEpEQkMgaW50ZWdyYXRpb24gZwAQc4UAC/AAD2gABFByZWEAABFJ+a8AAAAA/dhIQgQiTRh0cI4IAACA/////wAAAACGhpIIAAAAAIaGkgg=", + "headers" : { + "Accept-Ranges" : "bytes", + "Server" : "AmazonS3", + "ETag" : "\"f98ed378fcc39e30de450313a7aae5c9\"", + "Last-Modified" : "Fri, 27 Feb 2026 15:49:17 GMT", + "x-amz-request-id" : "BRH8HHKD18H7J65A", + "x-amz-server-side-encryption" : "AES256", + "x-amz-id-2" : "TmCoacHSFDKAtP+eKHWXsyHz9w4vxg8+QMxhCwRd2Jr5ydHyW7BUMRWbKABVwVqrqu10zqmAjmON3ZjHrWAgeaexEDwDF62N4JIL4olh6dw=", + "Date" : "Fri, 27 Feb 2026 15:49:18 GMT", + "Content-Type" : "binary/octet-stream" + } + }, + "uuid" : "ff17bdaf-78a9-4a58-8d34-ce77f1589736", + "insertionIndex" : 4 +} \ No newline at end of file diff --git a/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164920z_07f2c854-dc4a-4f97-ba88-49725454824a-71fda4a6-6195-4ecb-b6e0-1294445d3f5c.json b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164920z_07f2c854-dc4a-4f97-ba88-49725454824a-71fda4a6-6195-4ecb-b6e0-1294445d3f5c.json new file mode 100644 index 0000000000..31204d3ef5 --- /dev/null +++ b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164920z_07f2c854-dc4a-4f97-ba88-49725454824a-71fda4a6-6195-4ecb-b6e0-1294445d3f5c.json @@ -0,0 +1,25 @@ +{ + "id" : "71fda4a6-6195-4ecb-b6e0-1294445d3f5c", + "name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164920z_07f2c854-dc4a-4f97-ba88-49725454824a", + "request" : { + "url" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A20Z_07f2c854-dc4a-4f97-ba88-49725454824a?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154920Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=3540b7cdf950b82e6811ce1ebe78f6440a0db384d1970f21747d4c0ddbfdd049", + "method" : "GET" + }, + "response" : { + "status" : 200, + "base64Body" : "BCJNGHRwjvsAAACg/////4gBAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEA8QMABQAAABABAADAAAAAhAAAAFA2AKQAABb///8UAAAABAAAKwACZAACAgD1EAT///8NAAAAc3BlY2lmaWNfbmFtZQASABgAFAATABJwABcSRAAATABIAAAFAUgA/wJM////BwAAAGNvbW1lbnQAjngADEB8////zABycm91dGluZXcATwAAAMY4AAxXtP///w44AGBzY2hlbWGQAAK0AD8AABO0AAIQGCMAMAAABSIABAIAEQQCADcAAA9QAIBjYXRhbG9nACmPNjgAAAAA7SBirAQiTRh0cI5RAQAAov////+YAQAAFAABANAMABYABgAFAAgADAAMEwCAAwQAGAAAAMgMAAECAPEICgAYAAwABAAIAAoAAAAMAQAAEAAAAAEcAAICABIPBwABAgAEGAARCA4ABggAADQAAAIAEAQFACEAAGQAAAIABDAAECANAAcwABEoDgAGOAAQOA0AB3gAEUAOAAYwABBIDQBBAAAAFgYAMAAAYAUABzAAEWgOAAYwABBwDQBBAAAAKQYAMAAAoAUABzAAEagOAAYwABCwDQAHYAAAAgAXBTAABAIABNAABAIADxAAKQAsAUBtYWluTAAISAAATAH4AWpkYmNfdGVzdF9zY2hlbWEgAAAEAQYgAMpjb21wdXRlX2FyZWFIAAD8APAVVGVzdCBwcm9jZWR1cmUgZm9yIEpEQkMgaW50ZWdyYXRpb24gZwAQc4UAC/AAD2gABFByZWEAABFJ+a8AAAAA/dhIQgQiTRh0cI4IAACA/////wAAAACGhpIIAAAAAIaGkgg=", + "headers" : { + "Accept-Ranges" : "bytes", + "Server" : "AmazonS3", + "ETag" : "\"f98ed378fcc39e30de450313a7aae5c9\"", + "Last-Modified" : "Fri, 27 Feb 2026 15:49:21 GMT", + "x-amz-request-id" : "RRJXP0WGS9DKF1K5", + "x-amz-server-side-encryption" : "AES256", + "x-amz-id-2" : "GziQFfta7apLDHiGjI15nC0Z80n0ik167S0zuTNlmhsamX6/LIZigq8e2/JX9DRXq1zQqdovdfEVt8Mu3Jtai/SCRy2LchgjDNRQpkTlyfc=", + "Date" : "Fri, 27 Feb 2026 15:49:22 GMT", + "Content-Type" : "binary/octet-stream" + } + }, + "uuid" : "71fda4a6-6195-4ecb-b6e0-1294445d3f5c", + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164925z_4eeb4f54-014b-4555-a062-1fe9c646c5ae-8b56451a-af84-4083-a0ed-cd195cb8da96.json b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164925z_4eeb4f54-014b-4555-a062-1fe9c646c5ae-8b56451a-af84-4083-a0ed-cd195cb8da96.json new file mode 100644 index 0000000000..58cda8f1ac --- /dev/null +++ b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164925z_4eeb4f54-014b-4555-a062-1fe9c646c5ae-8b56451a-af84-4083-a0ed-cd195cb8da96.json @@ -0,0 +1,25 @@ +{ + "id" : "8b56451a-af84-4083-a0ed-cd195cb8da96", + "name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164925z_4eeb4f54-014b-4555-a062-1fe9c646c5ae", + "request" : { + "url" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A25Z_4eeb4f54-014b-4555-a062-1fe9c646c5ae?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154925Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=b3fe17c3e20d0c625c1e647f6279aa837fc469168af242261e9d8b7fdddc1db4", + "method" : "GET" + }, + "response" : { + "status" : 200, + "base64Body" : "BCJNGHRwjoECAACg/////4AEAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEAAiAA8SkEAACwAwAAdAMAADgDAADoAgAAsAIAAHgCAAA8AgAA7AEAAKABAABcAQAADAEAAMAAAAB4AAAAOGIApAAADv3//xQAAAAEAEIAAAUBkAACAgD/AkD8//8HAAAAY29tbWVudAA+MAAM/A9w/P//EQAAAHBhcmFtZXRlcl9kZWZhdWx0AAAAvvxsADIAAhhnAAECAJOg/v//AAAAASDcAPABb3JkaW5hbF9wb3NpdGlvbiUAHb6AACgCAUQAE+REAFBAAAAAFrgAUGhhcmFjiAD8AW9jdGV0X2xlbmd0aAAABv6MAApIACIs/4wAAEgAAGAABkgAdW1heGltdW1KAD8AAFJMAAwTeEwAANgA/wYNAAAAbnVtZXJpY19zY2FsZQAAAJJAAAwXuEAAFxdAAPcCcHJlY2lzaW9uX3JhZGl4ANpIABAcmQAhAAJMAQQCAHQIAAwACAAHQAIBHAAArAEEkAAFUABHAAAAakwAABwCSAAAAAUYAv8KWP7//w4AAABmdWxsX2RhdGFfdHlwZQAAojgADIeQ/v//CQAAADMALwDWNAAME8Q0AGJpc19yZXNMAiwK/8ABCqAAE/igAAaIAjBtb2RtAaUAEgAYABQAEwASWAMXEgADAuwACgQDIkT/7AAGTAB/bmFtZQAAjoQADEB8////8AGDc3BlY2lmaWM3AC8AxjgADFi0////DzgAZXNjaGVtYbwAPwAAE7wAAgCoAgAIAQAiAAQCABEEAgACZAQFiADwAGNhdGFsb2cAAAAAAAAAAC4TbCgAAAAANUB0CQQiTRh0cI6dAgAAov/////4AwAAFAABANAMABYABgAFAAgADAAMEwCQAwQAGAAAAGACDQAAAgDwCAoAGAAMAAQACAAKAAAAvAIAABAAAAADGwADAgATKggAAAIAEAEFAEEAAAAIBgACLAAAAgAAVAAAAgABZABBAAAAKAYABjAAEDANAAcwABFADgAGGAAQcA0ABzAAEXgOAAYwABCIDQBBAAAAQgYAMAAA0AUABzAAEdgOAAYwABDoDQBBAAAABgYAMAAA8AUABzAAEfgOAAYwABMI2QAQBxUAAKgAAxAAAwcAIwAYCQAAHAEAAgATKBAABGAAEzAQAAMHACMAOAkABDAAE0gQABASPQAAgAEDEAADBwAjAGgJAAQwABN4EAAEMAATkBAAAwcAIwCYCQAEcAETqBAAAwcAIwCwCQAEIAATwBAAAwcAIwDICQAEIAAT2BAAAwcAIwDgCQAE2AET+BAAAwcAIwAAQAIEIAATGBAABCAAEyAQAARgABMwEAAEIAATOBAABPAAE0gQAAQCAAQQAAQwABNQIAAEMAAGwAIGAgAAHAAIuAIAAgAPEABlBHgABAgABAIABBAADwgAHQQCAA84AA0EwAIAAgATBKgDAIQBRG1haW4EAAAcAAgoAAA4ARMgjAP/AWpkYmNfdGVzdF9zY2hlbWEQAA0ISABTFgAAACykAwY4ALdjb21wdXRlX2FyZU4ADxYADwCmAAqoAAAMAgAHAgB0AyB4eVAACiAAABwAAPQAAJQAcUlOSU5PVVQMAAQCAAQgAABAACBOTwIAAiwABAIAABgAACwBAHQDaERPVUJMRQYAAiYACFAACDAAaGRvdWJsZQYAAjAADwIABQRIAACUAAQEAA8CAEkEcAAAAgAESAEPAgAcUAAAAAAAdJiwWQAAAADp3QkHBCJNGHRwjggAAID/////AAAAAIaGkggAAAAAhoaSCA==", + "headers" : { + "Accept-Ranges" : "bytes", + "Server" : "AmazonS3", + "ETag" : "\"0b6b057178d123f5dbede6d7f15c5f60\"", + "Last-Modified" : "Fri, 27 Feb 2026 15:49:26 GMT", + "x-amz-request-id" : "9F8Q9H3HK013WBDD", + "x-amz-server-side-encryption" : "AES256", + "x-amz-id-2" : "dqNec4B2Urx3HaW9Af2xj8QB0I/Z6CkDjy3kED9X6AKjjVw+A6NblRk32vNCx6f4EYvD+v6QORus/08CxnsvAyDySyT7qYjeYM2lZ8gGUAY=", + "Date" : "Fri, 27 Feb 2026 15:49:27 GMT", + "Content-Type" : "binary/octet-stream" + } + }, + "uuid" : "8b56451a-af84-4083-a0ed-cd195cb8da96", + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164928z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb-34a44455-4998-432f-a241-3c63efcd1770.json b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164928z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb-34a44455-4998-432f-a241-3c63efcd1770.json new file mode 100644 index 0000000000..48b4866ae7 --- /dev/null +++ b/src/test/resources/cloudfetchapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164928z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb-34a44455-4998-432f-a241-3c63efcd1770.json @@ -0,0 +1,25 @@ +{ + "id" : "34a44455-4998-432f-a241-3c63efcd1770", + "name" : "oregon-staging_6051921418418893.jobs_sql_extended_results_2026-02-28t164928z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb", + "request" : { + "url" : "/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A28Z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154928Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=9c1bb47ff0c7fe781e9a98827ca3a744537d0685705235ca96fa902a4d680cfb", + "method" : "GET" + }, + "response" : { + "status" : 200, + "base64Body" : "BCJNGHRwjoECAACg/////4AEAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEAAiAA8SkEAACwAwAAdAMAADgDAADoAgAAsAIAAHgCAAA8AgAA7AEAAKABAABcAQAADAEAAMAAAAB4AAAAOGIApAAADv3//xQAAAAEAEIAAAUBkAACAgD/AkD8//8HAAAAY29tbWVudAA+MAAM/A9w/P//EQAAAHBhcmFtZXRlcl9kZWZhdWx0AAAAvvxsADIAAhhnAAECAJOg/v//AAAAASDcAPABb3JkaW5hbF9wb3NpdGlvbiUAHb6AACgCAUQAE+REAFBAAAAAFrgAUGhhcmFjiAD8AW9jdGV0X2xlbmd0aAAABv6MAApIACIs/4wAAEgAAGAABkgAdW1heGltdW1KAD8AAFJMAAwTeEwAANgA/wYNAAAAbnVtZXJpY19zY2FsZQAAAJJAAAwXuEAAFxdAAPcCcHJlY2lzaW9uX3JhZGl4ANpIABAcmQAhAAJMAQQCAHQIAAwACAAHQAIBHAAArAEEkAAFUABHAAAAakwAABwCSAAAAAUYAv8KWP7//w4AAABmdWxsX2RhdGFfdHlwZQAAojgADIeQ/v//CQAAADMALwDWNAAME8Q0AGJpc19yZXNMAiwK/8ABCqAAE/igAAaIAjBtb2RtAaUAEgAYABQAEwASWAMXEgADAuwACgQDIkT/7AAGTAB/bmFtZQAAjoQADEB8////8AGDc3BlY2lmaWM3AC8AxjgADFi0////DzgAZXNjaGVtYbwAPwAAE7wAAgCoAgAIAQAiAAQCABEEAgACZAQFiADwAGNhdGFsb2cAAAAAAAAAAC4TbCgAAAAANUB0CQQiTRh0cI5HAgAAov/////4AwAAFAABANAMABYABgAFAAgADAAMEwCQAwQAGAAAAFgBDQAAAgD1BwoAGAAMAAQACAAKAAAAvAIAABAAAAAfAEEAAAAqBgACAgAEGAASCA8ABQgAADQAAAIAEAQFACEAAGQAAAIABDAAECANAAcwABEoDgAGOAAQOA0AB3gAEUAOAAZoABBIDQBBAAAAFgYAMAAAYAUABzAAEWgOAAYwABBwDQAHkAAReA4ABpAAEIANAAeQABGIDgAwAAADBQBBAAAAkAYABjAAEJgNAAcwABGgDgAwAAACBQBBAAAAqAYABjAAELANAAcwABG4DgAwAAAGBQBBAAAAwAYABjAAEMgNAAcwABHQDgAGMAAQ2A0AB/AAEeAOAAbgABDoDQAHIAAR8A4ABiAAEPgNAAcgAAThAQSwARMIqQEDBwAjABAJAATgARMYEAADBwAjACAJAAQgABMoEAADBwAjADAJAARgABM4EAADBwAjAEAJAARAABNIEAAEAgAEEAAEFwATUAkABDAABsACBgIAAIQCAycABQIADxAAZQR4AAQIAAQCAAQQAA8IAB0EAgAPOAAVAAIAAHwBQG1haW4MAAQ4AAACAAAoAfgBamRiY190ZXN0X3NjaGVtYSAAAGQDBiAAymNvbXB1dGVfYXJlYUgAAGAAAhYAChgAAEQDME9VVBcABGcCAQIAACwDIU5PCwAJGAAAFANqRE9VQkxFSAAAGABiZG91YmxlNAAIAgAEsAAAVAAPAgAhD0AAGFAAAAAAADQL8GQAAAAAcAK/FwQiTRh0cI4IAACA/////wAAAACGhpIIAAAAAIaGkgg=", + "headers" : { + "Accept-Ranges" : "bytes", + "Server" : "AmazonS3", + "ETag" : "\"e1a6ffae5c9ba5fe05912be97c14514c\"", + "Last-Modified" : "Fri, 27 Feb 2026 15:49:29 GMT", + "x-amz-request-id" : "EMFYTXP3GDFT3R69", + "x-amz-server-side-encryption" : "AES256", + "x-amz-id-2" : "FUHA6THhdYua+jhnAWwiiP74AtG5tWPeHtTwaihlG7bujhmsDAbG7djjle1VUK+SQVxLkya4+4v9VvTEoNulDUOvRlRwFQKZIPtXBMp12Wk=", + "Date" : "Fri, 27 Feb 2026 15:49:31 GMT", + "Content-Type" : "binary/octet-stream" + } + }, + "uuid" : "34a44455-4998-432f-a241-3c63efcd1770", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions-b9f97ecd-5477-47de-ad83-6eaa1a00f57c.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions-b9f97ecd-5477-47de-ad83-6eaa1a00f57c.json new file mode 100644 index 0000000000..da05110f36 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions-b9f97ecd-5477-47de-ad83-6eaa1a00f57c.json @@ -0,0 +1,38 @@ +{ + "id" : "b9f97ecd-5477-47de-ad83-6eaa1a00f57c", + "name" : "api_2.0_sql_sessions", + "request" : { + "url" : "/api/2.0/sql/sessions/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"warehouse_id\":\"dd43ee29fedd958d\",\"schema\":\"default\",\"catalog\":\"SPARK\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\"}", + "headers" : { + "x-request-id" : "3b5476e7-1b0b-428b-a3cd-39cf990b0f31", + "date" : "Fri, 27 Feb 2026 15:49:11 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"3b5476e7-1b0b-428b-a3cd-39cf990b0f31\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "b9f97ecd-5477-47de-ad83-6eaa1a00f57c", + "insertionIndex" : 10 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions_01f113f3-dbfa-1457-b8fa-f01e05a73cd0-0e1eaea3-fa89-4465-8a41-1e25e840dfb7.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions_01f113f3-dbfa-1457-b8fa-f01e05a73cd0-0e1eaea3-fa89-4465-8a41-1e25e840dfb7.json new file mode 100644 index 0000000000..f314206379 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_sessions_01f113f3-dbfa-1457-b8fa-f01e05a73cd0-0e1eaea3-fa89-4465-8a41-1e25e840dfb7.json @@ -0,0 +1,32 @@ +{ + "id" : "0e1eaea3-fa89-4465-8a41-1e25e840dfb7", + "name" : "api_2.0_sql_sessions_01f113f3-dbfa-1457-b8fa-f01e05a73cd0", + "request" : { + "url" : "/api/2.0/sql/sessions/01f113f3-dbfa-1457-b8fa-f01e05a73cd0?warehouse_id=dd43ee29fedd958d", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "20c98752-6ca1-4fed-bada-6d5e16d645f9", + "date" : "Fri, 27 Feb 2026 15:49:35 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"20c98752-6ca1-4fed-bada-6d5e16d645f9\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "0e1eaea3-fa89-4465-8a41-1e25e840dfb7", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-4ecc7065-13f6-4a73-975f-f41897b1bc4f.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-4ecc7065-13f6-4a73-975f-f41897b1bc4f.json new file mode 100644 index 0000000000..6b0b6aa505 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-4ecc7065-13f6-4a73-975f-f41897b1bc4f.json @@ -0,0 +1,38 @@ +{ + "id" : "4ecc7065-13f6-4a73-975f-f41897b1bc4f", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"CREATE OR REPLACE PROCEDURE main.jdbc_test_schema.jdbc_test_compute_area(x DOUBLE, y DOUBLE, OUT area DOUBLE)\\nLANGUAGE SQL\\nSQL SECURITY INVOKER\\nCOMMENT 'Test procedure for JDBC integration tests'\\nAS BEGIN\\n SET area = x * y;\\nEND\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-dcc3-1501-987b-3a6cc9773eee\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":0},\"total_chunk_count\":0,\"total_row_count\":0,\"total_byte_count\":0,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{}}", + "headers" : { + "x-request-id" : "7a28c72b-a138-44e7-8c2a-000d5049bbbd", + "date" : "Fri, 27 Feb 2026 15:49:13 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"7a28c72b-a138-44e7-8c2a-000d5049bbbd\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "4ecc7065-13f6-4a73-975f-f41897b1bc4f", + "insertionIndex" : 9 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-508838af-30ec-411f-86e2-830b55318032.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-508838af-30ec-411f-86e2-830b55318032.json new file mode 100644 index 0000000000..70ebfc3a51 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-508838af-30ec-411f-86e2-830b55318032.json @@ -0,0 +1,38 @@ +{ + "id" : "508838af-30ec-411f-86e2-830b55318032", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"SELECT routine_catalog, routine_schema, routine_name, comment, specific_name FROM `main`.information_schema.routines WHERE routine_type = 'PROCEDURE' AND routine_schema LIKE 'jdbc_test_schema' AND routine_name LIKE 'jdbc_test_compute_area' ORDER BY routine_catalog, routine_schema, routine_name\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-de1f-148b-bfcf-c5ae0ae13505\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":5,\"columns\":[{\"name\":\"routine_catalog\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":0},{\"name\":\"routine_schema\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":1},{\"name\":\"routine_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":2},{\"name\":\"comment\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":3},{\"name\":\"specific_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":4}]},\"total_chunk_count\":1,\"chunks\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":1024}],\"total_row_count\":1,\"total_byte_count\":1024,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{\"external_links\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":665,\"external_link\":\"https://e2-dogfood-core.s3.us-west-2.amazonaws.com/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A16Z_689e65a4-0eb8-4021-a1b9-ba57027605dc?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154916Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=591a240a91d37cd1d8851014c0bb3685a56dd09b3acefdbe5a0387c53f65bce7\",\"expiration\":\"2026-02-27T16:04:16.633Z\"}]}}", + "headers" : { + "x-request-id" : "73961c44-3cbc-4a11-861e-d0f7ae5e5bd4", + "date" : "Fri, 27 Feb 2026 15:49:16 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"73961c44-3cbc-4a11-861e-d0f7ae5e5bd4\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "508838af-30ec-411f-86e2-830b55318032", + "insertionIndex" : 8 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-870d642a-3155-4d9d-8eca-93f608f9daa5.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-870d642a-3155-4d9d-8eca-93f608f9daa5.json new file mode 100644 index 0000000000..e2f2c350eb --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-870d642a-3155-4d9d-8eca-93f608f9daa5.json @@ -0,0 +1,38 @@ +{ + "id" : "870d642a-3155-4d9d-8eca-93f608f9daa5", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"SELECT routine_catalog, routine_schema, routine_name, comment, specific_name FROM `main`.information_schema.routines WHERE routine_type = 'PROCEDURE' AND routine_schema LIKE 'jdbc_test_schema' AND routine_name LIKE 'jdbc_test_%' ORDER BY routine_catalog, routine_schema, routine_name\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-e0c8-18ab-87b0-6d84889a7540\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":5,\"columns\":[{\"name\":\"routine_catalog\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":0},{\"name\":\"routine_schema\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":1},{\"name\":\"routine_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":2},{\"name\":\"comment\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":3},{\"name\":\"specific_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":4}]},\"total_chunk_count\":1,\"chunks\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":1024}],\"total_row_count\":1,\"total_byte_count\":1024,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{\"external_links\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":665,\"external_link\":\"https://e2-dogfood-core.s3.us-west-2.amazonaws.com/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A20Z_07f2c854-dc4a-4f97-ba88-49725454824a?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154920Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=3540b7cdf950b82e6811ce1ebe78f6440a0db384d1970f21747d4c0ddbfdd049\",\"expiration\":\"2026-02-27T16:04:20.478Z\"}]}}", + "headers" : { + "x-request-id" : "858a2818-a9e3-4611-a393-ad1d0891fed3", + "date" : "Fri, 27 Feb 2026 15:49:20 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"858a2818-a9e3-4611-a393-ad1d0891fed3\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "870d642a-3155-4d9d-8eca-93f608f9daa5", + "insertionIndex" : 7 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-b21e9430-296e-4023-8328-f28887f37db8.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-b21e9430-296e-4023-8328-f28887f37db8.json new file mode 100644 index 0000000000..4561bf13db --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-b21e9430-296e-4023-8328-f28887f37db8.json @@ -0,0 +1,38 @@ +{ + "id" : "b21e9430-296e-4023-8328-f28887f37db8", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"DROP PROCEDURE IF EXISTS main.jdbc_test_schema.jdbc_test_compute_area\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-e7d6-14de-9e32-e020b45e7c9e\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":0},\"total_chunk_count\":0,\"total_row_count\":0,\"total_byte_count\":0,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{}}", + "headers" : { + "x-request-id" : "41702296-c1ca-409a-aeae-4a9469b95059", + "date" : "Fri, 27 Feb 2026 15:49:31 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"41702296-c1ca-409a-aeae-4a9469b95059\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "b21e9430-296e-4023-8328-f28887f37db8", + "insertionIndex" : 4 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-eca6b1d3-04fe-4462-a75f-e1b2ff30bb16.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-eca6b1d3-04fe-4462-a75f-e1b2ff30bb16.json new file mode 100644 index 0000000000..2074b0a337 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-eca6b1d3-04fe-4462-a75f-e1b2ff30bb16.json @@ -0,0 +1,38 @@ +{ + "id" : "eca6b1d3-04fe-4462-a75f-e1b2ff30bb16", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"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, p.numeric_precision, p.numeric_precision_radix, p.numeric_scale, p.character_maximum_length, p.character_octet_length, p.ordinal_position, p.parameter_default, p.comment FROM `main`.information_schema.parameters p JOIN `main`.information_schema.routines r ON p.specific_catalog = r.specific_catalog AND p.specific_schema = r.specific_schema AND p.specific_name = r.specific_name WHERE r.routine_type = 'PROCEDURE' AND p.specific_schema LIKE 'jdbc_test_schema' AND p.specific_name LIKE 'jdbc_test_compute_area' AND p.parameter_name LIKE '%' ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-e2f7-166d-b972-fb1f4edf28d0\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":16,\"columns\":[{\"name\":\"specific_catalog\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":0},{\"name\":\"specific_schema\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":1},{\"name\":\"specific_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":2},{\"name\":\"parameter_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":3},{\"name\":\"parameter_mode\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":4},{\"name\":\"is_result\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":5},{\"name\":\"data_type\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":6},{\"name\":\"full_data_type\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":7},{\"name\":\"numeric_precision\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":8},{\"name\":\"numeric_precision_radix\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":9},{\"name\":\"numeric_scale\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":10},{\"name\":\"character_maximum_length\",\"type_text\":\"BIGINT\",\"type_name\":\"LONG\",\"position\":11},{\"name\":\"character_octet_length\",\"type_text\":\"BIGINT\",\"type_name\":\"LONG\",\"position\":12},{\"name\":\"ordinal_position\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":13},{\"name\":\"parameter_default\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":14},{\"name\":\"comment\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":15}]},\"total_chunk_count\":1,\"chunks\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":3,\"byte_count\":2800}],\"total_row_count\":3,\"total_byte_count\":2800,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{\"external_links\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":3,\"byte_count\":1387,\"external_link\":\"https://e2-dogfood-core.s3.us-west-2.amazonaws.com/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A25Z_4eeb4f54-014b-4555-a062-1fe9c646c5ae?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154925Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=b3fe17c3e20d0c625c1e647f6279aa837fc469168af242261e9d8b7fdddc1db4\",\"expiration\":\"2026-02-27T16:04:25.164Z\"}]}}", + "headers" : { + "x-request-id" : "8985fa71-7f9d-4f52-8f46-033939bf59d6", + "date" : "Fri, 27 Feb 2026 15:49:25 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"8985fa71-7f9d-4f52-8f46-033939bf59d6\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "eca6b1d3-04fe-4462-a75f-e1b2ff30bb16", + "insertionIndex" : 6 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-f16fdfd9-2ade-477e-a5e4-247ed86f8041.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-f16fdfd9-2ade-477e-a5e4-247ed86f8041.json new file mode 100644 index 0000000000..01a81d4ecb --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements-f16fdfd9-2ade-477e-a5e4-247ed86f8041.json @@ -0,0 +1,38 @@ +{ + "id" : "f16fdfd9-2ade-477e-a5e4-247ed86f8041", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"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, p.numeric_precision, p.numeric_precision_radix, p.numeric_scale, p.character_maximum_length, p.character_octet_length, p.ordinal_position, p.parameter_default, p.comment FROM `main`.information_schema.parameters p JOIN `main`.information_schema.routines r ON p.specific_catalog = r.specific_catalog AND p.specific_schema = r.specific_schema AND p.specific_name = r.specific_name WHERE r.routine_type = 'PROCEDURE' AND p.specific_schema LIKE 'jdbc_test_schema' AND p.specific_name LIKE 'jdbc_test_compute_area' AND p.parameter_name LIKE 'area' ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f113f3-dbfa-1457-b8fa-f01e05a73cd0\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f113f3-e5a9-1a2d-bafa-5a491450cee1\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":16,\"columns\":[{\"name\":\"specific_catalog\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":0},{\"name\":\"specific_schema\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":1},{\"name\":\"specific_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":2},{\"name\":\"parameter_name\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":3},{\"name\":\"parameter_mode\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":4},{\"name\":\"is_result\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":5},{\"name\":\"data_type\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":6},{\"name\":\"full_data_type\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":7},{\"name\":\"numeric_precision\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":8},{\"name\":\"numeric_precision_radix\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":9},{\"name\":\"numeric_scale\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":10},{\"name\":\"character_maximum_length\",\"type_text\":\"BIGINT\",\"type_name\":\"LONG\",\"position\":11},{\"name\":\"character_octet_length\",\"type_text\":\"BIGINT\",\"type_name\":\"LONG\",\"position\":12},{\"name\":\"ordinal_position\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":13},{\"name\":\"parameter_default\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":14},{\"name\":\"comment\",\"type_text\":\"STRING\",\"type_name\":\"STRING\",\"position\":15}]},\"total_chunk_count\":1,\"chunks\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":2536}],\"total_row_count\":1,\"total_byte_count\":2536,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{\"external_links\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":1,\"byte_count\":1301,\"external_link\":\"https://e2-dogfood-core.s3.us-west-2.amazonaws.com/oregon-staging/6051921418418893.jobs/sql/extended/results_2026-02-28T16%3A49%3A28Z_569c2bc1-2c43-4d5e-bb58-3a12eaa119eb?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260227T154928Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=9c1bb47ff0c7fe781e9a98827ca3a744537d0685705235ca96fa902a4d680cfb\",\"expiration\":\"2026-02-27T16:04:28.870Z\"}]}}", + "headers" : { + "x-request-id" : "5a22ded3-320c-4b5b-810d-62b870e38ec6", + "date" : "Fri, 27 Feb 2026 15:49:28 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"5a22ded3-320c-4b5b-810d-62b870e38ec6\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "f16fdfd9-2ade-477e-a5e4-247ed86f8041", + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-dcc3-1501-987b-3a6cc9773eee-153bfd8f-90e1-4df4-824d-9b12a426bb50.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-dcc3-1501-987b-3a6cc9773eee-153bfd8f-90e1-4df4-824d-9b12a426bb50.json new file mode 100644 index 0000000000..90d9bd93c7 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-dcc3-1501-987b-3a6cc9773eee-153bfd8f-90e1-4df4-824d-9b12a426bb50.json @@ -0,0 +1,32 @@ +{ + "id" : "153bfd8f-90e1-4df4-824d-9b12a426bb50", + "name" : "api_2.0_sql_statements_01f113f3-dcc3-1501-987b-3a6cc9773eee", + "request" : { + "url" : "/api/2.0/sql/statements/01f113f3-dcc3-1501-987b-3a6cc9773eee", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "501a94b7-8615-4551-8c9a-fce4ea976ae1", + "date" : "Fri, 27 Feb 2026 15:49:34 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"501a94b7-8615-4551-8c9a-fce4ea976ae1\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "153bfd8f-90e1-4df4-824d-9b12a426bb50", + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-e7d6-14de-9e32-e020b45e7c9e-f747f76f-4540-4278-8a5a-fcd039748a17.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-e7d6-14de-9e32-e020b45e7c9e-f747f76f-4540-4278-8a5a-fcd039748a17.json new file mode 100644 index 0000000000..6637699544 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/api_2.0_sql_statements_01f113f3-e7d6-14de-9e32-e020b45e7c9e-f747f76f-4540-4278-8a5a-fcd039748a17.json @@ -0,0 +1,32 @@ +{ + "id" : "f747f76f-4540-4278-8a5a-fcd039748a17", + "name" : "api_2.0_sql_statements_01f113f3-e7d6-14de-9e32-e020b45e7c9e", + "request" : { + "url" : "/api/2.0/sql/statements/01f113f3-e7d6-14de-9e32-e020b45e7c9e", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "721c87d3-ecd2-400f-acf3-bd411f6eb86f", + "date" : "Fri, 27 Feb 2026 15:49:32 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"721c87d3-ecd2-400f-acf3-bd411f6eb86f\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "f747f76f-4540-4278-8a5a-fcd039748a17", + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oidc_.well-known_oauth-authorization-server-be7e8572-6195-4608-b385-9344c6b044f6.json b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oidc_.well-known_oauth-authorization-server-be7e8572-6195-4608-b385-9344c6b044f6.json new file mode 100644 index 0000000000..5a5e99cb27 --- /dev/null +++ b/src/test/resources/sqlexecapi/metadataintegrationtests/testgetproceduresandprocedurecolumns/mappings/oidc_.well-known_oauth-authorization-server-be7e8572-6195-4608-b385-9344c6b044f6.json @@ -0,0 +1,33 @@ +{ + "id" : "be7e8572-6195-4608-b385-9344c6b044f6", + "name" : "oidc_.well-known_oauth-authorization-server", + "request" : { + "url" : "/oidc/.well-known/oauth-authorization-server", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"authorization_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/authorize\",\"token_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/token\",\"issuer\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\",\"jwks_uri\":\"https:\\/\\/oregon.staging.cloud.databricks.com\\/oidc\\/jwks.json\",\"scopes_supported\":[\"access-management\",\"alerts\",\"all-apis\",\"apps\",\"authentication\",\"billing\",\"cleanrooms\",\"clusters\",\"command-execution\",\"custom-llms\",\"dashboards\",\"dataclassification\",\"dataquality\",\"email\",\"environments\",\"files\",\"forecasting\",\"genie\",\"global-init-scripts\",\"identity\",\"instance-pools\",\"instance-profiles\",\"jobs\",\"libraries\",\"marketplace\",\"mlflow\",\"model-serving\",\"networking\",\"notifications\",\"offline_access\",\"openid\",\"pipelines\",\"postgres\",\"profile\",\"provisioning\",\"qualitymonitor\",\"query-history\",\"scim\",\"secrets\",\"settings\",\"sharing\",\"sql\",\"tags\",\"unity-catalog\",\"vector-search\",\"workspace\"],\"response_types_supported\":[\"code\",\"id_token\"],\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"grant_types_supported\":[\"client_credentials\",\"authorization_code\",\"refresh_token\"],\"code_challenge_methods_supported\":[\"S256\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"claims_supported\":[\"iss\",\"sub\",\"aud\",\"iat\",\"exp\",\"jti\",\"name\",\"family_name\",\"given_name\",\"preferred_username\"],\"request_uri_parameter_supported\":false}", + "headers" : { + "x-request-id" : "478cb8ea-5845-4df0-b820-c6fd6fc122e6", + "date" : "Fri, 27 Feb 2026 15:49:09 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-popp-fast-path-routing-reason" : "not_eligible", + "x-databricks-popp-shadow-routing-reason" : "spog-domain-checker-false", + "x-databricks-upstream-cluster" : "oregon-staging-h2", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json; charset=UTF-8", + "server-timing" : "request_id;dur=0;desc=\"478cb8ea-5845-4df0-b820-c6fd6fc122e6\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "be7e8572-6195-4608-b385-9344c6b044f6", + "insertionIndex" : 11 +} \ No newline at end of file From c3d80cec8569fe91cb0a14819e27631379164179 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 22:31:36 +0530 Subject: [PATCH 4/9] [PECOBLR-1746] Fix merge conflicts: adapt to MetadataOperationType API 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) --- .../jdbc/common/MetadataOperationType.java | 4 ++- .../sqlexec/DatabricksMetadataSdkClient.java | 6 +++-- .../thrift/DatabricksThriftServiceClient.java | 6 +++-- .../DatabricksMetadataSdkClientTest.java | 26 ++++++++++--------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/common/MetadataOperationType.java b/src/main/java/com/databricks/jdbc/common/MetadataOperationType.java index 861deba5cd..fdadfe5ee3 100644 --- a/src/main/java/com/databricks/jdbc/common/MetadataOperationType.java +++ b/src/main/java/com/databricks/jdbc/common/MetadataOperationType.java @@ -11,7 +11,9 @@ public enum MetadataOperationType { GET_COLUMNS("GetColumns"), GET_FUNCTIONS("GetFunctions"), GET_PRIMARY_KEYS("GetPrimaryKeys"), - GET_CROSS_REFERENCE("GetCrossReference"); + GET_CROSS_REFERENCE("GetCrossReference"), + GET_PROCEDURES("GetProcedures"), + GET_PROCEDURE_COLUMNS("GetProcedureColumns"); private final String headerValue; diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java index 360ea29d61..817fcd75af 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java @@ -255,7 +255,8 @@ public DatabricksResultSet listProcedures( String SQL = CommandConstants.buildProceduresSQL(catalog, schemaNamePattern, procedureNamePattern); LOGGER.debug("SQL command to fetch procedures: {}", SQL); - return metadataResultSetBuilder.getProceduresResult(getResultSet(SQL, session)); + return metadataResultSetBuilder.getProceduresResult( + getResultSet(SQL, session, MetadataOperationType.GET_PROCEDURES)); } @Override @@ -276,7 +277,8 @@ public DatabricksResultSet listProcedureColumns( CommandConstants.buildProcedureColumnsSQL( catalog, schemaNamePattern, procedureNamePattern, columnNamePattern); LOGGER.debug("SQL command to fetch procedure columns: {}", SQL); - return metadataResultSetBuilder.getProcedureColumnsResult(getResultSet(SQL, session)); + return metadataResultSetBuilder.getProcedureColumnsResult( + getResultSet(SQL, session, MetadataOperationType.GET_PROCEDURE_COLUMNS)); } @Override diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java index b88ee0d737..75c3b32c04 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java @@ -625,7 +625,8 @@ public DatabricksResultSet listProcedures( new HashMap<>(), StatementType.METADATA, session, - null)); + null, + MetadataOperationType.GET_PROCEDURES)); } @Override @@ -659,7 +660,8 @@ public DatabricksResultSet listProcedureColumns( new HashMap<>(), StatementType.METADATA, session, - null)); + null, + MetadataOperationType.GET_PROCEDURE_COLUMNS)); } @Override diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java index dba33c6d98..1c79104144 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java @@ -1341,12 +1341,13 @@ void testListProcedures( when(session.getComputeResource()).thenReturn(WAREHOUSE_COMPUTE); DatabricksMetadataSdkClient metadataClient = new DatabricksMetadataSdkClient(mockClient); when(mockClient.executeStatement( - sql, - WAREHOUSE_COMPUTE, - new HashMap(), - StatementType.METADATA, - session, - null)) + eq(sql), + eq(WAREHOUSE_COMPUTE), + any(), + eq(StatementType.METADATA), + eq(session), + any(), + eq(MetadataOperationType.GET_PROCEDURES))) .thenReturn(mockedResultSet); when(mockedResultSet.next()).thenReturn(true, false); when(mockedResultSet.getObject("routine_catalog")).thenReturn("main"); @@ -1445,12 +1446,13 @@ void testListProcedureColumns( when(session.getComputeResource()).thenReturn(WAREHOUSE_COMPUTE); DatabricksMetadataSdkClient metadataClient = new DatabricksMetadataSdkClient(mockClient); when(mockClient.executeStatement( - sql, - WAREHOUSE_COMPUTE, - new HashMap(), - StatementType.METADATA, - session, - null)) + eq(sql), + eq(WAREHOUSE_COMPUTE), + any(), + eq(StatementType.METADATA), + eq(session), + any(), + eq(MetadataOperationType.GET_PROCEDURE_COLUMNS))) .thenReturn(mockedResultSet); when(mockedResultSet.next()).thenReturn(true, false); when(mockedResultSet.getObject("specific_catalog")).thenReturn("main"); From 0ae5090c20eeb38b6abb29461d2a46d718c956a3 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 22:36:54 +0530 Subject: [PATCH 5/9] [PECOBLR-1746] Fix String.format crash when LIKE pattern contains '%' 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) --- .../jdbc/dbclient/impl/common/CommandConstants.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java index d954a84eec..411cccbe1e 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java @@ -54,10 +54,10 @@ public static String buildProceduresSQL( sql.append(" FROM ").append(routinesTable); sql.append(" WHERE ").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { - sql.append(String.format(" AND routine_schema LIKE '%s'", schemaPattern)); + sql.append(" AND routine_schema LIKE '").append(schemaPattern).append("'"); } if (procedureNamePattern != null) { - sql.append(String.format(" AND routine_name LIKE '%s'", procedureNamePattern)); + sql.append(" AND routine_name LIKE '").append(procedureNamePattern).append("'"); } sql.append(" ORDER BY routine_catalog, routine_schema, routine_name"); return sql.toString(); @@ -78,13 +78,13 @@ public static String buildProcedureColumnsSQL( sql.append(" AND p.specific_name = r.specific_name"); sql.append(" WHERE r.").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { - sql.append(String.format(" AND p.specific_schema LIKE '%s'", schemaPattern)); + sql.append(" AND p.specific_schema LIKE '").append(schemaPattern).append("'"); } if (procedureNamePattern != null) { - sql.append(String.format(" AND p.specific_name LIKE '%s'", procedureNamePattern)); + sql.append(" AND p.specific_name LIKE '").append(procedureNamePattern).append("'"); } if (columnNamePattern != null) { - sql.append(String.format(" AND p.parameter_name LIKE '%s'", columnNamePattern)); + sql.append(" AND p.parameter_name LIKE '").append(columnNamePattern).append("'"); } sql.append( " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position"); @@ -92,6 +92,6 @@ public static String buildProcedureColumnsSQL( } private static String getCatalogPrefix(String catalog) { - return (catalog == null) ? "system" : String.format("`%s`", catalog); + return (catalog == null) ? "system" : "`" + catalog + "`"; } } From 112cb9d4abd0801fb13a3dc6062bf781a7a50128 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 22:47:11 +0530 Subject: [PATCH 6/9] retrigger CI From 32e1fcde1a9cc0ad0aee65de9745dcd1234ae3a9 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 22:58:10 +0530 Subject: [PATCH 7/9] [PECOBLR-1746] Fix LOGGER.error crash when exception message contains '%' 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) --- .../databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java index b2c77dd0ad..4fb248dcba 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksDatabaseMetaData.java @@ -906,7 +906,7 @@ public ResultSet getProcedures(String catalog, String schemaPattern, String proc .getDatabricksMetadataClient() .listProcedures(session, catalog, schemaPattern, procedureNamePattern); } catch (Exception e) { - LOGGER.error(e, "Unable to fetch procedures, returning empty result set {}", e); + LOGGER.error(e, "Unable to fetch procedures, returning empty result set"); return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); } } @@ -928,7 +928,7 @@ public ResultSet getProcedureColumns( .listProcedureColumns( session, catalog, schemaPattern, procedureNamePattern, columnNamePattern); } catch (Exception e) { - LOGGER.error(e, "Unable to fetch procedure columns, returning empty result set {}", e); + LOGGER.error(e, "Unable to fetch procedure columns, returning empty result set"); return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); } } From 34760e3ff69b6de927b0ff337cbf75aae9bfd431 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 27 Feb 2026 23:13:06 +0530 Subject: [PATCH 8/9] [PECOBLR-1746] Update MetadataOperationTypeTest for new procedure enum 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) --- .../common/MetadataOperationTypeTest.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/databricks/jdbc/common/MetadataOperationTypeTest.java b/src/test/java/com/databricks/jdbc/common/MetadataOperationTypeTest.java index fe4009db1e..613f1f70af 100644 --- a/src/test/java/com/databricks/jdbc/common/MetadataOperationTypeTest.java +++ b/src/test/java/com/databricks/jdbc/common/MetadataOperationTypeTest.java @@ -13,7 +13,7 @@ public class MetadataOperationTypeTest { @Test void testAllEnumValuesExist() { // Verify all expected enum values exist - assertEquals(7, MetadataOperationType.values().length); + assertEquals(9, MetadataOperationType.values().length); assertNotNull(MetadataOperationType.GET_CATALOGS); assertNotNull(MetadataOperationType.GET_SCHEMAS); assertNotNull(MetadataOperationType.GET_TABLES); @@ -21,6 +21,8 @@ void testAllEnumValuesExist() { assertNotNull(MetadataOperationType.GET_FUNCTIONS); assertNotNull(MetadataOperationType.GET_PRIMARY_KEYS); assertNotNull(MetadataOperationType.GET_CROSS_REFERENCE); + assertNotNull(MetadataOperationType.GET_PROCEDURES); + assertNotNull(MetadataOperationType.GET_PROCEDURE_COLUMNS); } @ParameterizedTest @@ -31,7 +33,9 @@ void testAllEnumValuesExist() { "GET_COLUMNS, GetColumns", "GET_FUNCTIONS, GetFunctions", "GET_PRIMARY_KEYS, GetPrimaryKeys", - "GET_CROSS_REFERENCE, GetCrossReference" + "GET_CROSS_REFERENCE, GetCrossReference", + "GET_PROCEDURES, GetProcedures", + "GET_PROCEDURE_COLUMNS, GetProcedureColumns" }) void testHeaderValues(String enumName, String expectedHeaderValue) { MetadataOperationType operationType = MetadataOperationType.valueOf(enumName); @@ -72,4 +76,15 @@ void testGetPrimaryKeysHeaderValue() { void testGetCrossReferenceHeaderValue() { assertEquals("GetCrossReference", MetadataOperationType.GET_CROSS_REFERENCE.getHeaderValue()); } + + @Test + void testGetProceduresHeaderValue() { + assertEquals("GetProcedures", MetadataOperationType.GET_PROCEDURES.getHeaderValue()); + } + + @Test + void testGetProcedureColumnsHeaderValue() { + assertEquals( + "GetProcedureColumns", MetadataOperationType.GET_PROCEDURE_COLUMNS.getHeaderValue()); + } } From 19c5eca7009e5307029589e024185f03b80d536e Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 13 Mar 2026 18:25:50 +0530 Subject: [PATCH 9/9] [PECOBLR-1746] Use parameterized queries for procedure metadata SQL Replace string concatenation with ? placeholders and ImmutableSqlParameter for all LIKE clause values in buildProceduresSQL and buildProcedureColumnsSQL. This prevents SQL injection via user-provided schema/procedure/column patterns. The build methods now accept a Map that they populate with the parameter bindings. Callers pass this map through to executeStatement instead of an empty HashMap. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../impl/common/CommandConstants.java | 45 ++++++++++++++++--- .../sqlexec/DatabricksMetadataSdkClient.java | 24 +++++++--- .../thrift/DatabricksThriftServiceClient.java | 11 +++-- .../DatabricksMetadataSdkClientTest.java | 16 +++---- 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java index 411cccbe1e..5cefe00d54 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/CommandConstants.java @@ -1,5 +1,9 @@ package com.databricks.jdbc.dbclient.impl.common; +import com.databricks.jdbc.api.impl.ImmutableSqlParameter; +import com.databricks.jdbc.model.core.ColumnInfoTypeName; +import java.util.Map; + public class CommandConstants { public static final String METADATA_STATEMENT_ID = "metadata-statement"; public static final String GET_TABLES_STATEMENT_ID = "gettables-metadata"; @@ -45,29 +49,42 @@ public class CommandConstants { + " p.ordinal_position, p.parameter_default, p.comment"; public static String buildProceduresSQL( - String catalog, String schemaPattern, String procedureNamePattern) { + String catalog, + String schemaPattern, + String procedureNamePattern, + Map params) { String catalogPrefix = getCatalogPrefix(catalog); String routinesTable = catalogPrefix + "." + INFORMATION_SCHEMA_ROUTINES; + int paramIndex = 1; StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(ROUTINES_SELECT_COLUMNS); sql.append(" FROM ").append(routinesTable); sql.append(" WHERE ").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { - sql.append(" AND routine_schema LIKE '").append(schemaPattern).append("'"); + sql.append(" AND routine_schema LIKE ?"); + params.put(paramIndex, buildStringParam(paramIndex, schemaPattern)); + paramIndex++; } if (procedureNamePattern != null) { - sql.append(" AND routine_name LIKE '").append(procedureNamePattern).append("'"); + sql.append(" AND routine_name LIKE ?"); + params.put(paramIndex, buildStringParam(paramIndex, procedureNamePattern)); + paramIndex++; } sql.append(" ORDER BY routine_catalog, routine_schema, routine_name"); return sql.toString(); } public static String buildProcedureColumnsSQL( - String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) { + String catalog, + String schemaPattern, + String procedureNamePattern, + String columnNamePattern, + Map params) { String catalogPrefix = getCatalogPrefix(catalog); String parametersTable = catalogPrefix + "." + INFORMATION_SCHEMA_PARAMETERS + " p"; String routinesTable = catalogPrefix + "." + INFORMATION_SCHEMA_ROUTINES + " r"; + int paramIndex = 1; StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(PARAMETERS_SELECT_COLUMNS); @@ -78,19 +95,33 @@ public static String buildProcedureColumnsSQL( sql.append(" AND p.specific_name = r.specific_name"); sql.append(" WHERE r.").append(PROCEDURE_TYPE_FILTER); if (schemaPattern != null) { - sql.append(" AND p.specific_schema LIKE '").append(schemaPattern).append("'"); + sql.append(" AND p.specific_schema LIKE ?"); + params.put(paramIndex, buildStringParam(paramIndex, schemaPattern)); + paramIndex++; } if (procedureNamePattern != null) { - sql.append(" AND p.specific_name LIKE '").append(procedureNamePattern).append("'"); + sql.append(" AND p.specific_name LIKE ?"); + params.put(paramIndex, buildStringParam(paramIndex, procedureNamePattern)); + paramIndex++; } if (columnNamePattern != null) { - sql.append(" AND p.parameter_name LIKE '").append(columnNamePattern).append("'"); + sql.append(" AND p.parameter_name LIKE ?"); + params.put(paramIndex, buildStringParam(paramIndex, columnNamePattern)); + paramIndex++; } sql.append( " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position"); return sql.toString(); } + private static ImmutableSqlParameter buildStringParam(int index, String value) { + return ImmutableSqlParameter.builder() + .type(ColumnInfoTypeName.STRING) + .value(value) + .cardinal(index) + .build(); + } + private static String getCatalogPrefix(String catalog) { return (catalog == null) ? "system" : "`" + catalog + "`"; } diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java index 817fcd75af..2d5a7c0790 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClient.java @@ -4,6 +4,7 @@ import static com.databricks.jdbc.dbclient.impl.common.CommandConstants.METADATA_STATEMENT_ID; import com.databricks.jdbc.api.impl.DatabricksResultSet; +import com.databricks.jdbc.api.impl.ImmutableSqlParameter; import com.databricks.jdbc.api.internal.IDatabricksSession; import com.databricks.jdbc.common.MetadataOperationType; import com.databricks.jdbc.common.StatementType; @@ -21,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -252,11 +254,13 @@ public DatabricksResultSet listProcedures( } catalog = autoFillCatalog(catalog, currentCatalog); + Map params = new HashMap<>(); String SQL = - CommandConstants.buildProceduresSQL(catalog, schemaNamePattern, procedureNamePattern); + CommandConstants.buildProceduresSQL( + catalog, schemaNamePattern, procedureNamePattern, params); LOGGER.debug("SQL command to fetch procedures: {}", SQL); return metadataResultSetBuilder.getProceduresResult( - getResultSet(SQL, session, MetadataOperationType.GET_PROCEDURES)); + getResultSet(SQL, params, session, MetadataOperationType.GET_PROCEDURES)); } @Override @@ -273,12 +277,13 @@ public DatabricksResultSet listProcedureColumns( } catalog = autoFillCatalog(catalog, currentCatalog); + Map params = new HashMap<>(); String SQL = CommandConstants.buildProcedureColumnsSQL( - catalog, schemaNamePattern, procedureNamePattern, columnNamePattern); + catalog, schemaNamePattern, procedureNamePattern, columnNamePattern, params); LOGGER.debug("SQL command to fetch procedure columns: {}", SQL); return metadataResultSetBuilder.getProcedureColumnsResult( - getResultSet(SQL, session, MetadataOperationType.GET_PROCEDURE_COLUMNS)); + getResultSet(SQL, params, session, MetadataOperationType.GET_PROCEDURE_COLUMNS)); } @Override @@ -449,10 +454,19 @@ private String autoFillCatalog(String catalog, String currentCatalog) { private DatabricksResultSet getResultSet( String SQL, IDatabricksSession session, MetadataOperationType metadataOperationType) throws SQLException { + return getResultSet(SQL, new HashMap<>(), session, metadataOperationType); + } + + private DatabricksResultSet getResultSet( + String SQL, + Map params, + IDatabricksSession session, + MetadataOperationType metadataOperationType) + throws SQLException { return sdkClient.executeStatement( SQL, session.getComputeResource(), - new HashMap<>(), + params, StatementType.METADATA, session, null /* parentStatement */, diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java index 75c3b32c04..31de9afbf5 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java @@ -616,13 +616,15 @@ public DatabricksResultSet listProcedures( return metadataResultSetBuilder.getProceduresResult(new ArrayList<>()); } + Map params = new HashMap<>(); String sql = - CommandConstants.buildProceduresSQL(catalog, schemaNamePattern, procedureNamePattern); + CommandConstants.buildProceduresSQL( + catalog, schemaNamePattern, procedureNamePattern, params); return metadataResultSetBuilder.getProceduresResult( executeStatement( sql, session.getComputeResource(), - new HashMap<>(), + params, StatementType.METADATA, session, null, @@ -650,14 +652,15 @@ public DatabricksResultSet listProcedureColumns( return metadataResultSetBuilder.getProcedureColumnsResult(new ArrayList<>()); } + Map params = new HashMap<>(); String sql = CommandConstants.buildProcedureColumnsSQL( - catalog, schemaNamePattern, procedureNamePattern, columnNamePattern); + catalog, schemaNamePattern, procedureNamePattern, columnNamePattern, params); return metadataResultSetBuilder.getProcedureColumnsResult( executeStatement( sql, session.getComputeResource(), - new HashMap<>(), + params, StatementType.METADATA, session, null, diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java index 1c79104144..918d11b158 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksMetadataSdkClientTest.java @@ -1295,8 +1295,8 @@ private static Stream listProceduresTestParams() { "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + " FROM `catalog1`.information_schema.routines" + " WHERE routine_type = 'PROCEDURE'" - + " AND routine_schema LIKE 'testSchema'" - + " AND routine_name LIKE 'procedurePattern'" + + " AND routine_schema LIKE ?" + + " AND routine_name LIKE ?" + " ORDER BY routine_catalog, routine_schema, routine_name", TEST_CATALOG, TEST_SCHEMA, @@ -1306,7 +1306,7 @@ private static Stream listProceduresTestParams() { "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + " FROM `catalog1`.information_schema.routines" + " WHERE routine_type = 'PROCEDURE'" - + " AND routine_name LIKE 'procedurePattern'" + + " AND routine_name LIKE ?" + " ORDER BY routine_catalog, routine_schema, routine_name", TEST_CATALOG, null, @@ -1316,7 +1316,7 @@ private static Stream listProceduresTestParams() { "SELECT routine_catalog, routine_schema, routine_name, comment, specific_name" + " FROM `catalog1`.information_schema.routines" + " WHERE routine_type = 'PROCEDURE'" - + " AND routine_schema LIKE 'testSchema'" + + " AND routine_schema LIKE ?" + " ORDER BY routine_catalog, routine_schema, routine_name", TEST_CATALOG, TEST_SCHEMA, @@ -1383,9 +1383,9 @@ private static Stream listProcedureColumnsTestParams() { + " AND p.specific_schema = r.specific_schema" + " AND p.specific_name = r.specific_name" + " WHERE r.routine_type = 'PROCEDURE'" - + " AND p.specific_schema LIKE 'testSchema'" - + " AND p.specific_name LIKE 'procedurePattern'" - + " AND p.parameter_name LIKE 'columnPattern'" + + " AND p.specific_schema LIKE ?" + + " AND p.specific_name LIKE ?" + + " AND p.parameter_name LIKE ?" + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position", TEST_CATALOG, TEST_SCHEMA, @@ -1405,7 +1405,7 @@ private static Stream listProcedureColumnsTestParams() { + " AND p.specific_schema = r.specific_schema" + " AND p.specific_name = r.specific_name" + " WHERE r.routine_type = 'PROCEDURE'" - + " AND p.specific_name LIKE 'procedurePattern'" + + " AND p.specific_name LIKE ?" + " ORDER BY p.specific_catalog, p.specific_schema, p.specific_name, p.ordinal_position", TEST_CATALOG, null,